Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move test reinit to mocha's after hooks #557

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions test/integration/other/encryption.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { readFileSync } = require('fs');
const should = require('should');
const { sql } = require('slonik');
const { toText } = require('streamtest').v2;
const { testService, testContainerFullTrx, testContainer } = require(appRoot + '/test/integration/setup');
const { testService, withinFullTrxIt, testContainer } = require(appRoot + '/test/integration/setup');
const testData = require(appRoot + '/test/data/xml');
const { pZipStreamToFiles } = require(appRoot + '/test/util/zip');
const { Form, Key, Submission } = require(appRoot + '/lib/model/frames');
Expand All @@ -12,7 +12,7 @@ const { exhaust } = require(appRoot + '/lib/worker/worker');

describe('managed encryption', () => {
describe('lock management', () => {
it('should reject keyless forms in keyed projects @slow', testContainerFullTrx(async (container) => {
withinFullTrxIt('should reject keyless forms in keyed projects @slow', async (container) => {
// enable managed encryption.
await container.transacting(({ Projects }) =>
Projects.getById(1).then((o) => o.get())
Expand All @@ -30,9 +30,9 @@ describe('managed encryption', () => {
);

error.problemCode.should.equal(409.5);
}));
});

it('should reject forms created while project managed encryption is being enabled @slow', testContainerFullTrx(async (container) => {
withinFullTrxIt('should reject forms created while project managed encryption is being enabled @slow', async (container) => {
// enable managed encryption but don't allow the transaction to close.
let encReq;
const unblock = await new Promise((resolve) => {
Expand Down Expand Up @@ -70,7 +70,7 @@ describe('managed encryption', () => {

// now make sure we get the error we wanted.
error.problemCode.should.equal(409.5);
}));
});
});

describe('decryptor', () => {
Expand Down
30 changes: 15 additions & 15 deletions test/integration/other/migrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const appRoot = require('app-root-path');
const uuid = require('uuid/v4');
const should = require('should');
const config = require('config');
const { testServiceFullTrx } = require('../setup');
const { withServiceWithinFullTrx } = require('../setup');
const { sql } = require('slonik');
const { connect } = require(appRoot + '/lib/model/migrate');
const migrator = connect(config.get('test.database'));
Expand Down Expand Up @@ -35,7 +35,7 @@ const upToMigration = async (toName) => {
describe.skip('database migrations', function() {
this.timeout(4000);

it('should purge deleted forms via migration', testServiceFullTrx(async (service, container) => {
withServiceWithinFullTrx('should purge deleted forms via migration', async (service, container) => {
await upToMigration('20220121-01-form-cascade-delete.js');

await populateUsers(container);
Expand All @@ -50,9 +50,9 @@ describe.skip('database migrations', function() {

const count = await container.oneFirst(sql`select count(*) from forms`);
count.should.equal(1); // only the withrepeat base test should exist
}));
});

it('should not purge blobs that are still referenced', testServiceFullTrx(async (service, container) => {
withServiceWithinFullTrx('should not purge blobs that are still referenced', async (service, container) => {
// An earlier version of this migration [20220121-02-purge-deleted-forms.js]
// failed because it tried to purge blobs that were still being used as
// xlsBlobIds on active form definitons.
Expand All @@ -71,9 +71,9 @@ describe.skip('database migrations', function() {

const count = await container.oneFirst(sql`select count(*) from blobs`);
count.should.equal(1); // the xls blob should still exist
}));
});

it('should purge blobs of deleted forms', testServiceFullTrx(async (service, container) => {
withServiceWithinFullTrx('should purge blobs of deleted forms', async (service, container) => {
// An earlier version of this migration [20220121-02-purge-deleted-forms.js]
// failed because it tried to purge blobs that were still being used as
// xlsBlobIds on active form definitons.
Expand Down Expand Up @@ -110,9 +110,9 @@ describe.skip('database migrations', function() {

count = await container.oneFirst(sql`select count(*) from blobs`);
count.should.equal(0); // blobs should all be purged
}));
});

it('should not purge certain form defs that are either published or active drafts', testServiceFullTrx(async (service, container) => {
withServiceWithinFullTrx('should not purge certain form defs that are either published or active drafts', async (service, container) => {
// 20220209-01-purge-unneeded-drafts.js
await upToMigration('20220121-02-purge-deleted-forms.js');
await populateUsers(container);
Expand Down Expand Up @@ -153,9 +153,9 @@ describe.skip('database migrations', function() {

const after = await container.oneFirst(sql`select count(*) from form_defs`);
after.should.equal(before); // no defs purged
}));
});

it('should purge unneeded form draft defs', testServiceFullTrx(async (service, container) => {
withServiceWithinFullTrx('should purge unneeded form draft defs', async (service, container) => {
// 20220209-01-purge-unneeded-drafts.js
await upToMigration('20220121-02-purge-deleted-forms.js');
await populateUsers(container);
Expand Down Expand Up @@ -187,14 +187,14 @@ describe.skip('database migrations', function() {

const after = await container.oneFirst(sql`select count(*) from form_defs`);
after.should.equal(before - 1); // one purged
}));
});

});

describe('datbase migrations: removing default project', function() {
this.timeout(4000);

it('should put old forms into project', testServiceFullTrx(async (service, container) => {
withServiceWithinFullTrx('should put old forms into project', async (service, container) => {
// before 20181206-01-add-projects.js
await upToMigration('20181012-01-add-submissions-createdat-index.js');

Expand All @@ -216,14 +216,14 @@ describe('datbase migrations: removing default project', function() {

const formCount = await container.oneFirst(sql`select count(*) from forms where "projectId"=${proj.id}`);
formCount.should.equal(1);
}));
});

it('should not make a default project if no forms', testServiceFullTrx(async (service, container) => {
withServiceWithinFullTrx('should not make a default project if no forms', async (service, container) => {
// up to and including this default project migration
await upToMigration('20181206-01-add-projects.js');

// check projects and forms
const projCount = await container.oneFirst(sql`select count(*) from projects`);
projCount.should.equal(0);
}));
});
});
6 changes: 3 additions & 3 deletions test/integration/other/transactions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const should = require('should');
const appRoot = require('app-root-path');
const { sql } = require('slonik');
const { testContainerFullTrx } = require(appRoot + '/test/integration/setup');
const { withinFullTrxIt } = require(appRoot + '/test/integration/setup');
const { exhaust } = require(appRoot + '/lib/worker/worker');
const testData = require('../../data/xml');
const { Frame } = require(appRoot + '/lib/model/frame');
Expand Down Expand Up @@ -41,7 +41,7 @@ describe('transaction integration', () => {
const sometime = (ms) => new Promise((done) => setTimeout(done, ms));

describe('enketo worker transaction', () => {
it('should not allow a write conflict @slow', testContainerFullTrx(async (container) => {
withinFullTrxIt('should not allow a write conflict @slow', async (container) => {
const { Audits, Forms, oneFirst } = container;

const simple = (await Forms.getByProjectAndXmlFormId(1, 'simple')).get();
Expand All @@ -66,6 +66,6 @@ describe('enketo worker transaction', () => {

(await oneFirst(sql`select state from forms where "projectId"=1 and "xmlFormId"='simple'`))
.should.equal('closed');
}));
});
});

22 changes: 14 additions & 8 deletions test/integration/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ const initialize = () => migrator
.raw('drop owned by current_user')
.then(() => migrator.migrate.latest({ directory: appRoot + '/lib/model/migrations' }))
.then(() => withDefaults({ db, bcrypt }).transacting(populate));
const reinit = (f) => (x) => { initialize().then(() => f(x)); };

before(initialize);

Expand Down Expand Up @@ -120,10 +119,13 @@ const testService = (test) => () => new Promise((resolve, reject) => {

// for some tests we explicitly need to make concurrent requests, in which case
// the transaction butchering we do for testService will not work. for these cases,
// we offer testServiceFullTrx:
const testServiceFullTrx = (test) => () => new Promise((resolve, reject) =>
test(augment(request(service(baseContainer))), baseContainer)
.then(reinit(resolve), reinit(reject)));
// we offer withServiceWithinFullTrx:
const withServiceWithinFullTrx = (title, testFn) => {
describe('(within full transaction, with service)', () => {
after(initialize);
it(title, () => testFn(augment(request(service(baseContainer))), baseContainer));
});
};

// for some tests we just want a container, without any of the webservice stuffs between.
// this is that, with the same transaction trickery as a normal test.
Expand All @@ -135,8 +137,12 @@ const testContainer = (test) => () => new Promise((resolve, reject) => {
});

// complete the square of options:
const testContainerFullTrx = (test) => () => new Promise((resolve, reject) =>
test(baseContainer).then(reinit(resolve), reinit(reject)));
const withinFullTrxIt = (title, testFn) => {
describe('(within full transaction)', () => {
after(initialize);
it(title, () => testFn(baseContainer));
});
};

// called to get a container context per task. ditto all // from testService.
// here instead our weird hijack work involves injecting our own constructed
Expand All @@ -152,5 +158,5 @@ const testTask = (test) => () => new Promise((resolve, reject) => {
});//.catch(Promise.resolve.bind(Promise));
});

module.exports = { testService, testServiceFullTrx, testContainer, testContainerFullTrx, testTask };
module.exports = { testService, withServiceWithinFullTrx, testContainer, withinFullTrxIt, testTask };

50 changes: 25 additions & 25 deletions test/integration/worker/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const appRoot = require('app-root-path');
const { promisify } = require('util');
const { DateTime, Duration } = require('luxon');
const { sql } = require('slonik');
const { testContainerFullTrx, testContainer } = require('../setup');
const { withinFullTrxIt, testContainer } = require('../setup');
const { runner, checker, worker } = require(appRoot + '/lib/worker/worker');
const { Audit } = require(appRoot + '/lib/model/frames');
const { insert } = require(appRoot + '/lib/util/db');
Expand Down Expand Up @@ -34,7 +34,7 @@ describe('worker', () => {
runner(container, jobMap)({ action: 'test.event' }, done).should.equal(true);
});

it('should pass the container and event details to the job', testContainerFullTrx(async (container) => {
withinFullTrxIt('should pass the container and event details to the job', async (container) => {
let sentineledContainer = container.with({ testSentinel: 108 });
let checked = false;
const jobMap = { 'test.event': [ (c, e) => {
Expand All @@ -49,9 +49,9 @@ describe('worker', () => {
const event = { id: -1, action: 'test.event', details: { x: 42 } };
await promisify(runner(sentineledContainer, jobMap))(event);
checked.should.equal(true);
}));
});

it('should run all matched jobs', testContainerFullTrx(async (container) => {
withinFullTrxIt('should run all matched jobs', async (container) => {
let count = 0;
const jobMap = { 'test.event': [
() => Promise.resolve(count += 1),
Expand All @@ -61,9 +61,9 @@ describe('worker', () => {
const event = { id: -1, action: 'test.event' };
await promisify(runner(container, jobMap))(event);
count.should.equal(2);
}));
});

it('should mark the event as processed after on job completion', testContainerFullTrx(async (container) => {
withinFullTrxIt('should mark the event as processed after on job completion', async (container) => {
const { Audits, Users } = container;
const alice = (await Users.getByEmail('alice@getodk.org')).get();
await Audits.log(alice.actor, 'submission.attachment.create', alice.actor);
Expand All @@ -73,9 +73,9 @@ describe('worker', () => {
await promisify(runner(container, jobMap))(event);
const after = (await Audits.getLatestByAction('submission.attachment.create')).get();
after.processed.should.be.a.recentDate();
}));
});

it('should log to Sentry if a worker goes wrong', testContainerFullTrx(async (container) => {
withinFullTrxIt('should log to Sentry if a worker goes wrong', async (container) => {
let captured = null;
const Sentry = { captureException(err) { captured = err; } };
const hijackedContainer = container.with({ Sentry });
Expand All @@ -84,24 +84,24 @@ describe('worker', () => {
const jobMap = { 'test.event': [ () => Promise.reject({ uh: 'oh' }) ] };
await promisify(runner(hijackedContainer, jobMap))(event);
captured.should.eql({ uh: 'oh' });
}));
});

// ideally we'd test that the error gets written to stderr but i don't like
// hijacking globals in tests.
it('should still survive and reschedule if Sentry goes wrong', testContainerFullTrx(async (container) => {
withinFullTrxIt('should still survive and reschedule if Sentry goes wrong', async (container) => {
const Sentry = { captureException(err) { throw 'no sentry for you'; } };
const hijackedContainer = container.with({ Sentry });

const event = { id: -1, action: 'test.event', failures: 0 };
const jobMap = { 'test.event': [ () => Promise.reject({ uh: 'oh' }) ] };
await promisify(runner(hijackedContainer, jobMap))(event);
// not hanging is the test here.
}));
});

// we need to use a real event here that doesn't get auto-marked as processed, so
// we can test that it is not indeed processed afterwards.
// TODO: we should be able to not do this as of block 8.
it('should unclaim the event and mark failure in case of failure', testContainerFullTrx(async (container) => {
withinFullTrxIt('should unclaim the event and mark failure in case of failure', async (container) => {
const { Audits, Users } = container;
const alice = (await Users.getByEmail('alice@getodk.org')).get();
await Audits.log(alice.actor, 'submission.attachment.update', alice.actor);
Expand All @@ -114,7 +114,7 @@ describe('worker', () => {
should.not.exist(after.processed);
after.failures.should.equal(1);
after.lastFailure.should.be.a.recentDate();
}));
});
});

// we use submission.attachment.update throughout all these tests as it is currently
Expand Down Expand Up @@ -228,7 +228,7 @@ describe('worker', () => {
describe('worker', () => {
const millis = (x) => new Promise((done) => { setTimeout(done, x); });

it('should run a full loop right away', testContainerFullTrx(async (container) => {
withinFullTrxIt('should run a full loop right away', async (container) => {
const { Audits, Users } = container;
const alice = (await Users.getByEmail('alice@getodk.org')).get();
await Audits.log(alice.actor, 'submission.attachment.update', alice.actor);
Expand All @@ -243,9 +243,9 @@ describe('worker', () => {
cancel();
await millis(20); // buffer so the next check lands before the database is wiped on return
ran.should.equal(true);
}));
});

it('should run two full loops right away', testContainerFullTrx(async (container) => {
withinFullTrxIt('should run two full loops right away', async (container) => {
const { Audits, Users } = container;
const alice = (await Users.getByEmail('alice@getodk.org')).get();
await Audits.log(alice.actor, 'submission.attachment.update', alice.actor);
Expand All @@ -260,9 +260,9 @@ select count(*) from audits where action='submission.attachment.update' and proc

cancel();
await millis(20); // ditto above
}));
});

it('should run two full loops with an idle cycle in between', testContainerFullTrx(async (container) => {
withinFullTrxIt('should run two full loops with an idle cycle in between', async (container) => {
const { Audits, Users } = container;
const alice = (await Users.getByEmail('alice@getodk.org')).get();
await Audits.log(alice.actor, 'submission.attachment.update', alice.actor);
Expand All @@ -281,9 +281,9 @@ select count(*) from audits where action='submission.attachment.update' and proc

cancel();
await millis(20); // ditto above
}));
});

it('should restart if the check fails prequery', testContainerFullTrx(async (container) => {
withinFullTrxIt('should restart if the check fails prequery', async (container) => {
const { Audits, Users } = container;
const alice = (await Users.getByEmail('alice@getodk.org')).get();
await Audits.log(alice.actor, 'submission.attachment.update', alice.actor);
Expand All @@ -307,9 +307,9 @@ select count(*) from audits where action='submission.attachment.update' and proc
cancel();
await millis(20);
failed.should.equal(true);
}));
});

it('should restart if the check fails in-query', testContainerFullTrx(async (container) => {
withinFullTrxIt('should restart if the check fails in-query', async (container) => {
const { Audits, Users } = container;
const alice = (await Users.getByEmail('alice@getodk.org')).get();
await Audits.log(alice.actor, 'submission.attachment.update', alice.actor);
Expand All @@ -333,9 +333,9 @@ select count(*) from audits where action='submission.attachment.update' and proc
cancel();
await millis(20);
failed.should.equal(true);
}));
});

it('should restart if the process itself fails', testContainerFullTrx(async (container) => {
withinFullTrxIt('should restart if the process itself fails', async (container) => {
const { Audits, Users } = container;
const alice = (await Users.getByEmail('alice@getodk.org')).get();
await Audits.log(alice.actor, 'submission.attachment.update', alice.actor);
Expand All @@ -362,7 +362,7 @@ select count(*) from audits where action='submission.attachment.update' and proc
cancel();
await millis(20); // ditto above
checks.should.equal(2);
}));
});

// TODO: maybe someday test the watchdog loop too.
});
Expand Down