Skip to content

Commit

Permalink
Add more tests about entity update error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ktuite committed Oct 9, 2023
1 parent b012cc1 commit df67cbe
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 3 deletions.
3 changes: 2 additions & 1 deletion lib/model/query/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const { equals, extender, unjoiner, page, markDeleted } = require('../../util/db
const { map, mergeRight } = require('ramda');
const { blankStringToNull, construct } = require('../../util/util');
const { QueryOptions } = require('../../util/db');
const { getOrNotFound } = require('../../util/promise');
const { odataFilter } = require('../../data/odata-filter');
const { odataToColumnMap, parseSubmissionXml } = require('../../data/entity');
const { isTrue } = require('../../util/http');
Expand Down Expand Up @@ -160,7 +161,7 @@ const _updateEntity = (dataset, entityData, submissionId, submissionDef, submiss
const clientEntity = await Entity.fromParseEntityData(entityData); // validation happens here

// Get version of entity on the server
const serverEntity = await Entities.getById(dataset.id, clientEntity.uuid).then(o => o.get());
const serverEntity = await Entities.getById(dataset.id, clientEntity.uuid).then(getOrNotFound);

// merge data
const mergedData = mergeRight(serverEntity.aux.currentVersion.data, clientEntity.def.data);
Expand Down
172 changes: 170 additions & 2 deletions test/integration/worker/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,176 @@ describe('worker: entity', () => {
});
});

// TODO: describe('should catch problems updating entity'....
// TODO: validation errors, uuid missing, all kinds of things
describe('should catch problems updating entity', () => {
// TODO: these errors are getting logged as entity.create.error audit events
describe('validation errors', () => {
it('should fail because UUID is invalid', testService(async (service, container) => {
const asAlice = await service.login('alice');

// create an initial entity to update
await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.simpleEntity)
.set('Content-Type', 'application/xml')
.expect(200);
await asAlice.post('/v1/projects/1/datasets/people/entities')
.send({
uuid: '12345678-1234-4123-8234-123456789abc',
label: 'Johnny Doe',
data: { first_name: 'Johnny', age: '22' }
})
.expect(200);
await exhaust(container);
// create form and submission to update entity
await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.updateEntity)
.set('Content-Type', 'application/xml')
.expect(200);
await asAlice.post('/v1/projects/1/forms/updateEntity/submissions')
.send(testData.instances.updateEntity.one.replace('12345678-1234-4123-8234-123456789abc', 'bad_uuid'))
.set('Content-Type', 'application/xml')
.expect(200);
await exhaust(container);

// Submission event should look successful
const subEvent = await container.Audits.getLatestByAction('submission.create').then((o) => o.get());
should.exist(subEvent.processed);
subEvent.failures.should.equal(0);

const updateEvent = await container.Audits.getLatestByAction('entity.update');
updateEvent.isEmpty().should.be.true();

const event = await container.Audits.getLatestByAction('entity.create.error').then((o) => o.get());
event.actorId.should.equal(5); // Alice
event.details.submissionId.should.equal(subEvent.details.submissionId);
event.details.errorMessage.should.equal('Invalid input data type: expected (uuid) to be (valid UUID)');
event.details.problem.problemCode.should.equal(400.11);
}));

it('should fail because dataset attribute is missing', testService(async (service, container) => {
const asAlice = await service.login('alice');

// create an initial entity to update
await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.simpleEntity)
.set('Content-Type', 'application/xml')
.expect(200);
await asAlice.post('/v1/projects/1/datasets/people/entities')
.send({
uuid: '12345678-1234-4123-8234-123456789abc',
label: 'Johnny Doe',
data: { first_name: 'Johnny', age: '22' }
})
.expect(200);
await exhaust(container);
// create form and submission to update entity
await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.updateEntity)
.set('Content-Type', 'application/xml')
.expect(200);
await asAlice.post('/v1/projects/1/forms/updateEntity/submissions')
.send(testData.instances.updateEntity.one.replace('people', ''))
.set('Content-Type', 'application/xml')
.expect(200);
await exhaust(container);

// Submission event should look successful
const subEvent = await container.Audits.getLatestByAction('submission.create').then((o) => o.get());
should.exist(subEvent.processed);
subEvent.failures.should.equal(0);

const udpateEvent = await container.Audits.getLatestByAction('entity.update');
udpateEvent.isEmpty().should.be.true();

const event = await container.Audits.getLatestByAction('entity.create.error').then((o) => o.get());
event.actorId.should.equal(5); // Alice
event.details.submissionId.should.equal(subEvent.details.submissionId);
event.details.errorMessage.should.equal('Required parameter dataset missing.');
event.details.problem.problemCode.should.equal(400.2);
}));
});

