1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-02 01:17:58 +02:00

use ava as testrunner

This commit is contained in:
sveisvei 2016-11-13 15:41:35 +01:00 committed by Ivar Conradi Østhus
parent 8e9166add7
commit ce056df8b6
21 changed files with 770 additions and 759 deletions

1
.gitignore vendored
View File

@ -39,3 +39,4 @@ unleash-server.tar.gz
jsconfig.json jsconfig.json
typings typings
.vscode .vscode
.nyc_output

View File

@ -39,14 +39,13 @@
"start:dev:pg-chain": "export DATABASE_URL=postgres://$PGUSER:$PGPASSWORD@localhost:$PGPORT/postgres ; db-migrate up && npm run start:dev", "start:dev:pg-chain": "export DATABASE_URL=postgres://$PGUSER:$PGPASSWORD@localhost:$PGPORT/postgres ; db-migrate up && npm run start:dev",
"db-migrate": "db-migrate up", "db-migrate": "db-migrate up",
"db-migrate:down": "db-migrate down", "db-migrate:down": "db-migrate down",
"test": "export PORT=4243 ; mocha --recursive test", "test": "PORT=4243 ava **/**test.js",
"test:unit": "mocha test/unit/**/*.js ",
"test:docker": "./scripts/docker-postgres.sh", "test:docker": "./scripts/docker-postgres.sh",
"test:watch": "mocha --watch test test/*", "test:watch": "npm run test -- --watch",
"test:pg-virtualenv": "pg_virtualenv npm run test:pg-virtualenv-chai", "test:pg-virtualenv": "pg_virtualenv npm run test:pg-virtualenv-chai",
"test:pg-virtualenv-chain": "export TEST_DATABASE_URL=postgres://$PGUSER:$PGPASSWORD@localhost:$PGPORT/postgres ; npm run db-migrate-testdb && npm test", "test:pg-virtualenv-chain": "export TEST_DATABASE_URL=postgres://$PGUSER:$PGPASSWORD@localhost:$PGPORT/postgres ; npm run db-migrate-testdb && npm test",
"test:coverage": "istanbul cover ./node_modules/mocha/bin/_mocha test --report lcovonly -- -R spec --recursive", "test:coverage": "nyc npm run test",
"test:coverage-report": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" "test:coverage-report": "nyc report --reporter=text-lcov | coveralls && rm -rf ./coverage"
}, },
"dependencies": { "dependencies": {
"body-parser": "1.15.2", "body-parser": "1.15.2",
@ -70,13 +69,13 @@
"yallist": "^2.0.0" "yallist": "^2.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^6.0.46",
"ava": "^0.16.0", "ava": "^0.16.0",
"coveralls": "^2.11.14", "coveralls": "^2.11.15",
"istanbul": "^0.4.5", "nyc": "^8.4.0",
"mocha": "^3.0.2",
"mocha-lcov-reporter": "1.2.0",
"sinon": "^1.17.5", "sinon": "^1.17.5",
"supertest": "^2.0.0", "superagent": "^2.3.0",
"supertest": "^2.0.1",
"supervisor": "^0.11.0", "supervisor": "^0.11.0",
"unleash-frontend": "1.0.0-alpha.2" "unleash-frontend": "1.0.0-alpha.2"
} }

View File

@ -1,6 +0,0 @@
{
"env": {
"browser": true,
"mocha": true
}
}

View File

@ -1,27 +1,27 @@
'use strict'; 'use strict';
const specHelper = require('./util/test-helper'); const test = require('ava');
let request; const { setupApp } = require('./util/test-helper');
const logger = require('../../lib/logger');
describe('The event api', () => { test.beforeEach(() => {
beforeEach(done => { logger.setLevel('FATAL');
specHelper.setupApp().then((app) => { });
request = app.request;
done();
});
});
it('returns events', done => { test.serial('returns events', async (t) => {
request const { request, destroy } = await setupApp('event_api_serial');
return request
.get('/api/events') .get('/api/events')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done); .expect(200)
}); .then(destroy);
});
it('returns events given a name', done => { test.serial('returns events given a name', async (t) => {
request const { request, destroy } = await setupApp('event_api_serial');
return request
.get('/api/events/myname') .get('/api/events/myname')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done); .expect(200)
}); .then(destroy);
}); });

View File

