217 lines
6.0 KiB
TypeScript
217 lines
6.0 KiB
TypeScript
import { removeDirectiveBody, SpaceSync, SyncStatusItem } from "./sync.ts";
|
|
import { DiskSpacePrimitives } from "./disk_space_primitives.ts";
|
|
import { assertEquals } from "../../test_deps.ts";
|
|
|
|
Deno.test("Test store", async () => {
|
|
const primaryPath = await Deno.makeTempDir();
|
|
const secondaryPath = await Deno.makeTempDir();
|
|
console.log("Primary", primaryPath);
|
|
console.log("Secondary", secondaryPath);
|
|
const primary = new DiskSpacePrimitives(primaryPath);
|
|
const secondary = new DiskSpacePrimitives(secondaryPath);
|
|
const snapshot = new Map<string, SyncStatusItem>();
|
|
const sync = new SpaceSync(primary, secondary, {
|
|
conflictResolver: SpaceSync.primaryConflictResolver,
|
|
});
|
|
|
|
// Write one page to primary
|
|
await primary.writeFile("index", stringToBytes("Hello"));
|
|
assertEquals((await secondary.fetchFileList()).length, 0);
|
|
console.log("Initial sync ops", await doSync());
|
|
|
|
assertEquals((await secondary.fetchFileList()).length, 1);
|
|
assertEquals(
|
|
(await secondary.readFile("index")).data,
|
|
stringToBytes("Hello"),
|
|
);
|
|
|
|
// Should be a no-op
|
|
assertEquals(await doSync(), 0);
|
|
|
|
// Now let's make a change on the secondary
|
|
await secondary.writeFile("index", stringToBytes("Hello!!"));
|
|
await secondary.writeFile("test", stringToBytes("Test page"));
|
|
|
|
// And sync it
|
|
await doSync();
|
|
|
|
assertEquals((await primary.fetchFileList()).length, 2);
|
|
assertEquals((await secondary.fetchFileList()).length, 2);
|
|
|
|
assertEquals(
|
|
(await primary.readFile("index")).data,
|
|
stringToBytes("Hello!!"),
|
|
);
|
|
|
|
// Let's make some random edits on both ends
|
|
await primary.writeFile("index", stringToBytes("1"));
|
|
await primary.writeFile("index2", stringToBytes("2"));
|
|
await secondary.writeFile("index3", stringToBytes("3"));
|
|
await secondary.writeFile("index4", stringToBytes("4"));
|
|
await doSync();
|
|
|
|
assertEquals((await primary.fetchFileList()).length, 5);
|
|
assertEquals((await secondary.fetchFileList()).length, 5);
|
|
|
|
assertEquals(await doSync(), 0);
|
|
|
|
console.log("Deleting pages");
|
|
// Delete some pages
|
|
await primary.deleteFile("index");
|
|
await primary.deleteFile("index3");
|
|
|
|
await doSync();
|
|
|
|
assertEquals((await primary.fetchFileList()).length, 3);
|
|
assertEquals((await secondary.fetchFileList()).length, 3);
|
|
|
|
// No-op
|
|
assertEquals(await doSync(), 0);
|
|
|
|
await secondary.deleteFile("index4");
|
|
await primary.deleteFile("index2");
|
|
|
|
await doSync();
|
|
|
|
// Just "test" left
|
|
assertEquals((await primary.fetchFileList()).length, 1);
|
|
assertEquals((await secondary.fetchFileList()).length, 1);
|
|
|
|
// No-op
|
|
assertEquals(await doSync(), 0);
|
|
|
|
await secondary.writeFile("index", stringToBytes("I'm back"));
|
|
|
|
await doSync();
|
|
|
|
assertEquals(
|
|
(await primary.readFile("index")).data,
|
|
stringToBytes("I'm back"),
|
|
);
|
|
|
|
// Cause a conflict
|
|
console.log("Introducing a conflict now");
|
|
await primary.writeFile("index", stringToBytes("Hello 1"));
|
|
await secondary.writeFile("index", stringToBytes("Hello 2"));
|
|
|
|
await doSync();
|
|
|
|
// Sync conflicting copy back
|
|
await doSync();
|
|
|
|
// Verify that primary won
|
|
assertEquals(
|
|
(await primary.readFile("index")).data,
|
|
stringToBytes("Hello 1"),
|
|
);
|
|
assertEquals(
|
|
(await secondary.readFile("index")).data,
|
|
stringToBytes("Hello 1"),
|
|
);
|
|
|
|
// test + index + index.conflicting copy
|
|
assertEquals((await primary.fetchFileList()).length, 3);
|
|
assertEquals((await secondary.fetchFileList()).length, 3);
|
|
|
|
// Introducing a fake conflict (same content, so not really conflicting)
|
|
await primary.writeFile("index", stringToBytes("Hello 1"));
|
|
await secondary.writeFile("index", stringToBytes("Hello 1"));
|
|
|
|
// And two more files with different bodies, but only within a query directive — shouldn't conflict
|
|
await primary.writeFile(
|
|
"index.md",
|
|
stringToBytes(
|
|
"Hello\n<!-- #query page -->\nHello 1\n<!-- /query -->",
|
|
),
|
|
);
|
|
await secondary.writeFile(
|
|
"index.md",
|
|
stringToBytes("Hello\n<!-- #query page -->\nHello 2\n<!-- /query -->"),
|
|
);
|
|
|
|
await doSync();
|
|
await doSync();
|
|
|
|
// test + index + index.md + previous index.conflicting copy but nothing more
|
|
assertEquals((await primary.fetchFileList()).length, 4);
|
|
|
|
console.log("Bringing a third device in the mix");
|
|
|
|
const ternaryPath = await Deno.makeTempDir();
|
|
|
|
console.log("Ternary", ternaryPath);
|
|
|
|
const ternary = new DiskSpacePrimitives(ternaryPath);
|
|
const sync2 = new SpaceSync(
|
|
secondary,
|
|
ternary,
|
|
{
|
|
conflictResolver: SpaceSync.primaryConflictResolver,
|
|
},
|
|
);
|
|
const snapshot2 = new Map<string, SyncStatusItem>();
|
|
console.log(
|
|
"N ops",
|
|
await sync2.syncFiles(snapshot2),
|
|
);
|
|
await sleep(2);
|
|
assertEquals(await sync2.syncFiles(snapshot2), 0);
|
|
|
|
// I had to look up what follows ternary (https://english.stackexchange.com/questions/25116/what-follows-next-in-the-sequence-unary-binary-ternary)
|
|
const quaternaryPath = await Deno.makeTempDir();
|
|
const quaternary = new DiskSpacePrimitives(quaternaryPath);
|
|
const sync3 = new SpaceSync(
|
|
secondary,
|
|
quaternary,
|
|
{
|
|
isSyncCandidate: (path) => !path.startsWith("index"),
|
|
conflictResolver: SpaceSync.primaryConflictResolver,
|
|
},
|
|
);
|
|
const selectingOps = await sync3.syncFiles(new Map());
|
|
|
|
assertEquals(selectingOps, 1);
|
|
|
|
await Deno.remove(primaryPath, { recursive: true });
|
|
await Deno.remove(secondaryPath, { recursive: true });
|
|
await Deno.remove(ternaryPath, { recursive: true });
|
|
await Deno.remove(quaternaryPath, { recursive: true });
|
|
|
|
async function doSync() {
|
|
await sleep();
|
|
const r = await sync.syncFiles(snapshot);
|
|
await sleep();
|
|
return r;
|
|
}
|
|
});
|
|
|
|
function sleep(ms = 10): Promise<void> {
|
|
return new Promise((resolve) => {
|
|
setTimeout(resolve, ms);
|
|
});
|
|
}
|
|
|
|
Deno.test("Remove directive bodies", () => {
|
|
assertEquals(
|
|
removeDirectiveBody(`<!-- #query page -->
|
|
This is a body
|
|
bla bla bla
|
|
<!-- /query -->
|
|
Hello
|
|
<!-- #include [[test]] -->
|
|
This is a body
|
|
<!-- /include -->
|
|
`),
|
|
`<!-- #query page -->
|
|
<!-- /query -->
|
|
Hello
|
|
<!-- #include [[test]] -->
|
|
<!-- /include -->
|
|
`,
|
|
);
|
|
});
|
|
|
|
function stringToBytes(s: string): Uint8Array {
|
|
return new TextEncoder().encode(s);
|
|
}
|