@x5e/gink
Version:
an eventually consistent database
418 lines • 18.5 kB
JavaScript
"use strict";
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", { value: true });
const test_utils_1 = require("./test_utils");
const implementation_1 = require("../implementation");
const utils_1 = require("../implementation/utils");
it("push to a queue and peek", async function () {
// set up the objects
for (const store of [
new implementation_1.IndexedDbStore("list-test1", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const queue = await instance.createSequence();
await queue.push("dummy");
const muid = await queue.push("Hello, World!");
(0, utils_1.ensure)(muid.timestamp > 0);
const val = await queue.at(-1);
(0, utils_1.ensure)(val === "Hello, World!");
await store.close();
}
});
it("push and pop", async function () {
// set up the objects
for (const store of [
new implementation_1.IndexedDbStore("list-test2", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const list = await instance.createSequence();
await list.push("A");
await list.push("B");
await list.push("C");
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(), ["A", "B", "C"]));
const popped = await list.pop();
(0, utils_1.ensure)(popped === "C");
/*
for (const entry of await store.getAllEntries()) {
console.log(entry);
}
for (const exit of await store.getAllExits()) {
console.log(exit);
}
*/
const have = JSON.stringify(await list.toArray());
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(), ["A", "B"]), have);
const shifted = await list.shift();
(0, utils_1.ensure)(shifted === "A");
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(), ["B"]));
const dMuid = await list.push("D");
await list.push("E");
const poppedByMuid = await list.pop(dMuid);
(0, utils_1.ensure)(poppedByMuid === "D");
await list.push("F");
await list.push("G");
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(), ["B", "E", "F", "G"]));
const poppedByIndex = await list.pop(2);
(0, utils_1.ensure)(poppedByIndex === "F");
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(), ["B", "E", "G"]));
}
});
it("size and at", async function () {
// set up the objects
for (const store of [
new implementation_1.IndexedDbStore("list-test3", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const list = await instance.createSequence();
await list.push("A");
await list.push("B");
await list.push("C");
const size = await list.size();
(0, utils_1.ensure)(size === 3);
const atEnd = await list.at(-1);
(0, utils_1.ensure)(atEnd === "C");
const beginning = await list.at(0);
(0, utils_1.ensure)(beginning === "A");
const offEnd = await list.at(-4);
(0, utils_1.ensure)(offEnd === undefined);
const nearEnd = await list.at(-3);
(0, utils_1.ensure)(nearEnd === "A");
await list.pop();
const size2 = await list.size();
(0, utils_1.ensure)(size2 === 2);
const second = await list.at(1);
(0, utils_1.ensure)(second === "B");
const third = await list.at(3);
(0, utils_1.ensure)(third === undefined);
}
});
it("entries", async function () {
var _a, e_1, _b, _c;
// set up the objects
for (const store of [
new implementation_1.IndexedDbStore("list-test4", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const list = await instance.createSequence();
await list.push("A");
await list.push("B");
await list.push("C");
const buffer = [];
try {
for (var _d = true, _e = (e_1 = void 0, __asyncValues(list.entries())), _f; _f = await _e.next(), _a = _f.done, !_a;) {
_c = _f.value;
_d = false;
try {
const [muid, contents] = _c;
const val = await list.pop(muid);
(0, utils_1.ensure)(val === contents, `val=${val}, contents=${contents}`);
buffer.push(contents);
}
finally {
_d = true;
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
}
finally { if (e_1) throw e_1.error; }
}
(0, utils_1.ensure)((0, utils_1.matches)(buffer, ["A", "B", "C"]));
}
});
it("list-changeset", async function () {
var _a, e_2, _b, _c;
for (const store of [
new implementation_1.IndexedDbStore("list-test5", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const bundler = new implementation_1.Bundler();
const list = await instance.createSequence(bundler);
await list.push("A", bundler);
await list.push("B", bundler);
await list.push("C", bundler);
await instance.addBundler(bundler);
(0, utils_1.ensure)(bundler.timestamp !== undefined && bundler.timestamp > 0);
(0, utils_1.ensure)(list.address.timestamp === bundler.timestamp);
try {
for (var _d = true, _e = (e_2 = void 0, __asyncValues(list.entries())), _f; _f = await _e.next(), _a = _f.done, !_a;) {
_c = _f.value;
_d = false;
try {
const [muid, _] = _c;
(0, utils_1.ensure)(muid.timestamp === bundler.timestamp);
}
finally {
_d = true;
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
}
finally { if (e_2) throw e_2.error; }
}
const bundler2 = new implementation_1.Bundler();
await list.shift(false, bundler2);
await list.push("D", bundler2);
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(), ["A", "B", "C"]));
await instance.addBundler(bundler2);
const result = await list.toArray();
(0, utils_1.ensure)((0, utils_1.matches)(result, ["B", "C", "D"]));
}
});
it("List.toJSON", async function () {
// set up the objects
for (const store of [
new implementation_1.IndexedDbStore("list-toJSON", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const list = await instance.createSequence();
await list.push("A");
await list.push(true);
await list.push(false);
const subList = await instance.createSequence();
await subList.push(33);
await list.push(subList);
const subDir = await instance.createDirectory();
await subDir.set("cheese", "fries");
await list.push(subDir);
const bytes = new Uint8Array(3);
bytes[0] = 255;
bytes[1] = 94;
bytes[2] = 32;
await list.push(bytes);
const asJson = await list.toJson();
(0, utils_1.ensure)(asJson === `["A",true,false,[33],{"cheese":"fries"},"FF5E20"]`, asJson);
}
});
it("List.asOf", async function () {
// set up the objects
for (const store of [
new implementation_1.IndexedDbStore("list-asOf", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const list = await instance.createSequence();
const time0 = Date.now() * 1000;
await (0, test_utils_1.sleep)(10);
await list.push("A");
await (0, test_utils_1.sleep)(10);
const time1 = Date.now() * 1000;
await (0, test_utils_1.sleep)(10);
await list.push(true);
await (0, test_utils_1.sleep)(10);
const time2 = Date.now() * 1000;
await (0, test_utils_1.sleep)(10);
await list.push(false);
await (0, test_utils_1.sleep)(10);
const time3 = Date.now() * 1000;
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(Infinity, time3), ["A", true, false]));
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(Infinity, time2), ["A", true]));
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(Infinity, time1), ["A"]));
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(Infinity, time0), []));
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(Infinity, -1), ["A", true]));
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(Infinity, -2), ["A"]));
(0, utils_1.ensure)((0, utils_1.matches)(await list.toArray(Infinity, -3), []));
}
});
it("List.clear", async function () {
for (const store of [
new implementation_1.IndexedDbStore("list-clear", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const list = await instance.createSequence();
await list.push("hello");
await list.push("world");
let size = await list.size();
(0, utils_1.ensure)(size === 2);
const clearMuid = await list.clear();
size = await list.size();
(0, utils_1.ensure)(size === 0);
await list.push("goodbye");
size = await list.size();
(0, utils_1.ensure)(size === 1);
size = await list.size(clearMuid.timestamp);
(0, utils_1.ensure)(size === 2);
await list.clear(true);
size = await list.size(clearMuid.timestamp);
(0, utils_1.ensure)(size === 0, `size=${size}`);
}
});
it("List.purge_pop", async function () {
for (const store of [
new implementation_1.IndexedDbStore("list-purgePop", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const seq = await instance.createSequence();
await seq.push("foo");
await seq.push("bar");
const beforeFirstPop = (0, utils_1.generateTimestamp)();
const popped = await seq.pop();
(0, utils_1.ensure)(popped === "bar", `popped=${popped}`);
(0, utils_1.ensure)((0, utils_1.matches)(["foo"], await seq.toArray()), (await seq.toArray()).toString());
const previous = await seq.toArray(Infinity, beforeFirstPop);
(0, utils_1.ensure)((0, utils_1.matches)(["foo", "bar"], previous), "previous=" + previous.toString());
const shifted = await seq.shift(true);
(0, utils_1.ensure)(shifted === "foo");
(0, utils_1.ensure)((0, utils_1.matches)(["bar"], await seq.toArray(Infinity, beforeFirstPop)));
}
});
it("List.move", async function () {
for (const store of [
new implementation_1.IndexedDbStore("list-move", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const seq = await instance.createSequence();
await seq.push("A");
await (0, test_utils_1.sleep)(100);
await seq.push("B");
await (0, test_utils_1.sleep)(100);
const cMuid = await seq.push("C");
await seq.push("D");
await (0, test_utils_1.sleep)(100);
(0, utils_1.ensure)((await seq.toArray()).toString() === "A,B,C,D");
await seq.move(0, -1);
(0, utils_1.ensure)((await seq.toArray()).toString() === "B,C,D,A", (await seq.toArray()).toString());
await seq.move(2, 0);
(0, utils_1.ensure)((await seq.toArray()).toString() === "D,B,C,A", (await seq.toArray()).toString());
await seq.move(cMuid, 1);
(0, utils_1.ensure)((await seq.toArray()).toString() === "D,C,B,A", (await seq.toArray()).toString());
}
});
it("extend", async function () {
for (const store of [
new implementation_1.IndexedDbStore("list-extend", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const seq = await instance.createSequence();
const array = [0, 1, 2, 3, 4, 5, 6];
await seq.extend(array);
(0, utils_1.ensure)((await seq.at(0)) === 0);
(0, utils_1.ensure)((await seq.at(6)) === 6);
const bundler = new implementation_1.Bundler();
const array2 = [7, 8, 9, 10];
await seq.extend(array2, bundler);
await instance.addBundler(bundler);
(0, utils_1.ensure)((await seq.at(7)) === 7);
(0, utils_1.ensure)((await seq.at(10)) === 10);
}
});
it("List.reset", async function () {
for (const store of [
new implementation_1.IndexedDbStore("list-reset", true),
new implementation_1.MemoryStore(true),
]) {
const instance = new implementation_1.Database(store);
await instance.ready;
const seq = await instance.createSequence();
const prop1 = await instance.createProperty();
const prop2 = await instance.createProperty();
await prop1.set(seq, "foo");
await prop2.set(seq, "bar");
const array = [0, 1, 2, 3, 4, 5, 6];
await seq.extend(array);
(0, utils_1.ensure)((await seq.at(0)) === 0);
(0, utils_1.ensure)((await seq.at(6)) === 6);
const afterExtend = (0, utils_1.generateTimestamp)();
const array2 = [7, 8, 9];
await seq.extend(array2);
await prop1.set(seq, "foo2");
await prop2.set(seq, "bar2");
(0, utils_1.ensure)((await seq.size()) === 10);
const afterSecond = (0, utils_1.generateTimestamp)();
await seq.reset({ toTime: afterExtend });
(0, utils_1.ensure)((await seq.size()) === 7);
(0, utils_1.ensure)((await seq.at(0)) === 0);
(0, utils_1.ensure)((await seq.at(6)) === 6);
(0, utils_1.ensure)((await prop1.get(seq)) === "foo");
(0, utils_1.ensure)((await prop2.get(seq)) === "bar");
await seq.reset();
(0, utils_1.ensure)((await seq.size()) === 0);
(0, utils_1.ensure)((await prop1.get(seq)) === undefined);
(0, utils_1.ensure)((await prop2.get(seq)) === undefined);
await seq.reset({ toTime: afterSecond, skipProperties: true });
(0, utils_1.ensure)((await seq.size()) === 10, (await seq.size()).toString());
(0, utils_1.ensure)((await seq.at(0)) === 0);
(0, utils_1.ensure)((await seq.at(9)) === 9);
(0, utils_1.ensure)((await prop1.get(seq)) === undefined);
(0, utils_1.ensure)((await prop2.get(seq)) === undefined);
await seq.push(10);
await seq.push(11);
await seq.move(10, 0);
await seq.reset({ toTime: afterSecond, skipProperties: true });
(0, utils_1.ensure)((await seq.size()) === 10);
(0, utils_1.ensure)((await seq.at(0)) === 0);
(0, utils_1.ensure)((await seq.at(9)) === 9);
(0, utils_1.ensure)((await prop1.get(seq)) === undefined);
(0, utils_1.ensure)((await prop2.get(seq)) === undefined);
await seq.pop(0);
(0, utils_1.ensure)((await seq.size()) === 9);
await seq.reset({ toTime: afterSecond, skipProperties: true });
(0, utils_1.ensure)((await seq.size()) === 10);
(0, utils_1.ensure)((await seq.at(0)) === 0);
(0, utils_1.ensure)((await seq.at(9)) === 9);
// Test recursive reset
await seq.clear();
const box = await instance.createBox();
const dir = await instance.createDirectory();
await box.set(dir);
await dir.set("foo", "bar");
await seq.push(box);
const afterBox = (0, utils_1.generateTimestamp)();
await dir.set("foo", "baz");
await box.set("changed!");
await seq.push(1);
const beforeReset = (0, utils_1.generateTimestamp)();
await seq.reset({ toTime: afterBox, recurse: true });
(0, utils_1.ensure)((await seq.size()) === 1);
(0, utils_1.ensure)((await box.get()) instanceof implementation_1.Directory);
(0, utils_1.ensure)((await dir.get("foo")) === "bar");
// Reset back
await seq.reset({ toTime: beforeReset, recurse: true });
(0, utils_1.ensure)((await seq.size()) === 2);
(0, utils_1.ensure)((await seq.at(1)) === 1);
(0, utils_1.ensure)((await box.get()) === "changed!");
// This will not have been reset, since the directory was not held
// in the box at the time of the reset
(0, utils_1.ensure)((await dir.get("foo")) === "bar");
await seq.shift(); // Remove the box
await seq.reset({ toTime: beforeReset, recurse: true });
(0, utils_1.ensure)((await seq.size()) === 2);
(0, utils_1.ensure)((await seq.at(1)) === 1);
(0, utils_1.ensure)((await box.get()) === "changed!");
}
});
//# sourceMappingURL=Sequence.test.js.map