@ -1,57 +1,56 @@
'use strict'; 'use strict';
const { test } = require('ava');
const { setupApp } = require('./util/test-helper');
const logger = require('../../lib/logger'); const logger = require('../../lib/logger');
const assert = require('assert');
const specHelper = require('./util/test-helper');
const stringify = function (o) {
return JSON.stringify(o, null, ' ');
};
let request; test.beforeEach(() => {
logger.setLevel('FATAL');
});
describe('The features api', () => { test.serial('returns three feature toggles', async t => {
beforeEach(done => { const { request, destroy } = await setupApp('feature_api_serial');
specHelper.setupApp().then((app) => { return request
request = app.request;
done();
});
});
it('returns three feature toggles', done => {
request
.get('/features') .get('/features')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end((err, res) => { .expect((res) => {
assert(res.body.features.length === 3, `expected 3 features, got ${stringify(res.body)}`); t.true(res.body.features.length === 3);
done(); })
}); .then(destroy);
}); });
it('gets a feature by name', done => { test.serial('gets a feature by name', async t => {
request const { request, destroy } = await setupApp('feature_api_serial');
return request
.get('/features/featureX') .get('/features/featureX')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done); .expect(200)
}); .then(destroy);
});
it('cant get feature that dose not exist', done => { test.serial('cant get feature that dose not exist', async t => {
const { request, destroy } = await setupApp('feature_api_serial');
logger.setLevel('FATAL'); logger.setLevel('FATAL');
request return request
.get('/features/myfeature') .get('/features/myfeature')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(404, done); .expect(404)
}); .then(destroy);
});
it('creates new feature toggle', done => { test.serial('creates new feature toggle', async t => {
request const { request, destroy } = await setupApp('feature_api_serial');
return request
.post('/features') .post('/features')
.send({ name: 'com.test.feature', enabled: false }) .send({ name: 'com.test.feature', enabled: false })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(201, done); .expect(201)
}); .then(destroy);
});
it('creates new feature toggle with createdBy', done => { test.serial('creates new feature toggle with createdBy', async t => {
const { request, destroy } = await setupApp('feature_api_serial');
logger.setLevel('FATAL'); logger.setLevel('FATAL');
request request
.post('/features') .post('/features')
@ -59,85 +58,96 @@ describe('The features api', () => {
.set('Cookie', ['username=ivaosthu']) .set('Cookie', ['username=ivaosthu'])
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.end(() => { .end(() => {
request return request
.get('/api/events') .get('/api/events')
.end((err, res) => { .expect((res) => {
assert.equal(res.body.events[0].createdBy, 'ivaosthu'); t.true(res.body.events[0].createdBy === 'ivaosthu');
done(); })
}); .then(destroy);
});
}); });
});
it('require new feature toggle to have a name', done => { test.serial('require new feature toggle to have a name', async t => {
const { request, destroy } = await setupApp('feature_api_serial');
logger.setLevel('FATAL'); logger.setLevel('FATAL');
request return request
.post('/features') .post('/features')
.send({ name: '' }) .send({ name: '' })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(400, done); .expect(400)
}); .then(destroy);
});
it('can not change status of feature toggle that does not exist', done => { test.serial('can not change status of feature toggle that does not exist', async t => {
const { request, destroy } = await setupApp('feature_api_serial');
logger.setLevel('FATAL'); logger.setLevel('FATAL');
request return request
.put('/features/should-not-exist') .put('/features/should-not-exist')
.send({ name: 'should-not-exist', enabled: false }) .send({ name: 'should-not-exist', enabled: false })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(404, done); .expect(404).then(destroy);
}); });
it('can change status of feature toggle that does exist', done => { test.serial('can change status of feature toggle that does exist', async t => {
const { request, destroy } = await setupApp('feature_api_serial');
logger.setLevel('FATAL'); logger.setLevel('FATAL');
request return request
.put('/features/featureY') .put('/features/featureY')
.send({ name: 'featureY', enabled: true }) .send({ name: 'featureY', enabled: true })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(200, done); .expect(200).then(destroy);
}); });
it('archives a feature by name', done => { test.serial('archives a feature by name', async t => {
request const { request, destroy } = await setupApp('feature_api_serial');
return request
.delete('/features/featureX') .delete('/features/featureX')
.expect(200, done); .expect(200).then(destroy);
}); });
it('can not archive unknown feature', done => { test.serial('can not archive unknown feature', async t => {
request const { request, destroy } = await setupApp('feature_api_serial');
return request
.delete('/features/featureUnknown') .delete('/features/featureUnknown')
.expect(404, done); .expect(404).then(destroy);
}); });
it('refuses to create a feature with an existing name', done => { test.serial('refuses to create a feature with an existing name', async t => {
request const { request, destroy } = await setupApp('feature_api_serial');
return request
.post('/features') .post('/features')
.send({ name: 'featureX' }) .send({ name: 'featureX' })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(403, done); .expect(403).then(destroy);
}); });
it('refuses to validate a feature with an existing name', done => { test.serial('refuses to validate a feature with an existing name', async t => {
request const { request, destroy } = await setupApp('feature_api_serial');
return request
.post('/features-validate') .post('/features-validate')
.send({ name: 'featureX' }) .send({ name: 'featureX' })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(403, done); .expect(403).then(destroy);
}); });
describe('new strategies api', () => {
it('automatically map existing strategy to strategies array', (done) => { test.serial('new strategies api automatically map existing strategy to strategies array', async t => {
request const { request, destroy } = await setupApp('feature_api_serial');
t.plan(3);
return request
.get('/features/featureY') .get('/features/featureY')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.end((err, res) => { .expect((res) => {
assert.equal(res.body.strategies.length, 1, 'expected strategy added to strategies'); t.true(res.body.strategies.length === 1, 'expected strategy added to strategies');
assert.equal(res.body.strategy, res.body.strategies[0].name); t.true(res.body.strategy === res.body.strategies[0].name);
assert.deepEqual(res.body.parameters, res.body.strategies[0].parameters); t.deepEqual(res.body.parameters, res.body.strategies[0].parameters);
done(); })
}); .then(destroy);
}); });
it('can add two strategies to a feature toggle', (done) => { test.serial('new strategies api can add two strategies to a feature toggle', async t => {
request const { request, destroy } = await setupApp('feature_api_serial');
return request
.put('/features/featureY') .put('/features/featureY')
.send({ .send({
name: 'featureY', name: 'featureY',
@ -148,14 +158,17 @@ describe('The features api', () => {
name: 'baz', name: 'baz',
parameters: { foo: 'bar' }, parameters: { foo: 'bar' },
}, },
] }) ],
})
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(200, done); .expect(200)
}); .then(destroy);
});
it('should not be allowed to post both strategy and strategies', (done) => { test.serial('new strategies api should not be allowed to post both strategy and strategies', async t => {
const { request, destroy } = await setupApp('feature_api_serial');
logger.setLevel('FATAL'); logger.setLevel('FATAL');
request return request
.post('/features') .post('/features')
.send({ .send({
name: 'featureConfusing', name: 'featureConfusing',
@ -168,9 +181,10 @@ describe('The features api', () => {
name: 'baz', name: 'baz',
parameters: { foo: 'bar' }, parameters: { foo: 'bar' },
}, },
] }) ],
})
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(400, done); .expect(400)
}); .then(destroy);
});
}); });

