Skip to content

Commit

Permalink
Add telemetry tests. Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
mrsarm committed May 12, 2021
1 parent 39bf4d2 commit b6a63ad
Showing 1 changed file with 124 additions and 110 deletions.
234 changes: 124 additions & 110 deletions webapp/tests/karma/ts/services/telemetry.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { DbService } from '@mm-services/db.service';
import { SessionService } from '@mm-services/session.service';

describe('TelemetryService', () => {
const NOW = new Date(2018, 10, 10, 12, 33).getTime();
const NOW = new Date(2018, 10, 10, 12, 33).getTime(); // -> 2018-11-10T12:33:00
let service: TelemetryService;
let dbService;
let dbInstance;
Expand All @@ -18,7 +18,9 @@ describe('TelemetryService', () => {
let storageGetItemStub;
let storageSetItemStub;
let consoleErrorSpy;

const windowPouchOriginal = window.PouchDB;

const windowScreenOriginal = {
availWidth: window.screen.availWidth,
availHeight: window.screen.availHeight
Expand All @@ -28,7 +30,43 @@ describe('TelemetryService', () => {
hardwareConcurrency: window.navigator.hardwareConcurrency
};

function defineWindow() {
Object.defineProperty(window.navigator, 'userAgent',
{ value: 'Agent Smith', configurable: true });
Object.defineProperty(window.navigator, 'hardwareConcurrency',
{ value: 4, configurable: true });
Object.defineProperty(window.screen, 'availWidth',
{ value: 768, configurable: true });
Object.defineProperty(window.screen, 'availHeight',
{ value: 1024, configurable: true });
}

function restoreWindow() {
Object.defineProperty(window.navigator, 'userAgent',
{ value: windowNavigatorOriginal.userAgent, configurable: true });
Object.defineProperty(window.navigator, 'hardwareConcurrency',
{ value: windowNavigatorOriginal.hardwareConcurrency, configurable: true });
Object.defineProperty(window.screen, 'availWidth',
{ value: windowScreenOriginal.availWidth, configurable: true });
Object.defineProperty(window.screen, 'availHeight',
{ value: windowScreenOriginal.availHeight, configurable: true });
}

function subtractDays(numDays) {
return moment()
.subtract(numDays, 'days')
.valueOf()
.toString();
}

function sameDay() {
return moment()
.valueOf()
.toString();
}

beforeEach(() => {
defineWindow();
dbInstance = {
info: sinon.stub(),
put: sinon.stub(),
Expand All @@ -38,8 +76,13 @@ describe('TelemetryService', () => {
dbService = { get: () => dbInstance };
consoleErrorSpy = sinon.spy(console, 'error');
pouchDb = {
info: sinon.stub().resolves({doc_count: 10}),
post: sinon.stub().resolves(),
close: sinon.stub()
close: sinon.stub(),
destroy: sinon.stub().callsFake(() => {
pouchDb._destroyed = true;
return Promise.resolve();
})
};
sessionService = { userCtx: sinon.stub().returns({ name: 'greg' }) };
storageGetItemStub = sinon.stub(window.localStorage, 'getItem');
Expand All @@ -61,58 +104,27 @@ describe('TelemetryService', () => {
clock.restore();
sinon.restore();
window.PouchDB = windowPouchOriginal;
Object.defineProperty(
window.navigator,
'userAgent',
{ value: windowNavigatorOriginal.userAgent, configurable: true }
);
Object.defineProperty(
window.navigator,
'hardwareConcurrency',
{ value: windowNavigatorOriginal.hardwareConcurrency, configurable: true }
);
Object.defineProperty(
window.screen,
'availWidth',
{ value: windowScreenOriginal.availWidth, configurable: true }
);
Object.defineProperty(
window.screen,
'availHeight',
{ value: windowScreenOriginal.availHeight, configurable: true }
);
restoreWindow();
});

describe('record()', () => {
it('should record a piece of telemetry', async () => {
pouchDb.post = sinon.stub().resolves();
pouchDb.close = sinon.stub();
storageGetItemStub
.withArgs('medic-greg-telemetry-db')
.returns('dbname');
storageGetItemStub
.withArgs('medic-greg-telemetry-date')
.returns(Date.now().toString());
storageGetItemStub.withArgs('medic-greg-telemetry-db').returns('dbname');
storageGetItemStub.withArgs('medic-greg-telemetry-date').returns(Date.now().toString());

await service.record('test', 100);

expect(consoleErrorSpy.callCount).to.equal(0);
expect(pouchDb.post.callCount).to.equal(1);
expect(pouchDb.post.args[0][0]).to.deep.include({ key: 'test', value: 100 });
expect(pouchDb.post.args[0][0].date_recorded).to.be.above(0);
expect(storageGetItemStub.callCount).to.equal(2);
expect(storageGetItemStub.callCount).to.equal(3);
expect(pouchDb.close.callCount).to.equal(1);
});

it('should default the value to 1 if not passed', async () => {
pouchDb.post = sinon.stub().resolves();
pouchDb.close = sinon.stub();
storageGetItemStub
.withArgs('medic-greg-telemetry-db')
.returns('dbname');
storageGetItemStub
.withArgs('medic-greg-telemetry-date')
.returns(Date.now().toString());
storageGetItemStub.withArgs('medic-greg-telemetry-db').returns('dbname');
storageGetItemStub.withArgs('medic-greg-telemetry-date').returns(Date.now().toString());

await service.record('test');

Expand All @@ -121,52 +133,14 @@ describe('TelemetryService', () => {
expect(pouchDb.close.callCount).to.equal(1);
});

it('should set localStorage values', async () => {
pouchDb.post = sinon.stub().resolves();
pouchDb.close = sinon.stub();
storageGetItemStub
.withArgs('medic-greg-telemetry-db')
.returns(undefined);
storageGetItemStub
.withArgs('medic-greg-telemetry-date')
.returns(undefined);

await service.record('test', 1);

expect(consoleErrorSpy.callCount).to.equal(0);
expect(storageSetItemStub.callCount).to.equal(2);
expect(storageSetItemStub.args[0][0]).to.equal('medic-greg-telemetry-db');
expect(storageSetItemStub.args[0][1]).to.match(/medic-user-greg-telemetry-/); // ends with a UUID
expect(storageSetItemStub.args[1][0]).to.equal('medic-greg-telemetry-date');
expect(storageSetItemStub.args[1][1]).to.equal(NOW.toString());
});

it('should aggregate once a month and resets the db', async () => {
storageGetItemStub
.withArgs('medic-greg-telemetry-db')
.returns('dbname');
storageGetItemStub
.withArgs('medic-greg-telemetry-date')
.returns(
moment()
.subtract(5, 'weeks')
.valueOf()
.toString()
);

pouchDb.post = sinon.stub().resolves();
function setupDbMocks() {
storageGetItemStub.returns('dbname');
pouchDb.query = sinon.stub().resolves({
rows: [
{ key: 'foo', value: 'stats' },
{ key: 'bar', value: 'more stats' },
{ key: 'foo', value: {sum:2876, min:581, max:2295, count:2, sumsqr:5604586} },
{ key: 'bar', value: {sum:93, min:43, max:50, count:2, sumsqr:4349} },
],
});
pouchDb.destroy = sinon.stub().callsFake(() => {
pouchDb._destroyed = true;
return Promise.resolve();
});
pouchDb.close = sinon.stub();

dbInstance.info.resolves({ some: 'stats' });
dbInstance.put.resolves();
dbInstance.get
Expand All @@ -188,28 +162,28 @@ describe('TelemetryService', () => {
}
]
});
}

Object.defineProperty(window.navigator, 'userAgent', { value: 'Agent Smith', configurable: true });
Object.defineProperty(window.navigator, 'hardwareConcurrency', { value: 4, configurable: true });
Object.defineProperty(window.screen, 'availWidth', { value: 768, configurable: true });
Object.defineProperty(window.screen, 'availHeight', { value: 1024, configurable: true });
it('should aggregate once a day and resets the db first', async () => {
setupDbMocks();
storageGetItemStub.withArgs('medic-greg-telemetry-date').returns(subtractDays(5));

await service.record('test', 1);

expect(consoleErrorSpy.callCount).to.equal(0);
expect(pouchDb.post.callCount).to.equal(1);
expect(pouchDb.post.args[0][0]).to.deep.include({ key: 'test', value: 1 });
expect(dbInstance.put.callCount).to.equal(1);

expect(dbInstance.put.callCount).to.equal(1);
const aggregatedDoc = dbInstance.put.args[0][0];
expect(aggregatedDoc._id).to.match(/telemetry-2018-10-greg/);
expect(aggregatedDoc._id).to.match(/telemetry-2018-11-5-greg/);
expect(aggregatedDoc.metrics).to.deep.equal({
foo: 'stats',
bar: 'more stats',
foo: {sum:2876, min:581, max:2295, count:2, sumsqr:5604586},
bar: {sum:93, min:43, max:50, count:2, sumsqr:4349},
});
expect(aggregatedDoc.type).to.equal('telemetry');
expect(aggregatedDoc.metadata.year).to.equal(2018);
expect(aggregatedDoc.metadata.month).to.equal(10);
expect(aggregatedDoc.metadata.month).to.equal(11);
expect(aggregatedDoc.metadata.day).to.equal(5);
expect(aggregatedDoc.metadata.user).to.equal('greg');
expect(aggregatedDoc.metadata.versions).to.deep.equal({
app: '3.0.0',
Expand All @@ -227,23 +201,74 @@ describe('TelemetryService', () => {
},
deviceInfo: {}
});

expect(dbInstance.query.callCount).to.equal(1);
expect(dbInstance.query.args[0][0]).to.equal('medic-client/doc_by_type');
expect(dbInstance.query.args[0][1]).to.deep.equal({ key: ['form'], include_docs: true });
expect(pouchDb.destroy.callCount).to.equal(1);
expect(pouchDb.close.callCount).to.equal(0);

expect(consoleErrorSpy.callCount).to.equal(0); // no errors
});

it('should not aggregate when recording the day db was created and next day it should aggregate', async () => {
setupDbMocks();
storageGetItemStub.withArgs('medic-greg-telemetry-date').returns(sameDay());

await service.record('test', 10);

expect(pouchDb.post.callCount).to.equal(1);
expect(pouchDb.post.args[0][0]).to.deep.include({ key: 'test', value: 10 });
expect(dbInstance.put.callCount).to.equal(0); // NO telemetry has been recorded

clock = sinon.useFakeTimers(moment(NOW).add(1, 'minutes').valueOf()); // 1 min later ...
await service.record('test', 5);

expect(pouchDb.post.callCount).to.equal(2); // second call
expect(pouchDb.post.args[1][0]).to.deep.include({ key: 'test', value: 5 });
expect(dbInstance.put.callCount).to.equal(0); // still NO telemetry has been recorded (same day)

clock = sinon.useFakeTimers(moment(NOW).add(1, 'days').valueOf()); // 1 day later ...
await service.record('test', 2);

expect(pouchDb.post.callCount).to.equal(3); // third call
expect(pouchDb.post.args[2][0]).to.deep.include({ key: 'test', value: 2 });
expect(dbInstance.put.callCount).to.equal(1); // Now telemetry has been recorded

const aggregatedDoc = dbInstance.put.args[0][0];
expect(aggregatedDoc._id).to.match(/telemetry-2018-11-10-greg/); // Now is 2018-11-11 but aggregation
expect(pouchDb.destroy.callCount).to.equal(1); // is from from previous day

expect(consoleErrorSpy.callCount).to.equal(0); // no errors
});
});

describe('getDb()', () => {
it('should set localStorage values', async () => {
storageGetItemStub
.withArgs('medic-greg-telemetry-db')
.returns(undefined);
storageGetItemStub
.withArgs('medic-greg-telemetry-date')
.returns(undefined);

await service.record('test', 1);

expect(consoleErrorSpy.callCount).to.equal(0);
expect(storageSetItemStub.callCount).to.equal(3);
expect(storageSetItemStub.args[0][0]).to.equal('medic-greg-telemetry-db');
expect(storageSetItemStub.args[0][1]).to.match(/medic-user-greg-telemetry-/); // ends with a UUID
expect(storageSetItemStub.args[1][0]).to.equal('medic-greg-telemetry-date');
expect(storageSetItemStub.args[1][1]).to.equal(NOW.toString());
});
});

describe('storeConflictedAggregate()', () => {

it('should deal with conflicts by making the ID unique and noting the conflict in the new document', async () => {
storageGetItemStub.withArgs('medic-greg-telemetry-db').returns('dbname');
storageGetItemStub.withArgs('medic-greg-telemetry-date').returns(
moment()
.subtract(5, 'weeks')
.valueOf()
.toString()
);

pouchDb.post = sinon.stub().resolves();
storageGetItemStub.withArgs('medic-greg-telemetry-date').returns(subtractDays(5));

pouchDb.query = sinon.stub().resolves({
rows: [
{
Expand All @@ -266,16 +291,6 @@ describe('TelemetryService', () => {
}
});
dbInstance.query.resolves({ rows: [] });
pouchDb.destroy = sinon.stub().callsFake(() => {
pouchDb._destroyed = true;
return Promise.resolve();
});
pouchDb.close = sinon.stub();

Object.defineProperty(window.navigator, 'userAgent', { value: 'Agent Smith', configurable: true });
Object.defineProperty(window.navigator, 'hardwareConcurrency', { value: 4, configurable: true });
Object.defineProperty(window.screen, 'availWidth', { value: 768, configurable: true });
Object.defineProperty(window.screen, 'availHeight', { value: 1024, configurable: true });

await service.record('test', 1);

Expand All @@ -287,5 +302,4 @@ describe('TelemetryService', () => {
expect(pouchDb.close.callCount).to.equal(0);
});
});

});

0 comments on commit b6a63ad

Please sign in to comment.