diff --git a/src/migrations/index.js b/src/migrations/index.js index 350221c5..b470d313 100644 --- a/src/migrations/index.js +++ b/src/migrations/index.js @@ -8,14 +8,16 @@ import migrations from './migrations' // Logic to run all the migrations based on what was not yet run: export const runMigrations = async (): Promise => { - const current = await db.getNamespace('migrations') + // Legacy: the migration nonce was previously stored in separate file + // it can happen, so we have to check it from here also. + const current = (await db.getKey('app', 'migrations')) || (await db.getNamespace('migrations')) let { nonce } = current || { nonce: migrations.length } const outdated = migrations.length - nonce if (!outdated) { if (!current) { - await db.setNamespace('migrations', { nonce }) + await db.setKey('app', 'migrations.nonce', nonce) } return } @@ -31,6 +33,6 @@ export const runMigrations = async (): Promise => { } logger.log(`${outdated} migration(s) performed.`) } finally { - await db.setNamespace('migrations', { nonce }) + await db.setKey('app', 'migrations.nonce', nonce) } } diff --git a/src/migrations/migrations.js b/src/migrations/migrations.js index 185798ea..e67767d2 100644 --- a/src/migrations/migrations.js +++ b/src/migrations/migrations.js @@ -37,6 +37,17 @@ const migrations: Migration[] = [ ) }, }, + { + doc: 'merging migrations into app.json', + run: async () => { + const migrations = await db.getNamespace('migrations') + await db.setKey('app', 'migrations', migrations) + const dbPath = db.getDBPath() + try { + await fsUnlink(path.resolve(dbPath, 'migrations.json')) + } catch (err) {} // eslint-disable-line + }, + }, ] async function getFileData(dbPath, fileName) { diff --git a/src/migrations/migrations.spec.js b/src/migrations/migrations.spec.js index 0791326c..18af426e 100644 --- a/src/migrations/migrations.spec.js +++ b/src/migrations/migrations.spec.js @@ -12,6 +12,7 @@ import db from 'helpers/db' const rimraf = promisify(rimrafModule) const fsReaddir = promisify(fs.readdir) +const fsReadFile = promisify(fs.readFile) const tmpDir = os.tmpdir() @@ -20,14 +21,12 @@ const accountsTransform = { set: encodeAccountsModel, } -describe('migration 1', () => { +describe('from nonce 0', () => { describe('without encryption', () => { test('merging db files', async () => { const dir = await extractMock('userdata_v1.0.5_mock-01') - let files db.init(dir) - files = await fsReaddir(dir) - expect(files).toEqual([ + await expectFiles(dir, [ 'accounts.json', 'countervalues.json', 'migrations.json', @@ -35,8 +34,7 @@ describe('migration 1', () => { 'user.json', ]) await runMigrations() - files = await fsReaddir(dir) - expect(files).toEqual(['app.json', 'migrations.json', 'windowParams.json']) + await expectFiles(dir, ['app.json', 'windowParams.json']) db.init(dir) db.registerTransform('app', 'accounts', accountsTransform) const accounts = await db.getKey('app', 'accounts') @@ -53,9 +51,12 @@ describe('migration 1', () => { test('handle missing file without crash', async () => { const dir = await extractMock('userdata_v1.0.5_mock-03-missing-file') - let files - files = await fsReaddir(dir) - expect(files).toEqual(['countervalues.json', 'migrations.json', 'settings.json', 'user.json']) + await expectFiles(dir, [ + 'countervalues.json', + 'migrations.json', + 'settings.json', + 'user.json', + ]) db.init(dir) let err try { @@ -64,15 +65,12 @@ describe('migration 1', () => { err = e } expect(err).toBeUndefined() - files = await fsReaddir(dir) - expect(files).toEqual(['app.json', 'migrations.json', 'windowParams.json']) + await expectFiles(dir, ['app.json', 'windowParams.json']) }) test('handle where app.json is already present', async () => { const dir = await extractMock('userdata_v1.0.5_mock-04-app-json-present') - let files - files = await fsReaddir(dir) - expect(files).toEqual([ + await expectFiles(dir, [ 'accounts.json', 'app.json', 'countervalues.json', @@ -82,8 +80,7 @@ describe('migration 1', () => { ]) db.init(dir) await runMigrations() - files = await fsReaddir(dir) - expect(files).toEqual(['app.json', 'migrations.json', 'windowParams.json']) + await expectFiles(dir, ['app.json', 'windowParams.json']) }) }) @@ -94,8 +91,7 @@ describe('migration 1', () => { db.registerTransform('app', 'accounts', accountsTransform) await runMigrations() await db.setEncryptionKey('app', 'accounts', 'passw0rd') - const files = await fsReaddir(dir) - expect(files).toEqual(['app.json', 'migrations.json', 'windowParams.json']) + await expectFiles(dir, ['app.json', 'windowParams.json']) const accounts = await db.getKey('app', 'accounts') expect(accounts.length).toBe(6) expect(accounts[0].balance).toBeInstanceOf(BigNumber) @@ -114,6 +110,21 @@ describe('migration 1', () => { }) }) +describe('from nonce 1', () => { + test('merging migration file into app file', async () => { + const dir = await extractMock('userdata_v1.1.1_mock-01') + await expectFiles(dir, ['app.json', 'migrations.json', 'windowParams.json']) + const migrationsBefore = await fsReadFile(path.resolve(dir, 'migrations.json'), 'utf-8') + expect(migrationsBefore).toBe('{"data":{"nonce":1}}') + db.init(dir) + db.registerTransform('app', 'accounts', accountsTransform) + await runMigrations() + await expectFiles(dir, ['app.json', 'windowParams.json']) + const migrations = await db.getKey('app', 'migrations') + expect(migrations).toEqual({ nonce: 2 }) + }) +}) + async function extractMock(mockName) { const destDirectory = path.resolve(tmpDir, mockName) const zipFilePath = path.resolve(__dirname, 'mocks', `${mockName}.zip`) @@ -129,3 +140,8 @@ function extractZip(zipFilePath, destDirectory) { childProcess.on('error', reject) }) } + +async function expectFiles(dir, expectedFiles) { + const files = await fsReaddir(dir) + expect(files).toEqual(expectedFiles) +} diff --git a/src/migrations/mocks/userdata_v1.1.1_mock-01.zip b/src/migrations/mocks/userdata_v1.1.1_mock-01.zip new file mode 100644 index 00000000..c309f8b2 Binary files /dev/null and b/src/migrations/mocks/userdata_v1.1.1_mock-01.zip differ