View File

@ -1,45 +1,40 @@
'use strict'; 'use strict';
const assert = require('assert'); const test = require('ava');
const specHelper = require('./util/test-helper'); const { setupApp } = require('./util/test-helper');
const stringify = function (o) { const logger = require('../../lib/logger');
return JSON.stringify(o, null, ' ');
};
let request; test.beforeEach(() => {
logger.setLevel('FATAL');
});
describe('The archive features api', () => { test.serial('returns three archived toggles', async t => {
beforeEach(done => { const { request, destroy } = await setupApp('archive_serial');
specHelper.setupApp().then((app) => { return request
request = app.request;
done();
});
});
it('returns three archived toggles', done => {
request
.get('/api/archive/features') .get('/api/archive/features')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end((err, res) => { .expect((res) => {
assert(res.body.features.length === 3, `expected 3 features, got ${stringify(res.body)}`); t.true(res.body.features.length === 3);
done(); })
}); .then(destroy);
}); });
it('revives a feature by name', done => { test.serial('revives a feature by name', async t => {
request const { request, destroy } = await setupApp('archive_serial');
return request
.post('/api/archive/revive') .post('/api/archive/revive')
.send({ name: 'featureArchivedX' }) .send({ name: 'featureArchivedX' })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(200, done); .expect(200)
}); .then(destroy);
});
it('must set name when reviving toggle', done => { test.serial('must set name when reviving toggle', async t => {
request const { request, destroy } = await setupApp('archive_serial');
return request
.post('/api/archive/revive') .post('/api/archive/revive')
.send({ name: '' }) .send({ name: '' })
.expect(400, done); .expect(400)
}); .then(destroy);
}); });

View File

