diff --git a/__tests__/generate.test.js b/__tests__/generate.test.js index 3b97bd4..e1e4034 100644 --- a/__tests__/generate.test.js +++ b/__tests__/generate.test.js @@ -1,18 +1,21 @@ -const { execaNode } = require('execa') const fs = require('fs') const { join } = require('path') +const { handler: generateCommand } = require('../bin/commands/generate') +const { extractLogLinesFromConsole } = require('../__test-utils__/log') -//todo: rewrite to match refactored other tests? describe('generate', () => { it('should create a new migration file', async () => { + const stdout = extractLogLinesFromConsole() const migrationName = 'test-migration' - const { stdout } = await execaNode('./bin/cmp.js', ['generate', migrationName]) + await generateCommand({ name: migrationName }) - const migrationFileName = stdout.split(' ').find((m) => m.indexOf('js') > -1) - const migrationFile = fs.readFileSync(`${process.env.MIGRATIONS_DIR}/${migrationFileName}`, 'utf-8') + let numberOfMigrationsInMigrationsDir = fs.readdirSync(process.env.MIGRATIONS_DIR).length + expect(numberOfMigrationsInMigrationsDir).toBe(1) + const migrationFileName = stdout[0].split(' ').find((m) => m.indexOf('js') > -1) + const migrationFilePath = `${process.env.MIGRATIONS_DIR}/${migrationFileName}` + const migrationFile = fs.readFileSync(migrationFilePath, 'utf-8') const template = fs.readFileSync(join(__dirname, '..', 'templates', 'migration.mustache'), 'utf8') - expect(migrationFile).toEqual(template) }) }) diff --git a/__tests__/migrate.test.js b/__tests__/migrate.test.js index 9d73dbc..be8dbe3 100644 --- a/__tests__/migrate.test.js +++ b/__tests__/migrate.test.js @@ -1,4 +1,3 @@ -const { execaNode } = require('execa') const { handler: migrateCommand } = require('../bin/commands/migrate') const { extractLogLinesFromConsole } = require('../__test-utils__/log') const { setupMockedContentfulApi, closeMockedContentfulApi } = require('../mocks/contentful/baseContentfulHandler') diff --git a/__tests__/reset.test.js b/__tests__/reset.test.js new file mode 100644 index 0000000..e9dcc3e --- /dev/null +++ b/__tests__/reset.test.js @@ -0,0 +1,10 @@ +const { handler: resetCommand } = require('../bin/commands/reset') +const { extractLogLinesFromConsole } = require('../__test-utils__/log') + +describe('reset', () => { + it('should not reset environment if on master', async () => { + const stdout = extractLogLinesFromConsole() + await resetCommand({ force: false }) + expect(stdout).toContain(`Can't reset environment if you are on master.`) + }) +}) diff --git a/__tests__/rollback.test.js b/__tests__/rollback.test.js new file mode 100644 index 0000000..48202b3 --- /dev/null +++ b/__tests__/rollback.test.js @@ -0,0 +1,13 @@ +const fs = require('fs') +const { join } = require('path') +const { handler: rollbackCommand } = require('../bin/commands/rollback') +const { extractLogLinesFromConsole } = require('../__test-utils__/log') +const { handler: migrateCommand } = require('../bin/commands/migrate') + +describe('rollback', () => { + it('should demand user to use "--force" flag if running against master space', async () => { + const stdout = extractLogLinesFromConsole() + await rollbackCommand({ force: false }) + expect(stdout).toContain('Executing migrations against master requires the --force flag.') + }) +}) diff --git a/bin/commands/generate.js b/bin/commands/generate.js index befae88..793e6d9 100755 --- a/bin/commands/generate.js +++ b/bin/commands/generate.js @@ -1,5 +1,4 @@ #! /usr/bin/env node - const fs = require('fs') const { join } = require('path') const Mustache = require('mustache') @@ -19,10 +18,9 @@ exports.builder = (yargs) => { }) } -exports.handler = ({ name }) => { +exports.handler = async ({ name }) => { try { const templatePath = fs.readFileSync(join(__dirname, '..', '..', 'templates', 'migration.mustache'), 'utf8') - const migrationContents = Mustache.render(templatePath) const migrationFileName = `${utcTimestampMs()}-${camelToKebabCase(name)}.js` const migrationsDir = env('MIGRATIONS_DIR') @@ -35,14 +33,23 @@ exports.handler = ({ name }) => { const migrationPath = join(migrationsDir, migrationFileName) - fs.writeFile(migrationPath, migrationContents, { flag: 'w' }, (err) => { - if (err) { - throw err - } - log.success(`Migration file ${migrationFileName} created`) - }) + await writeFileAsync(migrationPath, migrationContents) + log.success(`Migration file ${migrationFileName} created`) } catch (e) { log.error('Migration file creation failed.', e) process.exitCode = 1 } } + +//todo: move to utility if we need this elsewhere +function writeFileAsync(filePath, data) { + return new Promise((resolve, reject) => { + fs.writeFile(filePath, data, (error) => { + if (error) { + reject(error) + } else { + resolve() + } + }) + }) +} diff --git a/jest.setupFiles.js b/jest.setupFiles.js index a374d9a..9485304 100644 --- a/jest.setupFiles.js +++ b/jest.setupFiles.js @@ -1,5 +1,4 @@ const dotenv = require('dotenv') -const { setupInterceptorServer } = require('./traffic/interceptor') dotenv.config({ path: './.env.test' }) //mock lib/log since encoding characters from chalk makes testing difficult diff --git a/mocks/contentful/baseContentfulHandler.js b/mocks/contentful/baseContentfulHandler.js index a8fdfbc..71338e9 100644 --- a/mocks/contentful/baseContentfulHandler.js +++ b/mocks/contentful/baseContentfulHandler.js @@ -10,6 +10,7 @@ module.exports.setupMockedContentfulApi = (handlers) => { if (handlers == null) { handlers = [] } + let totalNumberOfRequests = 0 log.info('setting up mocked contentful rest server') const server = setupServer(...spacesHandler, ...localeHandler, ...masterEnvironmentHandler, ...handlers) @@ -27,6 +28,8 @@ module.exports.setupMockedContentfulApi = (handlers) => { server.events.on('request:start', (request, response) => { // Record every dispatched request. log.info(`* intercepting ${request.method} request to ${request.url}`) + totalNumberOfRequests++ + log.info('Total number of requests: ', totalNumberOfRequests) }) mockedContentfulServer = server diff --git a/traffic/interceptor.js b/traffic/interceptor.js index 7330d19..03424d1 100644 --- a/traffic/interceptor.js +++ b/traffic/interceptor.js @@ -9,39 +9,45 @@ const requests = new Map() const transactions = new Set() const server = setupServer() -//this utility can be used to record all network traffic. +// this utility can be used to record all network traffic. // Each request and response during app lifetime is logged to json in the traffic/output directory. -//run setupInterceptorServer() in bin/cmp.js for this to work. -module.exports.setupInterceptorServer = () => { +// run setupInterceptorServer() in bin/cmp.js for this to work. +module.exports.setupInterceptorServer = ({ logUnhandledRequests, logBypassedTraffic, saveTrafficToFile }) => { log.info('setting up network traffic interceptor server') //catch requests from underlying dependencies - server.listen({ - onUnhandledRequest(req) { - log.warn(`* Found an unhandled ${req.method} request to ${req.url.href}`) - }, - }) + if (logUnhandledRequests) { + server.listen({ + onUnhandledRequest(req) { + log.warn(`* Found an unhandled ${req.method} request to ${req.url.href}`) + }, + }) + } - server.events.on('request:start', (request, response) => { - // Record every dispatched request. - log.info(`* intercepting ${request.method} request to ${request.url}`) - requests.set(request.id, request) - transactions.add([request, response]) - }) + if (logBypassedTraffic) { + server.events.on('request:start', (request, response) => { + // Record every dispatched request. + log.info(`* intercepting ${request.method} request to ${request.url}`) + requests.set(request.id, request) + transactions.add([request, response]) + }) - // Since we're not providing any request handlers - // to the "setupServer" call, all responses will be - // bypassed (performed as-is). This will allow us to - // collect the actual responses. - server.events.on('response:bypass', (response, requestId) => { - log.info(`* receiving status ${response.status} from ${requests.get(requestId).url}`) - const request = requests.get(requestId) - transactions.add([request, response]) - }) + // Since we're not providing any request handlers + // to the "setupServer" call, all responses will be + // bypassed (performed as-is). This will allow us to + // collect the actual responses. + server.events.on('response:bypass', (response, requestId) => { + log.info(`* receiving status ${response.status} from ${requests.get(requestId).url}`) + const request = requests.get(requestId) + transactions.add([request, response]) + }) + } process.on('exit', () => { log.info('handling exit') - this.writeTrafficToFile() + if (saveTrafficToFile) { + this.writeTrafficToFile() + } }) }