diff --git a/src/controller/review-object.controller/review-object.controller.js b/src/controller/review-object.controller/review-object.controller.js index 7248b7cc..3ef73148 100644 --- a/src/controller/review-object.controller/review-object.controller.js +++ b/src/controller/review-object.controller/review-object.controller.js @@ -14,6 +14,9 @@ async function getReviewObjectByOrgIdentifier (req, res, next) { } else { value = await repo.getOrgReviewObjectByOrgShortname(identifier) } + if (!value) { + return res.status(404).json({ message: 'Review Object does not exist' }) + } return res.status(200).json(value) } diff --git a/src/repositories/reviewObjectRepository.js b/src/repositories/reviewObjectRepository.js index fd5f69cf..b96fff4b 100644 --- a/src/repositories/reviewObjectRepository.js +++ b/src/repositories/reviewObjectRepository.js @@ -8,7 +8,7 @@ class ReviewObjectRepository extends BaseRepository { const baseOrgRepository = new BaseOrgRepository() const org = await baseOrgRepository.findOneByShortName(orgShortName) if (!org) { - throw new Error(`No organization found with short name ${orgShortName}`) + return null } const reviewObject = await ReviewObjectModel.find({ target_object_uuid: org.UUID }, null, options) @@ -34,7 +34,7 @@ class ReviewObjectRepository extends BaseRepository { const baseOrgRepository = new BaseOrgRepository() const org = await baseOrgRepository.findOneByShortName(orgShortName) if (!org) { - throw new Error(`No organization found with short name ${orgShortName}`) + return null } const reviewObject = await ReviewObjectModel.findOne({ target_object_uuid: org.UUID }, null, options) @@ -45,7 +45,7 @@ class ReviewObjectRepository extends BaseRepository { const baseOrgRepository = new BaseOrgRepository() const org = await baseOrgRepository.findOneByUUID(orgUUID) if (!org) { - throw new Error(`No organization found with UUID ${orgUUID}`) + return null } const reviewObject = await ReviewObjectModel.findOne({ target_object_uuid: org.UUID }, null, options) @@ -63,6 +63,9 @@ class ReviewObjectRepository extends BaseRepository { async updateReviewOrgObject (body, UUID, options = {}) { console.log('Updating review object with UUID:', UUID) const reviewObject = await this.findOneByUUID(UUID, options) + if (!reviewObject) { + return null + } // For each item waiting for approval, for testing we are going to just do shortname reviewObject.new_review_data.short_name = body.new_review_data.short_name || reviewObject.new_review_data.short_name diff --git a/test/integration-tests/constants.js b/test/integration-tests/constants.js index ea98cb0f..c27d7d97 100644 --- a/test/integration-tests/constants.js +++ b/test/integration-tests/constants.js @@ -377,6 +377,20 @@ const testRegistryOrg = { hard_quota: 100000 } +const testRegistryOrg2 = { + short_name: 'test_registry_org2', + long_name: 'Test Registry Organization2', + contact_info: { + poc: 'Dave', + poc_email: 'dave@test.org', + poc_phone: '555-1234', + org_email: 'contact@test.org', + website: 'https://test.org' + }, + authority: 'CNA', + hard_quota: 100000 +} + const existingOrg = { short_name: 'win_5', @@ -419,6 +433,7 @@ module.exports = { testAdp2, testOrg, testRegistryOrg, + testRegistryOrg2, existingOrg, existingRegistryOrg } diff --git a/test/integration-tests/review-object/reviewObjectTest.js b/test/integration-tests/review-object/reviewObjectTest.js new file mode 100644 index 00000000..44f0aec5 --- /dev/null +++ b/test/integration-tests/review-object/reviewObjectTest.js @@ -0,0 +1,149 @@ +/* eslint-disable no-unused-expressions */ +const chai = require('chai') +const expect = chai.expect +chai.use(require('chai-http')) + +const constants = require('../constants.js') +const app = require('../../../src/index.js') + +describe('Review Object Controller Integration Tests', () => { + let orgUUID + let reviewUUID + const reviewPayload = { + target_object_uuid: '', + new_review_data: {} + } + + context('Positive Tests', () => { + it('Creates an organization to use for review object tests', async () => { + const res = await chai + .request(app) + .post('/api/registry/org') + .set({ ...constants.headers }) + .send(constants.testRegistryOrg2) + expect(res).to.have.status(200) + expect(res.body).to.have.property('created') + expect(res.body.created).to.have.property('UUID') + orgUUID = res.body.created.UUID + }) + + it('Creates a review object for the organization', async () => { + reviewPayload.target_object_uuid = orgUUID + reviewPayload.new_review_data = constants.testRegistryOrg2 + const res = await chai + .request(app) + .post('/api/review/org/') + .set({ ...constants.headers }) + .send(reviewPayload) + expect(res).to.have.status(200) + expect(res.body).to.have.property('uuid') + expect(res.body).to.have.property('target_object_uuid', orgUUID) + expect(res.body).to.have.property('new_review_data') + reviewUUID = res.body.uuid + }) + + it('Retrieves the review object by org short_name', async () => { + const res = await chai + .request(app) + .get(`/api/review/org/${constants.testRegistryOrg2.short_name}`) + .set({ ...constants.headers }) + expect(res).to.have.status(200) + expect(res.body).to.have.property('uuid', reviewUUID) + }) + + it('Retrieves the review object by org UUID', async () => { + const res = await chai + .request(app) + .get(`/api/review/org/${orgUUID}`) + .set({ ...constants.headers }) + expect(res).to.have.status(200) + expect(res.body).to.have.property('uuid', reviewUUID) + }) + + it('Retrieves all review objects', async () => { + const res = await chai + .request(app) + .get('/api/review/orgs') + .set({ ...constants.headers }) + expect(res).to.have.status(200) + expect(res.body).to.be.an('array') + const found = res.body.find(obj => obj.uuid === reviewUUID) + expect(found).to.exist + }) + + it('Updates the review object with new short_name', async () => { + const updatePayload = { + new_review_data: constants.testRegistryOrg2 + } + + updatePayload.new_review_data.short_name = 'updated_org' + const res = await chai + .request(app) + .put(`/api/review/org/${reviewUUID}`) + .set({ ...constants.headers }) + .send(updatePayload) + expect(res).to.have.status(200) + expect(res.body).to.have.property('uuid', reviewUUID) + expect(res.body.new_review_data).to.have.property('short_name', 'updated_org') + }) + }) + + context('Negative Tests', () => { + it('Fails when target_object_uuid is missing', async () => { + const res = await chai + .request(app) + .post('/api/review/org/') + .set({ ...constants.headers }) + .send({ new_review_data: constants.testOrg }) + expect(res).to.have.status(400) + expect(res.body).to.have.property('message', 'Missing required field target_object_uuid') + }) + + it('Fails when new_review_data is missing', async () => { + const res = await chai + .request(app) + .post('/api/review/org/') + .set({ ...constants.headers }) + .send({ target_object_uuid: orgUUID }) + expect(res).to.have.status(400) + expect(res.body).to.have.property('message', 'Missing required field new_review_data') + }) + + it('Fails when uuid is provided in creation payload', async () => { + const res = await chai + .request(app) + .post('/api/review/org/') + .set({ ...constants.headers }) + .send({ + uuid: 'should-not-be-here', + target_object_uuid: orgUUID, + new_review_data: constants.testOrg + }) + expect(res).to.have.status(400) + expect(res.body).to.have.property('message', 'Do not pass in a uuid key when creating a review object') + }) + + it('Returns 404 for non-existent review object GET', async () => { + const res = await chai + .request(app) + .get('/api/review/org/nonexistent-org') + .set({ ...constants.headers }) + expect(res).to.have.status(404) + }) + + it('Returns 404 for non-existent review object UPDATE', async () => { + const updatePayload = { + new_review_data: constants.testRegistryOrg2 + } + + updatePayload.new_review_data.short_name = 'updated_org' + const res = await chai + .request(app) + .put('/api/review/org/nonexistent-uuid') + .set({ ...constants.headers }) + .send(updatePayload) + expect(res).to.have.status(404) + expect(res.body).to.have.property('message') + }) + }) +}) diff --git a/test/unit-tests/review-object/review-object.controller.test.js b/test/unit-tests/review-object/review-object.controller.test.js new file mode 100644 index 00000000..06d0416b --- /dev/null +++ b/test/unit-tests/review-object/review-object.controller.test.js @@ -0,0 +1,159 @@ +/* eslint-disable no-unused-vars */ +/* eslint-disable no-unused-expressions */ +const { expect } = require('chai') +const sinon = require('sinon') +const controller = require('../../../src/controller/review-object.controller/review-object.controller.js') + +describe('Review Object Controller', function () { + let req, res, next, repoStub, orgRepoStub + + beforeEach(() => { + repoStub = { } + orgRepoStub = { } + + req = { + params: {}, + body: {}, + ctx: { repositories: { getReviewObjectRepository: () => repoStub, getBaseOrgRepository: () => orgRepoStub } } + } + + res = { + status: sinon.stub().returnsThis(), + json: sinon.stub().returnsThis() + } + + next = sinon.stub() + }) + + describe('getReviewObjectByOrgIdentifier', function () { + it('should return 400 if identifier is missing', async () => { + await controller.getReviewObjectByOrgIdentifier(req, res, next) + expect(res.status.calledWith(400)).to.be.true + expect(res.json.calledWith({ message: 'Missing identifier parameter' })).to.be.true + }) + + it('should call getOrgReviewObjectByOrgUUID when identifier is a UUID', async () => { + const uuid = '123e4567-e89b-12d3-a456-426614174000' + req.params.identifier = uuid + repoStub.getOrgReviewObjectByOrgUUID = sinon.stub().resolves({ id: uuid }) + await controller.getReviewObjectByOrgIdentifier(req, res, next) + expect(repoStub.getOrgReviewObjectByOrgUUID.calledWith(uuid)).to.be.true + expect(res.status.calledWith(200)).to.be.true + expect(res.json.calledWith({ id: uuid })).to.be.true + }) + + it('should call getOrgReviewObjectByOrgShortname when identifier is not a UUID', async () => { + const short = 'myorg' + req.params.identifier = short + repoStub.getOrgReviewObjectByOrgShortname = sinon.stub().resolves({ name: short }) + await controller.getReviewObjectByOrgIdentifier(req, res, next) + expect(repoStub.getOrgReviewObjectByOrgShortname.calledWith(short)).to.be.true + expect(res.status.calledWith(200)).to.be.true + expect(res.json.calledWith({ name: short })).to.be.true + }) + }) + + describe('getAllReviewObjects', function () { + it('should return all review objects', async () => { + const data = [{ id: 1 }, { id: 2 }] + repoStub.getAllReviewObjects = sinon.stub().resolves(data) + await controller.getAllReviewObjects(req, res, next) + expect(repoStub.getAllReviewObjects.calledOnce).to.be.true + expect(res.status.calledWith(200)).to.be.true + expect(res.json.calledWith(data)).to.be.true + }) + }) + + describe('updateReviewObjectByReviewUUID', function () { + it('should return 400 if new_review_data is invalid', async () => { + req.params.uuid = 'some-uuid' + req.body.new_review_data = { invalid: true } + orgRepoStub.validateOrg = sinon.stub().returns({ isValid: false, errors: ['bad data'] }) + await controller.updateReviewObjectByReviewUUID(req, res, next) + expect(orgRepoStub.validateOrg.calledWith(req.body.new_review_data)).to.be.true + expect(res.status.calledWith(400)).to.be.true + expect(res.json.calledWith({ message: 'Invalid new_review_data', errors: ['bad data'] })).to.be.true + }) + + it('should return 404 if review object not found', async () => { + const uuid = 'rev-uuid' + req.params.uuid = uuid + req.body.new_review_data = { foo: 'bar' } + orgRepoStub.validateOrg = sinon.stub().returns({ isValid: true }) + repoStub.updateReviewOrgObject = sinon.stub().resolves(undefined) + await controller.updateReviewObjectByReviewUUID(req, res, next) + expect(repoStub.updateReviewOrgObject.calledWith(req.body, uuid)).to.be.true + expect(res.status.calledWith(404)).to.be.true + expect(res.json.calledWith({ message: `No review object found with UUID ${uuid}` })).to.be.true + }) + + it('should return 200 with updated value', async () => { + const uuid = 'rev-uuid' + const updated = { uuid } + req.params.uuid = uuid + req.body.new_review_data = { foo: 'bar' } + orgRepoStub.validateOrg = sinon.stub().returns({ isValid: true }) + repoStub.updateReviewOrgObject = sinon.stub().resolves(updated) + await controller.updateReviewObjectByReviewUUID(req, res, next) + expect(repoStub.updateReviewOrgObject.calledWith(req.body, uuid)).to.be.true + expect(res.status.calledWith(200)).to.be.true + expect(res.json.calledWith(updated)).to.be.true + }) + }) + + describe('createReviewObject', function () { + it('should return 400 if body contains uuid', async () => { + req.body.uuid = 'should-not-be-here' + await controller.createReviewObject(req, res, next) + expect(res.status.calledWith(400)).to.be.true + expect(res.json.calledWith({ message: 'Do not pass in a uuid key when creating a review object' })).to.be.true + }) + + it('should return 400 if target_object_uuid missing', async () => { + req.body.new_review_data = { foo: 'bar' } + await controller.createReviewObject(req, res, next) + expect(res.status.calledWith(400)).to.be.true + expect(res.json.calledWith({ message: 'Missing required field target_object_uuid' })).to.be.true + }) + + it('should return 400 if new_review_data missing', async () => { + req.body.target_object_uuid = 'obj-uuid' + await controller.createReviewObject(req, res, next) + expect(res.status.calledWith(400)).to.be.true + expect(res.json.calledWith({ message: 'Missing required field new_review_data' })).to.be.true + }) + + it('should return 400 if new_review_data is invalid', async () => { + req.body.target_object_uuid = 'obj-uuid' + req.body.new_review_data = { bad: true } + orgRepoStub.validateOrg = sinon.stub().returns({ isValid: false, errors: ['err'] }) + await controller.createReviewObject(req, res, next) + expect(orgRepoStub.validateOrg.calledWith(req.body.new_review_data)).to.be.true + expect(res.status.calledWith(400)).to.be.true + expect(res.json.calledWith({ message: 'Invalid new_review_data', errors: ['err'] })).to.be.true + }) + + it('should return 500 if repo create fails', async () => { + req.body.target_object_uuid = 'obj-uuid' + req.body.new_review_data = { foo: 'bar' } + orgRepoStub.validateOrg = sinon.stub().returns({ isValid: true }) + repoStub.createReviewOrgObject = sinon.stub().resolves(undefined) + await controller.createReviewObject(req, res, next) + expect(repoStub.createReviewOrgObject.calledWith(req.body)).to.be.true + expect(res.status.calledWith(500)).to.be.true + expect(res.json.calledWith({ message: 'Failed to create review object' })).to.be.true + }) + + it('should return 200 with created object', async () => { + const created = { uuid: 'new-uuid', target_object_uuid: 'obj-uuid', new_review_data: { foo: 'bar' } } + req.body.target_object_uuid = 'obj-uuid' + req.body.new_review_data = { foo: 'bar' } + orgRepoStub.validateOrg = sinon.stub().returns({ isValid: true }) + repoStub.createReviewOrgObject = sinon.stub().resolves(created) + await controller.createReviewObject(req, res, next) + expect(repoStub.createReviewOrgObject.calledWith(req.body)).to.be.true + expect(res.status.calledWith(200)).to.be.true + expect(res.json.calledWith(created)).to.be.true + }) + }) +})