diff --git a/.travis.yml b/.travis.yml index e2416dc..0ef92c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,13 +43,6 @@ jobs: firefox: latest script: npx aegir test -t browser -- --browsers FirefoxHeadless - - stage: test - name: sharness - os: - - linux - - osx - script: cd ./test/sharness && make - - stage: test name: electron-main os: osx diff --git a/README.md b/README.md index edee377..3a720a0 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,10 @@ This package is inspired by the [go-ipfs repo migration tool](https://github.com - [Use in a browser with browserify, webpack or any other bundler](#use-in-a-browser-with-browserify-webpack-or-any-other-bundler) - [Usage](#usage) - [API](#api) - - [`.migrate(path, toVersion, {ignoreLock, repoOptions, onProgress, isDryRun}) -> Promise`](#migratepath-toversion-ignorelock-repooptions-onprogress-isdryrun---promisevoid) + - [`.migrate(path, repoOptions, toVersion, {ignoreLock, onProgress, isDryRun}) -> Promise`](#migratepath-repooptions-toversion-ignorelock-onprogress-isdryrun---promisevoid) - [`onProgress(migration, counter, totalMigrations)`](#onprogressmigration-counter-totalmigrations) - - [`.revert(path, toVersion, {ignoreLock, repoOptions, onProgress, isDryRun}) -> Promise`](#revertpath-toversion-ignorelock-repooptions-onprogress-isdryrun---promisevoid) + - [`.revert(path, repoOptions, toVersion, {ignoreLock, onProgress, isDryRun}) -> Promise`](#revertpath-repooptions-toversion-ignorelock-onprogress-isdryrun---promisevoid) - [`getLatestMigrationVersion() -> int`](#getlatestmigrationversion---int) -- [CLI](#cli) - [Creating a new migration](#creating-a-new-migration) - [Architecture of a migration](#architecture-of-a-migration) - [`.migrate(repoPath, repoOptions)`](#migraterepopath-repooptions) @@ -50,7 +49,6 @@ This package is inspired by the [go-ipfs repo migration tool](https://github.com ## Background - As js-ipfs evolves and new technologies, algorithms and data structures are incorporated it is necessary to enable users to transition between versions. Different versions of js-ipfs may expect a different IPFS repo structure or content (see: [IPFS repo spec](https://github.com/ipfs/specs/blob/master/REPO.md), [JS implementation](https://github.com/ipfs/js-ipfs-repo) ). So the IPFS repo is versioned, and this package provides a framework to create migrations to transition @@ -93,10 +91,15 @@ const migrations = require('ipfs-repo-migrations') const repoPath = 'some/repo/path' const currentRepoVersion = 7 const latestVersion = migrations.getLatestMigrationVersion() +const repoOptions = { + ... // the same storage backend/storage options passed to `ipfs-repo` +} if(currentRepoVersion < latestVersion){ // Old repo! Lets migrate to latest version! - await migrations.migrate(repoPath, latestVersion) + await migrations.migrate(repoPath, latestVersion, { + repoOptions + }) } ``` @@ -104,17 +107,17 @@ To migrate your repository using the CLI, see the [how to run migrations](./run. ## API -### `.migrate(path, toVersion, {ignoreLock, repoOptions, onProgress, isDryRun}) -> Promise` +### `.migrate(path, repoOptions, toVersion, {ignoreLock, onProgress, isDryRun}) -> Promise` Executes a forward migration to a specific version, or to the latest version if a specific version is not specified. **Arguments:** * `path` (string, mandatory) - path to the repo to be migrated + * `repoOptions` (object, mandatory) - options that are passed to migrations, that use them to construct the datastore. (options are the same as for IPFSRepo). * `toVersion` (int, mandatory) - version to which the repo should be migrated. * `options` (object, optional) - options for the migration * `options.ignoreLock` (bool, optional) - if true will not lock the repo when applying migrations. Use with caution. - * `options.repoOptions` (object, optional) - options that are passed to migrations, that use them to construct the datastore. (options are the same as for IPFSRepo). * `options.onProgress` (function, optional) - callback that is called after finishing execution of each migration to report progress. * `options.isDryRun` (bool, optional) - flag that indicates if it is a dry run that should give the same output as running a migration but without making any actual changes. @@ -127,17 +130,17 @@ Signature of the progress callback. * `counter` (int) - index of current migration. * `totalMigrations` (int) - total count of migrations that will be run. -### `.revert(path, toVersion, {ignoreLock, repoOptions, onProgress, isDryRun}) -> Promise` +### `.revert(path, repoOptions, toVersion, {ignoreLock, onProgress, isDryRun}) -> Promise` Executes backward migration to a specific version. **Arguments:** * `path` (string, mandatory) - path to the repo to be reverted + * `repoOptions` (object, mandatory) - options that are passed to migrations, that use them to construct the datastore. (options are the same as for IPFSRepo). * `toVersion` (int, mandatory) - version to which the repo should be reverted to. * `options` (object, optional) - options for the reversion * `options.ignoreLock` (bool, optional) - if true will not lock the repo when applying migrations. Use with caution. - * `options.repoOptions` (object, optional) - options that are passed to migrations, that use them to construct the datastore. (options are the same as for IPFSRepo). * `options.onProgress` (function, optional) - callback that is called after finishing execution of each migration to report progress. * `options.isDryRun` (bool, optional) - flag that indicates if it is a dry run that should give the same output as running a migration but without making any actual changes. @@ -145,17 +148,6 @@ Executes backward migration to a specific version. Return the version of the latest migration. -## CLI - -The CLI is a NodeJS binary named `jsipfs-repo-migrations`. -It has several commands: - - * `migrate` - performs forward/backward migration to specific or latest version. - * `status` - check repo for migrations that should be run. - * `add` - bootstraps new migration. - -For further details see the `--help` pages. - ## Creating a new migration Migrations are one of those things that can be extremely painful on users. At the end of the day, we want users never to have to think about it. The process should be: @@ -174,8 +166,8 @@ be run again. All migrations are placed in the `/migrations` folder. Each folder there represents one migration that follows the migration API. -All migrations are collected in `/migrations/index.js`, which should not be edited manually. It is regenerated on -every run of `jsipfs-migrations add` (manual changes should follow the same style of modifications). +All migrations are collected in `/migrations/index.js`, which should not be edited manually. + **The order of migrations is important and migrations must be sorted in ascending order**. Each migration must follow this API. It must export an object in its `index.js` that has following properties: diff --git a/package.json b/package.json index ca0d3d8..98d30f8 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,6 @@ "./src/repo/lock.js": "./src/repo/lock-memory.js", "datastore-fs": "datastore-level" }, - "bin": { - "jsipfs-migrations": "./src/cli.js" - }, "repository": { "type": "git", "url": "https://github.com/ipfs/js-ipfs-repo-migrations.git" @@ -45,7 +42,6 @@ }, "dependencies": { "cbor": "^5.0.2", - "chalk": "^4.0.0", "cids": "^1.0.0", "datastore-core": "^2.0.0", "datastore-fs": "^2.0.0", @@ -60,9 +56,7 @@ "proper-lockfile": "^4.1.1", "protons": "^2.0.0", "uint8arrays": "^1.0.0", - "varint": "^5.0.0", - "yargs": "^15.3.1", - "yargs-promise": "^1.1.0" + "varint": "^5.0.0" }, "devDependencies": { "aegir": "^25.0.0", diff --git a/src/cli.js b/src/cli.js deleted file mode 100755 index 102c43c..0000000 --- a/src/cli.js +++ /dev/null @@ -1,62 +0,0 @@ -#! /usr/bin/env node - -'use strict' - -const YargsPromise = require('yargs-promise') -const yargs = require('yargs') -const process = require('process') -const log = require('debug')('repo-migrations:cli') - -const commands = require('./commands') - -function print (msg = '', newline = true) { - msg = newline ? msg + '\n' : msg - process.stdout.write(msg) -} - -async function main (args) { - const cli = yargs() - .option('repo-path', { - desc: 'Path to the IPFS repo', - type: 'string' - }) - .option('migrations', { - desc: 'Path to folder with migrations. Default: bundled migrations', - type: 'string' - }) - .command(commands.migrate) - .command(commands.status) - .command(commands.add) - .demandCommand(1, 'You need at least one command before continuing') - .strict() - .fail((msg, err, yargs) => { - if (err) { - throw err // preserve stack - } - - // Suppress "one command required" error when only help page is to be displayed - if (args.length > 0) { - print(msg) - } - - yargs.showHelp() - }) - - try { - const { data } = await new YargsPromise(cli).parse(args) - if (data) print(data) - } catch (err) { - log(err) - - // the argument can have a different shape depending on where the error came from - if (err.message || (err.error && err.error.message)) { - print(err.message || err.error.message) - } else { - print('Unknown error, please re-run the command with DEBUG=repo-migrations:cli to see debug output') - } - - process.exit(1) - } -} - -main(process.argv.slice(2)) diff --git a/src/commands.js b/src/commands.js deleted file mode 100644 index bcb2450..0000000 --- a/src/commands.js +++ /dev/null @@ -1,165 +0,0 @@ -'use strict' - -const os = require('os') -const path = require('path') -const fs = require('fs') -const process = require('process') -const util = require('util') -const readline = require('readline') - -const writeFile = util.promisify(fs.writeFile) -const mkdir = util.promisify(fs.mkdir) - -const chalk = require('chalk') - -const repoVersion = require('./repo/version') -const migratorUtils = require('./utils') -const migrator = require('./index') -const templates = require('./migration-templates') -const migrations = require('../migrations') - -function shouldContinueWithIrreversibleMigration () { - return new Promise((resolve, reject) => { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }) - - rl.question(chalk.yellow('There is irreversible migration in the migrations plan. Do you want to continue? [y/N]'), (answer) => { - rl.close() - resolve(answer === 'y') - }) - }) -} - -function asyncClosure (fnc) { - return function asyncWrapper ({ resolve, ...options }) { - resolve(fnc(options)) - } -} - -function reportingClosure (action) { - return (migration, currentlyMigrated, totalToMigrate) => - process.stdout.write(`${chalk.green(`[${currentlyMigrated}/${totalToMigrate}]`)} Successfully ${action} ${chalk.bold(migration.version)}: ${migration.description}\n`) -} - -async function migrate ({ repoPath, migrations, to, dry, revertOk }) { - repoPath = repoPath || process.env.IPFS_PATH || path.join(os.homedir(), '.jsipfs') - migrations = migrations === undefined ? require('../migrations') : require(migrations) - - if (!to) { - to = migrator.getLatestMigrationVersion(migrations) - } - - const version = await repoVersion.getVersion(repoPath) - - let action - if (dry) { - action = 'loaded migration' - } else { - if (!to || (version <= to)) { - action = 'migrated to version' - } else { - action = 'reverted version' - } - } - - const options = { - migrations: migrations, - toVersion: to, - ignoreLock: false, - onProgress: reportingClosure(action), - isDryRun: dry - } - - if (version === to) { - return chalk.yellow('Nothing to migrate! Versions matches') - } else if (version < to) { - if (migratorUtils.containsIrreversibleMigration(version, to, migrations)) { - if (!(await shouldContinueWithIrreversibleMigration())) { - return 'Migration aborted' - } - } - - await migrator.migrate(repoPath, to, options) - } else if (revertOk) { - await migrator.revert(repoPath, to, options) - } else { - throw new Error(`The migration would revert the repo version from ${version} to ${to}. To revert you must supply '--revert-ok'.`) - } -} - -async function status ({ repoPath, migrations }) { - repoPath = repoPath || process.env.IPFS_PATH || path.join(os.homedir(), '.jsipfs') - - // Import migrations if set - if (migrations) { - migrations = require(migrations) - } - - const version = await repoVersion.getVersion(repoPath) - const lastMigrationVersion = migrator.getLatestMigrationVersion(migrations) - const statusString = - version < lastMigrationVersion ? chalk.yellow('Repo is out of date') : chalk.green('Nothing to migrate') - - return `${statusString}\nCurrent repo version: ${version}\nLatest migration version: ${lastMigrationVersion}` -} - -async function add ({ empty }) { - const newMigrationVersion = migrator.getLatestMigrationVersion() + 1 - const newMigrationFolder = path.join(__dirname, '..', 'migrations', 'migration-' + newMigrationVersion) - - const migrationsImport = migrations.map((migration) => migration.empty ? ` Object.assign({version: ${parseInt(migration.version)}}, emptyMigration),` : ` require('./migration-${parseInt(migration.version)}'),`) - if (empty) { - migrationsImport.push(` Object.assign({version: ${newMigrationVersion}}, emptyMigration),`) - } else { - migrationsImport.push(` require('./migration-${newMigrationVersion}'),`) - } - const migrationsIndexJsContent = templates.migrationsIndexJs - .replace('{{imports}}', migrationsImport.join('\n')) - await writeFile(path.join(newMigrationFolder, '..', 'index.js'), migrationsIndexJsContent) - - if (empty) return - - await mkdir(newMigrationFolder) - - const indexJsContent = templates.indexJs - .replace(/{{version}}/gi, newMigrationVersion) - await writeFile(path.join(newMigrationFolder, 'index.js'), indexJsContent) -} - -module.exports = { - migrate: { - command: 'migrate', - describe: 'Migrate IPFS repo to latest or specific version', - handler: asyncClosure(migrate), - builder: yargv => yargv - .option('to', { - describe: 'Target version to migrate repo to', - type: 'number' - }) - .option('dry', { - describe: 'Output migration results but don\'t actually perform migration', - type: 'boolean' - }) - .option('revert-ok', { - describe: 'Allow migrating to a lower version (reverting)', - type: 'boolean' - }) - }, - status: { - command: 'status', - describe: 'Display migration status of IPFS repo', - handler: asyncClosure(status) - }, - add: { - command: 'add', - describe: 'Bootstrap new migration', - handler: asyncClosure(add), - builder: yargv => yargv - .option('empty', { - describe: 'Creates empty migration', - type: 'boolean' - }) - } -} diff --git a/src/errors.js b/src/errors.js index 1aaa2f1..592f783 100644 --- a/src/errors.js +++ b/src/errors.js @@ -60,3 +60,18 @@ class InvalidValueError extends Error { InvalidValueError.code = 'ERR_INVALID_VALUE' exports.InvalidValueError = InvalidValueError + +/** + * Exception raised when config is not passed. + */ +class MissingRepoOptionsError extends Error { + constructor (message) { + super(message) + this.name = 'MissingRepoOptionsError' + this.code = 'ERR_MISSING_REPO_OPTIONS' + this.message = message + } +} + +MissingRepoOptionsError.code = 'ERR_MISSING_REPO_OPTIONS' +exports.MissingRepoOptionsError = MissingRepoOptionsError diff --git a/src/index.js b/src/index.js index bdb7051..6da627b 100644 --- a/src/index.js +++ b/src/index.js @@ -36,16 +36,16 @@ exports.getLatestMigrationVersion = getLatestMigrationVersion * Signature of the progress callback is: function(migrationObject: object, currentMigrationNumber: int, totalMigrationsCount: int) * * @param {string} path - Path to initialized (!) JS-IPFS repo + * @param {Object} repoOptions - Options that are passed to migrations, that can use them to correctly construct datastore. Options are same like for IPFSRepo. * @param {int} toVersion - Version to which the repo should be migrated. - * @param {Object} options - Options for migration + * @param {Object?} options - Options for migration * @param {boolean?} options.ignoreLock - Won't lock the repo for applying the migrations. Use with caution. - * @param {object?} options.repoOptions - Options that are passed to migrations, that can use them to correctly construct datastore. Options are same like for IPFSRepo. * @param {function?} options.onProgress - Callback which will be called after each executed migration to report progress * @param {boolean?} options.isDryRun - Allows to simulate the execution of the migrations without any effect. * @param {array?} options.migrations - Array of migrations to migrate. If undefined, the bundled migrations are used. Mainly for testing purpose. * @returns {Promise} */ -async function migrate (path, toVersion, { ignoreLock = false, repoOptions, onProgress, isDryRun = false, migrations }) { +async function migrate (path, repoOptions, toVersion, { ignoreLock = false, onProgress, isDryRun = false, migrations }) { migrations = migrations || defaultMigrations onProgress = onProgress || (() => {}) @@ -53,6 +53,10 @@ async function migrate (path, toVersion, { ignoreLock = false, repoOptions, onPr throw new errors.RequiredParameterError('Path argument is required!') } + if (!repoOptions) { + throw new errors.RequiredParameterError('repoOptions argument is required!') + } + if (!toVersion) { throw new errors.RequiredParameterError('toVersion argument is required!') } @@ -61,7 +65,7 @@ async function migrate (path, toVersion, { ignoreLock = false, repoOptions, onPr throw new errors.InvalidValueError('Version has to be positive integer!') } - const currentVersion = await repoVersion.getVersion(path) + const currentVersion = await repoVersion.getVersion(path, repoOptions) if (currentVersion === toVersion) { log('Nothing to migrate.') @@ -105,7 +109,7 @@ async function migrate (path, toVersion, { ignoreLock = false, repoOptions, onPr log(`Migrating to version ${migration.version} finished`) } - if (!isDryRun) await repoVersion.setVersion(path, toVersion || getLatestMigrationVersion(migrations)) + if (!isDryRun) await repoVersion.setVersion(path, toVersion || getLatestMigrationVersion(migrations), repoOptions) log('Repo successfully migrated ', toVersion !== undefined ? `to version ${toVersion}!` : 'to latest version!') } finally { if (!isDryRun && !ignoreLock) await lock.close() @@ -121,16 +125,16 @@ exports.migrate = migrate * Signature of the progress callback is: function(migrationObject: object, currentMigrationNumber: int, totalMigrationsCount: int) * * @param {string} path - Path to initialized (!) JS-IPFS repo + * @param {Object} repoOptions - Options that are passed to migrations, that can use them to correctly construct datastore. Options are same like for IPFSRepo. * @param {int} toVersion - Version to which the repo will be reverted. - * @param {Object} options - Options for the reversion + * @param {Object?} options - Options for the reversion * @param {function?} options.onProgress - Callback which will be called after each reverted migration to report progress - * @param {object?} options.repoOptions - Options that are passed to migrations, that can use them to correctly construct datastore. Options are same like for IPFSRepo. * @param {boolean?} options.isDryRun - Allows to simulate the execution of the reversion without any effects. Make sense to utilize onProgress with this argument. * @param {boolean?} options.ignoreLock - Won't lock the repo for reverting the migrations. Use with caution. * @param {array?} options.migrations - Array of migrations to migrate. If undefined, the bundled migrations are used. Mainly for testing purpose. * @returns {Promise} */ -async function revert (path, toVersion, { ignoreLock = false, repoOptions, onProgress, isDryRun = false, migrations }) { +async function revert (path, repoOptions, toVersion, { ignoreLock = false, onProgress, isDryRun = false, migrations }) { migrations = migrations || defaultMigrations onProgress = onProgress || (() => {}) @@ -138,6 +142,10 @@ async function revert (path, toVersion, { ignoreLock = false, repoOptions, onPro throw new errors.RequiredParameterError('Path argument is required!') } + if (!repoOptions) { + throw new errors.RequiredParameterError('repoOptions argument is required!') + } + if (!toVersion) { throw new errors.RequiredParameterError('When reverting migrations, you have to specify to which version to revert!') } @@ -146,7 +154,7 @@ async function revert (path, toVersion, { ignoreLock = false, repoOptions, onPro throw new errors.InvalidValueError('Version has to be positive integer!') } - const currentVersion = await repoVersion.getVersion(path) + const currentVersion = await repoVersion.getVersion(path, repoOptions) if (currentVersion === toVersion) { log('Nothing to revert.') return @@ -182,7 +190,7 @@ async function revert (path, toVersion, { ignoreLock = false, repoOptions, onPro } catch (e) { const lastSuccessfullyRevertedVersion = migration.version log(`An exception was raised during execution of migration. Setting the repo's version to last successfully reverted version: ${lastSuccessfullyRevertedVersion}`) - await repoVersion.setVersion(path, lastSuccessfullyRevertedVersion) + await repoVersion.setVersion(path, lastSuccessfullyRevertedVersion, repoOptions) e.message = `During reversion to version ${migration.version} exception was raised: ${e.message}` throw e @@ -192,7 +200,7 @@ async function revert (path, toVersion, { ignoreLock = false, repoOptions, onPro log(`Reverting to version ${migration.version} finished`) } - if (!isDryRun) await repoVersion.setVersion(path, toVersion) + if (!isDryRun) await repoVersion.setVersion(path, toVersion, repoOptions) log(`All migrations successfully reverted to version ${toVersion}!`) } finally { if (!isDryRun && !ignoreLock) await lock.close() diff --git a/src/repo/init.js b/src/repo/init.js index bf5b8cb..400dbb0 100644 --- a/src/repo/init.js +++ b/src/repo/init.js @@ -1,20 +1,25 @@ 'use strict' -const Datastore = require('datastore-fs') const log = require('debug')('repo-migrations:repo:init') +const { CONFIG_KEY, VERSION_KEY, getDatastoreAndOptions } = require('../utils') +const { MissingRepoOptionsError } = require('../errors') -const Key = require('interface-datastore').Key - -const versionKey = new Key('/version') -const configKey = new Key('/config') +exports.isRepoInitialized = async function isRepoInitialized (path, repoOptions) { + if (!repoOptions) { + throw new MissingRepoOptionsError('Please pass repo options when trying to open a repo') + } -exports.isRepoInitialized = async function isRepoInitialized (path) { let root try { - root = new Datastore(path, { extension: '', createIfMissing: false }) + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + root = new StorageBackend(path, storageOptions) await root.open() - const versionCheck = await root.has(versionKey) - const configCheck = await root.has(configKey) + const versionCheck = await root.has(VERSION_KEY) + const configCheck = await root.has(CONFIG_KEY) if (!versionCheck || !configCheck) { log(`Version entry present: ${versionCheck}`) log(`Config entry present: ${configCheck}`) @@ -26,6 +31,8 @@ exports.isRepoInitialized = async function isRepoInitialized (path) { log('While checking if repo is initialized error was thrown: ' + e.message) return false } finally { - if (root !== undefined) await root.close() + if (root !== undefined) { + await root.close() + } } } diff --git a/src/repo/version.js b/src/repo/version.js index 621f09f..22ab720 100644 --- a/src/repo/version.js +++ b/src/repo/version.js @@ -1,14 +1,10 @@ 'use strict' -const errors = require('../errors') const repoInit = require('./init') -const Datastore = require('datastore-fs') +const { MissingRepoOptionsError, NotInitializedRepoError } = require('../errors') +const { VERSION_KEY, getDatastoreAndOptions } = require('../utils') const uint8ArrayFromString = require('uint8arrays/from-string') -const Key = require('interface-datastore').Key - -const versionKey = new Key('version') - exports.getVersion = getVersion /** @@ -17,17 +13,27 @@ exports.getVersion = getVersion * even in case of change of repo's versioning. * * @param {string} path + * @param {Object} repoOptions Options used to create a repo, the same as pased to ipfs-repo * @returns {Promise} */ -async function getVersion (path) { - if (!(await repoInit.isRepoInitialized(path))) { - throw new errors.NotInitializedRepoError(`Repo in path ${path} is not initialized!`) +async function getVersion (path, repoOptions) { + if (!(await repoInit.isRepoInitialized(path, repoOptions))) { + throw new NotInitializedRepoError(`Repo in path ${path} is not initialized!`) + } + + if (!repoOptions) { + throw new MissingRepoOptionsError('Please pass repo options when trying to open a repo') } - const store = new Datastore(path, { extension: '', createIfMissing: false }) + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + const store = new StorageBackend(path, storageOptions) await store.open() - const version = parseInt(await store.get(versionKey)) + const version = parseInt(await store.get(VERSION_KEY)) await store.close() return version @@ -38,12 +44,22 @@ async function getVersion (path) { * * @param {string} path * @param {int} version + * @param {Object} repoOptions Options used to create a repo, the same as pased to ipfs-repo * @returns {Promise} */ -async function setVersion (path, version) { - const store = new Datastore(path, { extension: '', createIfMissing: false }) +async function setVersion (path, version, repoOptions) { + if (!repoOptions) { + throw new MissingRepoOptionsError('Please pass repo options when trying to open a repo') + } + + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + const store = new StorageBackend(path, storageOptions) await store.open() - await store.put(versionKey, uint8ArrayFromString(String(version))) + await store.put(VERSION_KEY, uint8ArrayFromString(String(version))) await store.close() } diff --git a/src/utils.js b/src/utils.js index b180af1..62e279d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,10 @@ 'use strict' const Datastore = require('datastore-fs') +const Key = require('interface-datastore').Key + +exports.CONFIG_KEY = new Key('/config') +exports.VERSION_KEY = new Key('/version') exports.getDatastoreAndOptions = function getDatastoreAndOptions (options, key, defaultDatastore = Datastore) { let StorageBackend, storageBackendOptions diff --git a/test/browser.js b/test/browser.js index 83a3a8e..2fd1640 100644 --- a/test/browser.js +++ b/test/browser.js @@ -1,32 +1,39 @@ /* eslint-env mocha */ 'use strict' -const loadFixture = require('aegir/fixtures') -const Datastore = require('datastore-level') - -const Key = require('interface-datastore').Key -const CONFIG_KEY = new Key('config') -const VERSION_KEY = new Key('version') - -async function createRepo () { - const date = Date.now().toString() - const dir = 'test-repo-for-' + date - const store = new Datastore(dir, { extension: '', createIfMissing: true }) - await store.open() - await store.close() - return dir -} - -async function createAndLoadRepo () { - const date = Date.now().toString() - const dir = 'test-repo-for-' + date - const store = new Datastore(dir, { extension: '', createIfMissing: true }) - await store.open() - - await store.put(VERSION_KEY, loadFixture('test/fixtures/test-repo/version')) - await store.put(CONFIG_KEY, loadFixture('test/fixtures/test-repo/config')) +const { createRepo, createAndLoadRepo } = require('./fixtures/repo') - return dir +const repoOptions = { + lock: 'memory', + storageBackends: { + root: require('datastore-level'), + blocks: require('datastore-level'), + keys: require('datastore-level'), + datastore: require('datastore-level'), + pins: require('datastore-level') + }, + storageBackendOptions: { + root: { + extension: '', + prefix: '', + version: 2 + }, + blocks: { + sharding: false, + prefix: '', + version: 2 + }, + keys: { + sharding: false, + prefix: '', + version: 2 + }, + datastore: { + sharding: false, + prefix: '', + version: 2 + } + } } async function repoCleanup (dir) { @@ -35,23 +42,23 @@ async function repoCleanup (dir) { describe('Browser specific tests', () => { describe('lock.js tests', () => { describe('mem-lock tests', () => { - require('./lock-test')(require('../src/repo/lock-memory'), createRepo, repoCleanup) + require('./lock-test')(require('../src/repo/lock-memory'), () => createRepo(repoOptions), repoCleanup, repoOptions) }) }) describe('version tests', () => { - require('./version-test')(createRepo, repoCleanup) + require('./version-test')(() => createRepo(repoOptions), repoCleanup, repoOptions) }) - describe('migration tests', () => { - require('./migrations')(createRepo, repoCleanup) + describe('migrations tests', () => { + require('./migrations')(() => createRepo(repoOptions), repoCleanup, repoOptions) }) describe('init tests', () => { - require('./init-test')(createRepo, repoCleanup) + require('./init-test')(() => createRepo(repoOptions), repoCleanup, repoOptions) }) describe('integration tests', () => { - require('./integration-test')(createAndLoadRepo, repoCleanup) + require('./integration-test')(() => createAndLoadRepo(repoOptions), repoCleanup, repoOptions) }) }) diff --git a/test/fixtures/repo.js b/test/fixtures/repo.js new file mode 100644 index 0000000..5e19f44 --- /dev/null +++ b/test/fixtures/repo.js @@ -0,0 +1,49 @@ +'use strict' + +const { Buffer } = require('buffer') +const loadFixture = require('aegir/fixtures') +const { CONFIG_KEY, VERSION_KEY, getDatastoreAndOptions } = require('../../src/utils') + +async function createRepo (repoOptions) { + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + const date = Date.now().toString() + const dir = 'test-repo-for-' + date + const store = new StorageBackend(dir, { + ...storageOptions, + createIfMissing: true + }) + await store.open() + await store.close() + return dir +} + +async function createAndLoadRepo (repoOptions) { + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + const date = Date.now().toString() + const dir = 'test-repo-for-' + date + const store = new StorageBackend(dir, { + ...storageOptions, + createIfMissing: true + }) + await store.open() + + await store.put(VERSION_KEY, Buffer.from(loadFixture('test/fixtures/test-repo/version'))) + await store.put(CONFIG_KEY, Buffer.from(loadFixture('test/fixtures/test-repo/config'))) + + await store.close() + + return dir +} + +module.exports = { + createRepo, + createAndLoadRepo +} diff --git a/test/index.spec.js b/test/index.spec.js index b1d6c87..89960d4 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -44,6 +44,12 @@ function createOptions () { } } +function createRepoOptions () { + return { + + } +} + describe('index.js', () => { let getVersionStub let setVersionStub @@ -86,23 +92,31 @@ describe('index.js', () => { it('should error with out path argument', () => { const options = createOptions() - return expect(migrator.revert(undefined, undefined, options)) + return expect(migrator.revert(undefined, undefined, undefined, options)) .to.eventually.be.rejectedWith(errors.RequiredParameterError).with.property('code', errors.RequiredParameterError.code) }) - it('should error with out toVersion argument', () => { + it('should error without repo options argument', () => { + const options = createOptions() + + return expect(migrator.revert('/some/path', undefined, undefined, options)) + .to.eventually.be.rejectedWith(errors.RequiredParameterError).with.property('code', errors.RequiredParameterError.code) + }) + + it('should error without toVersion argument', () => { const options = createOptions() - return expect(migrator.revert('/some/path', undefined, options)) + return expect(migrator.revert('/some/path', {}, undefined, options)) .to.eventually.be.rejectedWith(errors.RequiredParameterError).with.property('code', errors.RequiredParameterError.code) }) it('should error with invalid toVersion argument', () => { const invalidValues = ['eight', '-1', '1', -1] const options = createOptions() + const repoOptions = createRepoOptions() return Promise.all( - invalidValues.map((value) => expect(migrator.revert('/some/path', value, options)) + invalidValues.map((value) => expect(migrator.revert('/some/path', repoOptions, value, options)) .to.eventually.be.rejectedWith(errors.InvalidValueError).with.property('code', errors.InvalidValueError.code)) ) }) @@ -110,8 +124,9 @@ describe('index.js', () => { it('should not revert if current repo version and toVersion matches', async () => { getVersionStub.returns(2) const options = createOptions() + const repoOptions = createRepoOptions() - await expect(migrator.revert('/some/path', 2, options)) + await expect(migrator.revert('/some/path', repoOptions, 2, options)) .to.eventually.be.fulfilled() expect(lockStub.called).to.be.false() @@ -120,8 +135,9 @@ describe('index.js', () => { it('should not revert if current repo version is lower then toVersion', async () => { getVersionStub.returns(2) const options = createOptions() + const repoOptions = createRepoOptions() - await expect(migrator.revert('/some/path', 3, options)) + await expect(migrator.revert('/some/path', repoOptions, 3, options)) .to.eventually.be.rejectedWith(errors.InvalidValueError).with.property('code', errors.InvalidValueError.code) expect(lockStub.called).to.be.false() @@ -131,19 +147,21 @@ describe('index.js', () => { const nonReversibleMigrationsMock = createMigrations() nonReversibleMigrationsMock[2].revert = undefined const options = { migrations: nonReversibleMigrationsMock } + const repoOptions = createRepoOptions() getVersionStub.returns(4) return expect( - migrator.revert('/some/path', 1, options) + migrator.revert('/some/path', repoOptions, 1, options) ).to.eventually.be.rejectedWith(errors.NonReversibleMigrationError) .with.property('code', errors.NonReversibleMigrationError.code) }) it('should revert expected migrations', async () => { const options = createOptions() + const repoOptions = createRepoOptions() getVersionStub.returns(3) - await expect(migrator.revert('/some/path', 1, options)) + await expect(migrator.revert('/some/path', repoOptions, 1, options)) .to.eventually.be.fulfilled() expect(lockCloseStub.calledOnce).to.be.true() @@ -159,9 +177,10 @@ describe('index.js', () => { it('should revert one migration as expected', async () => { const options = createOptions() + const repoOptions = createRepoOptions() getVersionStub.returns(2) - await expect(migrator.revert('/some/path', 1, options)) + await expect(migrator.revert('/some/path', repoOptions, 1, options)) .to.eventually.be.fulfilled() expect(lockCloseStub.calledOnce).to.be.true() @@ -185,9 +204,10 @@ describe('index.js', () => { } ] const options = { migrations: migrationsMock } + const repoOptions = createRepoOptions() getVersionStub.returns(2) - await expect(migrator.revert('/some/path', 1, options)) + await expect(migrator.revert('/some/path', repoOptions, 1, options)) .to.eventually.be.fulfilled() expect(lockCloseStub.calledOnce).to.be.true() @@ -200,10 +220,11 @@ describe('index.js', () => { it('should not have any side-effects when in dry run', async () => { const options = createOptions() + const repoOptions = createRepoOptions() getVersionStub.returns(4) options.isDryRun = true - await expect(migrator.revert('/some/path', 2, options)) + await expect(migrator.revert('/some/path', repoOptions, 2, options)) .to.eventually.be.fulfilled() expect(lockCloseStub.called).to.be.false() @@ -215,11 +236,12 @@ describe('index.js', () => { it('should not lock repo when ignoreLock is used', async () => { const options = createOptions() + const repoOptions = createRepoOptions() options.ignoreLock = true getVersionStub.returns(4) - await expect(migrator.revert('/some/path', 2, options)) + await expect(migrator.revert('/some/path', repoOptions, 2, options)) .to.eventually.be.fulfilled() expect(lockCloseStub.called).to.be.false() @@ -235,10 +257,11 @@ describe('index.js', () => { it('should report progress when progress callback is supplied', async () => { const options = createOptions() + const repoOptions = createRepoOptions() options.onProgress = sinon.stub() getVersionStub.returns(4) - await expect(migrator.revert('/some/path', 2, options)) + await expect(migrator.revert('/some/path', repoOptions, 2, options)) .to.eventually.be.fulfilled() expect(options.onProgress.getCall(0).calledWith(sinon.match.any, 1, 2)).to.be.true() @@ -248,9 +271,10 @@ describe('index.js', () => { it('should unlock repo when error is thrown', async () => { getVersionStub.returns(4) const options = createOptions() + const repoOptions = createRepoOptions() options.migrations[2].revert = sinon.stub().rejects() - await expect(migrator.revert('/some/path', 2, options)) + await expect(migrator.revert('/some/path', repoOptions, 2, options)) .to.eventually.be.rejected() expect(lockCloseStub.calledOnce).to.be.true() @@ -265,22 +289,31 @@ describe('index.js', () => { it('should error with out path argument', () => { const options = createOptions() - return expect(migrator.migrate(undefined, undefined, options)) + return expect(migrator.migrate(undefined, undefined, undefined, options)) + .to.eventually.be.rejectedWith(errors.RequiredParameterError).with.property('code', errors.RequiredParameterError.code) + }) + + it('should error with out repoOptions argument', () => { + const options = createOptions() + + return expect(migrator.migrate('/some/path', undefined, undefined, options)) .to.eventually.be.rejectedWith(errors.RequiredParameterError).with.property('code', errors.RequiredParameterError.code) }) it('should error with out toVersion argument', () => { const options = createOptions() + const repoOptions = createRepoOptions() - return expect(migrator.migrate('/some/path', undefined, options)) + return expect(migrator.migrate('/some/path', repoOptions, undefined, options)) .to.eventually.be.rejectedWith(errors.RequiredParameterError).with.property('code', errors.RequiredParameterError.code) }) it('should error with invalid toVersion argument', () => { const invalidValues = ['eight', '-1', '1', -1, {}] + const repoOptions = createRepoOptions() return Promise.all( - invalidValues.map((invalidValue) => expect(migrator.migrate('/some/path', invalidValue, createOptions())) + invalidValues.map((invalidValue) => expect(migrator.migrate('/some/path', repoOptions, invalidValue, createOptions())) .to.eventually.be.rejectedWith(errors.InvalidValueError).with.property('code', errors.InvalidValueError.code)) ) }) @@ -300,10 +333,11 @@ describe('index.js', () => { } ] } + const repoOptions = createRepoOptions() getVersionStub.returns(1) - return expect(migrator.migrate('/some/path', 3, options)) + return expect(migrator.migrate('/some/path', repoOptions, 3, options)) .to.eventually.be.rejectedWith(errors.InvalidValueError).with.property('code', errors.InvalidValueError.code) }) @@ -322,18 +356,20 @@ describe('index.js', () => { } ] } + const repoOptions = createRepoOptions() getVersionStub.returns(3) - return expect(migrator.migrate('/some/path', 5, options)) + return expect(migrator.migrate('/some/path', repoOptions, 5, options)) .to.eventually.be.rejectedWith(errors.InvalidValueError).with.property('code', errors.InvalidValueError.code) }) it('should not migrate if current repo version and toVersion matches', async () => { getVersionStub.returns(2) const options = createOptions() + const repoOptions = createRepoOptions() - await expect(migrator.migrate('/some/path', 2, options)) + await expect(migrator.migrate('/some/path', repoOptions, 2, options)) .to.eventually.be.fulfilled() expect(lockStub.called).to.be.false() @@ -342,8 +378,9 @@ describe('index.js', () => { it('should not migrate if current repo version is higher then toVersion', async () => { getVersionStub.returns(3) const options = createOptions() + const repoOptions = createRepoOptions() - await expect(migrator.migrate('/some/path', 2, options)) + await expect(migrator.migrate('/some/path', repoOptions, 2, options)) .to.eventually.be.rejectedWith(errors.InvalidValueError).with.property('code', errors.InvalidValueError.code) expect(lockStub.called).to.be.false() @@ -351,9 +388,10 @@ describe('index.js', () => { it('should migrate expected migrations', async () => { const options = createOptions() + const repoOptions = createRepoOptions() getVersionStub.returns(1) - await expect(migrator.migrate('/some/path', 3, options)) + await expect(migrator.migrate('/some/path', repoOptions, 3, options)) .to.eventually.be.fulfilled() expect(lockCloseStub.calledOnce).to.be.true() @@ -370,9 +408,10 @@ describe('index.js', () => { it('should not have any side-effects when in dry run', async () => { const options = createOptions() options.isDryRun = true + const repoOptions = createRepoOptions() getVersionStub.returns(2) - await expect(migrator.migrate('/some/path', 4, options)) + await expect(migrator.migrate('/some/path', repoOptions, 4, options)) .to.eventually.be.fulfilled() expect(lockCloseStub.called).to.be.false() @@ -385,9 +424,10 @@ describe('index.js', () => { it('should not lock repo when ignoreLock is used', async () => { const options = createOptions() options.ignoreLock = true + const repoOptions = createRepoOptions() getVersionStub.returns(2) - await expect(migrator.migrate('/some/path', 4, options)) + await expect(migrator.migrate('/some/path', repoOptions, 4, options)) .to.eventually.be.fulfilled() expect(lockCloseStub.called).to.be.false() @@ -404,9 +444,10 @@ describe('index.js', () => { it('should report progress when progress callback is supplied', async () => { const options = createOptions() options.onProgress = sinon.stub() + const repoOptions = createRepoOptions() getVersionStub.returns(2) - await expect(migrator.migrate('/some/path', 4, options)) + await expect(migrator.migrate('/some/path', repoOptions, 4, options)) .to.eventually.be.fulfilled() expect(options.onProgress.getCall(0).calledWith(sinon.match.any, 1, 2)).to.be.true() @@ -417,8 +458,9 @@ describe('index.js', () => { getVersionStub.returns(2) const options = createOptions() options.migrations[3].migrate = sinon.stub().rejects() + const repoOptions = createRepoOptions() - await expect(migrator.migrate('/some/path', 4, options)) + await expect(migrator.migrate('/some/path', repoOptions, 4, options)) .to.eventually.be.rejected() expect(lockCloseStub.calledOnce).to.be.true() diff --git a/test/init-test.js b/test/init-test.js index 6c8d534..e1bcc9b 100644 --- a/test/init-test.js +++ b/test/init-test.js @@ -2,13 +2,11 @@ 'use strict' const { expect } = require('./util') - -const Datastore = require('datastore-fs') -const Key = require('interface-datastore').Key +const { CONFIG_KEY, VERSION_KEY, getDatastoreAndOptions } = require('../src/utils') const repoInit = require('../src/repo/init') const uint8ArrayFromString = require('uint8arrays/from-string') -module.exports = (setup, cleanup) => { +module.exports = (setup, cleanup, repoOptions) => { let dir beforeEach(async () => { @@ -19,38 +17,58 @@ module.exports = (setup, cleanup) => { ) it('should return true with valid initialized repo', async () => { - const versionKey = new Key('version') - const configKey = new Key('config') - const store = new Datastore(dir, { extension: '', createIfMissing: false }) + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + const store = new StorageBackend(dir, { + ...storageOptions, + createIfMissing: false + }) await store.open() - await store.put(versionKey, uint8ArrayFromString('7')) - await store.put(configKey, uint8ArrayFromString('config')) + await store.put(VERSION_KEY, uint8ArrayFromString('7')) + await store.put(CONFIG_KEY, uint8ArrayFromString('config')) await store.close() - expect(await repoInit.isRepoInitialized(dir)).to.be.true() + expect(await repoInit.isRepoInitialized(dir, repoOptions)).to.be.true() }) it('should return false with missing version key', async () => { - const configKey = new Key('config') - const store = new Datastore(dir, { extension: '', createIfMissing: false }) + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + const store = new StorageBackend(dir, { + ...storageOptions, + createIfMissing: false + }) await store.open() - await store.put(configKey, '') + await store.put(CONFIG_KEY, '') await store.close() - expect(await repoInit.isRepoInitialized(dir)).to.be.false() + expect(await repoInit.isRepoInitialized(dir, repoOptions)).to.be.false() }) it('should return false with missing config key', async () => { - const versionKey = new Key('version') - const store = new Datastore(dir, { extension: '', createIfMissing: false }) + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + const store = new StorageBackend(dir, { + ...storageOptions, + createIfMissing: false + }) await store.open() - await store.put(versionKey, '') + await store.put(VERSION_KEY, '') await store.close() - expect(await repoInit.isRepoInitialized(dir)).to.be.false() + expect(await repoInit.isRepoInitialized(dir, repoOptions)).to.be.false() }) it('should return false if the repo does not exists', async () => { - return expect(await repoInit.isRepoInitialized('/some/random/dirrr')).to.be.false() + return expect(await repoInit.isRepoInitialized('/some/random/dirrr', repoOptions)).to.be.false() }) } diff --git a/test/integration-test.js b/test/integration-test.js index e09b929..f5ed59d 100644 --- a/test/integration-test.js +++ b/test/integration-test.js @@ -5,13 +5,9 @@ const { expect } = require('./util') const migrator = require('../src') const migrations = require('./test-migrations') +const { VERSION_KEY, CONFIG_KEY, getDatastoreAndOptions } = require('../src/utils') -const Datastore = require('datastore-fs') -const Key = require('interface-datastore').Key -const CONFIG_KEY = new Key('config') -const VERSION_KEY = new Key('version') - -module.exports = (setup, cleanup) => { +module.exports = (setup, cleanup, repoOptions) => { let dir beforeEach(async () => { @@ -22,9 +18,17 @@ module.exports = (setup, cleanup) => { ) it('migrate forward', async () => { - await migrator.migrate(dir, migrator.getLatestMigrationVersion(migrations), { migrations: migrations }) + await migrator.migrate(dir, repoOptions, migrator.getLatestMigrationVersion(migrations), { + migrations: migrations + }) + + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + const store = new StorageBackend(dir, storageOptions) - const store = new Datastore(dir, { extension: '', createIfMissing: false }) await store.open() const version = await store.get(VERSION_KEY) expect(version.toString()).to.be.equal('2') @@ -36,11 +40,21 @@ module.exports = (setup, cleanup) => { }) it('revert', async () => { - await migrator.migrate(dir, migrator.getLatestMigrationVersion(migrations), { migrations: migrations }) + await migrator.migrate(dir, repoOptions, migrator.getLatestMigrationVersion(migrations), { + migrations: migrations + }) + + await migrator.revert(dir, repoOptions, 1, { + migrations: migrations + }) + + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') - await migrator.revert(dir, 1, { migrations: migrations }) + const store = new StorageBackend(dir, storageOptions) - const store = new Datastore(dir, { extension: '', createIfMissing: false }) await store.open() const version = await store.get(VERSION_KEY) expect(version.toString()).to.be.equal('1') diff --git a/test/node.js b/test/node.js index f515c96..7d1f9d6 100644 --- a/test/node.js +++ b/test/node.js @@ -3,25 +3,28 @@ const promisify = require('util').promisify const asyncRimraf = promisify(require('rimraf')) -const asyncNcp = promisify(require('ncp').ncp) -const path = require('path') -const fs = require('fs') - -function createRepo () { - const testRepoPath = path.join(__dirname, 'fixtures', 'test-repo') - const date = Date.now().toString() - const dir = testRepoPath + '-for-' + date - fs.mkdirSync(dir) - - return dir -} - -async function createAndLoadRepo () { - const dir = createRepo() - const testRepoPath = path.join(__dirname, 'fixtures', 'test-repo') - - await asyncNcp(testRepoPath, dir) - return dir +const { createRepo, createAndLoadRepo } = require('./fixtures/repo') + +const repoOptions = { + lock: 'fs', + storageBackends: { + root: require('datastore-fs'), + blocks: require('datastore-fs'), + keys: require('datastore-fs'), + datastore: require('datastore-level'), + pins: require('datastore-level') + }, + storageBackendOptions: { + root: { + extension: '' + }, + blocks: { + sharding: true, + extension: '.data' + }, + keys: { + } + } } function repoCleanup (dir) { @@ -31,27 +34,27 @@ function repoCleanup (dir) { describe('Node specific tests', () => { describe('lock.js tests', () => { describe('fs-lock tests', () => { - require('./lock-test')(require('../src/repo/lock'), createRepo, repoCleanup) + require('./lock-test')(require('../src/repo/lock'), () => createRepo(repoOptions), repoCleanup, repoOptions) }) describe('mem-lock tests', () => { - require('./lock-test')(require('../src/repo/lock-memory'), createRepo, repoCleanup) + require('./lock-test')(require('../src/repo/lock-memory'), () => createRepo(repoOptions), repoCleanup, repoOptions) }) }) describe('version tests', () => { - require('./version-test')(createRepo, repoCleanup) + require('./version-test')(() => createRepo(repoOptions), repoCleanup, repoOptions) }) - describe('migration tests', () => { - require('./migrations')(createRepo, repoCleanup) + describe('migrations tests', () => { + require('./migrations')(() => createRepo(repoOptions), repoCleanup, repoOptions) }) describe('init tests', () => { - require('./init-test')(createRepo, repoCleanup) + require('./init-test')(() => createRepo(repoOptions), repoCleanup, repoOptions) }) describe('integration tests', () => { - require('./integration-test')(createAndLoadRepo, repoCleanup) + require('./integration-test')(() => createAndLoadRepo(repoOptions), repoCleanup, repoOptions) }) }) diff --git a/test/sharness/.gitignore b/test/sharness/.gitignore deleted file mode 100644 index a0b371d..0000000 --- a/test/sharness/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -!/lib/ -bin/ -lib/sharness/ -test-results/ -trash directory.*.sh/ diff --git a/test/sharness/Makefile b/test/sharness/Makefile deleted file mode 100644 index 0eb78b5..0000000 --- a/test/sharness/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -# Run sharness tests -# -# Copyright (c) 2016 Christian Couder -# MIT Licensed; see the LICENSE file in this repository. -# -# NOTE: run with TEST_VERBOSE=1 for verbose sharness tests. - -T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)) -LIBDIR = lib -SHARNESSDIR = sharness -AGGREGATE = $(LIBDIR)/$(SHARNESSDIR)/aggregate-results.sh - - -BINS = ../bin/jsipfs-migrations - -all: aggregate - -help: - @echo "- use 'make' or 'make all' to run all the tests" - @echo "- use 'make deps' to create an 'ipfs' executable in ../bin" - @echo "- to run tests manually, make sure to include ../bin in your PATH" - -clean: clean-test-results - @echo "*** $@ ***" - -rm -rf ../bin/ipfs - -clean-test-results: - @echo "*** $@ ***" - -rm -rf test-results - -$(T): clean-test-results deps - @echo "*** $@ ***" - ./$@ - -aggregate: clean-test-results $(T) - @echo "*** $@ ***" - ls test-results/t*-*.sh.*.counts | $(AGGREGATE) - -deps: sharness $(BINS) curl - -sharness: - @echo "*** checking $@ ***" - lib/install-sharness.sh - -../bin/jsipfs-migrations: - mkdir -p bin - -ln -s ../../../src/cli.js ./bin/jsipfs-migrations - -curl: - @which curl >/dev/null || (echo "Please install curl!" && false) - -.PHONY: all help clean clean-test-results $(T) aggregate deps sharness - diff --git a/test/sharness/lib/install-sharness.sh b/test/sharness/lib/install-sharness.sh deleted file mode 100755 index ecc5834..0000000 --- a/test/sharness/lib/install-sharness.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh -# install-sharness.sh -# -# Copyright (c) 2014 Juan Batiz-Benet -# Copyright (c) 2015 Christian Couder -# MIT Licensed; see the LICENSE file in this repository. -# -# This script checks that Sharness is installed in: -# -# $(pwd)/$clonedir/$sharnessdir/ -# -# where $clonedir and $sharnessdir are configured below. -# -# If Sharness is not installed, this script will clone it -# from $urlprefix (defined below). -# -# If Sharness is not uptodate with $version (defined below), -# this script will fetch and will update the installed -# version to $version. -# - -# settings -version=ecba410b0b58400dd6517cfa6594fdac243d9056 -urlprefix=https://github.com/chriscool/sharness.git -clonedir=lib -sharnessdir=sharness - -if test -f "$clonedir/$sharnessdir/SHARNESS_VERSION_$version" -then - # There is the right version file. Great, we are done! - exit 0 -fi - -die() { - echo >&2 "$@" - exit 1 -} - -checkout_version() { - git checkout "$version" || die "Could not checkout '$version'" - rm -f SHARNESS_VERSION_* || die "Could not remove 'SHARNESS_VERSION_*'" - touch "SHARNESS_VERSION_$version" || die "Could not create 'SHARNESS_VERSION_$version'" - echo "Sharness version $version is checked out!" -} - -if test -d "$clonedir/$sharnessdir/.git" -then - # We need to update sharness! - cd "$clonedir/$sharnessdir" || die "Could not cd into '$clonedir/$sharnessdir' directory" - git fetch || die "Could not fetch to update sharness" -else - # We need to clone sharness! - mkdir -p "$clonedir" || die "Could not create '$clonedir' directory" - cd "$clonedir" || die "Could not cd into '$clonedir' directory" - - git clone "$urlprefix" || die "Could not clone '$urlprefix'" - cd "$sharnessdir" || die "Could not cd into '$sharnessdir' directory" -fi - -checkout_version diff --git a/test/sharness/lib/test-lib.sh b/test/sharness/lib/test-lib.sh deleted file mode 100644 index 7c0f309..0000000 --- a/test/sharness/lib/test-lib.sh +++ /dev/null @@ -1,34 +0,0 @@ -# Test framework for go-ipfs -# -# Copyright (c) 2014 Christian Couder -# MIT Licensed; see the LICENSE file in this repository. -# -# We are using sharness (https://github.com/chriscool/sharness) -# which was extracted from the Git test framework. - -# add current directory to path. -BIN=$(echo `pwd`/bin) -PATH=${BIN}:${PATH} - -# assert the `jsipfs-migrations` we're using is the right one. -if [[ $(which jsipfs-migrations) != ${BIN}/jsipfs-migrations ]]; then - echo >&2 "Cannot find the test's local jsipfs-migrations tool." - echo >&2 "Please check test and ipfs tool installation." - JS_BIN=$(dirname $(dirname "${BIN}"))"/src/cli.js" - echo >&2 "For jsipfs-migrations, look for a symlink from '${BIN}/jsipfs-migrations' to '${JS_BIN}'." - echo >&2 "Use 'make' or 'make deps' as it should install this symlink." - exit 1 -fi - -# set sharness verbosity. we set the env var directly as -# it's too late to pass in --verbose, and --verbose is harder -# to pass through in some cases. -test "$TEST_VERBOSE" = 1 && verbose=t - -SHARNESS_LIB="lib/sharness/sharness.sh" - -. "$SHARNESS_LIB" || { - echo >&2 "Cannot source: $SHARNESS_LIB" - echo >&2 "Please check Sharness installation." - exit 1 -} diff --git a/test/sharness/t0000-sharness.sh b/test/sharness/t0000-sharness.sh deleted file mode 100755 index c55d40e..0000000 --- a/test/sharness/t0000-sharness.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -test_description="Show basic features of Sharness" - -. ./lib/sharness/sharness.sh - -test_expect_success "Success is reported like this" " - echo hello world | grep hello -" - -test_expect_success "Commands are chained this way" " - test x = 'x' && - test 2 -gt 1 && - echo success -" - -return_42() { - echo "Will return soon" - return 42 -} - -test_expect_success "You can test for a specific exit code" " - test_expect_code 42 return_42 -" - -test_expect_failure "We expect this to fail" " - test 1 = 2 -" - -test_done - -# vi: set ft=sh : diff --git a/test/sharness/t0010-basic-commands.sh b/test/sharness/t0010-basic-commands.sh deleted file mode 100755 index c3ee372..0000000 --- a/test/sharness/t0010-basic-commands.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2014 Christian Couder -# MIT Licensed; see the LICENSE file in this repository. -# - -test_description="Test installation and some basic commands" - -# setup temp repo -rm -rf ./tmp -cp -r ../fixtures/test-repo ./tmp -IPFS_PATH=$(echo `pwd`/tmp) -export IPFS_PATH - -. lib/test-lib.sh - -test_expect_success "current dir is writable" ' - echo "It works!" > test.txt -' - -test_expect_success "jsipfs-migrations version succeeds" ' - jsipfs-migrations --version > version.txt -' - -test_expect_success "jsipfs-migrations help succeeds" ' - jsipfs-migrations --help > help.txt && - grep "migrate" help.txt -' - -test_expect_success "jsipfs-migrations status shows migrations are needed" $' - jsipfs-migrations status --migrations ../test/test-migrations > status.txt && - grep "Repo is out of date" status.txt && - grep "Current repo version: 1" status.txt && - grep "Latest migration version: 2" status.txt -' - -test_expect_success "jsipfs-migrations successfully migrate to latest version" $' - jsipfs-migrations migrate --migrations ../test/test-migrations > migrate.txt && - grep "Successfully migrated to version 2" migrate.txt -' - -test_expect_success "jsipfs-migrations status shows NO migrations are needed" $' - jsipfs-migrations status --migrations ../test/test-migrations > status.txt && - grep "Nothing to migrate" status.txt && - grep "Current repo version: 2" status.txt && - grep "Latest migration version: 2" status.txt -' - -test_expect_success "jsipfs-migrations successfully reverts" $' - jsipfs-migrations migrate --to 1 --revert-ok --migrations ../test/test-migrations > revert.txt && - grep "Successfully reverted version 2" revert.txt -' - - -test_done diff --git a/test/version-test.js b/test/version-test.js index e4613b5..264ecba 100644 --- a/test/version-test.js +++ b/test/version-test.js @@ -2,9 +2,7 @@ 'use strict' const { expect } = require('./util') - -const Datastore = require('datastore-fs') -const Key = require('interface-datastore').Key +const { VERSION_KEY, CONFIG_KEY, getDatastoreAndOptions } = require('../src/utils') const version = require('../src/repo/version') const uint8ArrayFromString = require('uint8arrays/from-string') const errors = require('../src/errors') @@ -12,46 +10,62 @@ const errors = require('../src/errors') // When new versioning mechanism is introduced in new version don't forget to update // the range (from/to) of the previous version test's description -module.exports = (setup, cleanup) => { - it('getVersion should fail without any version in repo', async () => { - const dir = await setup() - await expect(version.getVersion(dir)).to.be.eventually.rejectedWith(errors.NotInitializedRepoError).with.property('code', errors.NotInitializedRepoError.code) - return cleanup(dir) +module.exports = (setup, cleanup, repoOptions) => { + let dir + + beforeEach(async () => { + dir = await setup() }) - describe('version 7 and bellow', () => { - let dir + afterEach(() => cleanup(dir)) - beforeEach(async () => { - dir = await setup() - }) - afterEach(() => - cleanup(dir) - ) + it('getVersion should fail without any version in repo', async () => { + await expect(version.getVersion(dir, repoOptions)).to.be.eventually.rejectedWith(errors.NotInitializedRepoError) + .with.property('code', errors.NotInitializedRepoError.code) + }) + describe('version 7 and bellow', () => { it('should get version number', async () => { // Create version file - const store = new Datastore(dir, { extension: '', createIfMissing: false }) + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + const store = new StorageBackend(dir, { + ...storageOptions, + createIfMissing: false + }) + await store.open() - await store.put(new Key('config'), uint8ArrayFromString('some dummy config')) - await store.put(new Key('version'), uint8ArrayFromString('7')) + await store.put(CONFIG_KEY, uint8ArrayFromString('some dummy config')) + await store.put(VERSION_KEY, uint8ArrayFromString('7')) await store.close() - expect(await version.getVersion(dir)).to.be.equal(7) + expect(await version.getVersion(dir, repoOptions)).to.be.equal(7) }) it('should set version number', async () => { - await expect(version.getVersion(dir)).to.be.eventually.rejectedWith(errors.NotInitializedRepoError).with.property('code', errors.NotInitializedRepoError.code) + await expect(version.getVersion(dir, repoOptions)).to.be.eventually.rejectedWith(errors.NotInitializedRepoError).with.property('code', errors.NotInitializedRepoError.code) // Create version file - const store = new Datastore(dir, { extension: '', createIfMissing: false }) + const { + StorageBackend, + storageOptions + } = getDatastoreAndOptions(repoOptions, 'root') + + const store = new StorageBackend(dir, { + ...storageOptions, + createIfMissing: false + }) + await store.open() - await store.put(new Key('config'), uint8ArrayFromString('some dummy config')) - await store.put(new Key('version'), uint8ArrayFromString('5')) + await store.put(CONFIG_KEY, uint8ArrayFromString('some dummy config')) + await store.put(VERSION_KEY, uint8ArrayFromString('5')) await store.close() - await version.setVersion(dir, 7) - expect(await version.getVersion(dir)).to.be.equal(7) + await version.setVersion(dir, 7, repoOptions) + expect(await version.getVersion(dir, repoOptions)).to.be.equal(7) }) }) }