Skip to content

Commit

Permalink
Improve error messaging
Browse files Browse the repository at this point in the history
  • Loading branch information
grod220 committed Aug 23, 2024
1 parent 0c90fb4 commit de23cdf
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 13 deletions.
30 changes: 17 additions & 13 deletions apps/extension/src/storage/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,19 +184,23 @@ export class ExtensionStorage<T extends { dbVersion: number }> {
* Initializes the database with defaults or performs migrations (multiple possible if a sequence is needed).
*/
private async migrateOrInitializeIfNeeded(): Promise<void> {
// If db is empty, initialize it with defaults.
const bytesInUse = await this.storage.getBytesInUse();
if (bytesInUse === 0) {
const allDefaults = { ...this.defaults, dbVersion: this.version.current };
// @ts-expect-error Typescript does not know how to combine the above types
await this._set(allDefaults);
return;
}

let storedVersion = (await this._get('dbVersion')) ?? 0; // default to zero
// If stored version is not the same, keep migrating versions until current
while (storedVersion !== this.version.current) {
storedVersion = await this.migrateAllFields(storedVersion);
try {
// If db is empty, initialize it with defaults.
const bytesInUse = await this.storage.getBytesInUse();
if (bytesInUse === 0) {
const allDefaults = { ...this.defaults, dbVersion: this.version.current };
// @ts-expect-error Typescript does not know how to combine the above types
await this._set(allDefaults);
return;
}

let storedVersion = (await this._get('dbVersion')) ?? 0; // default to zero
// If stored version is not the same, keep migrating versions until current
while (storedVersion !== this.version.current) {
storedVersion = await this.migrateAllFields(storedVersion);
}
} catch (e) {
throw new Error(`There was an error with migrating the database: ${String(e)}`);
}
}
}
153 changes: 153 additions & 0 deletions apps/extension/src/storage/migrations/base-migration.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { MockStorageArea } from '../mock';
import { ExtensionStorage, RequiredMigrations } from '../base';
import { localV0Migration } from './local-v1-migration';

interface MockV0State {
network: string;
Expand Down Expand Up @@ -295,5 +296,157 @@ describe('Storage migrations', () => {
const result = await v2ExtStorage.get('newField');
expect(result).toBeUndefined();
});

test('error during migration from v0 to v1', async () => {
const faultyMigration = new ExtensionStorage<MockV1State>({
storage: rawStorage,
defaults: {
network: '',
accounts: [],
seedPhrase: [],
frontend: 'http://default.com',
grpcUrl: { url: '' },
fullSyncHeight: 0,
},
version: {
current: 1,
migrations: {
0: () => {
throw new Error('network request error 404');
},
},
},
});

const mock0StorageState: Record<string, unknown> = {
network: '',
accounts: [],
seedPhrase: 'cat dog mouse horse',
frontend: 'http://default.com',
grpcUrl: 'grpc.void.test',
fullSyncHeight: 0,
} satisfies MockV0State;
await rawStorage.set(mock0StorageState);

await expect(faultyMigration.get('network')).rejects.toThrow(
'There was an error with migrating the database: Error: network request error 404',
);
});

test('error during migration from v1 to v2', async () => {
const mock1Storage = new ExtensionStorage<MockV1State>({
storage: rawStorage,
defaults: {
network: '',
accounts: [],
seedPhrase: [],
frontend: 'http://default.com',
grpcUrl: { url: '' },
fullSyncHeight: 0,
},
version: {
current: 1,
migrations: {
0: localV0Migration,
},
},
});

await mock1Storage.set('fullSyncHeight', 123);
const height = await mock1Storage.get('fullSyncHeight');
expect(height).toEqual(123);

const faultyMigration = new ExtensionStorage<MockV2State>({
storage: rawStorage,
defaults: {
network: '',
accounts: [],
seedPhrase: [],
frontend: 'http://default.com',
grpcUrl: { url: '', image: '' },
fullSyncHeight: 0n,
},
version: {
current: 2,
migrations: {
0: localV0Migration,
1: () => {
throw new Error('network request error 502');
},
},
},
});

await expect(faultyMigration.get('network')).rejects.toThrow(
'There was an error with migrating the database: Error: network request error 502',
);
});

test('error during migration propagates to multiple callers', async () => {
const originalNetworkVal = 'original.void.zone';
const mock1Storage = new ExtensionStorage<MockV1State>({
storage: rawStorage,
defaults: {
network: originalNetworkVal,
accounts: [],
seedPhrase: [],
frontend: 'http://default.com',
grpcUrl: { url: '' },
fullSyncHeight: 0,
},
version: {
current: 1,
migrations: {
0: localV0Migration,
},
},
});

await mock1Storage.set('fullSyncHeight', 123);
const height = await mock1Storage.get('fullSyncHeight');
expect(height).toEqual(123);

const faultyMigration = new ExtensionStorage<MockV2State>({
storage: rawStorage,
defaults: {
network: '',
accounts: [],
seedPhrase: [],
frontend: 'http://default.com',
grpcUrl: { url: '', image: '' },
fullSyncHeight: 0n,
},
version: {
current: 2,
migrations: {
0: localV0Migration,
1: () => {
throw new Error('network request error 502');
},
},
},
});

const expectedError =
'There was an error with migrating the database: Error: network request error 502';

const callA = faultyMigration.get('network');
await expect(callA).rejects.toThrow(expectedError);
const rawValueA = await rawStorage.get('network');
expect(rawValueA).toStrictEqual({ network: originalNetworkVal });

const callB = faultyMigration.set('network', 'xyz');
await expect(callB).rejects.toThrow(expectedError);
const rawValueB = await rawStorage.get('network');
expect(rawValueB).toStrictEqual({ network: originalNetworkVal });

const callC = faultyMigration.get('network');
await expect(callC).rejects.toThrow(expectedError);
const rawValueC = await rawStorage.get('network');
expect(rawValueC).toStrictEqual({ network: originalNetworkVal });

const callD = faultyMigration.get('accounts');
await expect(callD).rejects.toThrow(expectedError);
});
});
});

0 comments on commit de23cdf

Please sign in to comment.