describe('constraint errors', () => {
it('should fail if trying to update an entity by uuid that does not exist', testService(async (service, container) => {
const asAlice = await service.login('alice');

// create an initial entity to update
await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.simpleEntity)
.set('Content-Type', 'application/xml')
.expect(200);
await asAlice.post('/v1/projects/1/datasets/people/entities')
.send({
uuid: '12345678-1234-4123-8234-123456789bad', // not the uuid in updateEntity.one
label: 'Johnny Doe',
data: { first_name: 'Johnny', age: '22' }
})
.expect(200);
await exhaust(container);
// create form and submission to update entity
await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.updateEntity)
.set('Content-Type', 'application/xml')
.expect(200);
await asAlice.post('/v1/projects/1/forms/updateEntity/submissions')
.send(testData.instances.updateEntity.one)
.set('Content-Type', 'application/xml')
.expect(200);
await exhaust(container);

// most recent submission event should look like it was sucessfully processed
const subEvent = await container.Audits.getLatestByAction('submission.create').then((o) => o.get());
should.exist(subEvent.processed);
subEvent.failures.should.equal(0);

// the entity creation error should be logged
const event = await container.Audits.getLatestByAction('entity.create.error').then((o) => o.get());
event.actorId.should.equal(5); // Alice
event.details.submissionId.should.equal(subEvent.details.submissionId);
event.details.errorMessage.should.equal('Could not find the resource you were looking for.');
event.details.problem.problemCode.should.equal(404.1);
}));

it('should fail for other constraint errors like dataset name does not exist', testService(async (service, container) => {
const asAlice = await service.login('alice');

// create an initial entity to update
await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.simpleEntity)
.set('Content-Type', 'application/xml')
.expect(200);
await asAlice.post('/v1/projects/1/datasets/people/entities')
.send({
uuid: '12345678-1234-4123-8234-123456789bad', // not the uuid in updateEntity.one
label: 'Johnny Doe',
data: { first_name: 'Johnny', age: '22' }
})
.expect(200);
await exhaust(container);
// create form and submission to update entity
await asAlice.post('/v1/projects/1/forms?publish=true')
.send(testData.forms.updateEntity)
.set('Content-Type', 'application/xml')
.expect(200);
await asAlice.post('/v1/projects/1/forms/updateEntity/submissions')
.send(testData.instances.updateEntity.one.replace('people', 'frogs'))
.set('Content-Type', 'application/xml')
.expect(200);
await exhaust(container);

// most recent submission event should look like it was sucessfully processed
const subEvent = await container.Audits.getLatestByAction('submission.create').then((o) => o.get());
should.exist(subEvent.processed);
subEvent.failures.should.equal(0);

// the entity creation error should be logged
const event = await container.Audits.getLatestByAction('entity.create.error').then((o) => o.get());
event.actorId.should.equal(5); // Alice
event.details.submissionId.should.equal(subEvent.details.submissionId);
event.details.problem.problemCode.should.equal(404.7);
event.details.errorMessage.should.match(/The dataset \(frogs\) specified in the submission does not exist/);
}));
});
});

describe('event processing based on approvalRequired flag', () => {
it('should create entity on submission creation when approvalRequired is false', testService(async (service, container) => {
Expand Down

0 comments on commit df67cbe

Please sign in to comment.