From f13bdcce67cccfd426ae8861381a97e30464154c Mon Sep 17 00:00:00 2001 From: imcaizheng Date: Wed, 10 Mar 2021 07:50:43 +0800 Subject: [PATCH] add unit test file --- src/routes/billingAccounts/get.js | 5 + src/routes/billingAccounts/get.spec.js | 170 +++++++++++++++++++++++++ src/tests/util.js | 1 + 3 files changed, 176 insertions(+) create mode 100644 src/routes/billingAccounts/get.spec.js diff --git a/src/routes/billingAccounts/get.js b/src/routes/billingAccounts/get.js index 6697a1d3..30fca7cf 100644 --- a/src/routes/billingAccounts/get.js +++ b/src/routes/billingAccounts/get.js @@ -29,6 +29,11 @@ module.exports = [ attributes: ['id', 'billingAccountId'], raw: true, }); + if (!project) { + const err = new Error(`Project with id "${projectId}" not found`); + err.status = 404; + throw err; + } const billingAccountId = project.billingAccountId; if (!billingAccountId) { const err = new Error('Billing Account not found'); diff --git a/src/routes/billingAccounts/get.spec.js b/src/routes/billingAccounts/get.spec.js new file mode 100644 index 00000000..2171ee0d --- /dev/null +++ b/src/routes/billingAccounts/get.spec.js @@ -0,0 +1,170 @@ +/* eslint-disable no-unused-expressions */ +import chai from 'chai'; +import request from 'supertest'; +import sinon from 'sinon'; + +import models from '../../models'; +import server from '../../app'; +import testUtil from '../../tests/util'; +import SalesforceService from '../../services/salesforceService'; + +chai.should(); + +// demo data which might be returned by the `SalesforceService.query` +const billingAccountData = { + sfBillingAccountId: '123', + tcBillingAccountId: 123123, + name: 'Billing Account 1', + startDate: '2021-02-10T18:51:27Z', + endDate: '2021-03-10T18:51:27Z', +}; + +describe('Project Billing Accounts list', () => { + let project1; + let project2; + let salesforceAuthenticate; + let salesforceQuery; + + beforeEach((done) => { + testUtil.clearDb() + .then(() => testUtil.clearES()) + .then(() => models.Project.create({ + type: 'generic', + directProjectId: 1, + billingAccountId: 1, + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project1 = p; + // create members + return models.ProjectMember.create({ + userId: testUtil.userIds.copilot, + projectId: project1.id, + role: 'copilot', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }).then(() => models.ProjectMember.create({ + userId: testUtil.userIds.member, + projectId: project1.id, + role: 'customer', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + })); + })).then(() => models.Project.create({ + type: 'generic', + directProjectId: 1, + billingAccountId: null, // do not define billingAccountId + name: 'test1', + description: 'test project1', + status: 'draft', + details: {}, + createdBy: 1, + updatedBy: 1, + lastActivityAt: 1, + lastActivityUserId: '1', + }).then((p) => { + project2 = p; + // create members + return models.ProjectMember.create({ + userId: testUtil.userIds.copilot, + projectId: project2.id, + role: 'copilot', + isPrimary: true, + createdBy: 1, + updatedBy: 1, + }).then(() => models.ProjectMember.create({ + userId: testUtil.userIds.member, + projectId: project2.id, + role: 'customer', + isPrimary: false, + createdBy: 1, + updatedBy: 1, + })); + })) + .then(() => { + salesforceAuthenticate = sinon.stub(SalesforceService, 'authenticate', () => Promise.resolve({ + accessToken: 'mock', + instanceUrl: 'mock_url', + })); + // eslint-disable-next-line + salesforceQuery = sinon.stub(SalesforceService, 'queryBillingAccount', () => Promise.resolve(billingAccountData)); + done(); + }); + }); + + afterEach((done) => { + salesforceAuthenticate.restore(); + salesforceQuery.restore(); + done(); + }); + + after((done) => { + testUtil.clearDb(done); + }); + + describe('Get /projects/{id}/billingAccounts', () => { + it('should return 403 for anonymous user', (done) => { + request(server) + .get(`/v5/projects/${project1.id}/billingAccount`) + .expect(403, done); + }); + + it('should return 403 for admin', (done) => { + request(server) + .get(`/v5/projects/${project1.id}/billingAccount`) + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .send() + .expect(403, done); + }); + + it('should return 404 if the project is not found', (done) => { + request(server) + .get('/v5/projects/11223344/billingAccount') + .set({ + Authorization: `Bearer ${testUtil.m2m['read:project-billing-account-details']}`, + }) + .send() + .expect(404, done); + }); + + it('should return 404 if billing account is not defined in the project', (done) => { + request(server) + .get(`/v5/projects/${project2.id}/billingAccount`) + .set({ + Authorization: `Bearer ${testUtil.m2m['read:project-billing-account-details']}`, + }) + .send() + .expect(404, done); + }); + + it('should return billing account details using M2M token with "read:project-billing-account-details" scope', + (done) => { + request(server) + .get(`/v5/projects/${project1.id}/billingAccount`) + .set({ + Authorization: `Bearer ${testUtil.m2m['read:project-billing-account-details']}`, + }) + .send() + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + resJson.should.deep.equal(billingAccountData); + done(); + } + }); + }); + }); +}); diff --git a/src/tests/util.js b/src/tests/util.js index 85a4bc64..449f1c86 100644 --- a/src/tests/util.js +++ b/src/tests/util.js @@ -41,6 +41,7 @@ export default { [M2M_SCOPES.CONNECT_PROJECT_ADMIN]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoiYWxsOmNvbm5lY3RfcHJvamVjdCIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.q34b2IC1pw3ksl5RtnSEW5_HGwN0asx2MD3LV9-Wffg', // TODO update token to have correct scope, as of now it is copied from CONNECT_PROJECT_ADMIN [M2M_SCOPES.PROJECTS.READ_USER_BILLING_ACCOUNTS]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoiYWxsOmNvbm5lY3RfcHJvamVjdCIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.q34b2IC1pw3ksl5RtnSEW5_HGwN0asx2MD3LV9-Wffg', + [M2M_SCOPES.PROJECTS.READ_PROJECT_BILLING_ACCOUNT_DETAILS]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoicmVhZDpwcm9qZWN0LWJpbGxpbmctYWNjb3VudC1kZXRhaWxzIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.1uuT0zg9vSjyOaEZzpeWtjAiRiCJDXvzUEZ4B2cIj1Q', [M2M_SCOPES.PROJECTS.ALL]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoiYWxsOnByb2plY3RzIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.ixFXMCsBmIN9mQ9Z3s-Apkg20A3d86Pm9RouL7bZMV4', [M2M_SCOPES.PROJECTS.READ]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoicmVhZDpwcm9qZWN0cyIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.IpYgfbem-eR6tGjBoxQBPDw6YIulBTZLBn48NuyJT_g', [M2M_SCOPES.PROJECTS.WRITE]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoidGVzdEBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tMm0udG9wY29kZXItZGV2LmNvbS8iLCJpYXQiOjE1ODc3MzI0NTksImV4cCI6MjU4NzgxODg1OSwiYXpwIjoidGVzdCIsInNjb3BlIjoid3JpdGU6cHJvamVjdHMiLCJndHkiOiJjbGllbnQtY3JlZGVudGlhbHMifQ.cAMbmnSKXB8Xl4s4Nlo1LduPySBcvKz2Ygilq5b0OD0',