From 15ba961815d485bc6f4704b9a85bfec714f2fce0 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Fri, 8 Mar 2024 16:45:09 +0300 Subject: [PATCH 01/28] test: NodeFSStorageAdapter --- .../package.json | 3 +- .../test/NodeFSStorageAdapter.test.ts | 116 ++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts diff --git a/packages/automerge-repo-storage-nodefs/package.json b/packages/automerge-repo-storage-nodefs/package.json index e5358e139..8df7536e5 100644 --- a/packages/automerge-repo-storage-nodefs/package.json +++ b/packages/automerge-repo-storage-nodefs/package.json @@ -9,7 +9,8 @@ "main": "dist/index.js", "scripts": { "build": "tsc", - "watch": "npm-watch build" + "watch": "npm-watch build", + "test": "vitest" }, "dependencies": { "@automerge/automerge-repo": "workspace:*", diff --git a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts new file mode 100644 index 000000000..16ddb6820 --- /dev/null +++ b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts @@ -0,0 +1,116 @@ +import * as fs from "node:fs" +import * as os from "node:os" +import * as path from "node:path" +import * as crypto from "node:crypto" + +import { afterAll, beforeEach, describe, expect, it } from "vitest" + +import { NodeFSStorageAdapter } from "../src" + +function cleanDir(dir: string) { + try { + fs.rmSync(dir, { force: true, recursive: true }) + } catch (e) {} +} + +describe('NodeFSStorageAdapter', () => { + let baseDirectory: string; + let adapter: NodeFSStorageAdapter; + + beforeEach(async () => { + baseDirectory = path.join(os.tmpdir(), crypto.randomUUID()) + adapter = new NodeFSStorageAdapter(baseDirectory) + }) + + afterAll(async() => { + cleanDir(baseDirectory); + }) + + describe('getFilePath', () => { + it('should compose keys correctly', () => { + // @ts-ignore + const actual = adapter.getFilePath(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]) + expect(actual).toStrictEqual(path.join(baseDirectory) + '/3x/uJ5sVKdBaYS6uGgGJH1cGhBLiC/sync-state/d99d4820-fb1f-4f3a-a40f-d5997b2012cf') + }) + }) + + describe('load', () => { + it('should return undefined if there is no data', async () => { + expect( + await adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]) + ).toStrictEqual(undefined) + }) + }) + + describe('save and load', () => { + it('should be possible to save and load', async () => { + await adapter.save(["storage-adapter-id"], new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')); + const actual = await adapter.load(["storage-adapter-id"]); + expect(actual).toStrictEqual(new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')) + }) + + it('should work with composed keys', async () => { + await adapter.save(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) + const actual = await adapter.load(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"]); + expect(actual).toStrictEqual(new Uint8Array([0, 1, 127, 99, 154, 235 ])) + }) + }) + + describe('loadRange', () => { + it('should return empty array if there is no data', async () => { + expect( + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual([]) + }) + }) + + describe('save and loadRange', () => { + it.fails('should return all the data that is present', async () => { + await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); + await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); + await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])); + + expect( + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual([ + {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"]}, + {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} + ]) + + expect( + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) + ).toStrictEqual([ + {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} + ]) + }) + }) + + describe('save and remove', () => { + it('should be no data', async () => { + await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); + await adapter.remove(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]); + + expect( + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual([]) + expect( + await adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]) + ).toStrictEqual(undefined) + }) + }) + + describe('save and save', () => { + it.fails('should override the data', async () => { + await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); + await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); + + expect( + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) + ).toStrictEqual([ + {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + ]) + }) + }) +}) From 97e6415f957a0c8a2238421120cfdf6a26db8623 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Fri, 8 Mar 2024 16:46:32 +0300 Subject: [PATCH 02/28] fix: after calls to the save method three times the loadRange method should return all the data that is present and not more --- .../automerge-repo-storage-nodefs/src/index.ts | 14 +++++--------- .../test/NodeFSStorageAdapter.test.ts | 4 ++-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/automerge-repo-storage-nodefs/src/index.ts b/packages/automerge-repo-storage-nodefs/src/index.ts index 4a9079a24..fba63827d 100644 --- a/packages/automerge-repo-storage-nodefs/src/index.ts +++ b/packages/automerge-repo-storage-nodefs/src/index.ts @@ -112,20 +112,16 @@ export class NodeFSStorageAdapter implements StorageAdapterInterface { } private getFilePath(keyArray: string[]): string { - const [firstKey, ...remainingKeys] = keyArray - const firstKeyDir = path.join( - this.baseDirectory, - firstKey.slice(0, 2), - firstKey.slice(2) - ) - - return path.join(firstKeyDir, ...remainingKeys) + return path.join(this.baseDirectory, getKey(keyArray)) } } // HELPERS -const getKey = (key: StorageKey): string => path.join(...key) +const getKey = (keyArray: StorageKey): string => { + const [firstKey, ...remainingKeys] = keyArray + return path.join(firstKey.slice(0, 2), firstKey.slice(2), ...remainingKeys) +} /** returns all files in a directory, recursively */ const walkdir = async (dirPath: string): Promise => { diff --git a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts index 16ddb6820..f1bf136d9 100644 --- a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts +++ b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts @@ -65,7 +65,7 @@ describe('NodeFSStorageAdapter', () => { }) describe('save and loadRange', () => { - it.fails('should return all the data that is present', async () => { + it('should return all the data that is present', async () => { await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])); @@ -102,7 +102,7 @@ describe('NodeFSStorageAdapter', () => { }) describe('save and save', () => { - it.fails('should override the data', async () => { + it('should override the data', async () => { await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); From 98b7bba9384fba56f5e94b80ec8c9bd06a591672 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Fri, 8 Mar 2024 17:09:33 +0300 Subject: [PATCH 03/28] refactor: make the tests for a storage reusable --- .../test/NodeFSStorageAdapter.test.ts | 81 ++++++++++--------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts index f1bf136d9..9b53fcd39 100644 --- a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts +++ b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts @@ -13,45 +13,26 @@ function cleanDir(dir: string) { } catch (e) {} } -describe('NodeFSStorageAdapter', () => { - let baseDirectory: string; - let adapter: NodeFSStorageAdapter; - - beforeEach(async () => { - baseDirectory = path.join(os.tmpdir(), crypto.randomUUID()) - adapter = new NodeFSStorageAdapter(baseDirectory) - }) - - afterAll(async() => { - cleanDir(baseDirectory); - }) - - describe('getFilePath', () => { - it('should compose keys correctly', () => { - // @ts-ignore - const actual = adapter.getFilePath(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]) - expect(actual).toStrictEqual(path.join(baseDirectory) + '/3x/uJ5sVKdBaYS6uGgGJH1cGhBLiC/sync-state/d99d4820-fb1f-4f3a-a40f-d5997b2012cf') - }) - }) +function runStorageTests(sut: {adapter: NodeFSStorageAdapter}) { describe('load', () => { it('should return undefined if there is no data', async () => { expect( - await adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]) + await sut.adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]) ).toStrictEqual(undefined) }) }) describe('save and load', () => { it('should be possible to save and load', async () => { - await adapter.save(["storage-adapter-id"], new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')); - const actual = await adapter.load(["storage-adapter-id"]); + await sut.adapter.save(["storage-adapter-id"], new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')); + const actual = await sut.adapter.load(["storage-adapter-id"]); expect(actual).toStrictEqual(new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')) }) it('should work with composed keys', async () => { - await adapter.save(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) - const actual = await adapter.load(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"]); + await sut.adapter.save(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) + const actual = await sut.adapter.load(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"]); expect(actual).toStrictEqual(new Uint8Array([0, 1, 127, 99, 154, 235 ])) }) }) @@ -59,19 +40,19 @@ describe('NodeFSStorageAdapter', () => { describe('loadRange', () => { it('should return empty array if there is no data', async () => { expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) ).toStrictEqual([]) }) }) describe('save and loadRange', () => { it('should return all the data that is present', async () => { - await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); - await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); - await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])); expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) ).toStrictEqual([ {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"]}, @@ -79,7 +60,7 @@ describe('NodeFSStorageAdapter', () => { ]) expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) ).toStrictEqual([ {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} @@ -89,28 +70,52 @@ describe('NodeFSStorageAdapter', () => { describe('save and remove', () => { it('should be no data', async () => { - await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); - await adapter.remove(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); + await sut.adapter.remove(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]); expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) ).toStrictEqual([]) expect( - await adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]) + await sut.adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]) ).toStrictEqual(undefined) }) }) describe('save and save', () => { it('should override the data', async () => { - await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); - await adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) ).toStrictEqual([ {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, ]) }) }) +} + +describe('NodeFSStorageAdapter', () => { + let baseDirectory: string = path.join(os.tmpdir(), crypto.randomUUID()); + let sut: {adapter: NodeFSStorageAdapter} = {adapter: new NodeFSStorageAdapter(baseDirectory)} + + beforeEach(async () => { + baseDirectory = path.join(os.tmpdir(), crypto.randomUUID()) + sut.adapter = new NodeFSStorageAdapter(baseDirectory) + }) + + afterAll(async() => { + cleanDir(baseDirectory); + }) + + describe('getFilePath', () => { + it('should compose keys correctly', () => { + // @ts-ignore + const actual = sut.adapter.getFilePath(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]) + expect(actual).toStrictEqual(path.join(baseDirectory) + '/3x/uJ5sVKdBaYS6uGgGJH1cGhBLiC/sync-state/d99d4820-fb1f-4f3a-a40f-d5997b2012cf') + }) + }) + + runStorageTests(sut); }) From 16771e1d823fa33271a88ea28a7df7bc35338117 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Fri, 8 Mar 2024 18:06:59 +0300 Subject: [PATCH 04/28] refactor: move runStorageAdapterTests to separate file --- .../test/NodeFSStorageAdapter.test.ts | 88 +------------------ .../helpers/tests/storage-adapter-tests.ts | 86 ++++++++++++++++++ 2 files changed, 88 insertions(+), 86 deletions(-) create mode 100644 packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts diff --git a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts index 9b53fcd39..ad69e05f3 100644 --- a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts +++ b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts @@ -2,9 +2,8 @@ import * as fs from "node:fs" import * as os from "node:os" import * as path from "node:path" import * as crypto from "node:crypto" - import { afterAll, beforeEach, describe, expect, it } from "vitest" - +import { runStorageAdapterTests } from "@automerge/automerge-repo/src/helpers/tests/storage-adapter-tests" import { NodeFSStorageAdapter } from "../src" function cleanDir(dir: string) { @@ -13,89 +12,6 @@ function cleanDir(dir: string) { } catch (e) {} } -function runStorageTests(sut: {adapter: NodeFSStorageAdapter}) { - - describe('load', () => { - it('should return undefined if there is no data', async () => { - expect( - await sut.adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]) - ).toStrictEqual(undefined) - }) - }) - - describe('save and load', () => { - it('should be possible to save and load', async () => { - await sut.adapter.save(["storage-adapter-id"], new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')); - const actual = await sut.adapter.load(["storage-adapter-id"]); - expect(actual).toStrictEqual(new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')) - }) - - it('should work with composed keys', async () => { - await sut.adapter.save(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) - const actual = await sut.adapter.load(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"]); - expect(actual).toStrictEqual(new Uint8Array([0, 1, 127, 99, 154, 235 ])) - }) - }) - - describe('loadRange', () => { - it('should return empty array if there is no data', async () => { - expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual([]) - }) - }) - - describe('save and loadRange', () => { - it('should return all the data that is present', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])); - - expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual([ - {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, - {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"]}, - {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} - ]) - - expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) - ).toStrictEqual([ - {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, - {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} - ]) - }) - }) - - describe('save and remove', () => { - it('should be no data', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); - await sut.adapter.remove(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]); - - expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual([]) - expect( - await sut.adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]) - ).toStrictEqual(undefined) - }) - }) - - describe('save and save', () => { - it('should override the data', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); - - expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) - ).toStrictEqual([ - {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, - ]) - }) - }) -} - describe('NodeFSStorageAdapter', () => { let baseDirectory: string = path.join(os.tmpdir(), crypto.randomUUID()); let sut: {adapter: NodeFSStorageAdapter} = {adapter: new NodeFSStorageAdapter(baseDirectory)} @@ -117,5 +33,5 @@ describe('NodeFSStorageAdapter', () => { }) }) - runStorageTests(sut); + runStorageAdapterTests(sut); }) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts new file mode 100644 index 000000000..cd0efe8e1 --- /dev/null +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -0,0 +1,86 @@ +import { describe, expect, it } from "vitest" + +import type { StorageAdapterInterface } from "../../storage/StorageAdapterInterface.js" + +export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) { + + describe('load', () => { + it('should return undefined if there is no data', async () => { + expect( + await sut.adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]) + ).toStrictEqual(undefined) + }) + }) + + describe('save and load', () => { + it('should be possible to save and load', async () => { + await sut.adapter.save(["storage-adapter-id"], new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')); + const actual = await sut.adapter.load(["storage-adapter-id"]); + expect(actual).toStrictEqual(new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')) + }) + + it('should work with composed keys', async () => { + await sut.adapter.save(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) + const actual = await sut.adapter.load(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"]); + expect(actual).toStrictEqual(new Uint8Array([0, 1, 127, 99, 154, 235 ])) + }) + }) + + describe('loadRange', () => { + it('should return empty array if there is no data', async () => { + expect( + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual([]) + }) + }) + + describe('save and loadRange', () => { + it('should return all the data that is present', async () => { + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])); + + expect( + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual([ + {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"]}, + {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} + ]) + + expect( + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) + ).toStrictEqual([ + {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} + ]) + }) + }) + + describe('save and remove', () => { + it('should be no data', async () => { + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); + await sut.adapter.remove(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]); + + expect( + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual([]) + expect( + await sut.adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]) + ).toStrictEqual(undefined) + }) + }) + + describe('save and save', () => { + it('should override the data', async () => { + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); + + expect( + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) + ).toStrictEqual([ + {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + ]) + }) + }) +} From 032739bbb25c40935f3ff0223827f404d4c4458f Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Fri, 8 Mar 2024 18:10:36 +0300 Subject: [PATCH 05/28] style: remove all semicolons --- .../helpers/tests/storage-adapter-tests.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index cd0efe8e1..73875cd46 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -14,14 +14,14 @@ export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) describe('save and load', () => { it('should be possible to save and load', async () => { - await sut.adapter.save(["storage-adapter-id"], new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')); - const actual = await sut.adapter.load(["storage-adapter-id"]); + await sut.adapter.save(["storage-adapter-id"], new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')) + const actual = await sut.adapter.load(["storage-adapter-id"]) expect(actual).toStrictEqual(new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')) }) it('should work with composed keys', async () => { await sut.adapter.save(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) - const actual = await sut.adapter.load(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"]); + const actual = await sut.adapter.load(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"]) expect(actual).toStrictEqual(new Uint8Array([0, 1, 127, 99, 154, 235 ])) }) }) @@ -36,9 +36,9 @@ export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) describe('save and loadRange', () => { it('should return all the data that is present', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])) + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])) expect( await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) @@ -59,8 +59,8 @@ export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) describe('save and remove', () => { it('should be no data', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); - await sut.adapter.remove(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) + await sut.adapter.remove(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]) expect( await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) @@ -73,8 +73,8 @@ export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) describe('save and save', () => { it('should override the data', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])); - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])); + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])) expect( await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) From fa3d5721f55826fc54c99d1b23d2aed020c7b094 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Fri, 8 Mar 2024 19:12:27 +0300 Subject: [PATCH 06/28] fix: keys from loadRange should be returned unmodified --- packages/automerge-repo-storage-nodefs/src/index.ts | 12 ++++++++---- .../src/helpers/tests/storage-adapter-tests.ts | 12 ++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/automerge-repo-storage-nodefs/src/index.ts b/packages/automerge-repo-storage-nodefs/src/index.ts index fba63827d..54341498e 100644 --- a/packages/automerge-repo-storage-nodefs/src/index.ts +++ b/packages/automerge-repo-storage-nodefs/src/index.ts @@ -81,7 +81,7 @@ export class NodeFSStorageAdapter implements StorageAdapterInterface { ) // Combine and deduplicate the lists of keys - const allKeys = [...new Set([...cachedKeys, ...diskKeys])] + const allKeys = [...new Set([...cachedKeys, ...diskKeysToCachedKeys(diskKeys)])] // Load all files const chunks = await Promise.all( @@ -112,15 +112,19 @@ export class NodeFSStorageAdapter implements StorageAdapterInterface { } private getFilePath(keyArray: string[]): string { - return path.join(this.baseDirectory, getKey(keyArray)) + const [firstKey, ...remainingKeys] = keyArray + return path.join(this.baseDirectory, firstKey.slice(0, 2), firstKey.slice(2), ...remainingKeys) } } // HELPERS const getKey = (keyArray: StorageKey): string => { - const [firstKey, ...remainingKeys] = keyArray - return path.join(firstKey.slice(0, 2), firstKey.slice(2), ...remainingKeys) + return keyArray.join(path.posix.sep) +} + +const diskKeysToCachedKeys = (diskKeys: string[]): string[] => { + return diskKeys.map(k => k.slice(0, 2) + k.slice(3)) } /** returns all files in a directory, recursively */ diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index 73875cd46..8b262c670 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -43,16 +43,16 @@ export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) expect( await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) ).toStrictEqual([ - {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, - {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"]}, - {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} + {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"]}, + {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} ]) expect( await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) ).toStrictEqual([ - {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, - {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} + {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} ]) }) }) @@ -79,7 +79,7 @@ export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) expect( await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) ).toStrictEqual([ - {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3x","uJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, ]) }) }) From 048a3927ead74c4f900c89daaefaebb6506ef5b5 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Fri, 8 Mar 2024 19:14:34 +0300 Subject: [PATCH 07/28] test: DummyStorageAdapter --- .../automerge-repo/test/DummyStorageAdapter.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 packages/automerge-repo/test/DummyStorageAdapter.test.ts diff --git a/packages/automerge-repo/test/DummyStorageAdapter.test.ts b/packages/automerge-repo/test/DummyStorageAdapter.test.ts new file mode 100644 index 000000000..1df1288e8 --- /dev/null +++ b/packages/automerge-repo/test/DummyStorageAdapter.test.ts @@ -0,0 +1,13 @@ +import { beforeEach, describe, expect, it } from "vitest" +import { DummyStorageAdapter } from "./helpers/DummyStorageAdapter.js" +import { runStorageAdapterTests } from "../src/helpers/tests/storage-adapter-tests.js" + +describe('DummyStorageAdapter', () => { + let sut: {adapter: DummyStorageAdapter} = { adapter: new DummyStorageAdapter() } + + beforeEach(async () => { + sut.adapter = new DummyStorageAdapter() + }) + + runStorageAdapterTests(sut); +}) From 0fb2cfc52a98f311b6896ca104303dfdd2e82905 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Fri, 8 Mar 2024 21:13:57 +0300 Subject: [PATCH 08/28] test: use node env to run NodeFSStorageAdapter adapter --- packages/automerge-repo-storage-nodefs/vitest.config.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 packages/automerge-repo-storage-nodefs/vitest.config.ts diff --git a/packages/automerge-repo-storage-nodefs/vitest.config.ts b/packages/automerge-repo-storage-nodefs/vitest.config.ts new file mode 100644 index 000000000..904b29921 --- /dev/null +++ b/packages/automerge-repo-storage-nodefs/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig, mergeConfig } from "vitest/config" +import rootConfig from '../../vitest.config' + +export default mergeConfig(rootConfig, defineConfig({ + test: { + environment: "node" + }, +})) From 3b1f1610db2b5784ebbd6a27ffbf632a198c6004 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Fri, 8 Mar 2024 23:16:54 +0300 Subject: [PATCH 09/28] chore: remove unused imports --- packages/automerge-repo/test/DummyStorageAdapter.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/automerge-repo/test/DummyStorageAdapter.test.ts b/packages/automerge-repo/test/DummyStorageAdapter.test.ts index 1df1288e8..064994033 100644 --- a/packages/automerge-repo/test/DummyStorageAdapter.test.ts +++ b/packages/automerge-repo/test/DummyStorageAdapter.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it } from "vitest" +import { beforeEach, describe } from "vitest" import { DummyStorageAdapter } from "./helpers/DummyStorageAdapter.js" import { runStorageAdapterTests } from "../src/helpers/tests/storage-adapter-tests.js" From 178b84eed29f845b7ec08aaeb447d5f9fe97003e Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Fri, 8 Mar 2024 23:44:31 +0300 Subject: [PATCH 10/28] refactor: reduce number of loops --- .../automerge-repo-storage-nodefs/src/index.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/automerge-repo-storage-nodefs/src/index.ts b/packages/automerge-repo-storage-nodefs/src/index.ts index 54341498e..65eca4bde 100644 --- a/packages/automerge-repo-storage-nodefs/src/index.ts +++ b/packages/automerge-repo-storage-nodefs/src/index.ts @@ -76,12 +76,14 @@ export class NodeFSStorageAdapter implements StorageAdapterInterface { // The "keys" in the cache don't include the baseDirectory. // We want to de-dupe with the cached keys so we'll use getKey to normalize them. - const diskKeys: string[] = diskFiles.map((fileName: string) => - getKey([path.relative(this.baseDirectory, fileName)]) - ) + const diskKeys: string[] = diskFiles + .map((fileName: string) => { + const k = getKey([path.relative(this.baseDirectory, fileName)]) + return k.slice(0, 2) + k.slice(3) + }) // Combine and deduplicate the lists of keys - const allKeys = [...new Set([...cachedKeys, ...diskKeysToCachedKeys(diskKeys)])] + const allKeys = [...new Set([...cachedKeys, ...diskKeys])] // Load all files const chunks = await Promise.all( @@ -123,10 +125,6 @@ const getKey = (keyArray: StorageKey): string => { return keyArray.join(path.posix.sep) } -const diskKeysToCachedKeys = (diskKeys: string[]): string[] => { - return diskKeys.map(k => k.slice(0, 2) + k.slice(3)) -} - /** returns all files in a directory, recursively */ const walkdir = async (dirPath: string): Promise => { try { From 434c680d0f4056cfac460cad4b6d7e8968f4365d Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Sat, 9 Mar 2024 00:55:43 +0300 Subject: [PATCH 11/28] fix: fs keys should be the same as fs keys on windows also --- packages/automerge-repo-storage-nodefs/src/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/automerge-repo-storage-nodefs/src/index.ts b/packages/automerge-repo-storage-nodefs/src/index.ts index 65eca4bde..00fa34de1 100644 --- a/packages/automerge-repo-storage-nodefs/src/index.ts +++ b/packages/automerge-repo-storage-nodefs/src/index.ts @@ -121,9 +121,7 @@ export class NodeFSStorageAdapter implements StorageAdapterInterface { // HELPERS -const getKey = (keyArray: StorageKey): string => { - return keyArray.join(path.posix.sep) -} +const getKey = (key: StorageKey): string => path.join(...key) /** returns all files in a directory, recursively */ const walkdir = async (dirPath: string): Promise => { From a7f0f97378aadecb1c884f4872e53c5080899c77 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Sat, 9 Mar 2024 02:28:51 +0300 Subject: [PATCH 12/28] style: remove semicolons --- .../test/NodeFSStorageAdapter.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts index ad69e05f3..c4af7efac 100644 --- a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts +++ b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts @@ -22,7 +22,7 @@ describe('NodeFSStorageAdapter', () => { }) afterAll(async() => { - cleanDir(baseDirectory); + cleanDir(baseDirectory) }) describe('getFilePath', () => { @@ -33,5 +33,5 @@ describe('NodeFSStorageAdapter', () => { }) }) - runStorageAdapterTests(sut); + runStorageAdapterTests(sut) }) From 1c72305ec584d37ad15eab697460522e11b4ce82 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Sat, 9 Mar 2024 02:30:23 +0300 Subject: [PATCH 13/28] style: use the mkdtempSync function as it is used in StorageSubsystem.test.ts --- .../test/NodeFSStorageAdapter.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts index c4af7efac..2d1757557 100644 --- a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts +++ b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts @@ -12,12 +12,14 @@ function cleanDir(dir: string) { } catch (e) {} } +const tempDir = () => fs.mkdtempSync(path.join(os.tmpdir(), "automerge-repo-tests")) + describe('NodeFSStorageAdapter', () => { - let baseDirectory: string = path.join(os.tmpdir(), crypto.randomUUID()); + let baseDirectory: string = tempDir() let sut: {adapter: NodeFSStorageAdapter} = {adapter: new NodeFSStorageAdapter(baseDirectory)} beforeEach(async () => { - baseDirectory = path.join(os.tmpdir(), crypto.randomUUID()) + baseDirectory = tempDir() sut.adapter = new NodeFSStorageAdapter(baseDirectory) }) From b5513fe47670d4d117e97547a4ccad86ab86cba3 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Sat, 9 Mar 2024 23:33:25 +0300 Subject: [PATCH 14/28] chore: the jsx compiler option is not mandatory here --- packages/automerge-repo-storage-nodefs/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/automerge-repo-storage-nodefs/tsconfig.json b/packages/automerge-repo-storage-nodefs/tsconfig.json index 18d3db25a..c52bdbdfe 100644 --- a/packages/automerge-repo-storage-nodefs/tsconfig.json +++ b/packages/automerge-repo-storage-nodefs/tsconfig.json @@ -1,7 +1,6 @@ { "compilerOptions": { "target": "ESNext", - "jsx": "react", "module": "NodeNext", "moduleResolution": "Node16", "declaration": true, From a759033a7bbca06ed08fbd1c3536adb4136d84bd Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Sat, 9 Mar 2024 23:33:58 +0300 Subject: [PATCH 15/28] chore: remove unused import --- .../test/NodeFSStorageAdapter.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts index 2d1757557..542f45b50 100644 --- a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts +++ b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts @@ -1,7 +1,6 @@ import * as fs from "node:fs" import * as os from "node:os" import * as path from "node:path" -import * as crypto from "node:crypto" import { afterAll, beforeEach, describe, expect, it } from "vitest" import { runStorageAdapterTests } from "@automerge/automerge-repo/src/helpers/tests/storage-adapter-tests" import { NodeFSStorageAdapter } from "../src" From ef0a7c4aaf6bc9d0a71c3c22a65ec84a7a58225b Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Sun, 10 Mar 2024 15:05:02 +0300 Subject: [PATCH 16/28] test: add test cases for loadRange and removeRange methods --- .../helpers/tests/storage-adapter-tests.ts | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index 8b262c670..96ba4cd45 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -42,18 +42,31 @@ export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) expect( await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual([ + ).toStrictEqual(expect.arrayContaining([ {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"]}, {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} - ]) + ])) expect( await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) - ).toStrictEqual([ + ).toStrictEqual(expect.arrayContaining([ {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} - ]) + ])) + }) + + it("does not includes values which shouldn't be there", async () => { + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])) + + const actual = await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + expect(actual).toStrictEqual(expect.arrayContaining([ + {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + ])) + expect(actual).toStrictEqual(expect.not.arrayContaining([ + {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]}, + ])) }) }) @@ -83,4 +96,32 @@ export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) ]) }) }) + + describe('removeRange', () => { + it('should remove set of records', async () => { + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])) + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])) + + await sut.adapter.removeRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) + + expect( + await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual([ + {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"]}, + ]) + }) + + it('should not remove set of records that doesn\'t match', async () => { + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) + await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])) + + await sut.adapter.removeRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + + const actual = await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD"]) + expect(actual).toStrictEqual([ + {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]}, + ]) + }) + }) } From d0a40077d247d30968e7b6654041bc8795e87624 Mon Sep 17 00:00:00 2001 From: Bijela Gora Date: Mon, 11 Mar 2024 01:02:05 +0300 Subject: [PATCH 17/28] test: fix test For some reason the test doesn't pass in bunch with others, so I change it to make it work --- .../src/helpers/tests/storage-adapter-tests.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index 96ba4cd45..b70c44e9f 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -13,10 +13,22 @@ export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) }) describe('save and load', () => { - it('should be possible to save and load', async () => { - await sut.adapter.save(["storage-adapter-id"], new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')) + it('should return data that was saved', async () => { + await sut.adapter.save(["storage-adapter-id"], new Uint8Array([ + 56, 97, 51, 53, 99, 57, 98, 52, 45, + 49, 48, 57, 101, 45, 52, 97, 55, 102, + 45, 97, 51, 53, 101, 45, 97, 53, 52, + 54, 52, 49, 50, 49, 98, 54, 100, 100 + ])) + const actual = await sut.adapter.load(["storage-adapter-id"]) - expect(actual).toStrictEqual(new TextEncoder().encode('8a35c9b4-109e-4a7f-a35e-a5464121b6dd')) + + expect(actual).toStrictEqual(new Uint8Array([ + 56, 97, 51, 53, 99, 57, 98, 52, 45, + 49, 48, 57, 101, 45, 52, 97, 55, 102, + 45, 97, 51, 53, 101, 45, 97, 53, 52, + 54, 52, 49, 50, 49, 98, 54, 100, 100 + ])) }) it('should work with composed keys', async () => { From fcde512a9395823f579c7bc7ef867cd641d5144c Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Tue, 19 Mar 2024 15:18:37 +0100 Subject: [PATCH 18/28] rename runAdapterTests to runNetworkAdapterTests to clarify difference with runStorageAdapterTests --- .../test/index.test.ts | 4 ++-- .../test/index.test.ts | 6 +++--- .../automerge-repo-network-websocket/test/Websocket.test.ts | 4 ++-- .../src/helpers/tests/network-adapter-tests.ts | 6 ++++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/automerge-repo-network-broadcastchannel/test/index.test.ts b/packages/automerge-repo-network-broadcastchannel/test/index.test.ts index 5a533329c..19c9a812e 100644 --- a/packages/automerge-repo-network-broadcastchannel/test/index.test.ts +++ b/packages/automerge-repo-network-broadcastchannel/test/index.test.ts @@ -1,7 +1,7 @@ import { PeerId } from "@automerge/automerge-repo" import { describe, it } from "vitest" import { - runAdapterTests, + runNetworkAdapterTests, type SetupFn, } from "../../automerge-repo/src/helpers/tests/network-adapter-tests.js" import { BroadcastChannelNetworkAdapter } from "../src/index.js" @@ -15,7 +15,7 @@ describe("BroadcastChannel", () => { return { adapters: [a, b, c] } } - runAdapterTests(setup) + runNetworkAdapterTests(setup) it("allows a channel name to be specified in the options and limits messages to that channel", async () => { const a = new BroadcastChannelNetworkAdapter() diff --git a/packages/automerge-repo-network-messagechannel/test/index.test.ts b/packages/automerge-repo-network-messagechannel/test/index.test.ts index 3e104dae1..73571d58d 100644 --- a/packages/automerge-repo-network-messagechannel/test/index.test.ts +++ b/packages/automerge-repo-network-messagechannel/test/index.test.ts @@ -1,10 +1,10 @@ import { describe } from "vitest" -import { runAdapterTests } from "../../automerge-repo/src/helpers/tests/network-adapter-tests.js" +import { runNetworkAdapterTests } from "../../automerge-repo/src/helpers/tests/network-adapter-tests.js" import { MessageChannelNetworkAdapter as Adapter } from "../src/index.js" // bob is the hub, alice and charlie are spokes describe("MessageChannelNetworkAdapter", () => { - runAdapterTests(async () => { + runNetworkAdapterTests(async () => { const aliceBobChannel = new MessageChannel() const bobCharlieChannel = new MessageChannel() @@ -24,7 +24,7 @@ describe("MessageChannelNetworkAdapter", () => { }, "hub and spoke") // all 3 peers connected directly to each other - runAdapterTests(async () => { + runNetworkAdapterTests(async () => { const aliceBobChannel = new MessageChannel() const bobCharlieChannel = new MessageChannel() const aliceCharlieChannel = new MessageChannel() diff --git a/packages/automerge-repo-network-websocket/test/Websocket.test.ts b/packages/automerge-repo-network-websocket/test/Websocket.test.ts index a597bc67d..08b391f66 100644 --- a/packages/automerge-repo-network-websocket/test/Websocket.test.ts +++ b/packages/automerge-repo-network-websocket/test/Websocket.test.ts @@ -10,7 +10,7 @@ import { import { generateAutomergeUrl } from "@automerge/automerge-repo/dist/AutomergeUrl" import { eventPromise } from "@automerge/automerge-repo/src/helpers/eventPromise" import { headsAreSame } from "@automerge/automerge-repo/src/helpers/headsAreSame.js" -import { runAdapterTests } from "@automerge/automerge-repo/src/helpers/tests/network-adapter-tests.js" +import { runNetworkAdapterTests } from "@automerge/automerge-repo/src/helpers/tests/network-adapter-tests.js" import { DummyStorageAdapter } from "@automerge/automerge-repo/test/helpers/DummyStorageAdapter.js" import assert from "assert" import * as CBOR from "cbor-x" @@ -27,7 +27,7 @@ describe("Websocket adapters", () => { const serverPeerId = "server" as PeerId const documentId = parseAutomergeUrl(generateAutomergeUrl()).documentId - runAdapterTests(async () => { + runNetworkAdapterTests(async () => { const { clients: [aliceAdapter, bobAdapter], server, diff --git a/packages/automerge-repo/src/helpers/tests/network-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/network-adapter-tests.ts index d5c89189f..1a13de6d9 100644 --- a/packages/automerge-repo/src/helpers/tests/network-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/network-adapter-tests.ts @@ -17,7 +17,7 @@ import { pause } from "../pause.js" * - `teardown`: An optional function that will be called after the tests have run. This can be used * to clean up any resources that were created during the test. */ -export function runAdapterTests(_setup: SetupFn, title?: string): void { +export function runNetworkAdapterTests(_setup: SetupFn, title?: string): void { // Wrap the provided setup function const setup = async () => { const { adapters, teardown = NO_OP } = await _setup() @@ -28,7 +28,9 @@ export function runAdapterTests(_setup: SetupFn, title?: string): void { return { adapters: [a, b, c], teardown } } - describe(`Adapter acceptance tests ${title ? `(${title})` : ""}`, () => { + describe(`Network adapter acceptance tests ${ + title ? `(${title})` : "" + }`, () => { it("can sync 2 repos", async () => { const doTest = async ( a: NetworkAdapterInterface[], From 2a48ef160d2cd7ba9009e75f9c6b158a74829ce7 Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Tue, 19 Mar 2024 15:20:41 +0100 Subject: [PATCH 19/28] refactor params --- .../helpers/tests/storage-adapter-tests.ts | 360 ++++++++++++++---- 1 file changed, 279 insertions(+), 81 deletions(-) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index b70c44e9f..701ff3137 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -2,138 +2,336 @@ import { describe, expect, it } from "vitest" import type { StorageAdapterInterface } from "../../storage/StorageAdapterInterface.js" -export function runStorageAdapterTests(sut: {adapter: StorageAdapterInterface}) { - - describe('load', () => { - it('should return undefined if there is no data', async () => { +export function runStorageAdapterTests({ adapter }: Params) { + describe("load", () => { + it("should return undefined if there is no data", async () => { expect( - await sut.adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]) + await adapter.load([ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ]) ).toStrictEqual(undefined) }) }) - describe('save and load', () => { - it('should return data that was saved', async () => { - await sut.adapter.save(["storage-adapter-id"], new Uint8Array([ - 56, 97, 51, 53, 99, 57, 98, 52, 45, - 49, 48, 57, 101, 45, 52, 97, 55, 102, - 45, 97, 51, 53, 101, 45, 97, 53, 52, - 54, 52, 49, 50, 49, 98, 54, 100, 100 - ])) - - const actual = await sut.adapter.load(["storage-adapter-id"]) - - expect(actual).toStrictEqual(new Uint8Array([ - 56, 97, 51, 53, 99, 57, 98, 52, 45, - 49, 48, 57, 101, 45, 52, 97, 55, 102, - 45, 97, 51, 53, 101, 45, 97, 53, 52, - 54, 52, 49, 50, 49, 98, 54, 100, 100 - ])) + describe("save and load", () => { + it("should return data that was saved", async () => { + await adapter.save( + ["storage-adapter-id"], + new Uint8Array([ + 56, 97, 51, 53, 99, 57, 98, 52, 45, 49, 48, 57, 101, 45, 52, 97, 55, + 102, 45, 97, 51, 53, 101, 45, 97, 53, 52, 54, 52, 49, 50, 49, 98, 54, + 100, 100, + ]) + ) + + const actual = await adapter.load(["storage-adapter-id"]) + + expect(actual).toStrictEqual( + new Uint8Array([ + 56, 97, 51, 53, 99, 57, 98, 52, 45, 49, 48, 57, 101, 45, 52, 97, 55, + 102, 45, 97, 51, 53, 101, 45, 97, 53, 52, 54, 52, 49, 50, 49, 98, 54, + 100, 100, + ]) + ) }) - it('should work with composed keys', async () => { - await sut.adapter.save(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) - const actual = await sut.adapter.load(["pSq9fP9ekr1zembLzBJkgHTo7Wn","sync-state","3761c9f0-bb1d-44b6-88ac-f85072fc3273"]) - expect(actual).toStrictEqual(new Uint8Array([0, 1, 127, 99, 154, 235 ])) + it("should work with composed keys", async () => { + await adapter.save( + [ + "pSq9fP9ekr1zembLzBJkgHTo7Wn", + "sync-state", + "3761c9f0-bb1d-44b6-88ac-f85072fc3273", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + const actual = await adapter.load([ + "pSq9fP9ekr1zembLzBJkgHTo7Wn", + "sync-state", + "3761c9f0-bb1d-44b6-88ac-f85072fc3273", + ]) + expect(actual).toStrictEqual(new Uint8Array([0, 1, 127, 99, 154, 235])) }) }) - describe('loadRange', () => { - it('should return empty array if there is no data', async () => { + describe("loadRange", () => { + it("should return empty array if there is no data", async () => { expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) ).toStrictEqual([]) }) }) - describe('save and loadRange', () => { - it('should return all the data that is present', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])) - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])) + describe("save and loadRange", () => { + it("should return all the data that is present", async () => { + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", + ], + new Uint8Array([1, 76, 160, 53, 57, 10, 230]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) + ) expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual(expect.arrayContaining([ - {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, - {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"]}, - {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} - ])) + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual( + expect.arrayContaining([ + { + data: new Uint8Array([0, 1, 127, 99, 154, 235]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + }, + { + data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", + ], + }, + { + data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + }, + ]) + ) expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) - ).toStrictEqual(expect.arrayContaining([ - {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, - {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]} - ])) + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "sync-state"]) + ).toStrictEqual( + expect.arrayContaining([ + { + data: new Uint8Array([0, 1, 127, 99, 154, 235]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + }, + { + data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + }, + ]) + ) }) it("does not includes values which shouldn't be there", async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])) - - const actual = await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - expect(actual).toStrictEqual(expect.arrayContaining([ - {"data":new Uint8Array([0, 1, 127, 99, 154, 235 ]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, - ])) - expect(actual).toStrictEqual(expect.not.arrayContaining([ - {"data":new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]}, - ])) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) + ) + + const actual = await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + expect(actual).toStrictEqual( + expect.arrayContaining([ + { + data: new Uint8Array([0, 1, 127, 99, 154, 235]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + }, + ]) + ) + expect(actual).toStrictEqual( + expect.not.arrayContaining([ + { + data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + }, + ]) + ) }) }) - describe('save and remove', () => { - it('should be no data', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) - await sut.adapter.remove(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]) + describe("save and remove", () => { + it("should be no data", async () => { + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.remove([ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", + ]) expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) ).toStrictEqual([]) expect( - await sut.adapter.load(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056"]) + await adapter.load([ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", + ]) ).toStrictEqual(undefined) }) }) - describe('save and save', () => { - it('should override the data', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])) + describe("save and save", () => { + it("should override the data", async () => { + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([1, 76, 160, 53, 57, 10, 230]) + ) expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "sync-state"]) ).toStrictEqual([ - {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]}, + { + data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + }, ]) }) }) - describe('removeRange', () => { - it('should remove set of records', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])) - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])) + describe("removeRange", () => { + it("should remove set of records", async () => { + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", + ], + new Uint8Array([1, 76, 160, 53, 57, 10, 230]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) + ) - await sut.adapter.removeRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state"]) + await adapter.removeRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "sync-state"]) expect( - await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) ).toStrictEqual([ - {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","snapshot","7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870"]}, + { + data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", + ], + }, ]) }) - it('should not remove set of records that doesn\'t match', async () => { - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"], new Uint8Array([0, 1, 127, 99, 154, 235 ])) - await sut.adapter.save(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"], new Uint8Array([1, 76, 160, 53, 57, 10, 230])) + it("should not remove set of records that doesn't match", async () => { + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + new Uint8Array([1, 76, 160, 53, 57, 10, 230]) + ) - await sut.adapter.removeRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + await adapter.removeRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - const actual = await sut.adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD"]) + const actual = await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD"]) expect(actual).toStrictEqual([ - {"data":new Uint8Array([1, 76, 160, 53, 57, 10, 230]),"key":["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD","sync-state","0e05ed0c-41f5-4785-b27a-7cf334c1b741"]}, + { + data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + }, ]) }) }) } + +type Params = { + adapter: StorageAdapterInterface +} From 845d65ee353dae7a997e4c5c490e690bcc1dcc28 Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Tue, 19 Mar 2024 15:44:56 +0100 Subject: [PATCH 20/28] refactor to take setup function --- .../test/NodeFSStorageAdapter.test.ts | 43 +- .../helpers/tests/storage-adapter-tests.ts | 604 ++++++++++-------- .../test/DummyStorageAdapter.test.ts | 10 +- 3 files changed, 338 insertions(+), 319 deletions(-) diff --git a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts index 542f45b50..fc657beaa 100644 --- a/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts +++ b/packages/automerge-repo-storage-nodefs/test/NodeFSStorageAdapter.test.ts @@ -1,38 +1,19 @@ import * as fs from "node:fs" import * as os from "node:os" import * as path from "node:path" -import { afterAll, beforeEach, describe, expect, it } from "vitest" -import { runStorageAdapterTests } from "@automerge/automerge-repo/src/helpers/tests/storage-adapter-tests" +import { describe } from "vitest" +import { runStorageAdapterTests } from "../../automerge-repo/src/helpers/tests/storage-adapter-tests" import { NodeFSStorageAdapter } from "../src" -function cleanDir(dir: string) { - try { - fs.rmSync(dir, { force: true, recursive: true }) - } catch (e) {} -} +describe("NodeFSStorageAdapter", () => { + const setup = async () => { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), "automerge-repo-tests")) + const teardown = () => { + fs.rmSync(dir, { force: true, recursive: true }) + } + const adapter = new NodeFSStorageAdapter(dir) + return { adapter, teardown } + } -const tempDir = () => fs.mkdtempSync(path.join(os.tmpdir(), "automerge-repo-tests")) - -describe('NodeFSStorageAdapter', () => { - let baseDirectory: string = tempDir() - let sut: {adapter: NodeFSStorageAdapter} = {adapter: new NodeFSStorageAdapter(baseDirectory)} - - beforeEach(async () => { - baseDirectory = tempDir() - sut.adapter = new NodeFSStorageAdapter(baseDirectory) - }) - - afterAll(async() => { - cleanDir(baseDirectory) - }) - - describe('getFilePath', () => { - it('should compose keys correctly', () => { - // @ts-ignore - const actual = sut.adapter.getFilePath(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC","sync-state","d99d4820-fb1f-4f3a-a40f-d5997b2012cf"]) - expect(actual).toStrictEqual(path.join(baseDirectory) + '/3x/uJ5sVKdBaYS6uGgGJH1cGhBLiC/sync-state/d99d4820-fb1f-4f3a-a40f-d5997b2012cf') - }) - }) - - runStorageAdapterTests(sut) + runStorageAdapterTests(setup) }) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index 701ff3137..af2879894 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -2,172 +2,276 @@ import { describe, expect, it } from "vitest" import type { StorageAdapterInterface } from "../../storage/StorageAdapterInterface.js" -export function runStorageAdapterTests({ adapter }: Params) { - describe("load", () => { - it("should return undefined if there is no data", async () => { - expect( - await adapter.load([ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ]) - ).toStrictEqual(undefined) +export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { + const setup = async () => { + const { adapter, teardown = NO_OP } = await _setup() + return { adapter, teardown } + } + + describe(`Network adapter acceptance tests ${ + title ? `(${title})` : "" + }`, () => { + describe("load", () => { + it("should return undefined if there is no data", async () => { + const { adapter, teardown } = await setup() + expect( + await adapter.load([ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ]) + ).toStrictEqual(undefined) + teardown() + }) }) - }) - describe("save and load", () => { - it("should return data that was saved", async () => { - await adapter.save( - ["storage-adapter-id"], - new Uint8Array([ - 56, 97, 51, 53, 99, 57, 98, 52, 45, 49, 48, 57, 101, 45, 52, 97, 55, - 102, 45, 97, 51, 53, 101, 45, 97, 53, 52, 54, 52, 49, 50, 49, 98, 54, - 100, 100, - ]) - ) + describe("save and load", () => { + it("should return data that was saved", async () => { + const { adapter, teardown } = await setup() + await adapter.save( + ["storage-adapter-id"], + new Uint8Array([ + 56, 97, 51, 53, 99, 57, 98, 52, 45, 49, 48, 57, 101, 45, 52, 97, 55, + 102, 45, 97, 51, 53, 101, 45, 97, 53, 52, 54, 52, 49, 50, 49, 98, + 54, 100, 100, + ]) + ) - const actual = await adapter.load(["storage-adapter-id"]) + const actual = await adapter.load(["storage-adapter-id"]) - expect(actual).toStrictEqual( - new Uint8Array([ - 56, 97, 51, 53, 99, 57, 98, 52, 45, 49, 48, 57, 101, 45, 52, 97, 55, - 102, 45, 97, 51, 53, 101, 45, 97, 53, 52, 54, 52, 49, 50, 49, 98, 54, - 100, 100, - ]) - ) - }) + expect(actual).toStrictEqual( + new Uint8Array([ + 56, 97, 51, 53, 99, 57, 98, 52, 45, 49, 48, 57, 101, 45, 52, 97, 55, + 102, 45, 97, 51, 53, 101, 45, 97, 53, 52, 54, 52, 49, 50, 49, 98, + 54, 100, 100, + ]) + ) + teardown() + }) - it("should work with composed keys", async () => { - await adapter.save( - [ + it("should work with composed keys", async () => { + const { adapter, teardown } = await setup() + await adapter.save( + [ + "pSq9fP9ekr1zembLzBJkgHTo7Wn", + "sync-state", + "3761c9f0-bb1d-44b6-88ac-f85072fc3273", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + const actual = await adapter.load([ "pSq9fP9ekr1zembLzBJkgHTo7Wn", "sync-state", "3761c9f0-bb1d-44b6-88ac-f85072fc3273", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - const actual = await adapter.load([ - "pSq9fP9ekr1zembLzBJkgHTo7Wn", - "sync-state", - "3761c9f0-bb1d-44b6-88ac-f85072fc3273", - ]) - expect(actual).toStrictEqual(new Uint8Array([0, 1, 127, 99, 154, 235])) + ]) + expect(actual).toStrictEqual(new Uint8Array([0, 1, 127, 99, 154, 235])) + teardown() + }) }) - }) - describe("loadRange", () => { - it("should return empty array if there is no data", async () => { - expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual([]) + describe("loadRange", () => { + it("should return empty array if there is no data", async () => { + const { adapter, teardown } = await setup() + expect( + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual([]) + teardown() + }) }) - }) - describe("save and loadRange", () => { - it("should return all the data that is present", async () => { - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.save( - [ + describe("save and loadRange", () => { + it("should return all the data that is present", async () => { + const { adapter, teardown } = await setup() + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", + ], + new Uint8Array([1, 76, 160, 53, 57, 10, 230]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) + ) + + expect( + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual( + expect.arrayContaining([ + { + data: new Uint8Array([0, 1, 127, 99, 154, 235]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + }, + { + data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", + ], + }, + { + data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + }, + ]) + ) + + expect( + await adapter.loadRange([ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + ]) + ).toStrictEqual( + expect.arrayContaining([ + { + data: new Uint8Array([0, 1, 127, 99, 154, 235]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + }, + { + data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + }, + ]) + ) + }) + + it("does not includes values which shouldn't be there", async () => { + const { adapter, teardown } = await setup() + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) + ) + + const actual = await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + expect(actual).toStrictEqual( + expect.arrayContaining([ + { + data: new Uint8Array([0, 1, 127, 99, 154, 235]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + }, + ]) + ) + expect(actual).toStrictEqual( + expect.not.arrayContaining([ + { + data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + }, + ]) + ) + teardown() + }) + }) + + describe("save and remove", () => { + it("should be no data", async () => { + const { adapter, teardown } = await setup() + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.remove([ "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "snapshot", - "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", - ], - new Uint8Array([1, 76, 160, 53, 57, 10, 230]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) - ) - - expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual( - expect.arrayContaining([ - { - data: new Uint8Array([0, 1, 127, 99, 154, 235]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - }, - { - data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", - ], - }, - { - data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - }, + "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", ]) - ) - expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "sync-state"]) - ).toStrictEqual( - expect.arrayContaining([ - { - data: new Uint8Array([0, 1, 127, 99, 154, 235]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - }, - { - data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - }, - ]) - ) + expect( + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual([]) + expect( + await adapter.load([ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", + ]) + ).toStrictEqual(undefined) + teardown() + }) }) - it("does not includes values which shouldn't be there", async () => { - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) - ) + describe("save and save", () => { + it("should override the data", async () => { + const { adapter, teardown } = await setup() + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([1, 76, 160, 53, 57, 10, 230]) + ) - const actual = await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - expect(actual).toStrictEqual( - expect.arrayContaining([ + expect( + await adapter.loadRange([ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + ]) + ).toStrictEqual([ { - data: new Uint8Array([0, 1, 127, 99, 154, 235]), + data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), key: [ "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "sync-state", @@ -175,163 +279,99 @@ export function runStorageAdapterTests({ adapter }: Params) { ], }, ]) - ) - expect(actual).toStrictEqual( - expect.not.arrayContaining([ - { - data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - }, - ]) - ) - }) - }) - - describe("save and remove", () => { - it("should be no data", async () => { - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.remove([ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", - ]) - - expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual([]) - expect( - await adapter.load([ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", - ]) - ).toStrictEqual(undefined) + teardown() + }) }) - }) - describe("save and save", () => { - it("should override the data", async () => { - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([1, 76, 160, 53, 57, 10, 230]) - ) - - expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "sync-state"]) - ).toStrictEqual([ - { - data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), - key: [ + describe("removeRange", () => { + it("should remove set of records", async () => { + const { adapter, teardown } = await setup() + await adapter.save( + [ "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "sync-state", "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", ], - }, - ]) - }) - }) - - describe("removeRange", () => { - it("should remove set of records", async () => { - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", - ], - new Uint8Array([1, 76, 160, 53, 57, 10, 230]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) - ) - - await adapter.removeRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "sync-state"]) - - expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual([ - { - data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), - key: [ + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.save( + [ "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "snapshot", "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", ], - }, - ]) - }) + new Uint8Array([1, 76, 160, 53, 57, 10, 230]) + ) + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) + ) - it("should not remove set of records that doesn't match", async () => { - await adapter.save( - [ + await adapter.removeRange([ "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - new Uint8Array([1, 76, 160, 53, 57, 10, 230]) - ) + ]) - await adapter.removeRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + expect( + await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + ).toStrictEqual([ + { + data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "snapshot", + "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", + ], + }, + ]) + teardown() + }) - const actual = await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD"]) - expect(actual).toStrictEqual([ - { - data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), - key: [ + it("should not remove set of records that doesn't match", async () => { + const { adapter, teardown } = await setup() + await adapter.save( + [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", + "sync-state", + "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", + ], + new Uint8Array([0, 1, 127, 99, 154, 235]) + ) + await adapter.save( + [ "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", "sync-state", "0e05ed0c-41f5-4785-b27a-7cf334c1b741", ], - }, - ]) + new Uint8Array([1, 76, 160, 53, 57, 10, 230]) + ) + + await adapter.removeRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + + const actual = await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD"]) + expect(actual).toStrictEqual([ + { + data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), + key: [ + "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", + "sync-state", + "0e05ed0c-41f5-4785-b27a-7cf334c1b741", + ], + }, + ]) + teardown() + }) }) }) } -type Params = { +const NO_OP = () => {} + +export type SetupFn = () => Promise<{ adapter: StorageAdapterInterface -} + teardown?: () => void +}> diff --git a/packages/automerge-repo/test/DummyStorageAdapter.test.ts b/packages/automerge-repo/test/DummyStorageAdapter.test.ts index 064994033..3c6ddaed7 100644 --- a/packages/automerge-repo/test/DummyStorageAdapter.test.ts +++ b/packages/automerge-repo/test/DummyStorageAdapter.test.ts @@ -2,12 +2,10 @@ import { beforeEach, describe } from "vitest" import { DummyStorageAdapter } from "./helpers/DummyStorageAdapter.js" import { runStorageAdapterTests } from "../src/helpers/tests/storage-adapter-tests.js" -describe('DummyStorageAdapter', () => { - let sut: {adapter: DummyStorageAdapter} = { adapter: new DummyStorageAdapter() } - - beforeEach(async () => { - sut.adapter = new DummyStorageAdapter() +describe("DummyStorageAdapter", () => { + const setup = async () => ({ + adapter: new DummyStorageAdapter(), }) - runStorageAdapterTests(sut); + runStorageAdapterTests(setup, "DummyStorageAdapter") }) From 67b8cfab4743dbd050777f23f6d1ecfa8c465657 Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Tue, 19 Mar 2024 16:05:55 +0100 Subject: [PATCH 21/28] abbreviate keys & payloads for legibility --- .../helpers/tests/storage-adapter-tests.ts | 314 +++--------------- 1 file changed, 52 insertions(+), 262 deletions(-) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index af2879894..7b6e5cab3 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -2,6 +2,14 @@ import { describe, expect, it } from "vitest" import type { StorageAdapterInterface } from "../../storage/StorageAdapterInterface.js" +const PAYLOAD_A = new Uint8Array([ + 56, 97, 51, 53, 99, 57, 98, 52, 45, 49, 48, 57, 101, 45, 52, 97, 55, 102, 45, + 97, 51, 53, 101, 45, 97, 53, 52, 54, 52, 49, 50, 49, 98, 54, 100, 100, +]) +const PAYLOAD_B = new Uint8Array([0, 1, 127, 99, 154, 235]) +const PAYLOAD_C = new Uint8Array([1, 76, 160, 53, 57, 10, 230]) +const PAYLOAD_D = new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) + export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { const setup = async () => { const { adapter, teardown = NO_OP } = await _setup() @@ -15,11 +23,7 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should return undefined if there is no data", async () => { const { adapter, teardown } = await setup() expect( - await adapter.load([ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ]) + await adapter.load(["3xuJ5", "sync-state", "d99d4"]) ).toStrictEqual(undefined) teardown() }) @@ -28,53 +32,27 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("save and load", () => { it("should return data that was saved", async () => { const { adapter, teardown } = await setup() - await adapter.save( - ["storage-adapter-id"], - new Uint8Array([ - 56, 97, 51, 53, 99, 57, 98, 52, 45, 49, 48, 57, 101, 45, 52, 97, 55, - 102, 45, 97, 51, 53, 101, 45, 97, 53, 52, 54, 52, 49, 50, 49, 98, - 54, 100, 100, - ]) - ) + await adapter.save(["storage-adapter-id"], PAYLOAD_A) const actual = await adapter.load(["storage-adapter-id"]) - expect(actual).toStrictEqual( - new Uint8Array([ - 56, 97, 51, 53, 99, 57, 98, 52, 45, 49, 48, 57, 101, 45, 52, 97, 55, - 102, 45, 97, 51, 53, 101, 45, 97, 53, 52, 54, 52, 49, 50, 49, 98, - 54, 100, 100, - ]) - ) + expect(actual).toStrictEqual(PAYLOAD_A) teardown() }) - it("should work with composed keys", async () => { + it("should work with composite keys", async () => { const { adapter, teardown } = await setup() - await adapter.save( - [ - "pSq9fP9ekr1zembLzBJkgHTo7Wn", - "sync-state", - "3761c9f0-bb1d-44b6-88ac-f85072fc3273", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - const actual = await adapter.load([ - "pSq9fP9ekr1zembLzBJkgHTo7Wn", - "sync-state", - "3761c9f0-bb1d-44b6-88ac-f85072fc3273", - ]) - expect(actual).toStrictEqual(new Uint8Array([0, 1, 127, 99, 154, 235])) + await adapter.save(["pSq9f", "sync-state", "3761c"], PAYLOAD_B) + const actual = await adapter.load(["pSq9f", "sync-state", "3761c"]) + expect(actual).toStrictEqual(PAYLOAD_B) teardown() }) }) describe("loadRange", () => { - it("should return empty array if there is no data", async () => { + it("should return an empty array if there is no data", async () => { const { adapter, teardown } = await setup() - expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual([]) + expect(await adapter.loadRange(["3xuJ5"])).toStrictEqual([]) teardown() }) }) @@ -82,131 +60,40 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("save and loadRange", () => { it("should return all the data that is present", async () => { const { adapter, teardown } = await setup() - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", - ], - new Uint8Array([1, 76, 160, 53, 57, 10, 230]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) - ) + await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_B) + await adapter.save(["3xuJ5", "snapshot", "7848c"], PAYLOAD_C) + await adapter.save(["3xuJ5", "sync-state", "0e05e"], PAYLOAD_D) - expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual( + expect(await adapter.loadRange(["3xuJ5"])).toStrictEqual( expect.arrayContaining([ - { - data: new Uint8Array([0, 1, 127, 99, 154, 235]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - }, - { - data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", - ], - }, - { - data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - }, + { key: ["3xuJ5", "sync-state", "d99d4"], data: PAYLOAD_B }, + { key: ["3xuJ5", "snapshot", "7848c"], data: PAYLOAD_C }, + { key: ["3xuJ5", "sync-state", "0e05e"], data: PAYLOAD_D }, ]) ) - expect( - await adapter.loadRange([ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - ]) - ).toStrictEqual( + expect(await adapter.loadRange(["3xuJ5", "sync-state"])).toStrictEqual( expect.arrayContaining([ - { - data: new Uint8Array([0, 1, 127, 99, 154, 235]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - }, - { - data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - }, + { key: ["3xuJ5", "sync-state", "d99d4"], data: PAYLOAD_B }, + { key: ["3xuJ5", "sync-state", "0e05e"], data: PAYLOAD_D }, ]) ) }) it("does not includes values which shouldn't be there", async () => { const { adapter, teardown } = await setup() - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) - ) + await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_B) + await adapter.save(["3xuJ6", "sync-state", "0e05e"], PAYLOAD_D) - const actual = await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + const actual = await adapter.loadRange(["3xuJ5"]) expect(actual).toStrictEqual( expect.arrayContaining([ - { - data: new Uint8Array([0, 1, 127, 99, 154, 235]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - }, + { key: ["3xuJ5", "sync-state", "d99d4"], data: PAYLOAD_B }, ]) ) expect(actual).toStrictEqual( expect.not.arrayContaining([ - { - data: new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - }, + { key: ["3xuJ6", "sync-state", "0e05e"], data: PAYLOAD_D }, ]) ) teardown() @@ -216,68 +103,25 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("save and remove", () => { it("should be no data", async () => { const { adapter, teardown } = await setup() - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.remove([ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", - ]) + await adapter.save(["3xuJ5", "snapshot", "09014"], PAYLOAD_B) + await adapter.remove(["3xuJ5", "snapshot", "09014"]) + expect(await adapter.loadRange(["3xuJ5"])).toStrictEqual([]) expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual([]) - expect( - await adapter.load([ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "090144be3cabe2848d4af81ebf6c3f0c93dfcf814fd34a43cdc93d8564fda056", - ]) + await adapter.load(["3xuJ5", "snapshot", "09014"]) ).toStrictEqual(undefined) teardown() }) }) describe("save and save", () => { - it("should override the data", async () => { + it("should overwrite data saved with the same key", async () => { const { adapter, teardown } = await setup() - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([1, 76, 160, 53, 57, 10, 230]) - ) + await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_B) + await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_C) - expect( - await adapter.loadRange([ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - ]) - ).toStrictEqual([ - { - data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - }, + expect(await adapter.loadRange(["3xuJ5", "sync-state"])).toStrictEqual([ + { key: ["3xuJ5", "sync-state", "d99d4"], data: PAYLOAD_C }, ]) teardown() }) @@ -286,82 +130,28 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("removeRange", () => { it("should remove set of records", async () => { const { adapter, teardown } = await setup() - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", - ], - new Uint8Array([1, 76, 160, 53, 57, 10, 230]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) - ) + await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_B) + await adapter.save(["3xuJ5", "snapshot", "7848c"], PAYLOAD_C) + await adapter.save(["3xuJ5", "sync-state", "0e05e"], PAYLOAD_D) - await adapter.removeRange([ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - ]) + await adapter.removeRange(["3xuJ5", "sync-state"]) - expect( - await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) - ).toStrictEqual([ - { - data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "snapshot", - "7848c74d260d060ee02e12d69d43a21348fedf4f4a4783ac6aaaa2e338bca870", - ], - }, + expect(await adapter.loadRange(["3xuJ5"])).toStrictEqual([ + { key: ["3xuJ5", "snapshot", "7848c"], data: PAYLOAD_C }, ]) teardown() }) it("should not remove set of records that doesn't match", async () => { const { adapter, teardown } = await setup() - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiC", - "sync-state", - "d99d4820-fb1f-4f3a-a40f-d5997b2012cf", - ], - new Uint8Array([0, 1, 127, 99, 154, 235]) - ) - await adapter.save( - [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - new Uint8Array([1, 76, 160, 53, 57, 10, 230]) - ) + await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_B) + await adapter.save(["3xuJ6", "sync-state", "0e05e"], PAYLOAD_C) - await adapter.removeRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiC"]) + await adapter.removeRange(["3xuJ5"]) - const actual = await adapter.loadRange(["3xuJ5sVKdBaYS6uGgGJH1cGhBLiD"]) + const actual = await adapter.loadRange(["3xuJ6"]) expect(actual).toStrictEqual([ - { - data: new Uint8Array([1, 76, 160, 53, 57, 10, 230]), - key: [ - "3xuJ5sVKdBaYS6uGgGJH1cGhBLiD", - "sync-state", - "0e05ed0c-41f5-4785-b27a-7cf334c1b741", - ], - }, + { key: ["3xuJ6", "sync-state", "0e05e"], data: PAYLOAD_C }, ]) teardown() }) From dcc6f2320667d0028571465119336b298a1119fc Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Tue, 19 Mar 2024 16:14:03 +0100 Subject: [PATCH 22/28] refactor --- .../helpers/tests/storage-adapter-tests.ts | 106 ++++++++++-------- 1 file changed, 61 insertions(+), 45 deletions(-) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index 7b6e5cab3..c9e746c07 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -22,9 +22,10 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("load", () => { it("should return undefined if there is no data", async () => { const { adapter, teardown } = await setup() - expect( - await adapter.load(["3xuJ5", "sync-state", "d99d4"]) - ).toStrictEqual(undefined) + + const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]) + expect(actual).toBeUndefined() + teardown() }) }) @@ -32,19 +33,21 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("save and load", () => { it("should return data that was saved", async () => { const { adapter, teardown } = await setup() - await adapter.save(["storage-adapter-id"], PAYLOAD_A) + await adapter.save(["storage-adapter-id"], PAYLOAD_A) const actual = await adapter.load(["storage-adapter-id"]) - expect(actual).toStrictEqual(PAYLOAD_A) + teardown() }) it("should work with composite keys", async () => { const { adapter, teardown } = await setup() - await adapter.save(["pSq9f", "sync-state", "3761c"], PAYLOAD_B) - const actual = await adapter.load(["pSq9f", "sync-state", "3761c"]) + + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) + const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]) expect(actual).toStrictEqual(PAYLOAD_B) + teardown() }) }) @@ -52,7 +55,9 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("loadRange", () => { it("should return an empty array if there is no data", async () => { const { adapter, teardown } = await setup() - expect(await adapter.loadRange(["3xuJ5"])).toStrictEqual([]) + + expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([]) + teardown() }) }) @@ -60,42 +65,45 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("save and loadRange", () => { it("should return all the data that is present", async () => { const { adapter, teardown } = await setup() - await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_B) - await adapter.save(["3xuJ5", "snapshot", "7848c"], PAYLOAD_C) - await adapter.save(["3xuJ5", "sync-state", "0e05e"], PAYLOAD_D) - expect(await adapter.loadRange(["3xuJ5"])).toStrictEqual( + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) + await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_C) + await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_D) + + expect(await adapter.loadRange(["AAAAA"])).toStrictEqual( expect.arrayContaining([ - { key: ["3xuJ5", "sync-state", "d99d4"], data: PAYLOAD_B }, - { key: ["3xuJ5", "snapshot", "7848c"], data: PAYLOAD_C }, - { key: ["3xuJ5", "sync-state", "0e05e"], data: PAYLOAD_D }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B }, + { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_C }, + { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_D }, ]) ) - expect(await adapter.loadRange(["3xuJ5", "sync-state"])).toStrictEqual( + expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual( expect.arrayContaining([ - { key: ["3xuJ5", "sync-state", "d99d4"], data: PAYLOAD_B }, - { key: ["3xuJ5", "sync-state", "0e05e"], data: PAYLOAD_D }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B }, + { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_D }, ]) ) }) it("does not includes values which shouldn't be there", async () => { const { adapter, teardown } = await setup() - await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_B) - await adapter.save(["3xuJ6", "sync-state", "0e05e"], PAYLOAD_D) - const actual = await adapter.loadRange(["3xuJ5"]) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) + await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_D) + + const actual = await adapter.loadRange(["AAAAA"]) expect(actual).toStrictEqual( expect.arrayContaining([ - { key: ["3xuJ5", "sync-state", "d99d4"], data: PAYLOAD_B }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B }, ]) ) expect(actual).toStrictEqual( expect.not.arrayContaining([ - { key: ["3xuJ6", "sync-state", "0e05e"], data: PAYLOAD_D }, + { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_D }, ]) ) + teardown() }) }) @@ -103,13 +111,15 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("save and remove", () => { it("should be no data", async () => { const { adapter, teardown } = await setup() - await adapter.save(["3xuJ5", "snapshot", "09014"], PAYLOAD_B) - await adapter.remove(["3xuJ5", "snapshot", "09014"]) - expect(await adapter.loadRange(["3xuJ5"])).toStrictEqual([]) + await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_B) + await adapter.remove(["AAAAA", "snapshot", "xxxxx"]) + + expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([]) expect( - await adapter.load(["3xuJ5", "snapshot", "09014"]) - ).toStrictEqual(undefined) + await adapter.load(["AAAAA", "snapshot", "xxxxx"]) + ).toBeUndefined() + teardown() }) }) @@ -117,42 +127,48 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("save and save", () => { it("should overwrite data saved with the same key", async () => { const { adapter, teardown } = await setup() - await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_B) - await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_C) - expect(await adapter.loadRange(["3xuJ5", "sync-state"])).toStrictEqual([ - { key: ["3xuJ5", "sync-state", "d99d4"], data: PAYLOAD_C }, + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_C) + + expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual([ + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_C }, ]) + teardown() }) }) describe("removeRange", () => { - it("should remove set of records", async () => { + it("should remove a range of records", async () => { const { adapter, teardown } = await setup() - await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_B) - await adapter.save(["3xuJ5", "snapshot", "7848c"], PAYLOAD_C) - await adapter.save(["3xuJ5", "sync-state", "0e05e"], PAYLOAD_D) - await adapter.removeRange(["3xuJ5", "sync-state"]) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) + await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_C) + await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_D) + + await adapter.removeRange(["AAAAA", "sync-state"]) - expect(await adapter.loadRange(["3xuJ5"])).toStrictEqual([ - { key: ["3xuJ5", "snapshot", "7848c"], data: PAYLOAD_C }, + expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([ + { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_C }, ]) + teardown() }) - it("should not remove set of records that doesn't match", async () => { + it("should not remove records that doesn't match", async () => { const { adapter, teardown } = await setup() - await adapter.save(["3xuJ5", "sync-state", "d99d4"], PAYLOAD_B) - await adapter.save(["3xuJ6", "sync-state", "0e05e"], PAYLOAD_C) - await adapter.removeRange(["3xuJ5"]) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) + await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_C) - const actual = await adapter.loadRange(["3xuJ6"]) + await adapter.removeRange(["AAAAA"]) + + const actual = await adapter.loadRange(["BBBBB"]) expect(actual).toStrictEqual([ - { key: ["3xuJ6", "sync-state", "0e05e"], data: PAYLOAD_C }, + { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_C }, ]) + teardown() }) }) From ae4685a7e069d650d7cbc340787c1538459b83cc Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Tue, 19 Mar 2024 16:18:30 +0100 Subject: [PATCH 23/28] add large payload test --- .../helpers/tests/storage-adapter-tests.ts | 70 +++++++++++-------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index c9e746c07..dbf768d74 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -2,13 +2,11 @@ import { describe, expect, it } from "vitest" import type { StorageAdapterInterface } from "../../storage/StorageAdapterInterface.js" -const PAYLOAD_A = new Uint8Array([ - 56, 97, 51, 53, 99, 57, 98, 52, 45, 49, 48, 57, 101, 45, 52, 97, 55, 102, 45, - 97, 51, 53, 101, 45, 97, 53, 52, 54, 52, 49, 50, 49, 98, 54, 100, 100, -]) -const PAYLOAD_B = new Uint8Array([0, 1, 127, 99, 154, 235]) -const PAYLOAD_C = new Uint8Array([1, 76, 160, 53, 57, 10, 230]) -const PAYLOAD_D = new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) +const PAYLOAD_A = new Uint8Array([0, 1, 127, 99, 154, 235]) +const PAYLOAD_B = new Uint8Array([1, 76, 160, 53, 57, 10, 230]) +const PAYLOAD_C = new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) + +const LARGE_PAYLOAD = new Uint8Array(100000).map((_, i) => Math.random() * 256) export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { const setup = async () => { @@ -44,9 +42,19 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should work with composite keys", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) + const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]) + expect(actual).toStrictEqual(PAYLOAD_A) + + teardown() + }) + + it("should work with a large payload", async () => { + const { adapter, teardown } = await setup() + + await adapter.save(["AAAAA", "sync-state", "xxxxx"], LARGE_PAYLOAD) const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]) - expect(actual).toStrictEqual(PAYLOAD_B) + expect(actual).toStrictEqual(LARGE_PAYLOAD) teardown() }) @@ -66,22 +74,22 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should return all the data that is present", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) - await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_C) - await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_D) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) + await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B) + await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C) expect(await adapter.loadRange(["AAAAA"])).toStrictEqual( expect.arrayContaining([ - { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B }, - { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_C }, - { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_D }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A }, + { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B }, + { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C }, ]) ) expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual( expect.arrayContaining([ - { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B }, - { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_D }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A }, + { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C }, ]) ) }) @@ -89,18 +97,18 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("does not includes values which shouldn't be there", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) - await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_D) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) + await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_C) const actual = await adapter.loadRange(["AAAAA"]) expect(actual).toStrictEqual( expect.arrayContaining([ - { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A }, ]) ) expect(actual).toStrictEqual( expect.not.arrayContaining([ - { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_D }, + { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_C }, ]) ) @@ -112,7 +120,7 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should be no data", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_B) + await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_A) await adapter.remove(["AAAAA", "snapshot", "xxxxx"]) expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([]) @@ -128,11 +136,11 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should overwrite data saved with the same key", async () => { const { adapter, teardown } = await setup() + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_C) expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual([ - { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_C }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B }, ]) teardown() @@ -143,14 +151,14 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should remove a range of records", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) - await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_C) - await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_D) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) + await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B) + await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C) await adapter.removeRange(["AAAAA", "sync-state"]) expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([ - { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_C }, + { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B }, ]) teardown() @@ -159,14 +167,14 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should not remove records that doesn't match", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) - await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_C) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) + await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_B) await adapter.removeRange(["AAAAA"]) const actual = await adapter.loadRange(["BBBBB"]) expect(actual).toStrictEqual([ - { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_C }, + { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_B }, ]) teardown() From 649341fbcb8a68ea6cd3fb2789423e4acbbb6bc4 Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Sun, 24 Mar 2024 14:16:23 +0100 Subject: [PATCH 24/28] test names --- .../src/helpers/tests/storage-adapter-tests.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index dbf768d74..b35a3c9ff 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -71,7 +71,7 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { }) describe("save and loadRange", () => { - it("should return all the data that is present", async () => { + it("should return all the data that matches the key", async () => { const { adapter, teardown } = await setup() await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) @@ -94,7 +94,7 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { ) }) - it("does not includes values which shouldn't be there", async () => { + it("should only load values that match they key", async () => { const { adapter, teardown } = await setup() await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) @@ -117,7 +117,7 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { }) describe("save and remove", () => { - it("should be no data", async () => { + it("after removing, should be empty", async () => { const { adapter, teardown } = await setup() await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_A) @@ -164,7 +164,7 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { teardown() }) - it("should not remove records that doesn't match", async () => { + it("should not remove records that don't match", async () => { const { adapter, teardown } = await setup() await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) From fdb0b5c5b2a3fd63bf940ad866f88466a439d7e5 Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Sun, 24 Mar 2024 14:46:26 +0100 Subject: [PATCH 25/28] prettier --- .../automerge-repo-storage-nodefs/src/index.ts | 16 ++++++++++------ .../src/helpers/tests/storage-adapter-tests.ts | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/automerge-repo-storage-nodefs/src/index.ts b/packages/automerge-repo-storage-nodefs/src/index.ts index 00fa34de1..3c0a8f56a 100644 --- a/packages/automerge-repo-storage-nodefs/src/index.ts +++ b/packages/automerge-repo-storage-nodefs/src/index.ts @@ -76,11 +76,10 @@ export class NodeFSStorageAdapter implements StorageAdapterInterface { // The "keys" in the cache don't include the baseDirectory. // We want to de-dupe with the cached keys so we'll use getKey to normalize them. - const diskKeys: string[] = diskFiles - .map((fileName: string) => { - const k = getKey([path.relative(this.baseDirectory, fileName)]) - return k.slice(0, 2) + k.slice(3) - }) + const diskKeys: string[] = diskFiles.map((fileName: string) => { + const k = getKey([path.relative(this.baseDirectory, fileName)]) + return k.slice(0, 2) + k.slice(3) + }) // Combine and deduplicate the lists of keys const allKeys = [...new Set([...cachedKeys, ...diskKeys])] @@ -115,7 +114,12 @@ export class NodeFSStorageAdapter implements StorageAdapterInterface { private getFilePath(keyArray: string[]): string { const [firstKey, ...remainingKeys] = keyArray - return path.join(this.baseDirectory, firstKey.slice(0, 2), firstKey.slice(2), ...remainingKeys) + return path.join( + this.baseDirectory, + firstKey.slice(0, 2), + firstKey.slice(2), + ...remainingKeys + ) } } diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index b35a3c9ff..8da0812e4 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -6,7 +6,7 @@ const PAYLOAD_A = new Uint8Array([0, 1, 127, 99, 154, 235]) const PAYLOAD_B = new Uint8Array([1, 76, 160, 53, 57, 10, 230]) const PAYLOAD_C = new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) -const LARGE_PAYLOAD = new Uint8Array(100000).map((_, i) => Math.random() * 256) +const LARGE_PAYLOAD = new Uint8Array(100000).map(() => Math.random() * 256) export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { const setup = async () => { @@ -72,7 +72,7 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("save and loadRange", () => { it("should return all the data that matches the key", async () => { - const { adapter, teardown } = await setup() + const { adapter } = await setup() await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B) From 62ac01c85017c15dc635f9a691ee63ada1d66b71 Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Sun, 24 Mar 2024 14:48:32 +0100 Subject: [PATCH 26/28] missed a teardown --- .../automerge-repo/src/helpers/tests/storage-adapter-tests.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index 8da0812e4..a8a45c942 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -72,7 +72,7 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { describe("save and loadRange", () => { it("should return all the data that matches the key", async () => { - const { adapter } = await setup() + const { adapter, teardown } = await setup() await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B) @@ -92,6 +92,8 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C }, ]) ) + + teardown() }) it("should only load values that match they key", async () => { From a6ac292a01bc05510cdb93a0c1ed74368e977749 Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Sun, 24 Mar 2024 15:05:46 +0100 Subject: [PATCH 27/28] prettier --- .../vitest.config.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/automerge-repo-storage-nodefs/vitest.config.ts b/packages/automerge-repo-storage-nodefs/vitest.config.ts index 904b29921..4ed7cdb32 100644 --- a/packages/automerge-repo-storage-nodefs/vitest.config.ts +++ b/packages/automerge-repo-storage-nodefs/vitest.config.ts @@ -1,8 +1,11 @@ import { defineConfig, mergeConfig } from "vitest/config" -import rootConfig from '../../vitest.config' +import rootConfig from "../../vitest.config" -export default mergeConfig(rootConfig, defineConfig({ - test: { - environment: "node" - }, -})) +export default mergeConfig( + rootConfig, + defineConfig({ + test: { + environment: "node", + }, + }) +) From 847bbcd0824c98aa019fa9c2239896810e156442 Mon Sep 17 00:00:00 2001 From: Herb Caudill Date: Wed, 27 Mar 2024 14:12:08 +0100 Subject: [PATCH 28/28] convert PAYLOAD_A etc to functions --- .../helpers/tests/storage-adapter-tests.ts | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts index a8a45c942..9d077bf8c 100644 --- a/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts +++ b/packages/automerge-repo/src/helpers/tests/storage-adapter-tests.ts @@ -2,9 +2,9 @@ import { describe, expect, it } from "vitest" import type { StorageAdapterInterface } from "../../storage/StorageAdapterInterface.js" -const PAYLOAD_A = new Uint8Array([0, 1, 127, 99, 154, 235]) -const PAYLOAD_B = new Uint8Array([1, 76, 160, 53, 57, 10, 230]) -const PAYLOAD_C = new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) +const PAYLOAD_A = () => new Uint8Array([0, 1, 127, 99, 154, 235]) +const PAYLOAD_B = () => new Uint8Array([1, 76, 160, 53, 57, 10, 230]) +const PAYLOAD_C = () => new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]) const LARGE_PAYLOAD = new Uint8Array(100000).map(() => Math.random() * 256) @@ -32,9 +32,9 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should return data that was saved", async () => { const { adapter, teardown } = await setup() - await adapter.save(["storage-adapter-id"], PAYLOAD_A) + await adapter.save(["storage-adapter-id"], PAYLOAD_A()) const actual = await adapter.load(["storage-adapter-id"]) - expect(actual).toStrictEqual(PAYLOAD_A) + expect(actual).toStrictEqual(PAYLOAD_A()) teardown() }) @@ -42,9 +42,9 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should work with composite keys", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A()) const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]) - expect(actual).toStrictEqual(PAYLOAD_A) + expect(actual).toStrictEqual(PAYLOAD_A()) teardown() }) @@ -74,22 +74,22 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should return all the data that matches the key", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) - await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B) - await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A()) + await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B()) + await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C()) expect(await adapter.loadRange(["AAAAA"])).toStrictEqual( expect.arrayContaining([ - { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A }, - { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B }, - { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() }, + { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B() }, + { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C() }, ]) ) expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual( expect.arrayContaining([ - { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A }, - { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() }, + { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C() }, ]) ) @@ -99,18 +99,18 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should only load values that match they key", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) - await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_C) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A()) + await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_C()) const actual = await adapter.loadRange(["AAAAA"]) expect(actual).toStrictEqual( expect.arrayContaining([ - { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() }, ]) ) expect(actual).toStrictEqual( expect.not.arrayContaining([ - { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_C }, + { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_C() }, ]) ) @@ -122,7 +122,7 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("after removing, should be empty", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_A) + await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_A()) await adapter.remove(["AAAAA", "snapshot", "xxxxx"]) expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([]) @@ -138,11 +138,11 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should overwrite data saved with the same key", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A()) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B()) expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual([ - { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B }, + { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B() }, ]) teardown() @@ -153,14 +153,14 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should remove a range of records", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) - await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B) - await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A()) + await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B()) + await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C()) await adapter.removeRange(["AAAAA", "sync-state"]) expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([ - { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B }, + { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B() }, ]) teardown() @@ -169,14 +169,14 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void { it("should not remove records that don't match", async () => { const { adapter, teardown } = await setup() - await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A) - await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_B) + await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A()) + await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_B()) await adapter.removeRange(["AAAAA"]) const actual = await adapter.loadRange(["BBBBB"]) expect(actual).toStrictEqual([ - { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_B }, + { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_B() }, ]) teardown()