@ -1,19 +1,15 @@
'use strict'; 'use strict';
const test = require('ava');
const { setupApp } = require('./util/test-helper');
const logger = require('../../lib/logger');
const specHelper = require('./util/test-helper'); test.beforeEach(() => {
const assert = require('assert'); logger.setLevel('FATAL');
let request; });
describe('The metrics api', () => { test.serial('should register client', async (t) => {
beforeEach(done => { const { request, destroy } = await setupApp('metrics_serial');
specHelper.setupApp().then((app) => { return request
request = app.request;
done();
});
});
it('should register client', (done) => {
request
.post('/api/client/register') .post('/api/client/register')
.send({ .send({
appName: 'demo', appName: 'demo',
@ -22,11 +18,13 @@ describe('The metrics api', () => {
started: Date.now(), started: Date.now(),
interval: 10 interval: 10
}) })
.expect(202, done); .expect(202)
}); .then(destroy);
});
it('should accept client metrics', (done) => { test.serial('should accept client metrics', async t => {
request const { request, destroy } = await setupApp('metrics_serial');
return request
.post('/api/client/metrics') .post('/api/client/metrics')
.send({ .send({
appName: 'demo', appName: 'demo',
@ -37,28 +35,31 @@ describe('The metrics api', () => {
toggles: {} toggles: {}
} }
}) })
.expect(202, done) .expect(202)
}); .then(destroy);
});
it('should get client strategies', done => { test.serial('should get client strategies', async t => {
request const { request, destroy } = await setupApp('metrics_serial');
return request
.get('/api/client/strategies') .get('/api/client/strategies')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.end((err, res) => { .expect((res) => {
assert(res.status, 200); t.true(res.status === 200);
assert(res.body.length === 1, `expected 1 registerd client, got ${res.body}`); t.true(res.body.length === 1);
done(); })
});; .then(destroy);
}); });
it('should get client instances', done => { test.serial('should get client instances', async t => {
request const { request, destroy } = await setupApp('metrics_serial');
return request
.get('/api/client/instances') .get('/api/client/instances')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.end((err, res) => { .expect((res) => {
assert(res.status, 200); t.true(res.status === 200);
assert(res.body.length === 1, `expected 1 registerd client, got ${res.body}`); t.true(res.body.length === 1);
done(); })
});; .then(destroy);
});
}); });

View File

@ -1,23 +1,18 @@
'use strict'; 'use strict';
const specHelper = require('./util/test-helper'); const test = require('ava');
const { setupApp } = require('./util/test-helper');
const logger = require('../../lib/logger');
let request; test.beforeEach(() => {
logger.setLevel('FATAL');
});
describe('The routes', () => { test('returns health good', async (t) => {
beforeEach(done => { const { request, destroy } = await setupApp('health');
specHelper.setupApp().then((app) => { return request.get('/health')
request = app.request;
done();
});
});
describe('healthcheck', () => {
it('returns health good', done => {
request.get('/health')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.expect('{"health":"GOOD"}', done); .expect('{"health":"GOOD"}')
}); .then(destroy);
});
}); });

View File

@ -1,70 +1,81 @@
'use strict'; 'use strict';
const specHelper = require('./util/test-helper'); const test = require('ava');
let request; const { setupApp } = require('./util/test-helper');
const logger = require('../../lib/logger');
describe('The strategy api', () => { test.beforeEach(() => {
beforeEach(done => { logger.setLevel('FATAL');
specHelper.setupApp().then((app) => { });
request = app.request;
done();
});
});
it('gets all strategies', done => { test.serial('gets all strategies', async (t) => {
request const { request, destroy } = await setupApp('strategy_api_serial');
return request
.get('/api/strategies') .get('/api/strategies')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done); .expect(200)
}); .then(destroy);
});
it('gets a strategy by name', done => { test.serial('gets a strategy by name', async (t) => {
request const { request, destroy } = await setupApp('strategy_api_serial');
return request
.get('/api/strategies/default') .get('/api/strategies/default')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done); .expect(200)
}); .then(destroy);
});
it('cant get a strategy by name that dose not exist', done => { test.serial('cant get a strategy by name that dose not exist', async (t) => {
request const { request, destroy } = await setupApp('strategy_api_serial');
return request
.get('/api/strategies/mystrategy') .get('/api/strategies/mystrategy')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(404, done); .expect(404)
}); .then(destroy);
});
it('creates a new strategy', done => { test.serial('creates a new strategy', async (t) => {
request const { request, destroy } = await setupApp('strategy_api_serial');
return request
.post('/api/strategies') .post('/api/strategies')
.send({ name: 'myCustomStrategy', description: 'Best strategy ever.' }) .send({ name: 'myCustomStrategy', description: 'Best strategy ever.' })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(201, done); .expect(201)
}); .then(destroy);
});
it('requires new strategies to have a name', done => { test.serial('requires new strategies to have a name', async (t) => {
request const { request, destroy } = await setupApp('strategy_api_serial');
return request
.post('/api/strategies') .post('/api/strategies')
.send({ name: '' }) .send({ name: '' })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(400, done); .expect(400)
}); .then(destroy);
});
it('refuses to create a strategy with an existing name', done => { test.serial('refuses to create a strategy with an existing name', async (t) => {
request const { request, destroy } = await setupApp('strategy_api_serial');
return request
.post('/api/strategies') .post('/api/strategies')
.send({ name: 'default' }) .send({ name: 'default' })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(403, done); .expect(403)
}); .then(destroy);
});
it('deletes a new strategy', done => {
request test.serial('deletes a new strategy', async (t) => {
.delete('/api/strategies/usersWithEmail') const { request, destroy } = await setupApp('strategy_api_serial');
.expect(200, done); return request
}); .delete('/api/strategies/usersWithEmail')
.expect(200)
it('can\'t delete a strategy that dose not exist', done => { .then(destroy);
request });
.delete('/api/strategies/unknown')
.expect(404, done); test.serial('can\'t delete a strategy that dose not exist', async (t) => {
}); const { request, destroy } = await setupApp('strategy_api_serial', false);
return request
.delete('/api/strategies/unknown')
.expect(404);
}); });

