diff --git a/apps/extension/src/storage/base-defaults.test.ts b/apps/extension/src/storage/base-defaults.test.ts index 7e0c96e5..e02fbb63 100644 --- a/apps/extension/src/storage/base-defaults.test.ts +++ b/apps/extension/src/storage/base-defaults.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, test, vi } from 'vitest'; +import { beforeEach, describe, expect, test } from 'vitest'; import { ExtensionStorage } from './base'; import { MockStorageArea } from './mock'; diff --git a/apps/extension/src/storage/migrations/local-v1-migration.test.ts b/apps/extension/src/storage/migrations/local-v1-migration.test.ts index 264f22bd..d0a76de0 100644 --- a/apps/extension/src/storage/migrations/local-v1-migration.test.ts +++ b/apps/extension/src/storage/migrations/local-v1-migration.test.ts @@ -7,14 +7,19 @@ import { walletIdFromBech32m } from '@penumbra-zone/bech32m/penumbrawalletid'; import { fullViewingKeyFromBech32m } from '@penumbra-zone/bech32m/penumbrafullviewingkey'; import { LocalStorageState } from '../types'; import { localV0Migration, V0LocalStorageState, V0LocalStorageVersion } from './local-v1-migration'; +import { ChainRegistryClient } from '@penumbra-labs/registry'; +import { sample } from 'lodash'; +import { AppParameters } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/app/v1/app_pb'; +import { UserChoice } from '@penumbra-zone/types/user-choice'; -describe('migrate walletId and fullViewingKey from bech32 string to json stringified', () => { +const bech32FVK = + 'penumbrafullviewingkey1vzfytwlvq067g2kz095vn7sgcft47hga40atrg5zu2crskm6tyyjysm28qg5nth2fqmdf5n0q530jreumjlsrcxjwtfv6zdmfpe5kqsa5lg09'; +const bech32WalletId = + 'penumbrawalletid15r7q7qsf3hhsgj0g530n7ng9acdacmmx9ajknjz38dyt90u9gcgsmjre75'; + +describe('v1 old local schema migrations', () => { let rawStorage: MockStorageArea; let v1ExtStorage: ExtensionStorage; - const bech32FVK = - 'penumbrafullviewingkey1vzfytwlvq067g2kz095vn7sgcft47hga40atrg5zu2crskm6tyyjysm28qg5nth2fqmdf5n0q530jreumjlsrcxjwtfv6zdmfpe5kqsa5lg09'; - const bech32WalletId = - 'penumbrawalletid15r7q7qsf3hhsgj0g530n7ng9acdacmmx9ajknjz38dyt90u9gcgsmjre75'; beforeEach(() => { rawStorage = new MockStorageArea(); @@ -30,7 +35,91 @@ describe('migrate walletId and fullViewingKey from bech32 string to json stringi }); }); - test('should successfully migrate from V2 (old data structure) to dbVersion 1', async () => { + test('works with all v1 local migrations together', async () => { + const walletsVal = [ + { + fullViewingKey: bech32FVK, + label: 'Wallet 1', + id: bech32WalletId, + custody: { encryptedSeedPhrase: { nonce: '', cipherText: '' } }, + }, + ]; + const grpcEndpointVal = 'https://penumbra.stakewith.binary.builders'; + const frontendUrlVal = 'https://stake.with.starlingcyber.net'; + const passwordKeyPrintVal = { hash: 'xyz', salt: 'abc' }; + const fullSyncHeightVal = 13524524; + const knownSitesVal = [ + { + origin: 'google.com', + choice: UserChoice.Approved, + date: 12342342, + }, + ]; + const paramsVal = new AppParameters({ chainId: 'penumbra-1' }).toJsonString(); + + const mock0StorageState: Record = { + wallets: { + version: V0LocalStorageVersion.V1, + value: walletsVal, + }, + grpcEndpoint: { version: V0LocalStorageVersion.V1, value: grpcEndpointVal }, + frontendUrl: { version: V0LocalStorageVersion.V1, value: frontendUrlVal }, + passwordKeyPrint: { version: V0LocalStorageVersion.V1, value: passwordKeyPrintVal }, + fullSyncHeight: { version: V0LocalStorageVersion.V1, value: fullSyncHeightVal }, + knownSites: { version: V0LocalStorageVersion.V1, value: knownSitesVal }, + params: { version: V0LocalStorageVersion.V1, value: paramsVal }, + // Test missing field + // numeraires: { version: V0LocalStorageVersion.V1, value: numerairesVal }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const wallets = await v1ExtStorage.get('wallets'); + expect(WalletId.fromJsonString(wallets[0]?.id ?? '').inner).toEqual( + walletIdFromBech32m(bech32WalletId).inner, + ); + expect(FullViewingKey.fromJsonString(wallets[0]?.fullViewingKey ?? '').inner).toEqual( + fullViewingKeyFromBech32m(bech32FVK).inner, + ); + + const endpoint = await v1ExtStorage.get('grpcEndpoint'); + expect(endpoint).toEqual(grpcEndpointVal); + + const frontendUrl = await v1ExtStorage.get('frontendUrl'); + expect(frontendUrl).toEqual(frontendUrlVal); + + const passwordKeyPrint = await v1ExtStorage.get('passwordKeyPrint'); + expect(passwordKeyPrint).toEqual(passwordKeyPrintVal); + + const fullSyncHeight = await v1ExtStorage.get('fullSyncHeight'); + expect(fullSyncHeight).toEqual(fullSyncHeightVal); + + const knownSites = await v1ExtStorage.get('knownSites'); + expect(knownSites).toEqual(knownSitesVal); + + const params = await v1ExtStorage.get('params'); + expect(params).toEqual(paramsVal); + }); +}); + +describe('v1 old schema: migrate walletId and fullViewingKey', () => { + let rawStorage: MockStorageArea; + let v1ExtStorage: ExtensionStorage; + + beforeEach(() => { + rawStorage = new MockStorageArea(); + v1ExtStorage = new ExtensionStorage({ + storage: rawStorage, + defaults: localDefaults, + version: { + current: 1, + migrations: { + 0: localV0Migration, + }, + }, + }); + }); + + test('should successfully migrate from V1 (old data structure) to dbVersion 1 (new data structure)', async () => { const mock0StorageState: Record = { wallets: { version: V0LocalStorageVersion.V1, @@ -43,36 +132,39 @@ describe('migrate walletId and fullViewingKey from bech32 string to json stringi }, ], }, - grpcEndpoint: { version: V0LocalStorageVersion.V2, value: 'void.s9.gay' }, - frontendUrl: { version: V0LocalStorageVersion.V2, value: 'frontend.vercel' }, - passwordKeyPrint: { version: V0LocalStorageVersion.V2, value: { hash: '', salt: '' } }, - fullSyncHeight: { version: V0LocalStorageVersion.V2, value: 123 }, - knownSites: { version: V0LocalStorageVersion.V2, value: [] }, - params: { version: V0LocalStorageVersion.V2, value: '' }, - // numeraires: { version: V0LocalStorageVersion.V2, value: [] }, } satisfies Partial; await rawStorage.set(mock0StorageState); const versionA = await rawStorage.get('dbVersion'); expect(versionA).toStrictEqual({}); - const v1Wallets = (await rawStorage.get('wallets')) as Record< + const v0Wallets = (await rawStorage.get('wallets')) as Record< 'wallets', { version: string; value: { id: string; fullViewingKey: string }[] } >; - expect(v1Wallets.wallets.version).toBe(V0LocalStorageVersion.V1); - expect(v1Wallets.wallets.value[0]?.id === bech32WalletId).toBeTruthy(); - expect(v1Wallets.wallets.value[0]?.fullViewingKey === bech32FVK).toBeTruthy(); + expect(v0Wallets.wallets.version).toBe(V0LocalStorageVersion.V1); + expect(v0Wallets.wallets.value[0]?.id === bech32WalletId).toBeTruthy(); + expect(v0Wallets.wallets.value[0]?.fullViewingKey === bech32FVK).toBeTruthy(); - const v2Wallets = await v1ExtStorage.get('wallets'); - expect(WalletId.fromJsonString(v2Wallets[0]?.id ?? '').inner).toEqual( + const v1Wallets = await v1ExtStorage.get('wallets'); + expect(WalletId.fromJsonString(v1Wallets[0]?.id ?? '').inner).toEqual( walletIdFromBech32m(bech32WalletId).inner, ); - expect(FullViewingKey.fromJsonString(v2Wallets[0]?.fullViewingKey ?? '').inner).toEqual( + expect(FullViewingKey.fromJsonString(v1Wallets[0]?.fullViewingKey ?? '').inner).toEqual( fullViewingKeyFromBech32m(bech32FVK).inner, ); }); + test('work if no v0 wallets', async () => { + const mock0StorageState: Record = { + knownSites: { version: V0LocalStorageVersion.V1, value: [] }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const v1Wallets = await v1ExtStorage.get('wallets'); + expect(v1Wallets).toStrictEqual([]); + }); + test('should not migrate if its not needed', async () => { await v1ExtStorage.set('wallets', [ { @@ -92,3 +184,144 @@ describe('migrate walletId and fullViewingKey from bech32 string to json stringi ); }); }); + +describe('v2 old schema: validate grpc & frontendUrl', () => { + let rawStorage: MockStorageArea; + let v1ExtStorage: ExtensionStorage; + + beforeEach(() => { + rawStorage = new MockStorageArea(); + v1ExtStorage = new ExtensionStorage({ + storage: rawStorage, + defaults: localDefaults, + version: { + current: 1, + migrations: { + 0: localV0Migration, + }, + }, + }); + }); + + test('non-affected fields stay the same', async () => { + const mock0StorageState: Record = { + fullSyncHeight: { version: V0LocalStorageVersion.V2, value: 9483729 }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const versionA = await rawStorage.get('dbVersion'); + expect(versionA).toStrictEqual({}); + + const syncHeight = await v1ExtStorage.get('fullSyncHeight'); + expect(syncHeight).toEqual(9483729); + }); + + describe('frontends', () => { + test('not set frontend gets ignored', async () => { + const mock0StorageState: Record = { + frontendUrl: { version: V0LocalStorageVersion.V2, value: '' }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const url = await v1ExtStorage.get('frontendUrl'); + expect(url).toEqual(''); + }); + + test('have no change if user already selected frontend in registry', async () => { + const registryClient = new ChainRegistryClient(); + const { frontends } = registryClient.bundled.globals(); + const suggestedFrontend = sample(frontends.map(f => f.url)); + const mock0StorageState: Record = { + frontendUrl: { version: V0LocalStorageVersion.V2, value: suggestedFrontend }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const url = await v1ExtStorage.get('frontendUrl'); + expect(url).toEqual(suggestedFrontend); + }); + + test('user gets migrated to suggested frontend', async () => { + const registryClient = new ChainRegistryClient(); + const { frontends } = registryClient.bundled.globals(); + const mock0StorageState: Record = { + frontendUrl: { version: V0LocalStorageVersion.V2, value: 'http://badfrontend.void' }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const url = await v1ExtStorage.get('frontendUrl'); + expect(url).not.toEqual('http://badfrontend.void'); + expect(frontends.map(f => f.url).includes(url!)).toBeTruthy(); + }); + }); + + describe('grpcEndpoint', () => { + test('not set gets ignored', async () => { + const mock0StorageState: Record = { + grpcEndpoint: { version: V0LocalStorageVersion.V2, value: undefined }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const url = await v1ExtStorage.get('grpcEndpoint'); + expect(url).toEqual(undefined); + }); + + test('not connected to mainnet gets ignored', async () => { + const appParams = new AppParameters({ chainId: 'testnet-deimos-42' }); + const mock0StorageState: Record = { + params: { version: V0LocalStorageVersion.V2, value: appParams.toJsonString() }, + grpcEndpoint: { version: V0LocalStorageVersion.V2, value: 'grpc.testnet.void' }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const endpoint = await v1ExtStorage.get('grpcEndpoint'); + expect(endpoint).toEqual('grpc.testnet.void'); + }); + + test('user selected suggested endpoint', async () => { + const appParams = new AppParameters({ chainId: 'penumbra-1' }); + const registryClient = new ChainRegistryClient(); + const { rpcs } = registryClient.bundled.globals(); + const suggestedRpc = sample(rpcs.map(f => f.url)); + const mock0StorageState: Record = { + params: { version: V0LocalStorageVersion.V2, value: appParams.toJsonString() }, + grpcEndpoint: { version: V0LocalStorageVersion.V2, value: suggestedRpc }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const endpoint = await v1ExtStorage.get('grpcEndpoint'); + expect(endpoint).toEqual(suggestedRpc); + }); + + test('user gets migrated to suggested endpoint', async () => { + const appParams = new AppParameters({ chainId: 'penumbra-1' }); + const mock0StorageState: Record = { + params: { version: V0LocalStorageVersion.V2, value: appParams.toJsonString() }, + grpcEndpoint: { version: V0LocalStorageVersion.V2, value: 'http://badfrontend.void' }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const endpoint = await v1ExtStorage.get('grpcEndpoint'); + expect(endpoint).not.toEqual('http://badfrontend.void'); + + const registryClient = new ChainRegistryClient(); + const { rpcs } = registryClient.bundled.globals(); + expect(rpcs.map(r => r.url).includes(endpoint!)).toBeTruthy(); + }); + + test('works from V1 storage as well', async () => { + const appParams = new AppParameters({ chainId: 'penumbra-1' }); + const mock0StorageState: Record = { + params: { version: V0LocalStorageVersion.V1, value: appParams.toJsonString() }, + grpcEndpoint: { version: V0LocalStorageVersion.V1, value: 'http://badfrontend.void' }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const endpoint = await v1ExtStorage.get('grpcEndpoint'); + expect(endpoint).not.toEqual('http://badfrontend.void'); + + const registryClient = new ChainRegistryClient(); + const { rpcs } = registryClient.bundled.globals(); + expect(rpcs.map(r => r.url).includes(endpoint!)).toBeTruthy(); + }); + }); +}); diff --git a/apps/extension/src/storage/migrations/local-v2-migration.test.ts b/apps/extension/src/storage/migrations/local-v2-migration.test.ts deleted file mode 100644 index 39756c53..00000000 --- a/apps/extension/src/storage/migrations/local-v2-migration.test.ts +++ /dev/null @@ -1,143 +0,0 @@ -// import { beforeEach, describe, expect, test } from 'vitest'; -// import { MockStorageArea } from '../mock'; -// import { ExtensionStorage } from '../base'; -// import { localDefaults } from '../local'; -// import { LocalStorageState, LocalStorageVersion } from '../types'; -// import { localV1Migrations } from './local-v1-migrations'; -// import { localV2Migrations } from './local-v2-migrations'; -// import { ChainRegistryClient } from '@penumbra-labs/registry'; -// import { sample } from 'lodash'; -// import { AppParameters } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/app/v1/app_pb'; -// -// describe('v2 local storage migrations', () => { -// const storageArea = new MockStorageArea(); -// let v1ExtStorage: ExtensionStorage; -// let v2ExtStorage: ExtensionStorage; -// let v3ExtStorage: ExtensionStorage; -// -// beforeEach(() => { -// v1ExtStorage = new ExtensionStorage( -// storageArea, -// localDefaults, -// LocalStorageVersion.V1, -// ); -// -// v2ExtStorage = new ExtensionStorage( -// storageArea, -// localDefaults, -// LocalStorageVersion.V2, -// { -// [LocalStorageVersion.V1]: localV1Migrations, -// }, -// { -// [LocalStorageVersion.V1]: LocalStorageVersion.V2, -// }, -// ); -// -// v3ExtStorage = new ExtensionStorage( -// storageArea, -// localDefaults, -// LocalStorageVersion.V3, -// { -// [LocalStorageVersion.V1]: localV1Migrations, -// [LocalStorageVersion.V2]: localV2Migrations, -// }, -// { -// [LocalStorageVersion.V1]: LocalStorageVersion.V2, -// [LocalStorageVersion.V2]: LocalStorageVersion.V3, -// }, -// ); -// }); -// -// test('non-affected fields stay the same', async () => { -// await v1ExtStorage.set('fullSyncHeight', 9483729); -// const syncHeight = await v3ExtStorage.get('fullSyncHeight'); -// expect(syncHeight).toEqual(9483729); -// }); -// -// describe('frontends', () => { -// test('not set frontend gets ignored', async () => { -// await v1ExtStorage.set('frontendUrl', ''); -// const url = await v3ExtStorage.get('frontendUrl'); -// expect(url).toEqual(''); -// }); -// -// test('have no change if user already selected frontend in registry', async () => { -// const registryClient = new ChainRegistryClient(); -// const { frontends } = registryClient.bundled.globals(); -// const suggestedFrontend = sample(frontends.map(f => f.url)); -// await v1ExtStorage.set('frontendUrl', suggestedFrontend); -// const url = await v3ExtStorage.get('frontendUrl'); -// expect(url).toEqual(suggestedFrontend); -// }); -// -// test('user gets migrated to suggested frontend', async () => { -// const registryClient = new ChainRegistryClient(); -// const { frontends } = registryClient.bundled.globals(); -// await v1ExtStorage.set('frontendUrl', 'http://badfrontend.void'); -// const url = await v3ExtStorage.get('frontendUrl'); -// expect(url).not.toEqual('http://badfrontend.void'); -// expect(frontends.map(f => f.url).includes(url!)).toBeTruthy(); -// }); -// -// test('works from v2 storage as well', async () => { -// const registryClient = new ChainRegistryClient(); -// const { frontends } = registryClient.bundled.globals(); -// await v2ExtStorage.set('frontendUrl', 'http://badfrontend.void'); -// const url = await v3ExtStorage.get('frontendUrl'); -// expect(url).not.toEqual('http://badfrontend.void'); -// expect(frontends.map(f => f.url).includes(url!)).toBeTruthy(); -// }); -// }); -// -// describe('grpcEndpoint', () => { -// test('not set gets ignored', async () => { -// await v1ExtStorage.set('grpcEndpoint', undefined); -// const url = await v3ExtStorage.get('grpcEndpoint'); -// expect(url).toEqual(undefined); -// }); -// -// test('not connected to mainnet gets ignored', async () => { -// const appParams = new AppParameters({ chainId: 'testnet-deimos-42' }); -// await v1ExtStorage.set('params', appParams.toJsonString()); -// await v1ExtStorage.set('grpcEndpoint', 'grpc.testnet.void'); -// const endpoint = await v3ExtStorage.get('grpcEndpoint'); -// expect(endpoint).toEqual('grpc.testnet.void'); -// }); -// -// test('user selected suggested endpoint', async () => { -// const appParams = new AppParameters({ chainId: 'penumbra-1' }); -// await v1ExtStorage.set('params', appParams.toJsonString()); -// const registryClient = new ChainRegistryClient(); -// const { rpcs } = registryClient.bundled.globals(); -// const suggestedRpc = sample(rpcs.map(f => f.url)); -// await v1ExtStorage.set('grpcEndpoint', suggestedRpc); -// const endpoint = await v3ExtStorage.get('grpcEndpoint'); -// expect(endpoint).toEqual(suggestedRpc); -// }); -// -// test('user gets migrated to suggested frontend', async () => { -// const appParams = new AppParameters({ chainId: 'penumbra-1' }); -// await v1ExtStorage.set('params', appParams.toJsonString()); -// await v1ExtStorage.set('grpcEndpoint', 'http://badfrontend.void'); -// const endpoint = await v3ExtStorage.get('grpcEndpoint'); -// expect(endpoint).not.toEqual('http://badfrontend.void'); -// -// const registryClient = new ChainRegistryClient(); -// const { rpcs } = registryClient.bundled.globals(); -// expect(rpcs.map(r => r.url).includes(endpoint!)).toBeTruthy(); -// }); -// -// test('works from v2 storage as well', async () => { -// const appParams = new AppParameters({ chainId: 'penumbra-1' }); -// await v2ExtStorage.set('params', appParams.toJsonString()); -// await v2ExtStorage.set('grpcEndpoint', 'http://badfrontend.void'); -// const endpoint = await v3ExtStorage.get('grpcEndpoint'); -// expect(endpoint).not.toEqual('http://badfrontend.void'); -// -// const registryClient = new ChainRegistryClient(); -// const { rpcs } = registryClient.bundled.globals(); -// expect(rpcs.map(r => r.url).includes(endpoint!)).toBeTruthy(); -// }); -// }); -// }); diff --git a/apps/extension/src/storage/migrations/session-v1-migration.test.ts b/apps/extension/src/storage/migrations/session-v1-migration.test.ts new file mode 100644 index 00000000..19349f29 --- /dev/null +++ b/apps/extension/src/storage/migrations/session-v1-migration.test.ts @@ -0,0 +1,58 @@ +import { beforeEach, describe, expect, test } from 'vitest'; +import { MockStorageArea } from '../mock'; +import { ExtensionStorage } from '../base'; +import { sessionDefaults, SessionStorageState } from '../session'; +import { + sessionV0Migration, + V0SessionStorageState, + V0SessionStorageVersion, +} from './session-v1-migration'; + +describe('v1/v2 old schema: validate grpc & frontendUrl', () => { + let rawStorage: MockStorageArea; + let v1ExtStorage: ExtensionStorage; + + beforeEach(() => { + rawStorage = new MockStorageArea(); + v1ExtStorage = new ExtensionStorage({ + storage: rawStorage, + defaults: sessionDefaults, + version: { + current: 1, + migrations: { + 0: sessionV0Migration, + }, + }, + }); + }); + + test('non-affected fields stay the same', async () => { + const jsonWebKey = { + alg: 'A256GCM', + ext: true, + k: '2l2K1HKpGWaOriS58zwdDTwAMtMuczuUQc4IYzGxyhM', + kty: 'oct', + key_ops: ['encrypt', 'decrypt'], + }; + const mock0StorageState: Record = { + passwordKey: { + version: V0SessionStorageVersion.V1, + value: { + _inner: jsonWebKey, + }, + }, + } satisfies Partial; + await rawStorage.set(mock0StorageState); + + const versionA = await rawStorage.get('dbVersion'); + expect(versionA).toStrictEqual({}); + + const passwordKey = await v1ExtStorage.get('passwordKey'); + expect(passwordKey?._inner).toEqual(jsonWebKey); + }); + + test('undefined stays undefined', async () => { + const passwordKey = await v1ExtStorage.get('passwordKey'); + expect(passwordKey).toBeUndefined(); + }); +}); diff --git a/apps/extension/src/storage/migrations/session-v1-migration.ts b/apps/extension/src/storage/migrations/session-v1-migration.ts index f710a215..3bb87501 100644 --- a/apps/extension/src/storage/migrations/session-v1-migration.ts +++ b/apps/extension/src/storage/migrations/session-v1-migration.ts @@ -2,7 +2,7 @@ import { MigrationFn } from '../base'; import { KeyJson } from '@penumbra-zone/crypto-web/encryption'; import { SessionStorageState } from '../session'; -enum V0SessionStorageVersion { +export enum V0SessionStorageVersion { V1 = 'V1', } @@ -12,7 +12,7 @@ interface StorageItem { } // Note: previous session storage used to key a version on each individual field -interface V0SessionStorageState { +export interface V0SessionStorageState { passwordKey: StorageItem; } diff --git a/apps/extension/src/storage/mock.ts b/apps/extension/src/storage/mock.ts index 3928311a..c735318b 100644 --- a/apps/extension/src/storage/mock.ts +++ b/apps/extension/src/storage/mock.ts @@ -2,6 +2,8 @@ import { ExtensionStorage, IStorage } from './base'; import { localDefaults } from './local'; import { sessionDefaults, SessionStorageState } from './session'; import { LocalStorageState } from './types'; +import { sessionV0Migration } from './migrations/session-v1-migration'; +import { localV0Migration } from './migrations/local-v1-migration'; // Helpful for testing interactions with session & local storage export class MockStorageArea implements IStorage { @@ -76,12 +78,22 @@ export const mockSessionExtStorage = () => new ExtensionStorage({ storage: new MockStorageArea(), defaults: sessionDefaults, - version: { current: 1 }, + version: { + current: 1, + migrations: { + 0: sessionV0Migration, + }, + }, }); export const mockLocalExtStorage = () => new ExtensionStorage({ storage: new MockStorageArea(), defaults: localDefaults, - version: { current: 1 }, + version: { + current: 1, + migrations: { + 0: localV0Migration, + }, + }, }); diff --git a/apps/extension/src/utils/tests-setup.js b/apps/extension/src/utils/tests-setup.js index aa6b7b79..d4b4a7a3 100644 --- a/apps/extension/src/utils/tests-setup.js +++ b/apps/extension/src/utils/tests-setup.js @@ -9,13 +9,15 @@ global.chrome = { }, local: { set: vi.fn(), - get: vi.fn().mockReturnValue({}), + get: vi.fn().mockReturnValue({ wallets: [] }), + getBytesInUse: vi.fn().mockReturnValue(0), remove: vi.fn(), }, session: { set: vi.fn(), get: vi.fn().mockReturnValue({}), remove: vi.fn(), + getBytesInUse: vi.fn().mockReturnValue(0), }, }, runtime: {