Skip to content
This repository was archived by the owner on Oct 1, 2021. It is now read-only.

Commit c9d4724

Browse files
committed
Adding check if repo is initialized
License: MIT Signed-off-by: Adam Uhlir <uhlir.a@gmail.com>
1 parent b64412b commit c9d4724

File tree

9 files changed

+206
-43
lines changed

9 files changed

+206
-43
lines changed

src/errors.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,15 @@ class UnknownRepoStructure extends Error {
2424
}
2525
}
2626
exports.UnknownRepoStructure = UnknownRepoStructure
27+
28+
/**
29+
* Exception raised when repo is not initialized.
30+
*/
31+
class NotInitializedRepo extends Error {
32+
constructor (message) {
33+
super(message)
34+
this.name = 'NotInitializedRepo'
35+
this.message = message
36+
}
37+
}
38+
exports.NotInitializedRepo = NotInitializedRepo

src/index.js

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@
33
const defaultMigrations = require('../migrations')
44
const repoVersion = require('./repo/version')
55
const repoLock = require('./repo/lock')
6+
const repoInit = require('./repo/init')
67
const isBrowser = require('./option-node')
78
const errors = require('./errors')
8-
const debug = require('debug')
99

10-
const log = debug('js-ipfs-repo-migrations:migrator')
10+
const log = require('debug')('js-ipfs-repo-migrations:migrator')
1111

1212
exports.getCurrentRepoVersion = repoVersion.getVersion
13+
exports.errors = errors
1314

