From 7544a968cf1d3eb3b64661e6ba04443a28cdd8f9 Mon Sep 17 00:00:00 2001 From: Andrea Scartabelli Date: Fri, 20 Sep 2024 10:57:33 +0200 Subject: [PATCH] web-wallet: Move cache test helpers in a common folder Resolves #2443 --- .../__tests__/fillCacheDatabase.spec.js | 38 ++++++ .../__tests__/getCacheDatabase.spec.js | 22 ++++ .../__tests__/getCacheTableCount.spec.js | 30 +++++ .../__tests__/sortCacheNotes.spec.js | 15 +++ .../src/lib/test-helpers/fillCacheDatabase.js | 26 ++++ .../src/lib/test-helpers/getCacheDatabase.js | 16 +++ .../lib/test-helpers/getCacheTableCount.js | 13 ++ web-wallet/src/lib/test-helpers/index.js | 4 + .../src/lib/test-helpers/sortCacheNotes.js | 15 +++ .../lib/wallet-cache/__tests__/index.spec.js | 123 +++++++----------- web-wallet/src/lib/wallet-cache/index.js | 2 +- .../src/lib/wallet-cache/wallet-cache.d.ts | 2 + 12 files changed, 226 insertions(+), 80 deletions(-) create mode 100644 web-wallet/src/lib/test-helpers/__tests__/fillCacheDatabase.spec.js create mode 100644 web-wallet/src/lib/test-helpers/__tests__/getCacheDatabase.spec.js create mode 100644 web-wallet/src/lib/test-helpers/__tests__/getCacheTableCount.spec.js create mode 100644 web-wallet/src/lib/test-helpers/__tests__/sortCacheNotes.spec.js create mode 100644 web-wallet/src/lib/test-helpers/fillCacheDatabase.js create mode 100644 web-wallet/src/lib/test-helpers/getCacheDatabase.js create mode 100644 web-wallet/src/lib/test-helpers/getCacheTableCount.js create mode 100644 web-wallet/src/lib/test-helpers/index.js create mode 100644 web-wallet/src/lib/test-helpers/sortCacheNotes.js diff --git a/web-wallet/src/lib/test-helpers/__tests__/fillCacheDatabase.spec.js b/web-wallet/src/lib/test-helpers/__tests__/fillCacheDatabase.spec.js new file mode 100644 index 0000000000..e74e8395ca --- /dev/null +++ b/web-wallet/src/lib/test-helpers/__tests__/fillCacheDatabase.spec.js @@ -0,0 +1,38 @@ +import { describe, expect, it } from "vitest"; +import { getKey, sortWith } from "lamb"; + +import { + cacheHistory, + cacheSpentNotes, + cacheUnspentNotes, +} from "$lib/mock-data"; + +import { fillCacheDatabase, getCacheDatabase, sortCacheNotes } from ".."; + +const sortHistory = sortWith([getKey("id")]); + +describe("fillCacheDatabase", () => { + it("should fill the database tables with mock data", async () => { + const expectedHistory = sortHistory(cacheHistory); + const expectedSpentNotes = sortCacheNotes(cacheSpentNotes); + const expectedUnspentNotes = sortCacheNotes(cacheUnspentNotes); + + await fillCacheDatabase(); + + const db = getCacheDatabase(); + + await db.open(); + + await expect( + db.table("history").toArray().then(sortHistory) + ).resolves.toStrictEqual(expectedHistory); + await expect( + db.table("spentNotes").toArray().then(sortCacheNotes) + ).resolves.toStrictEqual(expectedSpentNotes); + await expect( + db.table("unspentNotes").toArray().then(sortCacheNotes) + ).resolves.toStrictEqual(expectedUnspentNotes); + + db.close(); + }); +}); diff --git a/web-wallet/src/lib/test-helpers/__tests__/getCacheDatabase.spec.js b/web-wallet/src/lib/test-helpers/__tests__/getCacheDatabase.spec.js new file mode 100644 index 0000000000..0f4a9ff170 --- /dev/null +++ b/web-wallet/src/lib/test-helpers/__tests__/getCacheDatabase.spec.js @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vitest"; +import { getKey } from "lamb"; + +import { getCacheDatabase } from ".."; + +describe("getCacheDatabase", () => { + it("should return the Dexie database used for the cache", async () => { + const db = getCacheDatabase(); + + await db.open(); + + expect(db.tables.map(getKey("name"))).toMatchInlineSnapshot(` + [ + "history", + "spentNotes", + "unspentNotes", + ] + `); + + db.close(); + }); +}); diff --git a/web-wallet/src/lib/test-helpers/__tests__/getCacheTableCount.spec.js b/web-wallet/src/lib/test-helpers/__tests__/getCacheTableCount.spec.js new file mode 100644 index 0000000000..958e1695ba --- /dev/null +++ b/web-wallet/src/lib/test-helpers/__tests__/getCacheTableCount.spec.js @@ -0,0 +1,30 @@ +import { describe, expect, it } from "vitest"; +import { getKey } from "lamb"; + +import { fillCacheDatabase, getCacheDatabase, getCacheTableCount } from ".."; + +describe("getCacheTableCount", () => { + it("should return the amount of items in a database table", async () => { + const db = getCacheDatabase(); + + await db.open(); + + const tableNames = /** @type {WalletCacheTableName[]} */ ( + db.tables.map(getKey("name")) + ); + + for (const tableName of tableNames) { + await expect(getCacheTableCount(tableName)).resolves.toBe(0); + } + + await fillCacheDatabase(); + + for (const tableName of tableNames) { + await expect(getCacheTableCount(tableName)).resolves.toBe( + await db.table(tableName).count() + ); + } + + db.close(); + }); +}); diff --git a/web-wallet/src/lib/test-helpers/__tests__/sortCacheNotes.spec.js b/web-wallet/src/lib/test-helpers/__tests__/sortCacheNotes.spec.js new file mode 100644 index 0000000000..d229dd51b0 --- /dev/null +++ b/web-wallet/src/lib/test-helpers/__tests__/sortCacheNotes.spec.js @@ -0,0 +1,15 @@ +import { describe, expect, it } from "vitest"; + +import { cacheSpentNotes } from "$lib/mock-data"; + +import { sortCacheNotes } from ".."; + +describe("sortCacheNotes", () => { + it("should sort a list of notes by their nullifier", () => { + const sortedNotes = cacheSpentNotes.toSorted((a, b) => + a.nullifier.toString() > b.nullifier.toString() ? 1 : -1 + ); + + expect(sortCacheNotes(cacheSpentNotes)).toStrictEqual(sortedNotes); + }); +}); diff --git a/web-wallet/src/lib/test-helpers/fillCacheDatabase.js b/web-wallet/src/lib/test-helpers/fillCacheDatabase.js new file mode 100644 index 0000000000..0ff82346ad --- /dev/null +++ b/web-wallet/src/lib/test-helpers/fillCacheDatabase.js @@ -0,0 +1,26 @@ +import { getCacheDatabase } from "."; + +import { + cacheHistory, + cacheSpentNotes, + cacheUnspentNotes, +} from "$lib/mock-data"; + +/** @type {() => Promise} */ +async function fillCacheDatabase() { + const db = getCacheDatabase(); + + await db.open(); + + return db + .transaction("rw", ["history", "spentNotes", "unspentNotes"], async () => { + await db.table("history").bulkPut(cacheHistory); + await db.table("spentNotes").bulkPut(cacheSpentNotes); + await db.table("unspentNotes").bulkPut(cacheUnspentNotes); + }) + .finally(() => { + db.close(); + }); +} + +export default fillCacheDatabase; diff --git a/web-wallet/src/lib/test-helpers/getCacheDatabase.js b/web-wallet/src/lib/test-helpers/getCacheDatabase.js new file mode 100644 index 0000000000..3c4abbcbb6 --- /dev/null +++ b/web-wallet/src/lib/test-helpers/getCacheDatabase.js @@ -0,0 +1,16 @@ +import { Dexie } from "dexie"; + +/** @type {() => Dexie} */ +function getCacheDatabase() { + const db = new Dexie("@dusk-network/wallet-cache"); + + db.version(1).stores({ + history: "psk", + spentNotes: "nullifier,&pos,psk", + unspentNotes: "nullifier,&pos,psk", + }); + + return db; +} + +export default getCacheDatabase; diff --git a/web-wallet/src/lib/test-helpers/getCacheTableCount.js b/web-wallet/src/lib/test-helpers/getCacheTableCount.js new file mode 100644 index 0000000000..21388012e2 --- /dev/null +++ b/web-wallet/src/lib/test-helpers/getCacheTableCount.js @@ -0,0 +1,13 @@ +import { getCacheDatabase } from "."; + +/** @type {(tableName: WalletCacheTableName) => Promise} */ +async function getCacheTableCount(tableName) { + const db = await getCacheDatabase().open(); + + return db + .table(tableName) + .count() + .finally(() => db.close()); +} + +export default getCacheTableCount; diff --git a/web-wallet/src/lib/test-helpers/index.js b/web-wallet/src/lib/test-helpers/index.js new file mode 100644 index 0000000000..ede647d293 --- /dev/null +++ b/web-wallet/src/lib/test-helpers/index.js @@ -0,0 +1,4 @@ +export { default as fillCacheDatabase } from "./fillCacheDatabase"; +export { default as getCacheDatabase } from "./getCacheDatabase"; +export { default as getCacheTableCount } from "./getCacheTableCount"; +export { default as sortCacheNotes } from "./sortCacheNotes"; diff --git a/web-wallet/src/lib/test-helpers/sortCacheNotes.js b/web-wallet/src/lib/test-helpers/sortCacheNotes.js new file mode 100644 index 0000000000..fd1b9d0431 --- /dev/null +++ b/web-wallet/src/lib/test-helpers/sortCacheNotes.js @@ -0,0 +1,15 @@ +import { sortWith } from "lamb"; + +/** + * We need to sort the notes in tests as the + * database doesn't guarantee a sort order. + * + * @type {(notes: WalletCacheNote[]) => WalletCacheNote[]} + */ +const sortCacheNotes = sortWith([ + /** @type {(entry: WalletCacheNote) => string} */ ( + (entry) => entry.nullifier.toString() + ), +]); + +export default sortCacheNotes; diff --git a/web-wallet/src/lib/wallet-cache/__tests__/index.spec.js b/web-wallet/src/lib/wallet-cache/__tests__/index.spec.js index 496bdc29e9..d4e23d3a36 100644 --- a/web-wallet/src/lib/wallet-cache/__tests__/index.spec.js +++ b/web-wallet/src/lib/wallet-cache/__tests__/index.spec.js @@ -1,6 +1,5 @@ import { beforeEach, describe, expect, it } from "vitest"; -import { Dexie } from "dexie"; -import { filterWith, partition, setKey, sortWith } from "lamb"; +import { filterWith, partition, setKey } from "lamb"; import { cacheHistory, @@ -8,68 +7,24 @@ import { cacheUnspentNotes, } from "$lib/mock-data"; import { arrayMaxByKey } from "$lib/dusk/array"; +import { + fillCacheDatabase, + getCacheDatabase, + getCacheTableCount, + sortCacheNotes, +} from "$lib/test-helpers"; import walletCache from ".."; -function getDatabase() { - const db = new Dexie("@dusk-network/wallet-cache"); - - db.version(1).stores({ - history: "psk", - spentNotes: "nullifier,&pos,psk", - unspentNotes: "nullifier,&pos,psk", - }); - - return db; -} - -/** @type {(tableName: "history" | "spentNotes" | "unspentNotes") => Promise} */ -async function getTableCount(tableName) { - const db = await getDatabase().open(); - - return db - .table(tableName) - .count() - .finally(() => db.close()); -} - const getMaxLastBlockHeight = arrayMaxByKey("lastBlockHeight"); const getMaxPos = arrayMaxByKey("pos"); -/** - * We need to sort the notes as the database doesn't guarantee - * a sort order. - * - * @type {(notes: WalletCacheNote[]) => WalletCacheNote[]} - */ -const sortNotes = sortWith([ - /** @type {(entry: WalletCacheNote) => string} */ ( - (entry) => entry.nullifier.toString() - ), -]); - -async function fillDatabase() { - const db = getDatabase(); - - await db.open(); - - return db - .transaction("rw", ["history", "spentNotes", "unspentNotes"], async () => { - await db.table("history").bulkPut(cacheHistory); - await db.table("spentNotes").bulkPut(cacheSpentNotes); - await db.table("unspentNotes").bulkPut(cacheUnspentNotes); - }) - .finally(() => { - db.close(); - }); -} - describe("Wallet cache", () => { - const db = getDatabase(); + const db = getCacheDatabase(); beforeEach(async () => { - await getDatabase().delete(); - await fillDatabase(); + await getCacheDatabase().delete(); + await fillCacheDatabase(); }); describe("Reading and clearing the cache", async () => { @@ -108,9 +63,11 @@ describe("Wallet cache", () => { }); it("should expose a method to retrieve all existing notes and optionally filter them by a `psk`", async () => { - const allDbNotes = sortNotes(await walletCache.getAllNotes()); - const allNotes = sortNotes(cacheUnspentNotes.concat(cacheSpentNotes)); - const dbNotesByPsk = sortNotes(await walletCache.getAllNotes(psk)); + const allDbNotes = sortCacheNotes(await walletCache.getAllNotes()); + const allNotes = sortCacheNotes( + cacheUnspentNotes.concat(cacheSpentNotes) + ); + const dbNotesByPsk = sortCacheNotes(await walletCache.getAllNotes(psk)); const notesByPsk = filterByPsk(allNotes); expect(allDbNotes).toStrictEqual(allNotes); @@ -128,9 +85,11 @@ describe("Wallet cache", () => { }); it("should expose a method to retrieve the spent notes and optionally filter them by a `psk`", async () => { - const spentDbNotes = sortNotes(await walletCache.getSpentNotes()); - const spentNotes = sortNotes(cacheSpentNotes); - const spentDbNotesByPsk = sortNotes(await walletCache.getSpentNotes(psk)); + const spentDbNotes = sortCacheNotes(await walletCache.getSpentNotes()); + const spentNotes = sortCacheNotes(cacheSpentNotes); + const spentDbNotesByPsk = sortCacheNotes( + await walletCache.getSpentNotes(psk) + ); const spentNotesByPsk = filterByPsk(spentNotes); expect(spentDbNotes).toStrictEqual(spentNotes); @@ -139,9 +98,11 @@ describe("Wallet cache", () => { }); it("should expose a method to retrieve the unspent notes and optionally filter them by a `psk`", async () => { - const unspentDbNotes = sortNotes(await walletCache.getUnspentNotes()); - const unspentNotes = sortNotes(cacheUnspentNotes); - const unspentDbNotesByPsk = sortNotes( + const unspentDbNotes = sortCacheNotes( + await walletCache.getUnspentNotes() + ); + const unspentNotes = sortCacheNotes(cacheUnspentNotes); + const unspentDbNotesByPsk = sortCacheNotes( await walletCache.getUnspentNotes(psk) ); const unspentNotesByPsk = filterByPsk(unspentNotes); @@ -191,7 +152,7 @@ describe("Wallet cache", () => { ).resolves.toStrictEqual(entry); } - await expect(getTableCount("history")).resolves.toBe( + await expect(getCacheTableCount("history")).resolves.toBe( cacheHistory.length + 1 ); }); @@ -214,7 +175,9 @@ describe("Wallet cache", () => { await expect( walletCache.getHistoryEntry(cacheHistory[1].psk) ).resolves.toStrictEqual(cacheHistory[1]); - await expect(getTableCount("history")).resolves.toBe(cacheHistory.length); + await expect(getCacheTableCount("history")).resolves.toBe( + cacheHistory.length + ); }); it("should leave the history as it was before if an error occurs during writing", async () => { @@ -229,7 +192,9 @@ describe("Wallet cache", () => { ).resolves.toStrictEqual(entry); } - await expect(getTableCount("history")).resolves.toBe(cacheHistory.length); + await expect(getCacheTableCount("history")).resolves.toBe( + cacheHistory.length + ); }); }); @@ -279,15 +244,15 @@ describe("Wallet cache", () => { await walletCache.addSpentNotes(newNotes); await expect( - walletCache.getUnspentNotes().then(sortNotes) - ).resolves.toStrictEqual(sortNotes(expectedUnspentNotes)); + walletCache.getUnspentNotes().then(sortCacheNotes) + ).resolves.toStrictEqual(sortCacheNotes(expectedUnspentNotes)); await expect( - walletCache.getSpentNotes().then(sortNotes) - ).resolves.toStrictEqual(sortNotes(expectedSpentNotes)); - await expect(getTableCount("spentNotes")).resolves.toBe( + walletCache.getSpentNotes().then(sortCacheNotes) + ).resolves.toStrictEqual(sortCacheNotes(expectedSpentNotes)); + await expect(getCacheTableCount("spentNotes")).resolves.toBe( cacheSpentNotes.length + notesBeingSpent.length + 1 ); - await expect(getTableCount("unspentNotes")).resolves.toBe( + await expect(getCacheTableCount("unspentNotes")).resolves.toBe( cacheUnspentNotes.length - notesBeingSpent.length ); }); @@ -300,10 +265,10 @@ describe("Wallet cache", () => { Error ); - const allNotes = sortNotes(await walletCache.getAllNotes()); + const allNotes = sortCacheNotes(await walletCache.getAllNotes()); expect( - sortNotes(cacheSpentNotes.concat(cacheUnspentNotes)) + sortCacheNotes(cacheSpentNotes.concat(cacheUnspentNotes)) ).toStrictEqual(allNotes); }); @@ -335,8 +300,8 @@ describe("Wallet cache", () => { await walletCache.addUnspentNotes(newNotes); await expect( - walletCache.getUnspentNotes().then(sortNotes) - ).resolves.toStrictEqual(sortNotes(expectedUnspentNotes)); + walletCache.getUnspentNotes().then(sortCacheNotes) + ).resolves.toStrictEqual(sortCacheNotes(expectedUnspentNotes)); }); it("should leave the unspent notes as they were if an error occurs during insertion", async () => { @@ -347,8 +312,8 @@ describe("Wallet cache", () => { Error ); - expect(sortNotes(cacheUnspentNotes)).toStrictEqual( - sortNotes(await walletCache.getUnspentNotes()) + expect(sortCacheNotes(cacheUnspentNotes)).toStrictEqual( + sortCacheNotes(await walletCache.getUnspentNotes()) ); }); }); diff --git a/web-wallet/src/lib/wallet-cache/index.js b/web-wallet/src/lib/wallet-cache/index.js index 053e6d28d7..fbaaab4a82 100644 --- a/web-wallet/src/lib/wallet-cache/index.js +++ b/web-wallet/src/lib/wallet-cache/index.js @@ -8,7 +8,7 @@ class WalletCache { #db; /** - * @template {"history" | "spentNotes" | "unspentNotes"} TName + * @template {WalletCacheTableName} TName * @param {TName} tableName * @param {string} [psk] * @returns {Promise} diff --git a/web-wallet/src/lib/wallet-cache/wallet-cache.d.ts b/web-wallet/src/lib/wallet-cache/wallet-cache.d.ts index 9e31427f7d..8bfc10a0d6 100644 --- a/web-wallet/src/lib/wallet-cache/wallet-cache.d.ts +++ b/web-wallet/src/lib/wallet-cache/wallet-cache.d.ts @@ -11,3 +11,5 @@ type WalletCacheHistoryEntry = { lastBlockHeight: number; psk: string; }; + +type WalletCacheTableName = "history" | "spentNotes" | "unspentNotes";