ydn.db
Version:
Javascript database library for IndexedDB, WebDatabase (WebSQL) and WebStorage (localStorage) storage mechanisms supporting version migration, advanced query and transaction workflow.
675 lines (534 loc) • 16.8 kB
JavaScript
var options = {}; // options = {mechanisms: ['websql']};
if (/log/.test(location.hash)) {
var level = /finest/.test(location.hash) ? 'finest' : 'finer';
if (/ui/.test(location.hash)) {
if (ydn.debug && ydn.debug.log) {
var div = document.createElement('div');
document.body.appendChild(div);
ydn.debug.log('ydn.db', level, div);
} else {
console.log('no logging facility');
}
} else {
if (ydn.debug && ydn.debug.log) {
ydn.debug.log('ydn.db', level);
} else {
console.log('no logging facility');
}
}
}
if (/websql/.test(location.hash)) {
options['mechanisms'] = ['websql'];
}
QUnit.config.testTimeout = 2000;
var suite_name = 'workflow';
var reporter = new ydn.testing.Reporter('ydn-db', ydn.db.version);
var db;
var db_name_event = "tck_test_1_1";
var store_inline = "ts"; // in-line key store
var store_outline = "ts2"; // out-of-line key store
var events_schema = {
stores: [
{
name: store_inline,
keyPath: 'id',
dispatchEvents: true,
type: 'NUMERIC'},
{
name: store_outline,
dispatchEvents: true,
type: 'NUMERIC'}
]};
(function () {
var db;
var test_env = {
setup: function () {
var db_name = 'test_tb' + Math.random();
db = new ydn.db.Storage(db_name, events_schema);
},
teardown: function () {
ydn.db.deleteDatabase(db.getName(), db.getType());
db.close();
}
};
var suite_name = 'Promise';
module(suite_name, test_env);
reporter.createTestSuite(suite_name, 'chaining');
asyncTest("use promise chaining", 2, function () {
db.addEventListener('ready', function (e) {
var data = [{id: 1, value: 'a'}, {id: 2, value: 'b'}];
db.put(store_inline, data);
db.get(store_inline, 1).done(function(x) {
equal(x.value, 'a', 'direct result');
x.value = 'A';
return x;
}).done(function(x) {
equal(x.value, 'A', 'transform result');
start();
});
});
});
// FIXME: QUnit do not support testing Promise throwing error
/*
asyncTest("use promise chaining with error", 2, function () {
db.addEventListener('ready', function (e) {
var data = [{id: 1, value: 'a'}, {id: 2, value: 'b'}];
db.put(store_inline, data);
db.get(store_inline, 1).then(function(x) {
equal(x.value, 'a', 'direct result');
throw new Error('switch to error');
}).then(function(x) {
ok(false, 'should not get result');
start();
}, function(e) {
ok(true, 'got error');
start();
});
});
});
*/
asyncTest("multi stores chaining", 3, function () {
db.addEventListener('ready', function (e) {
var data = [{id: 1, value: 'a'}, {id: 2, value: 'b'}];
db.put(store_inline, data);
db.put(store_outline, {value: 'c'}, 3);
var req = db.values(store_inline, null).then(function(x) {
var id = x.reduce(function(p, x) {
return p + x.id;
}, 0);
equal(id, 3, 'sum');
return db.get(store_outline, id);
});
ok(!!req && !!req.then, 'return a promise');
req.then(function(x) {
equal(x.value, 'c', 'result');
start();
});
});
});
})();
(function () {
var db_name_event = 'test_tb' + Math.random();
db_name_event = db_name_event.replace('.', '');
var schema = {stores: [
{
name: store_inline
}
]};
var test_env = {
setup: function () {
},
teardown: function () {
}
};
module("Storage Event", test_env);
reporter.createTestSuite(suite_name, 'storage-event');
asyncTest("connected to a new database and existing", 12, function () {
var db = new ydn.db.Storage(db_name_event, schema);
db.addEventListener('ready', function (e) {
equal(e.name, 'ReadyEvent', 'event name');
equal(e.type, 'ready', 'event type');
equal(e.getVersion(), 1, 'version number');
ok(isNaN(e.getOldVersion()), 'old version number');
db.values(store_inline).always(function () {
db.close();
db = new ydn.db.Storage(db_name_event, schema);
db.addEventListener('ready', function (e) {
equal(e.name, 'ReadyEvent', 'event name');
equal(e.type, 'ready', 'event type');
equal(e.getVersion(), 1, 'version number');
equal(e.getOldVersion(), 1, 'old version number, existing');
db.values(store_inline).always(function () {
db.close();
var tb_name = 'new_tb' + Math.random();
tb_name = tb_name.replace('.', '');
var new_schema = {stores: [
{
name: tb_name
}
]};
db = new ydn.db.Storage(db_name_event, new_schema);
db.addEventListener('ready', function (e) {
equal(e.name, 'ReadyEvent', 'event name');
equal(e.type, 'ready', 'event type');
equal(e.getVersion(), 2, 'updated version number');
equal(e.getOldVersion(), 1, 'old version number, existing db, new schema');
var type = db.getType();
db.values(tb_name).always(function () { // make sure all run.
db.close();
ydn.db.deleteDatabase(db.getName(), type);
start();
});
});
});
});
});
});
});
})();
(function() {
module('RecordEvent Event');
reporter.createTestSuite(suite_name, 'record-event');
asyncTest('created', 6, function() {
var db_name_event = 'test-created-1';
var db = new ydn.db.Storage(db_name_event, events_schema);
var key = Math.ceil(Math.random() * 100000);
var data = {test: 'random value', name: 'name ' + key, id: key};
db.addEventListener('created', function(e) {
//console.log(e);
equal(e.type, 'created', 'type');
equal(e.getKey(), key, 'key');
deepEqual(e.getValue(), data, 'value');
});
db.addEventListener('updated', function(e) {
//console.log(e);
equal(e.name, 'RecordEvent', 'event name');
equal(e.getStoreName(), store_inline, 'store name');
deepEqual(e.getValue(), data, 'value');
var type = db.getType();
db.close();
ydn.db.deleteDatabase(db.getName(), type);
start();
});
db.add(store_inline, data).then(function(x) {
// console.log(x);
}, function(e) {
throw e;
});
db.put(store_inline, data).then(function(x) {
// console.log(x);
}, function(e) {
throw e;
});
});
asyncTest("updated", function () {
expect(10);
var db = new ydn.db.Storage(db_name_event, events_schema);
var key = Math.ceil(Math.random() * 100000);
var data = { test: "random value", name: "name " + key, id: key };
var firedCreatedEvent = false;
db.addEventListener(['created'], function (e) {
//console.log(e);
equal(e.name, 'RecordEvent', 'event name');
equal(e.getStoreName(), store_inline, 'store name');
//equal(e.store_name, store_inline, 'store name');
equal(e.type, 'created', 'type');
equal(e.getKey(), key, 'key');
deepEqual(e.getValue(), data, 'value');
firedCreatedEvent = true;
});
db.addEventListener(['updated'], function (e) {
//console.log(e);
equal(e.name, 'RecordEvent', 'event name');
equal(e.getStoreName(), store_inline, 'store name');
//equal(e.store_name, store_inline, 'store name');
equal(e.type, 'updated', 'type');
equal(e.getKey(), key, 'key');
deepEqual(e.getValue(), data, 'value');
var type = db.getType();
db.close();
ydn.db.deleteDatabase(db.getName(), type);
start();
});
db.add(store_inline, data);
db.put(store_inline, data);
});
})();
(function () {
var test_env = {
setup: function () {
},
teardown: function () {
}
};
module("Store Event", test_env);
reporter.createTestSuite(suite_name, 'store-event');
asyncTest("created", 5, function () {
var db = new ydn.db.Storage(db_name_event, events_schema);
var keys = [Math.ceil(Math.random() * 100000),
Math.ceil(Math.random() * 100000)];
var data = [
{name: "rand key 1", id: keys[0]},
{name: "rand key 2", id: keys[1]}
];
// console.log(data);
db.addEventListener('created', function (e) {
// console.log(e);
equal(e.name, 'StoreEvent', 'event name');
equal(e.getStoreName(), store_inline, 'store name');
//equal(e.store_name, store_inline, 'store name');
equal(e.type, 'created', 'type');
deepEqual(e.getKeys(), keys, 'keys');
deepEqual(e.getValues(), data, 'values');
var type = db.getType();
db.close();
ydn.db.deleteDatabase(db.getName(), type);
start();
});
db.add(store_inline, data).then(function(x) {
// console.log(x);
}, function(e) {
throw e;
});
});
asyncTest("updated", function () {
expect(10);
var db = new ydn.db.Storage(db_name_event, events_schema);
var keys = [Math.ceil(Math.random() * 100000),
Math.ceil(Math.random() * 100000)];
var data = [
{name: "rand key 1", id: keys[0]},
{name: "rand key 2", id: keys[1]}
];
var firedCreatedEvent = false;
db.addEventListener(['created'], function (e) {
//console.log(e);
equal(e.name, 'StoreEvent', 'event name');
equal(e.getStoreName(), store_inline, 'store name');
//equal(e.store_name, store_inline, 'store name');
equal(e.type, 'created', 'type');
deepEqual(e.getKeys(), keys, 'created key');
deepEqual(e.getValues(), data, 'created value');
});
db.addEventListener(['updated'], function (e) {
//console.log(e);
equal(e.name, 'StoreEvent', 'event name');
equal(e.getStoreName(), store_inline, 'store name');
//equal(e.store_name, store_inline, 'store name');
equal(e.type, 'updated', 'type');
deepEqual(e.getKeys(), keys, 'updated key');
deepEqual(e.getValues(), data, 'updated value');
var type = db.getType();
db.close();
ydn.db.deleteDatabase(db.getName(), type);
start();
});
db.add(store_inline, data);
db.put(store_inline, data);
});
})();
(function () {
var test_env = {
setup: function () {
},
teardown: function () {
}
};
module("Run in transaction", test_env);
reporter.createTestSuite(suite_name, 'run');
asyncTest("all requests in one transaction", 4, function () {
var db_name = 'test_run_1';
var db = new ydn.db.Storage(db_name, events_schema);
var req = db.run(function(tdb) {
var key1 = Math.ceil(Math.random() * 100000);
var obj = {test: 'first value', id: key1};
tdb.add(store_inline, obj).always(function(x) {
equal(key1, x, 'add key');
});
var key = Math.ceil(Math.random() * 100000);
var data = { test: "random value", name: "name " + key, id: key };
tdb.put(store_inline, data).always(function(x) {
equal(key, x, 'put key');
});
tdb.values(store_inline, [key1, key]).always(function(x) {
deepEqual([obj, data], x, 'get objects');
});
tdb.clear(store_inline).always(function(x) {
equal(1, x, 'clear');
});
}, null, 'readwrite');
req.always(function() {
start();
var type = db.getType();
db.close();
ydn.db.deleteDatabase(db.getName(), type);
});
});
})();
(function () {
var test_env = {
setup: function () {
},
teardown: function () {
}
};
module("Abort", test_env);
reporter.createTestSuite(suite_name, 'abort');
asyncTest("abort a put operation request method", 7, function () {
var db_name = 'test_abort_1';
var st1 = 's' + Math.random();
var st2 = 's' + Math.random();
var st3 = 's' + Math.random();
var schema = {
stores: [
{
name: st1,
keyPath: 'id',
type: 'NUMERIC'
}, {
name: st2,
keyPath: 'id',
type: 'NUMERIC'
}, {
name: st3,
keyPath: 'id',
type: 'NUMERIC'
}]
};
var obj = {
id: Math.random(),
value: 'msg' + Math.random()
};
var db = new ydn.db.Storage(db_name, schema);
var adb = db.branch('atomic', true); // atomic-serial
var done_count = 0;
var done = function() {
done_count++;
if (done_count >= 3) {
start();
ydn.db.deleteDatabase(db_name, db.getType());
db.close();
}
};
var req1 = db.put(st1, obj).always(function (key) {
equal(obj.id, key, 'aborted store key');
req1.abort();
});
db.get(st1, obj.id).always(function (result) {
equal(undefined, result, 'aborted store result');
done();
});
db.put(st2, obj).always(function (key) {
equal(obj.id, key, 'store 2 key');
});
db.get(st2, obj.id).always(function (result) {
equal(obj.value, result.value, 'store 2 result');
done();
});
var req3 = adb.put(st3, obj).always(function (key) {
equal(obj.id, key, 'atomic store key');
throws (function () { // this is an assertion too
req3.abort();
}, undefined, 'atomic tx cannot be aborted');
});
adb.get(st3, obj.id).always(function (result) {
equal(obj.value, result.value, 'atomic store result');
done();
});
});
asyncTest("abort in run", 4, function () {
var db_name = 'test_abort_2';
var schema = {
stores: [
{
name: 's1',
keyPath: 'id',
type: 'NUMERIC'
}, {
name: 's2',
keyPath: 'id',
type: 'NUMERIC'
}, {
name: 's3',
keyPath: 'id',
type: 'NUMERIC'
}]
};
var obj = {
id: Math.random(),
value: 'msg' + Math.random()
};
var obj2 = {
id: Math.random(),
value: 'msg' + Math.random()
};
var db = new ydn.db.Storage(db_name, schema);
var adb = db.branch('atomic', true); // atomic-serial
var done_count = 0;
var done = function() {
done_count++;
if (done_count >= 2) {
start();
ydn.db.deleteDatabase(db_name, db.getType());
db.close();
}
};
var req = db.run(function (tdb) {
tdb.put('s1', obj).always(function (key) {
tdb.get('s1', obj.id).then(function (result) {
equal(obj.value, result.value, 'store 1 result');
req.abort();
}, function (e) {
ok(false, 'store 1 get not error');
});
});
}, ['s1'], 'readwrite');
req.always(function (x) {
// console.log(x);
db.get('s1', obj.id).always(function (result) {
equal(undefined, result, 'aborted store 1 done result');
done();
});
});
db.run(function (tdb) {
tdb.put('s2', obj).always(function (key) {
tdb.get('s2', obj.id).always(function (result) {
equal(obj.value, result.value, 'store 2 result');
});
});
}, ['s2'], 'readwrite').always(function (t, e) {
db.get('s2', obj.id).always(function (result) {
equal(obj.value, result.value, 'store 2 done result');
done();
});
});
});
})();
(function () {
var test_env = {
setup: function () {
},
teardown: function () {
}
};
module("Error", test_env);
reporter.createTestSuite(suite_name, 'error');
var db_name = 'test_constrained_error' + Math.random();
var schema = {
stores: [
{
name: 'st',
keyPath: 'id',
type: 'NUMERIC'
}]
};
var db = new ydn.db.Storage(db_name, schema, options);
var obj = {id: 1, value: 'v' + Math.random()};
asyncTest("ConstraintError on adding existing key", 2, function () {
db.add('st', obj).always(function (k) {
equal(k, 1, 'key 1 added')
});
db.add('st', obj).then(function (x) {
ok(false, 'should not add again with existing key');
start();
ydn.db.deleteDatabase(db_name, db.getType());
db.close();
}, function (e) {
equal(e.name, 'ConstraintError', 'got ConstraintError');
start();
ydn.db.deleteDatabase(db_name, db.getType());
db.close();
});
});
})();
QUnit.testDone(function(result) {
reporter.addResult(suite_name, result.module,
result.name, result.failed, result.passed, result.duration);
});
QUnit.moduleDone(function(result) {
reporter.endTestSuite(suite_name, result.name,
{passed: result.passed, failed: result.failed});
});
QUnit.done(function() {
reporter.report();
});