diff --git a/.huskyrc b/.huskyrc new file mode 100644 index 0000000..4d077c8 --- /dev/null +++ b/.huskyrc @@ -0,0 +1,5 @@ +{ + "hooks": { + "pre-commit": "lint-staged" + } +} diff --git a/.lintstagedrc b/.lintstagedrc new file mode 100644 index 0000000..aeaced9 --- /dev/null +++ b/.lintstagedrc @@ -0,0 +1,7 @@ +{ + "*.js": [ + "eslint --fix", + "prettier --write --loglevel warn", + "git add" + ] +} diff --git a/README.md b/README.md index f1734d6..61a2726 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ Useful Note: The handler function for each route is specified by the `operationI Recommend reviewing the [Open API Specification](https://swagger.io/docs/specification/about/) before making any changes to the `swagger.yaml` file. + - Updates to the swagger may require updates to the mock handlers in the test files. See section on API testing below. + # Logging A centralized logger has been created (see `api/helpers/logger.js`). @@ -128,23 +130,23 @@ log.debug('Useful for logging objects and other developer data', JSON.stringify( ## Info -### Technolgies used +This project contains two kinds of unit tests. Regular unit tests and API unit tests, which require some special considerations and setup, as detailed in the API Testing section below. -[Jasmine](https://jasmine.github.io/), [Karma](https://karma-runner.github.io/latest/index.html), [Protractor](http://www.protractortest.org/) +### Technolgies used -## Initial Setup +[Jest](jasmine), [SuperTest](https://www.npmjs.com/package/supertest), [Nock](https://www.npmjs.com/package/nock), [Mongodb-Memory-Server](https://www.npmjs.com/package/mongodb-memory-server) -1) Start server and create database by running `npm start` in root +## Run Tests -2) Add Admin user to users collection +* Run the unit and api tests. + * Note: the `package.json` `tests` command sets the `UPLOAD_DIRECTORY` environment variable, the command for which may be OS specific and therefore may need adjusting depending on your machines OS. - `` - db.users.insert({ "username": #{username}, "password": #{password}, roles: [['sysadmin'],['public']] }) - `` +``` +npm run tests +``` -3) Seed local database as described in [seed README](seed/README.md) -## API Testing +## API Tests This project is using [jest](http://jestjs.io/) as a testing framework. You can run tests with `yarn test` or `jest`. Running either command with the `--watch` flag will re-run the tests every time a file is changed. @@ -234,19 +236,10 @@ This project uses [Keycloak](https://www.keycloak.org/) to handle authentication Required environment variables: ``` -TTLS_API_ENDPOINT="" -WEBADE_AUTH_ENDPOINT="" -WEBADE_USERNAME="" -WEBADE_PASSWORD="" -``` -_Note: Get the values for TTLS_API_ENDPOINT, WEBADE_AUTH_ENDPOINT, WEBADE_USERNAME and WEBADE_PASSWORD, at [Openshift](https://console.pathfinder.gov.bc.ca:8443/console/projects) → Natural Resource Public Review and Comment (dev) project → Applications → Pods → prc-api pod → Environment._ - -2. Before starting the local Admin project, in file `src/app/services/keycloak.service.ts`, around line 17, change code to: -``` -case 'http://localhost:4200': - // Local - this.keycloakEnabled = true; - break; +TTLS_API_ENDPOINT="" +WEBADE_AUTH_ENDPOINT="" +WEBADE_USERNAME="" +WEBADE_PASSWORD="" ``` # VSCode Extensions diff --git a/api/helpers/ttlsUtils.test.js b/api/helpers/ttlsUtils.test.js index f724a09..1039552 100644 --- a/api/helpers/ttlsUtils.test.js +++ b/api/helpers/ttlsUtils.test.js @@ -4,7 +4,7 @@ var _ = require('lodash'); describe('TTLSUtils', () => { const webADENock = { - domain: 'https://api.nrs.gov.bc.ca', + domain: 'https://t1api.nrs.gov.bc.ca', path: '/oauth2/v1/oauth/token?grant_type=client_credentials&disableDeveloperFilter=true&scope=TTLS.*', headers: { reqheaders: { @@ -58,7 +58,7 @@ describe('TTLSUtils', () => { const accessToken = 'ACCESS_TOKEN'; const fileNumber = '99999'; - const fileSearchPath = '/ttls-api/v1/landUseApplications?fileNumber=99999'; + const fileSearchPath = '/ttls-api/v1/landUseApplications?fileNumber=99999&pageNumber=1&pageRowCount=100'; const ttlsApiResponse = { '@type': 'LandUseApplicationResources', links: [ @@ -102,6 +102,12 @@ describe('TTLSUtils', () => { code: 'GS', description: 'DISPOSITION IN GOOD STANDING' }, + reasonCode: { + '@type': 'ReasonCodeResource', + links: [], + code: 'C', + description: 'AMENDMENT APPROVED - APPLICATION' + }, landUseTypeCode: { '@type': 'LandUseTypeCodeResource', links: [], @@ -158,12 +164,13 @@ describe('TTLSUtils', () => { }); }); - it('returns an application with the expected tenure status, stage, and location attrs', done => { + it('returns an application with the expected tenure status, reason, stage, and location attrs', done => { TTLSUtils.getApplicationByFilenumber(accessToken, fileNumber).then(response => { expect(response.length).toEqual(1); let firstApplication = response[0]; expect(firstApplication.TENURE_STATUS).toEqual('DISPOSITION IN GOOD STANDING'); + expect(firstApplication.TENURE_REASON).toEqual('AMENDMENT APPROVED - APPLICATION'); expect(firstApplication.TENURE_STAGE).toEqual('TENURE'); expect(firstApplication.TENURE_LOCATION).toEqual('Over the River and through the woods'); @@ -211,7 +218,7 @@ describe('TTLSUtils', () => { } }; - const landUseAppSearchPath = '/ttls-api/v1/landUseApplications/666666'; + const landUseAppSearchPath = '/ttls-api/v1/landUseApplications/666666?pageNumber=1&pageRowCount=100'; const ttlsApiResponse = { '@type': 'LandUseApplicationResource', links: [ @@ -246,6 +253,12 @@ describe('TTLSUtils', () => { code: 'AC', description: 'DISPOSITION IN GOOD STANDING' }, + reasonCode: { + '@type': 'ReasonCodeResource', + links: [], + code: 'C', + description: 'AMENDMENT APPROVED - APPLICATION' + }, landUseTypeCode: { '@type': 'LandUseTypeCodeResource', links: [], @@ -329,6 +342,7 @@ describe('TTLSUtils', () => { let firstApplication = response; expect(firstApplication.TENURE_STATUS).toEqual('DISPOSITION IN GOOD STANDING'); + expect(firstApplication.TENURE_REASON).toEqual('AMENDMENT APPROVED - APPLICATION'); expect(firstApplication.TENURE_STAGE).toEqual('TENURE'); expect(firstApplication.TENURE_LOCATION).toEqual('Over the River and through the woods'); @@ -424,8 +438,10 @@ describe('TTLSUtils', () => { const firstParcel = application.parcels[0]; const geometry = firstParcel.geometry; expect(geometry).not.toBeNull(); - expect(geometry.type).toEqual('Polygon'); - expect(geometry.coordinates).toBeDefined(); + expect(geometry.type).toEqual('GeometryCollection'); + expect(geometry.geometries).toBeDefined(); + expect(geometry.geometries[0].type).toEqual('Polygon'); + expect(geometry.geometries[0].coordinates).toBeDefined(); done(); }); diff --git a/api/test/application.test.js b/api/test/application.test.js index e5a659e..af63543 100644 --- a/api/test/application.test.js +++ b/api/test/application.test.js @@ -5,7 +5,6 @@ const mongoose = require('mongoose'); const request = require('supertest'); // const nock = require('nock'); // const tantalisResponse = require('./fixtures/tantalis_response.json'); -const fieldNames = ['description', 'tantalisID']; const _ = require('lodash'); const TTLSUtils = require('../helpers/ttlsUtils'); @@ -14,7 +13,13 @@ require('../helpers/models/application'); require('../helpers/models/feature'); const Application = mongoose.model('Application'); const Feature = mongoose.model('Feature'); + +/************************************* + Mock Route Handlers + Helper Methods +*************************************/ + const idirUsername = 'idir/i_am_a_bot'; +const fieldNames = ['description', 'tantalisID']; function paramsWithAppId(req) { let params = test_helper.buildParams({ appId: req.params.id }); @@ -68,6 +73,10 @@ app.put('/api/application/:id/unpublish', function(req, res) { return applicationController.protectedUnPublish(paramsWithAppId(req), res); }); +/************************************* + General Test Data + Helper Methods +*************************************/ + const applicationsData = [ { description: 'SPECIAL', name: 'Special Application', tags: [['public'], ['sysadmin']], isDeleted: false }, { description: 'VANILLA', name: 'Vanilla Ice Cream', tags: [['public']], isDeleted: false }, @@ -88,6 +97,10 @@ function setupApplications(applicationsData) { }); } +/************************************* + Tests +*************************************/ + describe('GET /application', () => { test('returns a list of non-deleted, public and sysadmin Applications', done => { setupApplications(applicationsData).then(documents => { @@ -336,7 +349,7 @@ describe('POST /application', () => { }); }); - test('defaults to sysadmin for tags and review tags', done => { + test('defaults to sysadmin for tags', done => { request(app) .post('/api/application') .send(applicationObj) @@ -505,7 +518,7 @@ describe('POST /application', () => { describe('when the login call fails', () => { let loginPromise = new Promise(function(resolve, reject) { - reject({ statusCode: 503, message: 'Ooh boy something went wrong' }); + reject({ code: 503, message: 'Ooh boy something went wrong' }); }); beforeEach(() => { diff --git a/api/test/comment.test.js b/api/test/comment.test.js index 91146cf..39098b5 100644 --- a/api/test/comment.test.js +++ b/api/test/comment.test.js @@ -4,15 +4,17 @@ const mongoose = require('mongoose'); const commentFactory = require('./factories/comment_factory').factory; const commentPeriodFactory = require('./factories/comment_period_factory').factory; const request = require('supertest'); - -const fieldNames = ['comment', 'name']; - const _ = require('lodash'); const commentController = require('../controllers/comment.js'); require('../helpers/models/comment'); const Comment = mongoose.model('Comment'); +/************************************* + Mock Route Handlers + Helper Methods +*************************************/ +const fieldNames = ['comment', 'name']; + function paramsWithCommentId(req) { let params = test_helper.buildParams({ CommentId: req.params.id }); return test_helper.createSwaggerParams(fieldNames, params); @@ -61,6 +63,10 @@ app.put('/api/comment/:id/unpublish', function(req, res) { return commentController.protectedUnPublish(paramsWithCommentId(req), res); }); +/************************************* + General Test Data + Helper Methods +*************************************/ + const commentsData = [ { name: 'Special Comment', @@ -86,6 +92,10 @@ function setupComments(commentsData) { }); } +/************************************* + Tests +*************************************/ + describe('GET /Comment', () => { test('returns a list of non-deleted, public and sysadmin Comments', done => { setupComments(commentsData).then(documents => { @@ -278,7 +288,7 @@ describe('POST /public/comment', () => { }); describe('tags', () => { - test('defaults to sysadmin for tags and review tags', done => { + test('defaults to sysadmin for tags', done => { let commentObj = { name: 'Victoria', comment: 'Victoria is a great place' @@ -294,9 +304,6 @@ describe('POST /public/comment', () => { expect(comment.tags.length).toEqual(1); expect(comment.tags[0]).toEqual(expect.arrayContaining(['sysadmin'])); - - expect(comment.review.tags.length).toEqual(1); - expect(comment.review.tags[0]).toEqual(expect.arrayContaining(['sysadmin'])); done(); }); }); @@ -393,73 +400,6 @@ describe('PUT /comment/:id', () => { }); }); - describe('review tags', () => { - test('sets sysadmin and public tags when commentStatus is "Accepted" ', done => { - let updateData = { - review: {}, - commentStatus: 'Accepted' - }; - let uri = '/api/comment/' + existingComment._id; - request(app) - .put(uri, updateData) - .send(updateData) - .then(response => { - Comment.findById(existingComment._id).exec(function(error, updatedComment) { - expect(updatedComment).not.toBeNull(); - expect(updatedComment.review).not.toBeNull(); - let reviewTags = updatedComment.review.tags; - expect(reviewTags.length).toEqual(2); - expect(reviewTags[0]).toEqual(expect.arrayContaining(['sysadmin'])); - expect(reviewTags[1]).toEqual(expect.arrayContaining(['public'])); - done(); - }); - }); - }); - - test('sets sysadmin tags when commentStatus is "Pending" ', done => { - let updateData = { - review: {}, - commentStatus: 'Pending' - }; - - let uri = '/api/comment/' + existingComment._id; - request(app) - .put(uri, updateData) - .send(updateData) - .then(response => { - Comment.findById(existingComment._id).exec(function(error, updatedComment) { - expect(updatedComment).not.toBeNull(); - expect(updatedComment.review).not.toBeNull(); - let reviewTags = updatedComment.review.tags; - expect(reviewTags.length).toEqual(1); - expect(reviewTags[0]).toEqual(expect.arrayContaining(['sysadmin'])); - done(); - }); - }); - }); - test('sets sysadmin tags when commentStatus is "Rejected" ', done => { - let updateData = { - review: {}, - commentStatus: 'Rejected' - }; - - let uri = '/api/comment/' + existingComment._id; - request(app) - .put(uri, updateData) - .send(updateData) - .then(response => { - Comment.findById(existingComment._id).exec(function(error, updatedComment) { - expect(updatedComment).not.toBeNull(); - expect(updatedComment.review).not.toBeNull(); - let reviewTags = updatedComment.review.tags; - expect(reviewTags.length).toEqual(1); - expect(reviewTags[0]).toEqual(expect.arrayContaining(['sysadmin'])); - done(); - }); - }); - }); - }); - describe('comment author tags', () => { test('sets sysadmin tags when commentAuthor requestedAnonymous ', done => { let updateData = { diff --git a/api/test/commentperiod.test.js b/api/test/commentperiod.test.js index 04f480d..4360b1d 100644 --- a/api/test/commentperiod.test.js +++ b/api/test/commentperiod.test.js @@ -13,6 +13,10 @@ require('../helpers/models/application'); const Application = mongoose.model('Application'); const CommentPeriod = mongoose.model('CommentPeriod'); +/************************************* + Mock Route Handlers + Helper Methods +*************************************/ + const idirUsername = 'idir/i_am_a_bot'; function paramsWithCommentPerId(req) { @@ -66,16 +70,21 @@ app.put('/api/commentperiod/:id/unpublish', function(req, res) { app.delete('/api/commentperiod/:id', function(req, res) { return commentPeriodController.protectedDelete(paramsWithCommentPerId(req), res); }); + +/************************************* + General Test Data + Helper Methods +*************************************/ + const specialApplication = new Application(); const vanillaApplication = new Application(); const confidentialApplication = new Application(); const deletedApplication = new Application(); const commentPeriodsData = [ - { name: 'Special Comment', _application: specialApplication.id, tags: [['public'], ['sysadmin']], isDeleted: false }, - { name: 'Vanilla Ice Cream', _application: vanillaApplication.id, tags: [['public']], isDeleted: false }, - { name: 'Confidential Comment', _application: confidentialApplication.id, tags: [['sysadmin']], isDeleted: false }, - { name: 'Deleted Comment', _application: deletedApplication.id, tags: [['public'], ['sysadmin']], isDeleted: true } + { name: 'Special Comment', _application: specialApplication._id, tags: [['public'], ['sysadmin']], isDeleted: false }, + { name: 'Vanilla Ice Cream', _application: vanillaApplication._id, tags: [['public']], isDeleted: false }, + { name: 'Confidential Comment', _application: confidentialApplication._id, tags: [['sysadmin']], isDeleted: false }, + { name: 'Deleted Comment', _application: deletedApplication._id, tags: [['public'], ['sysadmin']], isDeleted: true } ]; function setupCommentPeriods(commentPeriodsData) { @@ -91,6 +100,10 @@ function setupCommentPeriods(commentPeriodsData) { }); } +/************************************* + Tests +*************************************/ + describe('GET /commentperiod', () => { test('returns a list of non-deleted, public and sysadmin comment periods', done => { setupCommentPeriods(commentPeriodsData).then(documents => { @@ -102,17 +115,17 @@ describe('GET /commentperiod', () => { let firstCommentPeriod = _.find(response.body, { name: 'Special Comment' }); expect(firstCommentPeriod).toHaveProperty('_id'); - expect(firstCommentPeriod._application).toBe(specialApplication.id); + expect(firstCommentPeriod._application).toBe(specialApplication._id.toString()); expect(firstCommentPeriod['tags']).toEqual(expect.arrayContaining([['public'], ['sysadmin']])); let secondCommentPeriod = _.find(response.body, { name: 'Vanilla Ice Cream' }); expect(secondCommentPeriod).toHaveProperty('_id'); - expect(secondCommentPeriod._application).toBe(vanillaApplication.id); + expect(secondCommentPeriod._application).toBe(vanillaApplication._id.toString()); expect(secondCommentPeriod['tags']).toEqual(expect.arrayContaining([['public']])); let secretCommentPeriod = _.find(response.body, { name: 'Confidential Comment' }); expect(secretCommentPeriod).toHaveProperty('_id'); - expect(secretCommentPeriod._application).toBe(confidentialApplication.id); + expect(secretCommentPeriod._application).toBe(confidentialApplication._id.toString()); expect(secretCommentPeriod['tags']).toEqual(expect.arrayContaining([['sysadmin']])); done(); }); @@ -122,13 +135,13 @@ describe('GET /commentperiod', () => { test('can search based on application', done => { applicationFactory.create('application', { name: 'Detailed application with comment period' }).then(application => { let commentPeriodAttrs = { - _application: application.id, + _application: application._id, name: 'Controversial Comment Period' }; commentPeriodFactory.create('commentperiod', commentPeriodAttrs, { public: false }).then(commentperiod => { request(app) .get('/api/commentperiod') - .query({ _application: application.id }) + .query({ _application: application._id }) .expect(200) .then(response => { expect(response.body.length).toBe(1); @@ -189,12 +202,12 @@ describe('GET /public/commentperiod', () => { let firstCommentPeriod = _.find(response.body, { name: 'Special Comment' }); expect(firstCommentPeriod).toHaveProperty('_id'); - expect(firstCommentPeriod._application).toBe(specialApplication.id); + expect(firstCommentPeriod._application).toBe(specialApplication._id.toString()); expect(firstCommentPeriod['tags']).toEqual(expect.arrayContaining([['public'], ['sysadmin']])); let secondCommentPeriod = _.find(response.body, { name: 'Vanilla Ice Cream' }); expect(secondCommentPeriod).toHaveProperty('_id'); - expect(secondCommentPeriod._application).toBe(vanillaApplication.id); + expect(secondCommentPeriod._application).toBe(vanillaApplication._id.toString()); expect(secondCommentPeriod['tags']).toEqual(expect.arrayContaining([['public']])); done(); }); @@ -243,13 +256,13 @@ describe('GET /public/commentperiod/{id}', () => { test('can search based on application', done => { applicationFactory.create('application', { name: 'Detailed application with comment period' }).then(application => { let commentPeriodAttrs = { - _application: application.id, + _application: application._id, name: 'Controversial Comment Period' }; commentPeriodFactory.create('commentperiod', commentPeriodAttrs, { public: true }).then(commentperiod => { request(app) .get('/api/public/commentperiod') - .query({ _application: application.id }) + .query({ _application: application._id }) .expect(200) .then(response => { expect(response.body.length).toBe(1); @@ -305,7 +318,7 @@ describe('POST /commentperiod', () => { }); }); - test('defaults to sysadmin for tags and review tags', done => { + test('defaults to sysadmin for tags', done => { let commentObj = { name: 'Victoria', description: 'Victoria is a great place' diff --git a/api/test/decision.test.js b/api/test/decision.test.js index 38941e8..a4de084 100644 --- a/api/test/decision.test.js +++ b/api/test/decision.test.js @@ -4,15 +4,17 @@ const decisionFactory = require('./factories/decision_factory').factory; const applicationFactory = require('./factories/application_factory').factory; const mongoose = require('mongoose'); const request = require('supertest'); - const _ = require('lodash'); const decisionController = require('../controllers/decision.js'); require('../helpers/models/decision'); - const Decision = mongoose.model('Decision'); -const fieldNames = ['name', 'description']; +/************************************* + Mock Route Handlers + Helper Methods +*************************************/ + +const fieldNames = ['name']; function paramsWithDecId(req) { let params = test_helper.buildParams({ decisionId: req.params.id }); @@ -60,16 +62,19 @@ app.delete('/api/decision/:id', function(req, res) { return decisionController.protectedDelete(paramsWithDecId(req), res); }); +/************************************* + General Test Data + Helper Methods +*************************************/ + const decisionsData = [ { name: 'Special Decision', - description: 'We have decided to save the environment', tags: [['public'], ['sysadmin']], isDeleted: false }, - { name: 'Vanilla Ice Cream', description: 'Ice cream store will be built', tags: [['public']], isDeleted: false }, - { name: 'Confidential Decision', description: 'No comment', tags: [['sysadmin']], isDeleted: false }, - { name: 'Deleted Decision', description: 'Trolling for suckers', tags: [['public'], ['sysadmin']], isDeleted: true } + { name: 'Vanilla Ice Cream', tags: [['public']], isDeleted: false }, + { name: 'Confidential Decision', tags: [['sysadmin']], isDeleted: false }, + { name: 'Deleted Decision', tags: [['public'], ['sysadmin']], isDeleted: true } ]; function setupDecisions(decisionsData) { @@ -85,9 +90,13 @@ function setupDecisions(decisionsData) { }); } +/************************************* + Tests +*************************************/ + describe('GET /decision', () => { test('returns a list of non-deleted, public and sysadmin decision', done => { - setupDecisions(decisionsData).then(documents => { + setupDecisions(decisionsData).then(decisions => { request(app) .get('/api/decision') .expect(200) @@ -96,17 +105,14 @@ describe('GET /decision', () => { let firstDecision = _.find(response.body, { name: 'Special Decision' }); expect(firstDecision).toHaveProperty('_id'); - expect(firstDecision.description).toBe('We have decided to save the environment'); expect(firstDecision['tags']).toEqual(expect.arrayContaining([['public'], ['sysadmin']])); let secondDecision = _.find(response.body, { name: 'Vanilla Ice Cream' }); expect(secondDecision).toHaveProperty('_id'); - expect(secondDecision.description).toBe('Ice cream store will be built'); expect(secondDecision['tags']).toEqual(expect.arrayContaining([['public']])); let secretDecision = _.find(response.body, { name: 'Confidential Decision' }); expect(secretDecision).toHaveProperty('_id'); - expect(secretDecision.description).toBe('No comment'); expect(secretDecision['tags']).toEqual(expect.arrayContaining([['sysadmin']])); done(); }); @@ -127,20 +133,18 @@ describe('GET /decision', () => { test('can search based on application', done => { applicationFactory.create('application', { name: 'Detailed application with decision' }).then(application => { let decisionAttrs = { - _application: application.id, - description: 'Decision with Attachment', + _application: application._id, name: 'Important Decision' }; decisionFactory.create('decision', decisionAttrs, { public: false }).then(decision => { request(app) .get('/api/decision') - .query({ _application: application.id }) + .query({ _application: application._id }) .expect(200) .then(response => { expect(response.body.length).toBe(1); let resultingDecision = response.body[0]; expect(resultingDecision).not.toBeNull(); - expect(resultingDecision.description).toBe('Decision with Attachment'); done(); }); }); @@ -150,32 +154,34 @@ describe('GET /decision', () => { describe('GET /decision/{id}', () => { test('returns a single Decision ', done => { - setupDecisions(decisionsData).then(documents => { - Decision.findOne({ name: 'Special Decision' }).exec(function(error, decision) { - let decisionId = decision._id.toString(); - let uri = '/api/decision/' + decisionId; - - request(app) - .get(uri) - .expect(200) - .then(response => { - expect(response.body.length).toBe(1); - let responseObject = response.body[0]; - expect(responseObject).toMatchObject({ - _id: decisionId, - tags: expect.arrayContaining([['public'], ['sysadmin']]), - name: 'Special Decision' + setupDecisions(decisionsData).then(decisions => { + Decision.findOne({ name: 'Special Decision' }) + .exec() + .then(decision => { + const decisionId = decision._id.toString(); + let uri = `/api/decision/${decisionId}`; + + request(app) + .get(uri) + .expect(200) + .then(response => { + expect(response.body.length).toBe(1); + let responseObj = response.body[0]; + expect(responseObj).toMatchObject({ + _id: decisionId, + tags: expect.arrayContaining([['public'], ['sysadmin']]), + name: 'Special Decision' + }); + done(); }); - done(); - }); - }); + }); }); }); }); describe('GET /public/decision', () => { test('returns a list of public decisions', done => { - setupDecisions(decisionsData).then(documents => { + setupDecisions(decisionsData).then(decisions => { request(app) .get('/api/public/decision') .expect(200) @@ -184,12 +190,10 @@ describe('GET /public/decision', () => { let firstDecision = _.find(response.body, { name: 'Special Decision' }); expect(firstDecision).toHaveProperty('_id'); - expect(firstDecision.description).toBe('We have decided to save the environment'); expect(firstDecision['tags']).toEqual(expect.arrayContaining([['public'], ['sysadmin']])); let secondDecision = _.find(response.body, { name: 'Vanilla Ice Cream' }); expect(secondDecision).toHaveProperty('_id'); - expect(secondDecision.description).toBe('Ice cream store will be built'); expect(secondDecision['tags']).toEqual(expect.arrayContaining([['public']])); done(); }); @@ -199,20 +203,18 @@ describe('GET /public/decision', () => { test('can search based on application', done => { applicationFactory.create('application', { name: 'Detailed application with decision' }).then(application => { let decisionAttrs = { - _application: application.id, - description: 'Decision with Attachment', + _application: application._id, name: 'Important Decision' }; decisionFactory.create('decision', decisionAttrs, { public: true }).then(decision => { request(app) .get('/api/public/decision') - .query({ _application: application.id }) + .query({ _application: application._id }) .expect(200) .then(response => { expect(response.body.length).toBe(1); let resultingDecision = response.body[0]; expect(resultingDecision).not.toBeNull(); - expect(resultingDecision.description).toBe('Decision with Attachment'); done(); }); }); @@ -233,7 +235,7 @@ describe('GET /public/decision', () => { describe('GET /public/decision/{id}', () => { test('returns a single public decision ', done => { - setupDecisions(decisionsData).then(documents => { + setupDecisions(decisionsData).then(decisions => { Decision.findOne({ name: 'Special Decision' }).exec(function(error, decision) { if (error) { throw error; @@ -262,8 +264,7 @@ describe('GET /public/decision/{id}', () => { describe('POST /decision', () => { test('creates a new decision', done => { let decisionObj = { - name: 'Victoria', - description: 'Victoria is a great place' + name: 'Victoria' }; request(app) @@ -275,16 +276,14 @@ describe('POST /decision', () => { Decision.findById(response.body['_id']).exec(function(error, decision) { expect(decision).not.toBeNull(); expect(decision.name).toBe('Victoria'); - expect(decision.description).toBe('Victoria is a great place'); done(); }); }); }); - test('defaults to sysadmin for tags and review tags', done => { + test('defaults to sysadmin for tags', done => { let decisionObj = { - name: 'Victoria', - description: 'Victoria is a great place' + name: 'Victoria' }; request(app) .post('/api/decision') @@ -308,22 +307,21 @@ describe('PUT /decision/:id', () => { let existingDecision; beforeEach(() => { existingDecision = new Decision({ - name: 'SOME_DECISION', - description: 'The decision has been approved.' + name: 'SOME_DECISION' }); return existingDecision.save(); }); test('updates a decision', done => { let updateData = { - description: 'This decision is pending' + name: 'SOME_NEW_DECISION' }; let uri = '/api/decision/' + existingDecision._id; request(app) .put(uri) .send(updateData) .then(response => { - Decision.findOne({ description: 'The decision has been approved.' }).exec(function(error, decision) { + Decision.findOne({ name: 'SOME_DECISION' }).exec(function(error, decision) { expect(decision).toBeDefined(); expect(decision).not.toBeNull(); done(); @@ -335,7 +333,7 @@ describe('PUT /decision/:id', () => { let uri = '/api/decision/' + 'NON_EXISTENT_ID'; request(app) .put(uri) - .send({ description: 'hacker_man', internal: { tags: [] } }) + .send({ name: 'hacker_man', internal: { tags: [] } }) .expect(404) .then(response => { done(); @@ -371,7 +369,6 @@ describe('PUT /decision/:id/publish', () => { test('publishes a decision', done => { let existingDecision = new Decision({ name: 'EXISTING', - description: 'I love this project', tags: [] }); existingDecision.save().then(decision => { @@ -406,7 +403,6 @@ describe('PUT /decision/:id/unpublish', () => { test('unpublishes a decision', done => { let existingDecision = new Decision({ name: 'EXISTING', - description: 'I love this project', tags: [['public']] }); existingDecision.save().then(decision => { @@ -439,7 +435,7 @@ describe('PUT /decision/:id/unpublish', () => { describe('DELETE /decision/id', () => { test('It soft deletes a decision', done => { - setupDecisions(decisionsData).then(documents => { + setupDecisions(decisionsData).then(decisions => { Decision.findOne({ name: 'Vanilla Ice Cream' }).exec(function(error, decision) { let vanillaDecisionId = decision._id.toString(); let uri = '/api/decision/' + vanillaDecisionId; diff --git a/api/test/document.test.js b/api/test/document.test.js index 44bd424..a1ae97c 100644 --- a/api/test/document.test.js +++ b/api/test/document.test.js @@ -7,14 +7,16 @@ const applicationFactory = require('./factories/application_factory').factory; const decisionFactory = require('./factories/decision_factory').factory; const request = require('supertest'); const shell = require('shelljs'); - const _ = require('lodash'); const documentController = require('../controllers/document.js'); require('../helpers/models/document'); - const Document = mongoose.model('Document'); +/************************************* + Mock Route Handlers + Helper Methods +*************************************/ + const fieldNames = ['displayName', 'documentFileName']; const idirUsername = 'idir/i_am_a_bot'; @@ -87,6 +89,10 @@ app.delete('/api/document/:id', function(req, res) { return documentController.protectedDelete(paramsWithDocId(req), res); }); +/************************************* + General Test Data + Helper Methods +*************************************/ + const documentsData = [ { displayName: 'Special File', @@ -118,6 +124,10 @@ function cleanupTestDocumentFiles() { } } +/************************************* + Tests +*************************************/ + afterAll(() => { cleanupTestDocumentFiles(); }); @@ -431,9 +441,9 @@ describe('POST /document', () => { .send(documentParams) .expect(200) .then(response => { - expect(response.body.id).toBeDefined(); - expect(response.body.id).not.toBeNull(); - Document.findById(response.body.id).exec(function(error, document) { + expect(response.body._id).toBeDefined(); + expect(response.body._id).not.toBeNull(); + Document.findById(response.body._id).exec(function(error, document) { expect(document).not.toBeNull(); expect(document.displayName).toBe('Critically Important File'); done(); @@ -448,8 +458,8 @@ describe('POST /document', () => { .send(documentParams) .expect(200) .then(response => { - expect(response.body.id).not.toBeNull(); - Document.findById(response.body.id).exec(function(error, document) { + expect(response.body._id).not.toBeNull(); + Document.findById(response.body._id).exec(function(error, document) { expect(document).not.toBeNull(); expect(document._application.toString()).toBe(applicationId); expect(document._comment.toString()).toBe(commentId); @@ -466,8 +476,8 @@ describe('POST /document', () => { .send(documentParams) .expect(200) .then(response => { - expect(response.body.id).not.toBeNull(); - Document.findById(response.body.id).exec(function(error, document) { + expect(response.body._id).not.toBeNull(); + Document.findById(response.body._id).exec(function(error, document) { expect(document).not.toBeNull(); expect(document.internalMime).toBe('text/plain'); @@ -487,8 +497,8 @@ describe('POST /document', () => { .send(documentParams) .expect(200) .then(response => { - expect(response.body.id).not.toBeNull(); - Document.findById(response.body.id).exec(function(error, document) { + expect(response.body._id).not.toBeNull(); + Document.findById(response.body._id).exec(function(error, document) { expect(document).not.toBeNull(); expect(document._addedBy).not.toBeNull(); expect(document._addedBy).toEqual(idirUsername); @@ -646,9 +656,9 @@ describe('POST /public/document', () => { .send(documentParams) .expect(200) .then(response => { - expect(response.body.id).toBeDefined(); - expect(response.body.id).not.toBeNull(); - Document.findById(response.body.id).exec(function(error, document) { + expect(response.body._id).toBeDefined(); + expect(response.body._id).not.toBeNull(); + Document.findById(response.body._id).exec(function(error, document) { expect(document).not.toBeNull(); expect(document.displayName).toBe('Critically Important File'); done(); @@ -663,8 +673,8 @@ describe('POST /public/document', () => { .send(documentParams) .expect(200) .then(response => { - expect(response.body.id).not.toBeNull(); - Document.findById(response.body.id).exec(function(error, document) { + expect(response.body._id).not.toBeNull(); + Document.findById(response.body._id).exec(function(error, document) { expect(document).not.toBeNull(); expect(document._application.toString()).toBe(applicationId); expect(document._comment.toString()).toBe(commentId); @@ -681,8 +691,8 @@ describe('POST /public/document', () => { .send(documentParams) .expect(200) .then(response => { - expect(response.body.id).not.toBeNull(); - Document.findById(response.body.id).exec(function(error, document) { + expect(response.body._id).not.toBeNull(); + Document.findById(response.body._id).exec(function(error, document) { expect(document).not.toBeNull(); expect(document.internalMime).toBe('text/plain'); diff --git a/api/test/feature.test.js b/api/test/feature.test.js index 6811dab..e29b60b 100644 --- a/api/test/feature.test.js +++ b/api/test/feature.test.js @@ -11,6 +11,10 @@ require('../helpers/models/feature'); require('../helpers/models/application'); const Feature = mongoose.model('Feature'); +/************************************* + Mock Route Handlers + Helper Methods +*************************************/ + const fieldNames = ['tags', 'properties', 'applicationID']; function paramsWithFeatureId(req) { @@ -87,6 +91,10 @@ app.put('/api/feature/:id/unpublish', function(req, res) { return featureController.protectedUnPublish(paramsWithFeatureId(req), res); }); +/************************************* + General Test Data + Helper Methods +*************************************/ + const applicationsData = [ { name: 'Special Application', tags: [['public'], ['sysadmin']], isDeleted: false }, { name: 'Vanilla Ice Cream', tags: [['public']], isDeleted: false }, @@ -168,6 +176,10 @@ function buildFeaturesData() { ]; } +/************************************* + Tests +*************************************/ + beforeEach(done => { setupApplications(applicationsData).then(applicationsArray => { specialApplicationId = applicationsArray[0]._id; diff --git a/api/test/search.test.js b/api/test/search.test.js index e90ef30..3d6e7bd 100644 --- a/api/test/search.test.js +++ b/api/test/search.test.js @@ -1,37 +1,38 @@ const test_helper = require('./test_helper'); const app = test_helper.app; -const mongoose = require('mongoose'); const request = require('supertest'); const fieldNames = []; const TTLSUtils = require('../helpers/ttlsUtils'); -function publicParamsWithDtId(req) { - let params = test_helper.buildParams({ dtId: req.params.id }); - return test_helper.createPublicSwaggerParams(fieldNames, params); -} - const searchController = require('../controllers/search.js'); require('../helpers/models/application'); require('../helpers/models/feature'); -const Feature = mongoose.model('Feature'); -app.get('/api/search/ttlsapi/crownLandFileNumber/:id', function(req, res) { +/************************************* + Mock Route Handlers + Helper Methods +*************************************/ + +app.get('/api/ttlsapi/crownLandFileNumber/:id', function(req, res) { let extraFields = test_helper.buildParams({ fileNumber: req.params.id }); let params = test_helper.createSwaggerParams(fieldNames, extraFields); return searchController.protectedTTLSGetApplicationsByFileNumber(params, res); }); -app.get('/api/search/ttlsapi/dispositionTransactionId/:id', function(req, res) { +app.get('/api/ttlsapi/dispositionTransactionId/:id', function(req, res) { let extraFields = test_helper.buildParams({ dtId: req.params.id }); let params = test_helper.createSwaggerParams(fieldNames, extraFields); return searchController.protectedTTLSGetApplicationByDisp(params, res); }); -app.get('/api/public/search/dispositionTransactionId/:id', function(req, res) { - return searchController.publicGetDispositionTransactionId(publicParamsWithDtId(req), res); -}); +/************************************* + General Test Data + Helper Methods +*************************************/ + +/************************************* + Tests +*************************************/ -describe('GET /api/search/ttlsapi/crownLandFileNumber/', () => { +describe('GET /api/ttlsapi/crownLandFileNumber/', () => { let clFileNumber = 555555; const firstResult = { DISPOSITION_TRANSACTION_SID: 111111 }; const secondResult = { DISPOSITION_TRANSACTION_SID: 222222 }; @@ -60,7 +61,7 @@ describe('GET /api/search/ttlsapi/crownLandFileNumber/', () => { test('logs in and then searches TTLS by CLFileNumber with that access token', done => { request(app) - .get('/api/search/ttlsapi/crownLandFileNumber/' + clFileNumber) + .get('/api/ttlsapi/crownLandFileNumber/' + clFileNumber) .expect(200) .then(response => { expect(TTLSUtils.loginWebADE).toHaveBeenCalled(); @@ -71,7 +72,7 @@ describe('GET /api/search/ttlsapi/crownLandFileNumber/', () => { test('searches TTLS getApplicationByDispositionID once for each disp returned by the file number search', done => { request(app) - .get('/api/search/ttlsapi/crownLandFileNumber/' + clFileNumber) + .get('/api/ttlsapi/crownLandFileNumber/' + clFileNumber) .expect(200) .then(response => { expect(TTLSUtils.getApplicationByFilenumber).toHaveBeenCalledWith('ACCESS_TOKEN', '555555'); @@ -85,7 +86,7 @@ describe('GET /api/search/ttlsapi/crownLandFileNumber/', () => { test('returns the search results from each getAppliationByDispositionID call', done => { request(app) - .get('/api/search/ttlsapi/crownLandFileNumber/' + clFileNumber) + .get('/api/ttlsapi/crownLandFileNumber/' + clFileNumber) .expect(200) .then(response => { expect(response.body.length).toEqual(2); @@ -98,7 +99,7 @@ describe('GET /api/search/ttlsapi/crownLandFileNumber/', () => { describe('when the ttls api login call fails', () => { let loginPromise = new Promise(function(resolve, reject) { - reject({ statusCode: 503, message: 'Ooh boy something went wrong' }); + reject({ code: 503, message: 'Ooh boy something went wrong' }); }); beforeEach(() => { @@ -107,7 +108,7 @@ describe('GET /api/search/ttlsapi/crownLandFileNumber/', () => { test('returns that error response', done => { request(app) - .get('/api/search/ttlsapi/crownLandFileNumber/' + clFileNumber) + .get('/api/ttlsapi/crownLandFileNumber/' + clFileNumber) .expect(503) .then(response => { expect(response.body.message).toEqual('Ooh boy something went wrong'); @@ -117,7 +118,7 @@ describe('GET /api/search/ttlsapi/crownLandFileNumber/', () => { }); }); -describe('GET /api/search/ttlsapi/dispositionTransactionId/', () => { +describe('GET /api/ttlsapi/dispositionTransactionId/', () => { let dispositionId = 666666; const searchResult = { DISPOSITION_TRANSACTION_SID: 666666 @@ -140,7 +141,7 @@ describe('GET /api/search/ttlsapi/dispositionTransactionId/', () => { test('logs in and then retrieves the application with that access token', done => { request(app) - .get('/api/search/ttlsapi/dispositionTransactionId/' + dispositionId) + .get('/api/ttlsapi/dispositionTransactionId/' + dispositionId) .expect(200) .then(response => { expect(TTLSUtils.loginWebADE).toHaveBeenCalled(); @@ -152,7 +153,7 @@ describe('GET /api/search/ttlsapi/dispositionTransactionId/', () => { describe('when the ttls api login call fails', () => { let loginPromise = new Promise(function(resolve, reject) { - reject({ statusCode: 503, message: 'Ooh boy something went wrong' }); + reject({ code: 503, message: 'Ooh boy something went wrong' }); }); beforeEach(() => { @@ -161,7 +162,7 @@ describe('GET /api/search/ttlsapi/dispositionTransactionId/', () => { test('returns that error response', done => { request(app) - .get('/api/search/ttlsapi/dispositionTransactionId/' + dispositionId) + .get('/api/ttlsapi/dispositionTransactionId/' + dispositionId) .expect(503) .then(response => { expect(response.body.message).toEqual('Ooh boy something went wrong'); @@ -170,29 +171,3 @@ describe('GET /api/search/ttlsapi/dispositionTransactionId/', () => { }); }); }); - -describe('GET /api/public/search/dispositionTransactionId', () => { - let dispositionId = 666666; - - test('finds the matching feature in the database', done => { - let existingFeature = new Feature({ - properties: { - DISPOSITION_TRANSACTION_SID: dispositionId - } - }); - existingFeature.save().then(() => { - request(app) - .get('/api/public/search/dispositionTransactionId/' + dispositionId) - .expect(200) - .then(response => { - expect(response.body).toBeDefined(); - expect(response.body).not.toBeNull(); - expect(response.body).toHaveProperty('crs'); - expect(response.body).toHaveProperty('features'); - expect(response.body.features.length).toBe(1); - expect(response.body.features[0]._id).toBe(existingFeature._id.toString()); - done(); - }); - }); - }); -}); diff --git a/package.json b/package.json index 126ed7d..9e365a8 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,6 @@ { "name": "nrts-prc-api", "version": "1.0.7", - "author": "Mark Lisé", - "contributors": [ - "Mark Lisé " - ], - "description": "ACRFD API", - "keywords": [], "license": "Apache-2.0", "main": "app", "scripts": { @@ -19,6 +13,10 @@ "lint-fix:2": "prettier ./**/*.js --write --loglevel warn", "lint-fix": "npm-run-all -l -s -c lint-fix:*" }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 6.0.0" + }, "dependencies": { "@turf/helpers": "6.1.4", "@turf/turf": "5.1.6", @@ -66,17 +64,5 @@ "prettier": "1.18.2", "shelljs": "0.8.3", "supertest": "4.0.2" - }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, - "lint-staged": { - "*.js": [ - "eslint --fix", - "prettier --write --loglevel warn", - "git add" - ] } } diff --git a/seed/package.json b/seed/package.json index 5b4611c..19431e9 100644 --- a/seed/package.json +++ b/seed/package.json @@ -1,14 +1,11 @@ { "name": "nrts-prc-api-seed", "version": "0.0.1", - "author": "Severin Beauvais", - "contributors": [ - "Severin Beauvais " - ], - "description": "NRTS PRC API SEED", - "keywords": [], - "license": "UNLICENSED", + "license": "Apache-2.0", "main": "seed", + "scripts": { + "start": "node seed admin admin http localhost 3000" + }, "dependencies": { "@turf/helpers": "6.1.4", "body-parser": "1.19.0", @@ -19,7 +16,4 @@ "request": "2.88.0", "winston": "2.4.4" }, - "scripts": { - "start": "node seed admin admin http localhost 3000" - } }