View File

@ -2,28 +2,39 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
let supertest = require('supertest'); const supertest = require('supertest');
const options = {
databaseUri: require('./database-config').getDatabaseUri(),
databaseSchema: 'test'
};
const migrator = require('../../../migrator'); const migrator = require('../../../migrator');
const { createStores } = require('../../../lib/db'); const { createStores } = require('../../../lib/db');
const { createDb } = require('../../../lib/db/db-pool');
const _app = require('../../../app');
// because of migrator bug // because of migrator bug
delete process.env.DATABASE_URL; delete process.env.DATABASE_URL;
const db = require('../../../lib/db/db-pool').createDb(options.databaseUri); function createApp (databaseSchema = 'test') {
const options = {
databaseUri: require('./database-config').getDatabaseUri(),
databaseSchema,
minPool: 0,
maxPool: 0,
};
const db = createDb({ databaseUri: options.databaseUri, minPool: 0, maxPool: 0 });
const createApp = db.raw(`DROP SCHEMA IF EXISTS ${options.databaseSchema} CASCADE; CREATE SCHEMA ${options.databaseSchema}`) return db.raw(`DROP SCHEMA IF EXISTS ${options.databaseSchema} CASCADE; CREATE SCHEMA ${options.databaseSchema}`)
.then(() => migrator(options.databaseUri, options.databaseSchema)) .then(() => migrator(options))
.then(() => { .then(() => {
db.destroy();
const stores = createStores(options); const stores = createStores(options);
const app = require('../../../app')({stores}); const app = _app({ stores });
return { stores, request: supertest(app) }; return {
stores,
request: supertest(app),
destroy () {
return stores.db.destroy();
},
};
}); });
}
function createStrategies (stores) { function createStrategies (stores) {
return [ return [
@ -49,7 +60,7 @@ function createClientStrategy (stores) {
instanceId: 'test-1', instanceId: 'test-1',
strategies: ['default'], strategies: ['default'],
started: Date.now(), started: Date.now(),
interval: 10 interval: 10,
}, },
].map(client => stores.clientStrategyStore.insert(client)); ].map(client => stores.clientStrategyStore.insert(client));
} }
@ -61,7 +72,7 @@ function createClientInstance (stores) {
instanceId: 'test-1', instanceId: 'test-1',
strategies: ['default'], strategies: ['default'],
started: Date.now(), started: Date.now(),
interval: 10 interval: 10,
}, },
].map(client => stores.clientInstanceStore.insert(client)); ].map(client => stores.clientInstanceStore.insert(client));
} }
@ -135,7 +146,7 @@ function resetDatabase (stores) {
stores.db('strategies').del(), stores.db('strategies').del(),
stores.db('features').del(), stores.db('features').del(),
stores.db('client_strategies').del(), stores.db('client_strategies').del(),
stores.db('client_instances').del() stores.db('client_instances').del(),
]); ]);
} }
@ -144,15 +155,15 @@ function setupDatabase (stores) {
createStrategies(stores) createStrategies(stores)
.concat(createFeatures(stores) .concat(createFeatures(stores)
.concat(createClientInstance(stores)) .concat(createClientInstance(stores))
.concat(createClientStrategy(stores)))) .concat(createClientStrategy(stores))));
} }
module.exports = { module.exports = {
setupApp () { setupApp (name) {
return createApp.then((app) => { return createApp(name).then((app) => {
return resetDatabase(app.stores) return resetDatabase(app.stores)
.then(() => setupDatabase(app.stores)) .then(() => setupDatabase(app.stores))
.then(() => app); .then(() => app);
}); });
} },
}; };

View File