1415
/**
1516
* Returns the version of latest migration.
1617
*
18+
* @param {array?} migrations - Array of migrations to consider. If undefined, the bundled migrations are used. Mainly for testing purpose.
1719
* @returns {int}
1820
*/
1921
function getLatestMigrationVersion (migrations) {
@@ -48,6 +50,10 @@ async function migrate (path, toVersion, progressCb, isDryRun, migrations) {
4850
throw new Error('Path argument is required!')
4951
}
5052

53+
if (!(await repoInit.isRepoInitialized(path))) {
54+
throw new errors.NotInitializedRepo(`Repo in path ${path} is not initialized!`)
55+
}
56+
5157
if (toVersion && (!Number.isInteger(toVersion) || toVersion <= 0)) {
5258
throw new Error('Version has to be positive integer!')
5359
}
@@ -75,21 +81,20 @@ async function migrate (path, toVersion, progressCb, isDryRun, migrations) {
7581
if (toVersion !== undefined && migration.version > toVersion) {
7682
break
7783
}
84+
if (migration.version <= currentVersion) {
85+
continue
86+
}
7887

79-
if (migration.version > currentVersion) {
80-
counter++
81-
log(`Migrating version ${migration.version}`)
82-
if (!isDryRun) {
83-
try {
84-
await migration.migrate(path, isBrowser)
85-
} catch (e) {
86-
e.message = `During migration to version ${migration.version} exception was raised: ${e.message}`
87-
throw e
88-
}
89-
}
90-
typeof progressCb === 'function' && progressCb(migration, counter, totalMigrations) // Reports on migration process
91-
log(`Migrating to version ${migration.version} finished`)
88+
counter++
89+
log(`Migrating version ${migration.version}`)
90+
try {
91+
if (!isDryRun) await migration.migrate(path, isBrowser)
92+
} catch (e) {
93+
e.message = `During migration to version ${migration.version} exception was raised: ${e.message}`
94+
throw e
9295
}
96+
typeof progressCb === 'function' && progressCb(migration, counter, totalMigrations) // Reports on migration process
97+
log(`Migrating to version ${migration.version} finished`)
9398
}
9499

95100
if (!isDryRun) await repoVersion.setVersion(path, toVersion || getLatestMigrationVersion(migrations))
@@ -121,6 +126,10 @@ async function revert (path, toVersion, progressCb, isDryRun, migrations) {
121126
throw new Error('Path argument is required!')
122127
}
123128

129+
if (!(await repoInit.isRepoInitialized(path))) {
130+
throw new errors.NotInitializedRepo(`Repo in path ${path} is not initialized!`)
131+
}
132+
124133
if (!toVersion) {
125134
throw new Error('When reverting migrations, you have to specify to which version to revert!')
126135
}
@@ -135,7 +144,7 @@ async function revert (path, toVersion, progressCb, isDryRun, migrations) {
135144
return
136145
}
137146

138-
if (currentVersion < toVersion){
147+
if (currentVersion < toVersion) {
139148
log(`Current repo's version (${currentVersion} is lower then toVersion (${toVersion}), nothing to revert.`)
140149
return
141150
}
@@ -158,20 +167,20 @@ async function revert (path, toVersion, progressCb, isDryRun, migrations) {
158167
break
159168
}
160169

161-
if (migration.version <= currentVersion) {
162-
counter++
163-
log(`Reverting migration version ${migration.version}`)
164-
if (!isDryRun) {
165-
try {
166-
await migration.revert(path, isBrowser)
167-
} catch (e) {
168-
e.message = `During reversion to version ${migration.version} exception was raised: ${e.message}`
169-
throw e
170-
}
171-
}
172-
typeof progressCb === 'function' && progressCb(migration, counter, totalMigrations) // Reports on migration process
173-
log(`Reverting to version ${migration.version} finished`)
170+
if (migration.version > currentVersion) {
171+
continue
172+
}
173+
174+
counter++
175+
log(`Reverting migration version ${migration.version}`)
176+
try {
177+
if (!isDryRun) await migration.revert(path, isBrowser)
178+
} catch (e) {
179+
e.message = `During reversion to version ${migration.version} exception was raised: ${e.message}`
180+
throw e
174181
}
182+
typeof progressCb === 'function' && progressCb(migration, counter, totalMigrations) // Reports on migration process
183+
log(`Reverting to version ${migration.version} finished`)
175184
}
176185

177186
if (!isDryRun) await repoVersion.setVersion(path, toVersion)
@@ -199,17 +208,16 @@ function verifyReversibility (migrations, fromVersion, toVersion) {
199208
break
200209
}
201210

202-
if (migration.version >= toVersion) {
203-
migrationCounter++
211+
if (migration.version > toVersion) {
212+
if (!migration.reversible) return { reversible: false, version: migration.version }
204213

205-
if (!migration.reversible)
206-
return { reversible: false, version: migration.version }
214+
migrationCounter++
207215
}
208216
}
209217

210-
if (migrationCounter !== (fromVersion - toVersion)){
211-
throw new Error('There are missing migration to perform the reversion!')
218+
if (migrationCounter !== (fromVersion - toVersion)) {
219+
throw new Error(`There are missing migration to perform the reversion! ${migrationCounter}/${(fromVersion - toVersion)}`)
212220
}
213221

214-
return { reversible: true, version: undefined }
222+
return { reversible: true, version: undefined }
215223
}

src/repo/init.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict'
2+
3+
const Datastore = require('datastore-fs')
4+
const log = require('debug')('js-ipfs-repo-migrations:repo:init')
5+
6+
const Key = require('interface-datastore').Key
7+
8+
const versionKey = new Key('/version')
9+
const configKey = new Key('/config')
10+
11+
exports.isRepoInitialized = async function isRepoInitialized (path) {
12+
let root
13+
try {
14+
root = new Datastore(path, { extension: '', createIfMissing: false })
15+
16+
const versionCheck = await root.has(versionKey)
17+
const configCheck = await root.has(configKey)
18+
if (!versionCheck || !configCheck) {
19+
log(`Version entry present: ${versionCheck}`)
20+
log(`Config entry present: ${configCheck}`)
21+
return false
22+
}
23+
24+
return true
25+
} catch (e) {
26+
log('While checking if repo is initialized error was thrown: ' + e.message)
27+
return false
28+
} finally {
29+
if (root !== undefined) await root.close()
30+
}
31+
}

test/browser.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,8 @@ describe('Browser specific tests', () => {
2525
describe('version tests', () => {
2626
require('./version-test')(repoSetup, repoCleanup)
2727
})
28+
29+
describe('init tests', () => {
30+
require('./init-test')(repoSetup, repoCleanup)
31+
})
2832
})

test/index.spec.js

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ const chai = require('chai')
55
chai.use(require('dirty-chai'))
66
const expect = chai.expect
77
const sinon = require('sinon')
8-
const chaiAsPromised = require("chai-as-promised")
8+
const chaiAsPromised = require('chai-as-promised')
99
chai.use(chaiAsPromised)
1010

1111
const migrator = require('../src/index')
1212
const repoVersion = require('../src/repo/version')
1313
const repoLock = require('../src/repo/lock')
14+
const repoInit = require('../src/repo/init')
1415

1516
function createMigrations () {
1617
return [
@@ -45,24 +46,31 @@ describe('index.js', () => {
4546
let getVersionStub
4647
let setVersionStub
4748
let lockStub
49+
let initStub
4850
let lockCloseStub
4951

5052
beforeEach(() => {
5153
// Reset all stubs
5254
sinon.reset()
5355

56+
initStub.resolves(true)
57+
lockCloseStub.resolves()
58+
lockStub.resolves({ close: lockCloseStub })
59+
})
60+
61+
before(() => {
5462
getVersionStub = sinon.stub(repoVersion, 'getVersion')
5563
setVersionStub = sinon.stub(repoVersion, 'setVersion')
5664
lockCloseStub = sinon.stub()
57-
lockCloseStub.resolves()
5865
lockStub = sinon.stub(repoLock, 'lock')
59-
lockStub.resolves({ close: lockCloseStub })
66+
initStub = sinon.stub(repoInit, 'isRepoInitialized')
6067
})
6168

62-
afterEach(() => {
69+
after(() => {
6370
getVersionStub.restore()
6471
setVersionStub.restore()
6572
lockStub.restore()
73+
initStub.restore()
6674
})
6775

6876
it('get version of the latest migration', () => {
@@ -110,7 +118,7 @@ describe('index.js', () => {
110118
expect(lockStub.called).to.be.false()
111119
})
112120

113-
it('should not rever if current repo version is lower then toVersion', async () => {
121+
it('should not revert if current repo version is lower then toVersion', async () => {
114122
getVersionStub.returns(2)
115123
const migrationsMock = createMigrations()
116124

@@ -148,6 +156,46 @@ describe('index.js', () => {
148156
expect(migrationsMock[0].revert.called).to.be.false()
149157
})
150158

159+
it('should revert one migration as expected', async () => {
160+
const migrationsMock = createMigrations()
161+
getVersionStub.returns(2)
162+
163+
await expect(migrator.revert('/some/path', 1, undefined, undefined, migrationsMock))
164+
.to.eventually.be.fulfilled()
165+
166+
expect(lockCloseStub.calledOnce).to.be.true()
167+
expect(lockStub.calledOnce).to.be.true()
168+
expect(setVersionStub.calledOnceWith('/some/path', 1)).to.be.true()
169+
170+
// Checking migrations
171+
expect(migrationsMock[3].revert.called).to.be.false()
172+
expect(migrationsMock[2].revert.called).to.be.false()
173+
expect(migrationsMock[1].revert.calledOnce).to.be.true()
174+
expect(migrationsMock[0].revert.called).to.be.false()
175+
})
176+
177+
it('should reversion with one migration', async () => {
178+
const migrationsMock = [
179+
{
180+
version: 2,
181+
reversible: true,
182+
migrate: sinon.stub().resolves(),
183+
revert: sinon.stub().resolves()
184+
}
185+
]
186+
getVersionStub.returns(2)
187+
188+
await expect(migrator.revert('/some/path', 1, undefined, undefined, migrationsMock))
189+
.to.eventually.be.fulfilled()
190+
191+
expect(lockCloseStub.calledOnce).to.be.true()
192+
expect(lockStub.calledOnce).to.be.true()
193+
expect(setVersionStub.calledOnceWith('/some/path', 1)).to.be.true()
194+
195+
// Checking migrations
196+
expect(migrationsMock[0].revert.calledOnce).to.be.true()
197+
})
198+
151199
it('should not have any side-effects when in dry run', async () => {
152200
const migrationsMock = createMigrations()
153201
getVersionStub.returns(4)

test/init-test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* eslint-env mocha */
2+
'use strict'
3+
4+
const chai = require('chai')
5+
chai.use(require('dirty-chai'))
6+
const expect = chai.expect
7+
8+
const Datastore = require('datastore-fs')
9+
const Key = require('interface-datastore').Key
10+
const repoInit = require('../src/repo/init')
11+
12+
module.exports = (setup, cleanup) => {
13+
let dir
14+
15+
beforeEach(async () => {
16+
dir = await setup()
17+
})
18+
afterEach(() =>
19+
cleanup(dir)
20+
)
21+
22+
it('should return true with valid initialized repo', async () => {
23+
const versionKey = new Key('version')
24+
const configKey = new Key('config')
25+
const store = new Datastore(dir, { extension: '', createIfMissing: false })
26+
await store.open()
27+
await store.put(versionKey, Buffer.from('7'))
28+
await store.put(configKey, '')
29+
await store.close()
30+
31+
expect(await repoInit.isRepoInitialized(dir)).to.be.true()
32+
})
33+
34+
it('should return false with missing version key', async () => {
35+
const configKey = new Key('config')
36+
const store = new Datastore(dir, { extension: '', createIfMissing: false })
37+
await store.open()
38+
await store.put(configKey, '')
39+
await store.close()
40+
41+
expect( await repoInit.isRepoInitialized(dir)).to.be.false()
42+
})
43+
44+
it('should return false with missing config key', async () => {
45+
const versionKey = new Key('version')
46+
const store = new Datastore(dir, { extension: '', createIfMissing: false })
47+
await store.open()
48+
await store.put(versionKey, '')
49+
await store.close()
50+
51+
expect(await repoInit.isRepoInitialized(dir)).to.be.false()
52+
})
53+
54+
it('should return false if the repo does not exists', async () => {
55+
return expect(await repoInit.isRepoInitialized('/some/random/dirrr')).to.be.false()
56+
})
57+
}

test/lock-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = (locker, setup, cleanup) => {
1212
describe('version 7 and bellow', () => {
1313
let dir
1414

15-
beforeEach(async () => {dir = await setup()})
15+
beforeEach(async () => { dir = await setup() })
1616
afterEach(() => cleanup(dir))
1717

1818
it('should return lock object', async () => {

test/node.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,8 @@ describe('Node specific tests', () => {
3434
describe('version tests', () => {
3535
require('./version-test')(repoSetup, repoCleanup)
3636
})
37+
38+
describe('init tests', () => {
39+
require('./init-test')(repoSetup, repoCleanup)
40+
})
3741
})

test/version-test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,5 @@ module.exports = (setup, cleanup) => {
4545
await version.setVersion(dir, 7)
4646
expect(await version.getVersion(dir)).to.be.equal(7)
4747
})
48-
4948
})
5049
}

0 commit comments

Comments
 (0)