1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-23 00:22:19 +01:00

add tests, rename folders to ava defaults for helpers/fixtures, remove migration og

This commit is contained in:
sveisvei 2016-11-13 20:33:23 +01:00
parent df99a547e8
commit bb4cf30d22
29 changed files with 187 additions and 43 deletions

View File

@ -12,10 +12,53 @@ test('should work without state', (t) => {
t.truthy(metrics.getMetricsOverview()); t.truthy(metrics.getMetricsOverview());
t.truthy(metrics.getTogglesMetrics()); t.truthy(metrics.getTogglesMetrics());
t.truthy(metrics.toJSON());
metrics.destroy(); metrics.destroy();
}); });
test.cb('data should expire', (t) => {
const clock = sinon.useFakeTimers();
const metrics = new UnleashClientMetrics();
metrics.addPayload({
appName,
instanceId,
bucket: {
start: Date.now() - 2000,
stop: Date.now() - 1000,
toggles: {
toggleX: {
yes: 123,
no: 0,
},
},
},
});
let lastHourExpires = 0;
metrics.lastHourList.on('expire', () => {
lastHourExpires++;
});
let lastMinExpires = 0;
metrics.lastMinuteList.on('expire', () => {
lastMinExpires++;
});
clock.tick(60 * 1000);
t.true(lastMinExpires === 1);
t.true(lastHourExpires === 0);
clock.tick(60 * 60 * 1000);
t.true(lastMinExpires === 1);
t.true(lastHourExpires === 1);
sinon.restore();
t.end();
});
test('addPayload', t => { test('addPayload', t => {
const metrics = new UnleashClientMetrics(); const metrics = new UnleashClientMetrics();
metrics.addPayload({ metrics.addPayload({

View File

@ -15,13 +15,6 @@ class Node {
} }
} }
/*
* linked list
* ranged list, assumes start to end(tail) is a known order
* remove() is only implemented in reverse order for the usecase
* emits events on eviction
*/
module.exports = class List extends EventEmitter { module.exports = class List extends EventEmitter {
constructor () { constructor () {
super(); super();
@ -116,17 +109,17 @@ module.exports = class List extends EventEmitter {
return result; return result;
} }
toArrayReverse () { // toArrayReverse () {
const result = []; // const result = [];
if (this.tail) { // if (this.tail) {
let cursor = this.tail; // let cursor = this.tail;
while (cursor) { // while (cursor) {
result.push(cursor.value); // result.push(cursor.value);
cursor = cursor.prev; // cursor = cursor.prev;
} // }
} // }
return result; // return result;
} // }
}; };

View File

@ -77,6 +77,16 @@ test('list can be cleared and re-add entries', (t) => {
t.true(list.toArray().length === 3); t.true(list.toArray().length === 3);
}); });
test('should not iterate empty list ', (t) => {
const list = new List();
let iterateCount = 0;
list.iterate(() => {
iterateCount++;
});
t.true(iterateCount === 0);
});
test('should iterate', (t) => { test('should iterate', (t) => {
const list = getList(); const list = getList();
@ -105,3 +115,13 @@ test('should reverse iterate', (t) => {
}); });
t.true(iterateCount === 5); t.true(iterateCount === 5);
}); });
test('should not reverse iterate empty list', (t) => {
const list = new List();
let iterateCount = 0;
list.iterateReverse(() => {
iterateCount++;
});
t.true(iterateCount === 0);
});

View File

@ -1,17 +1,19 @@
'use strict'; 'use strict';
const POLL_INTERVAL = 10000;
const { EventEmitter } = require('events'); const { EventEmitter } = require('events');
module.exports = class UnleashClientMetrics extends EventEmitter { module.exports = class UnleashClientMetrics extends EventEmitter {
constructor (metricsDb) { constructor (metricsDb, interval = 10000) {
super(); super();
this.interval = interval;
this.db = metricsDb; this.db = metricsDb;
this.highestIdSeen = 0; this.highestIdSeen = 0;
this.db.getMetricsLastHour().then(metrics => { this.db.getMetricsLastHour().then(metrics => {
this.addMetrics(metrics); this.addMetrics(metrics);
this.startPoller(); this.startPoller();
this.emit('ready');
}); });
this.timer = null;
} }
addMetrics (metrics) { addMetrics (metrics) {
@ -22,13 +24,20 @@ module.exports = class UnleashClientMetrics extends EventEmitter {
} }
startPoller () { startPoller () {
setInterval(() => { this.timer = setInterval(() => {
this.db.getNewMetrics(this.highestIdSeen) this.db.getNewMetrics(this.highestIdSeen)
.then(metrics => this.addMetrics(metrics)); .then(metrics => this.addMetrics(metrics));
}, POLL_INTERVAL).unref(); }, this.interval);
this.timer.unref();
} }
insert (metrics) { insert (metrics) {
return this.db.insert(metrics); return this.db.insert(metrics);
} }
destroy () {
try {
clearTimeout(this.timer);
} catch (e) {}
}
}; };

View File

@ -0,0 +1,66 @@
'use strict';
const { test } = require('ava');
const MetricsService = require('./service');
const sinon = require('sinon');
function getMockDb () {
const list = [{ id: 2 }, { id: 3 }, { id: 4 }];
const db = {
getMetricsLastHour () {
return Promise.resolve([{ id: 1 }]);
},
getNewMetrics () {
return Promise.resolve([list.pop() || { id: 0 }]);
},
};
return {
db,
};
}
test.cb('should call database on startup', (t) => {
const mock = getMockDb();
const service = new MetricsService(mock.db);
t.plan(2);
service.on('metrics', ([metric]) => {
t.true(service.highestIdSeen === 1);
t.true(metric.id === 1);
t.end();
service.destroy();
});
});
test.cb('should poll for updates', (t) => {
const clock = sinon.useFakeTimers();
const mock = getMockDb();
const service = new MetricsService(mock.db, 100);
const metrics = [];
service.on('metrics', (_metrics) => {
_metrics.forEach(m => m && metrics.push(m));
});
t.true(metrics.length === 0);
service.on('ready', () => {
t.true(metrics.length === 1);
clock.tick(300);
clock.restore();
process.nextTick(() => {
t.true(metrics.length === 4);
t.true(metrics[0].id === 1);
t.true(metrics[1].id === 4);
t.true(metrics[2].id === 3);
t.true(metrics[3].id === 2);
service.destroy();
t.end();
});
});
});

View File

@ -5,13 +5,14 @@ const List = require('./list');
const moment = require('moment'); const moment = require('moment');
// this list must have entries with sorted ttl range // this list must have entries with sorted ttl range
module.exports = class FIFOTTLList extends EventEmitter { module.exports = class TTLList extends EventEmitter {
constructor ({ constructor ({
interval = 1000, interval = 1000,
expireAmount = 1, expireAmount = 1,
expireType = 'hours', expireType = 'hours',
} = {}) { } = {}) {
super(); super();
this.interval = interval;
this.expireAmount = expireAmount; this.expireAmount = expireAmount;
this.expireType = expireType; this.expireType = expireType;
@ -20,10 +21,18 @@ module.exports = class FIFOTTLList extends EventEmitter {
this.list.on('evicted', ({ value, ttl }) => { this.list.on('evicted', ({ value, ttl }) => {
this.emit('expire', value, ttl); this.emit('expire', value, ttl);
}); });
this.startTimer();
}
this.timer = setInterval(() => { startTimer () {
this.timedCheck(); if (this.list) {
}, interval); this.timer = setTimeout(() => {
if (this.list) {
this.timedCheck();
}
}, this.interval);
this.timer.unref();
}
} }
add (value, timestamp = new Date()) { add (value, timestamp = new Date()) {
@ -34,11 +43,13 @@ module.exports = class FIFOTTLList extends EventEmitter {
timedCheck () { timedCheck () {
const now = moment(new Date()); const now = moment(new Date());
this.list.reverseRemoveUntilTrue(({ value }) => now.isBefore(value.ttl)); this.list.reverseRemoveUntilTrue(({ value }) => now.isBefore(value.ttl));
this.startTimer();
} }
destroy () { destroy () {
clearTimeout(this.timer); // https://github.com/nodejs/node/issues/9561
delete this.timer; // clearTimeout(this.timer);
// this.timer = null;
this.list = null; this.list = null;
} }
}; };

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const test = require('ava'); const test = require('ava');
const mapper = require('../../../lib/helper/legacy-feature-mapper'); const mapper = require('./legacy-feature-mapper');
test('adds old fields to feature', t => { test('adds old fields to feature', t => {
const feature = { const feature = {

View File

@ -8,7 +8,7 @@ const ValidationError = require('../error/validation-error.js');
const validateRequest = require('../error/validate-request'); const validateRequest = require('../error/validate-request');
const extractUser = require('../extract-user'); const extractUser = require('../extract-user');
const legacyFeatureMapper = require('../helper/legacy-feature-mapper'); const legacyFeatureMapper = require('../data-helper/legacy-feature-mapper');
const version = 1; const version = 1;
const handleErrors = (req, res, error) => { const handleErrors = (req, res, error) => {

View File

@ -1,3 +1,5 @@
'use strict';
const joi = require('joi'); const joi = require('joi');
const clientMetricsSchema = joi.object().keys({ const clientMetricsSchema = joi.object().keys({
@ -21,4 +23,4 @@ const clientRegisterSchema = joi.object().keys({
interval: joi.number().required(), interval: joi.number().required(),
}); });
module.exports = { clientMetricsSchema, clientRegisterSchema } module.exports = { clientMetricsSchema, clientRegisterSchema }

View File

@ -39,7 +39,7 @@
"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": "PORT=4243 ava **/**/*test.js", "test": "PORT=4243 ava test lib/*/*.test.js",
"test:docker": "./scripts/docker-postgres.sh", "test:docker": "./scripts/docker-postgres.sh",
"test:watch": "npm run test -- --watch", "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",

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const test = require('ava'); const test = require('ava');
const { setupApp } = require('./util/test-helper'); const { setupApp } = require('./helpers/test-helper');
const logger = require('../../lib/logger'); const logger = require('../../lib/logger');
test.beforeEach(() => { test.beforeEach(() => {

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const { test } = require('ava'); const { test } = require('ava');
const { setupApp } = require('./util/test-helper'); const { setupApp } = require('./helpers/test-helper');
const logger = require('../../lib/logger'); const logger = require('../../lib/logger');
test.beforeEach(() => { test.beforeEach(() => {

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const test = require('ava'); const test = require('ava');
const { setupApp } = require('./util/test-helper'); const { setupApp } = require('./helpers/test-helper');
const logger = require('../../lib/logger'); const logger = require('../../lib/logger');
test.beforeEach(() => { test.beforeEach(() => {

View File

@ -7,6 +7,7 @@ const migrator = require('../../../migrator');
const { createStores } = require('../../../lib/db'); const { createStores } = require('../../../lib/db');
const { createDb } = require('../../../lib/db/db-pool'); const { createDb } = require('../../../lib/db/db-pool');
const _app = require('../../../app'); const _app = require('../../../app');
require('db-migrate-shared').log.silence(true);
// because of migrator bug // because of migrator bug
delete process.env.DATABASE_URL; delete process.env.DATABASE_URL;

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
const test = require('ava'); const test = require('ava');
const { setupApp } = require('./util/test-helper'); const { setupApp } = require('./helpers/test-helper');
const logger = require('../../lib/logger'); const logger = require('../../lib/logger');
test.beforeEach(() => { test.beforeEach(() => {

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const test = require('ava'); const test = require('ava');
const { setupApp } = require('./util/test-helper'); const { setupApp } = require('./helpers/test-helper');
const logger = require('../../lib/logger'); const logger = require('../../lib/logger');
test.beforeEach(() => { test.beforeEach(() => {

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const test = require('ava'); const test = require('ava');
const { setupApp } = require('./util/test-helper'); const { setupApp } = require('./helpers/test-helper');
const logger = require('../../lib/logger'); const logger = require('../../lib/logger');
test.beforeEach(() => { test.beforeEach(() => {

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const test = require('ava'); const test = require('ava');
const store = require('./mocks/store'); const store = require('./fixtures/store');
const supertest = require('supertest'); const supertest = require('supertest');
const logger = require('../../../lib/logger'); const logger = require('../../../lib/logger');

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const test = require('ava'); const test = require('ava');
const store = require('./mocks/store'); const store = require('./fixtures/store');
const supertest = require('supertest'); const supertest = require('supertest');
const logger = require('../../../lib/logger'); const logger = require('../../../lib/logger');
@ -29,7 +29,6 @@ test('should give 500 when db is failing', t => {
db.select = () => ({ db.select = () => ({
from: () => Promise.reject(new Error('db error')), from: () => Promise.reject(new Error('db error')),
}); });
return request return request
.get('/health') .get('/health')
.expect(500) .expect(500)

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const test = require('ava'); const test = require('ava');
const store = require('./mocks/store'); const store = require('./fixtures/store');
const supertest = require('supertest'); const supertest = require('supertest');
const logger = require('../../../lib/logger'); const logger = require('../../../lib/logger');

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const test = require('ava'); const test = require('ava');
const store = require('./mocks/store'); const store = require('./fixtures/store');
const supertest = require('supertest'); const supertest = require('supertest');
const logger = require('../../../lib/logger'); const logger = require('../../../lib/logger');