@ -1,22 +1,26 @@
'use strict'; 'use strict';
const test = require('ava');
const eventDiffer = require('../../lib/event-differ'); const eventDiffer = require('../../lib/event-differ');
const eventType = require('../../lib/event-type'); const eventType = require('../../lib/event-type');
const assert = require('assert'); const logger = require('../../lib/logger');
describe('eventDiffer', () => { test.beforeEach(() => {
it('fails if events include an unknown event type', () => { logger.setLevel('FATAL');
});
test('fails if events include an unknown event type', t => {
const events = [ const events = [
{ type: eventType.featureCreated, data: {} }, { type: eventType.featureCreated, data: {} },
{ type: 'unknown-type', data: {} }, { type: 'unknown-type', data: {} },
]; ];
assert.throws(() => { t.throws(() => {
eventDiffer.addDiffs(events); eventDiffer.addDiffs(events);
}); });
}); });
it('diffs a feature-update event', () => { test('diffs a feature-update event', t => {
const feature = 'foo'; const feature = 'foo';
const desc = 'bar'; const desc = 'bar';
@ -33,15 +37,15 @@ describe('eventDiffer', () => {
eventDiffer.addDiffs(events); eventDiffer.addDiffs(events);
assert.deepEqual(events[0].diffs, [ t.deepEqual(events[0].diffs, [
{ kind: 'E', path: ['enabled'], lhs: false, rhs: true }, { kind: 'E', path: ['enabled'], lhs: false, rhs: true },
{ kind: 'E', path: ['parameters', 'value'], lhs: 1, rhs: 2 }, { kind: 'E', path: ['parameters', 'value'], lhs: 1, rhs: 2 },
]); ]);
assert.strictEqual(events[1].diffs, null); t.true(events[1].diffs === null);
}); });
it('diffs only against features with the same name', () => { test('diffs only against features with the same name', t => {
const events = [ const events = [
{ {
type: eventType.featureUpdated, type: eventType.featureUpdated,
@ -63,13 +67,13 @@ describe('eventDiffer', () => {
eventDiffer.addDiffs(events); eventDiffer.addDiffs(events);
assert.strictEqual(events[0].diffs[0].rhs, true); t.true(events[0].diffs[0].rhs === true);
assert.strictEqual(events[1].diffs[0].rhs, false); t.true(events[1].diffs[0].rhs === false);
assert.strictEqual(events[2].diffs, null); t.true(events[2].diffs === null);
assert.strictEqual(events[3].diffs, null); t.true(events[3].diffs === null);
}); });
it('sets an empty array of diffs if nothing was changed', () => { test('sets an empty array of diffs if nothing was changed', t => {
const events = [ const events = [
{ {
type: eventType.featureUpdated, type: eventType.featureUpdated,
@ -82,10 +86,10 @@ describe('eventDiffer', () => {
]; ];
eventDiffer.addDiffs(events); eventDiffer.addDiffs(events);
assert.deepEqual(events[0].diffs, []); t.deepEqual(events[0].diffs, []);
}); });
it('sets diffs to null if there was nothing to diff against', () => { test('sets diffs to null if there was nothing to diff against', t => {
const events = [ const events = [
{ {
type: eventType.featureUpdated, type: eventType.featureUpdated,
@ -94,6 +98,6 @@ describe('eventDiffer', () => {
]; ];
eventDiffer.addDiffs(events); eventDiffer.addDiffs(events);
assert.strictEqual(events[0].diffs, null); t.true(events[0].diffs === null);
});
}); });

View File

@ -1,11 +1,9 @@
'use strict'; 'use strict';
const assert = require('assert'); const test = require('ava');
const mapper = require('../../../lib/helper/legacy-feature-mapper'); const mapper = require('../../../lib/helper/legacy-feature-mapper');
describe('legacy-feature-mapper', () => { test('adds old fields to feature', t => {
it('adds old fields to feature', () => {
const feature = { const feature = {
name: 'test', name: 'test',
enabled: 0, enabled: 0,
@ -19,14 +17,14 @@ describe('legacy-feature-mapper', () => {
const mappedFeature = mapper.addOldFields(feature); const mappedFeature = mapper.addOldFields(feature);
assert.equal(mappedFeature.name, feature.name); t.true(mappedFeature.name === feature.name);
assert.equal(mappedFeature.enabled, feature.enabled); t.true(mappedFeature.enabled === feature.enabled);
assert.equal(mappedFeature.strategy, feature.strategies[0].name); t.true(mappedFeature.strategy === feature.strategies[0].name);
assert.notEqual(mappedFeature.parameters, feature.strategies[0].parameters); t.true(mappedFeature.parameters !== feature.strategies[0].parameters);
assert.deepEqual(mappedFeature.parameters, feature.strategies[0].parameters); t.deepEqual(mappedFeature.parameters, feature.strategies[0].parameters);
}); });
it('transforms fields to new format', () => { test('transforms fields to new format', t => {
const feature = { const feature = {
name: 'test', name: 'test',
enabled: 0, enabled: 0,
@ -38,16 +36,16 @@ describe('legacy-feature-mapper', () => {
const mappedFeature = mapper.toNewFormat(feature); const mappedFeature = mapper.toNewFormat(feature);
assert.equal(mappedFeature.name, feature.name); t.true(mappedFeature.name === feature.name);
assert.equal(mappedFeature.enabled, feature.enabled); t.true(mappedFeature.enabled === feature.enabled);
assert.equal(mappedFeature.strategies.length, 1); t.true(mappedFeature.strategies.length === 1);
assert.equal(mappedFeature.strategies[0].name, feature.strategy); t.true(mappedFeature.strategies[0].name === feature.strategy);
assert.deepEqual(mappedFeature.strategies[0].parameters, feature.parameters); t.deepEqual(mappedFeature.strategies[0].parameters, feature.parameters);
assert(mappedFeature.strategy === undefined); t.true(mappedFeature.strategy === undefined);
assert(mappedFeature.parameters === undefined); t.true(mappedFeature.parameters === undefined);
}); });
it('should not transform if it already is the new format', () => { test('should not transform if it already is the new format', t => {
const feature = { const feature = {
name: 'test', name: 'test',
enabled: 0, enabled: 0,
@ -61,6 +59,5 @@ describe('legacy-feature-mapper', () => {
const mappedFeature = mapper.toNewFormat(feature); const mappedFeature = mapper.toNewFormat(feature);
assert.equal(mappedFeature, feature); t.true(mappedFeature === feature);
});
}); });

View File

@ -1,61 +1,63 @@
'use strict'; 'use strict';
const test = require('ava');
const store = require('./mocks/store'); const store = require('./mocks/store');
const supertest = require('supertest'); const supertest = require('supertest');
const assert = require('assert'); const logger = require('../../../lib/logger');
test.beforeEach(() => {
logger.setLevel('FATAL');
});
let request; function getSetup () {
let featureToggleStore; const base = `/random${Math.round(Math.random() * 1000)}`;
describe('Unit: The features api', () => {
beforeEach(done => {
const stores = store.createStores(); const stores = store.createStores();
const app = require('../../../app')({ const app = require('../../../app')({
baseUriPath: '', baseUriPath: base,
stores: stores, stores,
}); });
featureToggleStore = stores.featureToggleStore; return {
request = supertest(app); base,
done(); featureToggleStore: stores.featureToggleStore,
}); request: supertest(app),
};
}
it('should get empty getFeatures', (done) => { test('should get empty getFeatures', t => {
request const { request, base } = getSetup();
.get('/features') return request
.get(`${base}/features`)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end((err, res) => { .expect((res) => {
assert(res.body.features.length === 0); t.true(res.body.features.length === 0);
done();
});
});
it('should get one getFeature', (done) => {
featureToggleStore.addFeature( { name: 'test', strategies: [{ name: 'default' }] } );
request
.get('/features')
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
assert(res.body.features.length === 1);
done();
});
});
it('should add version numbers for /features', (done) => {
featureToggleStore.addFeature( { name: 'test', strategies: [{ name: 'default' }] } );
request
.get('/features')
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
assert.equal(res.body.version, 1);
done();
});
}); });
}); });
test('should get one getFeature', t => {
const { request, featureToggleStore, base } = getSetup();
featureToggleStore.addFeature({ name: 'test_', strategies: [{ name: 'default_' }] });
return request
.get(`${base}/features`)
.expect('Content-Type', /json/)
.expect(200)
.expect((res) => {
t.true(res.body.features.length === 1);
});
});
test('should add version numbers for /features', t => {
const { request, featureToggleStore, base } = getSetup();
featureToggleStore.addFeature({ name: 'test2', strategies: [{ name: 'default' }] });
return request
.get(`${base}/features`)
.expect('Content-Type', /json/)
.expect(200)
.expect((res) => {
t.true(res.body.version === 1);
});
});

View File

@ -1,58 +1,65 @@
'use strict'; 'use strict';
const test = require('ava');
const store = require('./mocks/store'); const store = require('./mocks/store');
const supertest = require('supertest'); const supertest = require('supertest');
const assert = require('assert'); const logger = require('../../../lib/logger');
let request; test.beforeEach(() => {
logger.setLevel('FATAL');
});
describe('Unit: The metrics api', () => { function getSetup () {
beforeEach(done => {
const stores = store.createStores(); const stores = store.createStores();
const app = require('../../../app')({ const app = require('../../../app')({
baseUriPath: '', baseUriPath: '',
stores: stores, stores,
}); });
request = supertest(app); return {
done(); request: supertest(app),
}); };
}
it('should register client', (done) => { test('should register client', () => {
request const { request } = getSetup();
return request
.post('/api/client/register') .post('/api/client/register')
.send({ .send({
appName: 'demo', appName: 'demo',
instanceId: 'test', instanceId: 'test',
strategies: ['default'], strategies: ['default'],
started: Date.now(), started: Date.now(),
interval: 10 interval: 10,
}) })
.expect(202, done); .expect(202);
}); });
it('should require appName field', (done) => { test('should require appName field', () => {
request const { request } = getSetup();
return request
.post('/api/client/register') .post('/api/client/register')
.expect(400, done) .expect(400);
}); });
it('should require strategies field', (done) => { test('should require strategies field', () => {
request const { request } = getSetup();
return request
.post('/api/client/register') .post('/api/client/register')
.send({ .send({
appName: 'demo', appName: 'demo',
instanceId: 'test', instanceId: 'test',
//strategies: ['default'], // strategies: ['default'],
started: Date.now(), started: Date.now(),
interval: 10 interval: 10,
}) })
.expect(400, done) .expect(400);
}); });
it('should accept client metrics', (done) => { test('should accept client metrics', () => {
request const { request } = getSetup();
return request
.post('/api/client/metrics') .post('/api/client/metrics')
.send({ .send({
appName: 'demo', appName: 'demo',
@ -60,9 +67,8 @@ describe('Unit: The metrics api', () => {
bucket: { bucket: {
start: Date.now(), start: Date.now(),
stop: Date.now(), stop: Date.now(),
toggles: {} toggles: {},
} },
}) })
.expect(202, done) .expect(202);
});
}); });

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
module.exports = { module.exports = () => ({
reset: () => {},
insert: () => Promise.resolve(), insert: () => Promise.resolve(),
}; });

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
module.exports = { module.exports = () => ({
reset: () => {},
insert: () => Promise.resolve(), insert: () => Promise.resolve(),
}; });

View File

@ -1,11 +1,10 @@
'use strict'; 'use strict';
const _features = [];
module.exports = { module.exports = () => {
const _features = [];
return {
getFeatures: () => Promise.resolve(_features), getFeatures: () => Promise.resolve(_features),
addFeature: (feature) => _features.push(feature), addFeature: (feature) => _features.push(feature),
reset: () => { };
_features.lengyh = 0;
},
}; };

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
module.exports = { module.exports = () => ({
reset: () => {},
getMetricsLastHour: () => Promise.resolve([]), getMetricsLastHour: () => Promise.resolve([]),
insert: () => Promise.resolve(), insert: () => Promise.resolve(),
}; });

View File

@ -1,12 +1,13 @@
'use strict'; 'use strict';
const _strategies = [{ name: 'default', parameters: {} }];
module.exports = {
module.exports = () => {
const _strategies = [{ name: 'default', parameters: {} }];
return {
getStrategies: () => Promise.resolve(_strategies), getStrategies: () => Promise.resolve(_strategies),
addStrategy: (strat) => _strategies.push(strat), addStrategy: (strat) => _strategies.push(strat),
reset: () => { };
_strategies.length = 0;
},
}; };

View File

@ -1,4 +1,4 @@
const sinon = require('sinon'); 'use strict';
const clientMetricsStore = require('./fake-metrics-store'); const clientMetricsStore = require('./fake-metrics-store');
const clientStrategyStore = require('./fake-client-strategy-store'); const clientStrategyStore = require('./fake-client-strategy-store');
@ -11,27 +11,18 @@ const strategyStore = require('./fake-strategies-store');
module.exports = { module.exports = {
createStores: () => { createStores: () => {
const db = { const db = {
select: () => { select: () => ({
return { from: () => Promise.resolve(),
from: () => Promise.resolve() }),
} };
}
}
clientMetricsStore.reset();
clientStrategyStore.reset();
clientInstanceStore.reset();
featureToggleStore.reset();
strategyStore.reset();
return { return {
db, db,
clientMetricsStore, clientMetricsStore: clientMetricsStore(),
clientStrategyStore, clientStrategyStore: clientStrategyStore(),
clientInstanceStore, clientInstanceStore: clientInstanceStore(),
featureToggleStore, featureToggleStore: featureToggleStore(),
strategyStore, strategyStore: strategyStore(),
} };
},
}
}; };

View File

@ -1,35 +1,28 @@
'use strict'; 'use strict';
const test = require('ava');
const store = require('./mocks/store'); const store = require('./mocks/store');
const supertest = require('supertest'); const supertest = require('supertest');
const assert = require('assert'); const logger = require('../../../lib/logger');
const sinon = require('sinon');
let request; test.beforeEach(() => {
let strategyStore; logger.setLevel('FATAL');
});
describe('Unit: The strategies api', () => { test('should add version numbers for /stategies', t => {
beforeEach(done => {
const stores = store.createStores(); const stores = store.createStores();
const app = require('../../../app')({ const app = require('../../../app')({
baseUriPath: '', baseUriPath: '',
stores: stores, stores,
});
strategyStore = stores.strategyStore;
request = supertest(app);
done();
}); });
it('should add version numbers for /stategies', (done) => { const request = supertest(app);
request
return request
.get('/api/strategies') .get('/api/strategies')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end((err, res) => { .expect((res) => {
assert.equal(res.body.version, 1); t.true(res.body.version === 1);
done();
});
}); });
}); });