diff --git a/config/test.js b/config/test.js new file mode 100644 index 0000000..c462fb0 --- /dev/null +++ b/config/test.js @@ -0,0 +1,10 @@ +/** + * The default configuration file. + */ + +module.exports = { + zapier: { + ZAPIER_SWITCH: process.env.ZAPIER_SWITCH || 'ON', + ZAPIER_JOB_CANDIDATE_SWITCH: process.env.ZAPIER_JOB_CANDIDATE_SWITCH || 'ON' + } +} diff --git a/package-lock.json b/package-lock.json index 91412d7..6a4fb26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -216,6 +216,41 @@ } } }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.0.4.tgz", + "integrity": "sha512-fW3SzjLF0sjI0x1Opc7cUG4J/Nr4U0TXPNnKNAgrxA4xXsQNk6nypZK0yJg5FNw5cCo2yC/ZMdaVhDTKeeF6zg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@sinonjs/samsam": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.1.tgz", + "integrity": "sha512-zJ+xzDBMETj/kFkagaZBG4G8e80Et182r6xpCzpubS7cavdTLwBKtCU3sgmPvZDC0u41gd87atcoUxcmiamBgw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -962,6 +997,58 @@ "safe-buffer": "^5.0.1" } }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -2635,6 +2722,12 @@ "object.assign": "^4.1.0" } }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, "jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -2759,6 +2852,12 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -3246,6 +3345,45 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.0.1.tgz", + "integrity": "sha512-U6qdfulSDpEgx3WSoeMlKZ6hGaTMKtyW7CY3bjj0MK3uzHvmugyteB2zyHQRRvi5I91oErSUR595s7htS6IYRQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + }, + "dependencies": { + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + } + } + }, "no-kafka": { "version": "3.4.3", "resolved": "https://registry.npm.taobao.org/no-kafka/download/no-kafka-3.4.3.tgz", @@ -4270,6 +4408,43 @@ "is-arrayish": "^0.3.1" } }, + "sinon": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-10.0.1.tgz", + "integrity": "sha512-1rf86mvW4Mt7JitEIgmNaLXaWnrWd/UrVKZZlL+kbeOujXVf9fmC4kQEQ/YeHoiIA23PLNngYWK+dngIx/AumA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^7.0.4", + "@sinonjs/samsam": "^6.0.1", + "diff": "^4.0.2", + "nise": "^5.0.1", + "supports-color": "^7.1.0" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", @@ -4948,6 +5123,12 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", diff --git a/package.json b/package.json index 7798acd..73f7cc9 100644 --- a/package.json +++ b/package.json @@ -10,19 +10,21 @@ "create-index": "node src/scripts/createIndex.js", "delete-index": "node src/scripts/deleteIndex.js", "view-data": "node src/scripts/view-data.js", - "test": "mocha test/unit/test.js --require test/unit/prepare.js --timeout 20000 --exit", - "test:cov": "nyc --reporter=html --reporter=text mocha test/unit/test.js --require test/unit/prepare.js --timeout 20000 --exit", - "e2e": "mocha test/e2e/test.js --timeout 20000 --exit", - "e2e:cov": "nyc --reporter=html --reporter=text mocha test/e2e/test.js --timeout 20000 --exit" + "test": "cross-env NODE_ENV=test mocha test/unit/test.js --require test/unit/prepare.js --timeout 20000 --exit", + "test:cov": "cross-env NODE_ENV=test nyc --reporter=html --reporter=text mocha test/unit/test.js --require test/unit/prepare.js --timeout 20000 --exit", + "e2e": "cross-env NODE_ENV=test mocha test/e2e/test.js --timeout 20000 --exit", + "e2e:cov": "cross-env NODE_ENV=test nyc --reporter=html --reporter=text mocha test/e2e/test.js --timeout 20000 --exit" }, "author": "TCSCODER", "license": "none", "devDependencies": { + "cross-env": "^7.0.3", "mocha": "^7.1.2", "mocha-prepare": "^0.1.0", "nock": "^12.0.3", "nyc": "^14.1.1", "should": "^13.2.3", + "sinon": "^10.0.1", "standard": "^12.0.1", "stringcase": "^4.3.1", "superagent": "^5.1.0" diff --git a/src/common/helper.js b/src/common/helper.js index d06b1a1..281b335 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -109,6 +109,22 @@ function getESClient () { } } + // get document or catch not found error + esClient.getExtra = async function (data) { + let doc + + try { + doc = await esClient.getSource(data) + } catch (err) { + if (err.statusCode === 404) { + throw new Error(`id: ${data.id} "${data.index}" not found`) + } + throw err + } + + return doc + } + // delete document or catch not found error esClient.deleteExtra = async function (data) { try { diff --git a/src/services/JobCandidateProcessorService.js b/src/services/JobCandidateProcessorService.js index 3d0243c..3eeaaaa 100644 --- a/src/services/JobCandidateProcessorService.js +++ b/src/services/JobCandidateProcessorService.js @@ -20,9 +20,9 @@ const localLogger = { * @param {Object} message the message object * @returns {undefined} */ -async function updateCandidateStatus ({ type, payload }) { - if (!payload.status) { - localLogger.debug({ context: 'updateCandidateStatus', message: 'status not updated' }) +async function updateCandidateStatus ({ type, payload, previousData }) { + if (previousData.status === payload.status) { + localLogger.debug({ context: 'updateCandidateStatus', message: `jobCandidate is already in status: ${payload.status}` }) return } if (!['rejected', 'shortlist'].includes(payload.status)) { @@ -56,13 +56,13 @@ async function updateCandidateStatus ({ type, payload }) { * @param {Object} message the message object * @returns {undefined} */ -async function postMessageToZapier ({ type, payload }) { +async function postMessageToZapier ({ type, payload, previousData }) { if (config.zapier.ZAPIER_JOB_CANDIDATE_SWITCH === constants.Zapier.Switch.OFF) { localLogger.debug({ context: 'postMessageToZapier', message: 'Zapier Switch off via config, no messages sent' }) return } if (type === constants.Zapier.MessageType.JobCandidateUpdate) { - await updateCandidateStatus({ type, payload }) + await updateCandidateStatus({ type, payload, previousData }) return } throw new Error(`unrecognized message type: ${type}`) @@ -113,6 +113,12 @@ processCreate.schema = { */ async function processUpdate (message, transactionId) { const data = message.payload + // save previous data for Zapier logic + // NOTE: ideally if we update Kafka event message to have both: pervious and updated value so we don't have to request it again + const { body: previousData } = await esClient.getExtra({ + index: config.get('esConfig.ES_INDEX_JOB_CANDIDATE'), + id: data.id + }) await esClient.updateExtra({ index: config.get('esConfig.ES_INDEX_JOB_CANDIDATE'), id: data.id, @@ -124,7 +130,8 @@ async function processUpdate (message, transactionId) { }) await postMessageToZapier({ type: constants.Zapier.MessageType.JobCandidateUpdate, - payload: data + payload: data, + previousData }) } diff --git a/test/e2e/test.js b/test/e2e/test.js index 0c32a5c..1e09ccc 100644 --- a/test/e2e/test.js +++ b/test/e2e/test.js @@ -3,7 +3,6 @@ */ const config = require('config') -const _ = require('lodash') const stringcase = require('stringcase') const app = require('../../src/app') const request = require('superagent') @@ -89,14 +88,14 @@ describe('Taas ES Processor E2E Test', () => { index, id: testData.messages[model].create.message.payload.id }) - should.deepEqual(doc.body._source, _.omit(testData.messages[model].create.message.payload, ['id'])) + should.deepEqual(doc.body._source, testData.messages[model].create.message.payload, ['id']) }) it(`Should handle ${modelInSpaceCase} updating message`, async () => { await testHelper.esClient.create({ index, id: testData.messages[model].create.message.payload.id, - body: _.omit(testData.messages[model].create.message.payload, ['id']), + body: testData.messages[model].create.message.payload, refresh: 'true' }) await testHelper.sendMessage(testData.messages[model].update.message) @@ -105,14 +104,14 @@ describe('Taas ES Processor E2E Test', () => { index, id: testData.messages[model].update.message.payload.id }) - should.deepEqual(_.omit(doc.body._source, ['createdAt', 'createdBy']), _.omit(testData.messages[model].update.message.payload, ['id'])) + should.deepEqual(doc.body._source, testData.messages[model].update.message.payload) }) it(`Should handle ${modelInSpaceCase} deletion message`, async () => { await testHelper.esClient.create({ index, id: testData.messages[model].create.message.payload.id, - body: _.omit(testData.messages[model].create.message.payload, ['id']), + body: testData.messages[model].create.message.payload, refresh: 'true' }) await testHelper.sendMessage(testData.messages[model].delete.message) @@ -133,7 +132,7 @@ describe('Taas ES Processor E2E Test', () => { await testHelper.esClient.create({ index, id: testData.messages[model].create.message.payload.id, - body: _.omit(testData.messages[model].create.message.payload, ['id']), + body: testData.messages[model].create.message.payload, refresh: 'true' }) await testHelper.sendMessage(testData.messages[model].create.message) diff --git a/test/messages/taas.job.create.event.json b/test/messages/taas.job.create.event.json index b6cc0a5..e2b12bb 100644 --- a/test/messages/taas.job.create.event.json +++ b/test/messages/taas.job.create.event.json @@ -1 +1 @@ -{"topic":"taas.job.create","originator":"taas-api","timestamp":"2020-11-05T19:00:17.563Z","mime-type":"application/json","payload":{"projectId":21,"externalId":"1212","description":"Dummy Description","startDate":"2020-09-27T04:17:23.131Z","endDate":"2020-09-27T04:17:23.131Z","numPositions":13,"resourceType":"Dummy Resource Type","rateType":"hourly","skills":["56fdc405-eccc-4189-9e83-c78abf844f50","f91ae184-aba2-4485-a8cb-9336988c05ab","edfc7b4f-636f-44bd-96fc-949ffc58e38b","4ca63bb6-f515-4ab0-a6bc-c2d8531e084f","ee03c041-d53b-4c08-b7d9-80d7461da3e4"],"id":"ffbc24f7-301e-48d3-bf01-c056916056a2","createdAt":"2020-11-05T19:00:16.268Z","createdBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a","status":"sourcing"}} \ No newline at end of file +{"topic":"taas.job.create","originator":"taas-api","timestamp":"2020-11-05T19:00:17.563Z","mime-type":"application/json","payload":{"title":"Job Title","projectId":21,"externalId":"1212","description":"Dummy Description","startDate":"2020-09-27T04:17:23.131Z","duration":17,"numPositions":13,"resourceType":"Dummy Resource Type","rateType":"hourly","skills":["56fdc405-eccc-4189-9e83-c78abf844f50","f91ae184-aba2-4485-a8cb-9336988c05ab","edfc7b4f-636f-44bd-96fc-949ffc58e38b","4ca63bb6-f515-4ab0-a6bc-c2d8531e084f","ee03c041-d53b-4c08-b7d9-80d7461da3e4"],"id":"ffbc24f7-301e-48d3-bf01-c056916056a2","createdAt":"2020-11-05T19:00:16.268Z","createdBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a","status":"sourcing","isApplicationPageActive":false}} \ No newline at end of file diff --git a/test/messages/taas.job.update.event.json b/test/messages/taas.job.update.event.json index f5bf395..fced890 100644 --- a/test/messages/taas.job.update.event.json +++ b/test/messages/taas.job.update.event.json @@ -1 +1 @@ -{"topic":"taas.job.update","originator":"taas-api","timestamp":"2020-11-05T19:00:19.015Z","mime-type":"application/json","payload":{"id":"ffbc24f7-301e-48d3-bf01-c056916056a2","projectId":21,"externalId":"1212","description":"Dummy Description","startDate":"2020-09-27T04:17:23.131Z","endDate":"2020-09-27T04:17:23.131Z","numPositions":13,"resourceType":"Dummy Resource Type","rateType":"hourly","skills":["3fa85f64-5717-4562-b3fc-2c963f66afa6","cc41ddc4-cacc-4570-9bdb-1229c12b9784"],"status":"sourcing","updatedAt":"2020-11-05T19:00:17.612Z","updatedBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a"}} \ No newline at end of file +{"topic":"taas.job.update","originator":"taas-api","timestamp":"2020-11-05T19:00:19.015Z","mime-type":"application/json","payload":{"id":"ffbc24f7-301e-48d3-bf01-c056916056a2","title":"Job Title Updated","projectId":21,"externalId":"1212","description":"Dummy Description","startDate":"2020-09-27T04:17:23.131Z","duration":19,"numPositions":13,"resourceType":"Dummy Resource Type","rateType":"hourly","skills":["3fa85f64-5717-4562-b3fc-2c963f66afa6","cc41ddc4-cacc-4570-9bdb-1229c12b9784"],"status":"sourcing","updatedAt":"2020-11-05T19:00:17.612Z","updatedBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a","createdAt":"2020-11-05T19:00:16.268Z","createdBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a","isApplicationPageActive":false}} \ No newline at end of file diff --git a/test/messages/taas.jobcandidate.update.event.json b/test/messages/taas.jobcandidate.update.event.json index 32d4b7f..79530f9 100644 --- a/test/messages/taas.jobcandidate.update.event.json +++ b/test/messages/taas.jobcandidate.update.event.json @@ -1 +1 @@ -{"topic":"taas.jobcandidate.update","originator":"taas-api","timestamp":"2020-11-05T19:00:23.003Z","mime-type":"application/json","payload":{"id":"0cb99adb-8bcd-4952-9203-9867dd45ef6f","jobId":"ffbc24f7-301e-48d3-bf01-c056916056a2","userId":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a","status":"selected","updatedAt":"2020-11-05T19:00:21.625Z","updatedBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a"}} \ No newline at end of file +{"topic":"taas.jobcandidate.update","originator":"taas-api","timestamp":"2020-11-05T19:00:23.003Z","mime-type":"application/json","payload":{"id":"0cb99adb-8bcd-4952-9203-9867dd45ef6f","jobId":"ffbc24f7-301e-48d3-bf01-c056916056a2","userId":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a","status":"selected","updatedAt":"2020-11-05T19:00:21.625Z","updatedBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a","createdAt":"2020-11-05T19:00:16.268Z","createdBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a"}} \ No newline at end of file diff --git a/test/messages/taas.resourcebooking.update.event.json b/test/messages/taas.resourcebooking.update.event.json index de8080e..2c07a4e 100644 --- a/test/messages/taas.resourcebooking.update.event.json +++ b/test/messages/taas.resourcebooking.update.event.json @@ -1 +1 @@ -{"topic":"taas.resourcebooking.update","originator":"taas-api","timestamp":"2020-11-05T19:00:26.407Z","mime-type":"application/json","payload":{"id":"60d97713-8621-476e-b006-7cb9589c7777","projectId":21,"userId":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a","jobId":"ffbc24f7-301e-48d3-bf01-c056916056a2","startDate":"2020-09-27T04:17:23.131Z","endDate":"2020-09-27T04:17:23.131Z","memberRate":13.23,"customerRate":13,"rateType":"hourly","status":"assigned","updatedAt":"2020-11-05T19:00:25.062Z","updatedBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a"}} \ No newline at end of file +{"topic":"taas.resourcebooking.update","originator":"taas-api","timestamp":"2020-11-05T19:00:26.407Z","mime-type":"application/json","payload":{"id":"60d97713-8621-476e-b006-7cb9589c7777","projectId":21,"userId":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a","jobId":"ffbc24f7-301e-48d3-bf01-c056916056a2","startDate":"2020-09-27T04:17:23.131Z","endDate":"2020-09-27T04:17:23.131Z","memberRate":13.23,"customerRate":13,"rateType":"hourly","status":"assigned","updatedAt":"2020-11-05T19:00:25.062Z","updatedBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a","createdAt":"2020-11-05T19:00:16.268Z","createdBy":"a55fe1bc-1754-45fa-9adc-cf3d6d7c377a"}} \ No newline at end of file diff --git a/test/unit/test.js b/test/unit/test.js index 05426bd..b7d9db7 100644 --- a/test/unit/test.js +++ b/test/unit/test.js @@ -8,24 +8,45 @@ const config = require('config') const stringcase = require('stringcase') const testData = require('../common/testData') const testHelper = require('../common/testHelper') +const sinon = require('sinon') +const logger = require('../../src/common/logger') +const helper = require('../../src/common/helper') +const constants = require('../../src/common/constants') const services = { JobProcessorService: require('../../src/services/JobProcessorService'), JobCandidateProcessorService: require('../../src/services/JobCandidateProcessorService'), ResourceBookingProcessorService: require('../../src/services/ResourceBookingProcessorService') } -describe('UBahn - Elasticsearch Data Processor Unit Test', () => { - // random transaction id here - const transactionId = '2023692c-a9d3-4250-86c4-b83f381a5d03' +// random transaction id here +const transactionId = '2023692c-a9d3-4250-86c4-b83f381a5d03' - after(() => { +describe('General Logic Tests', () => { + let sandbox + + before(() => { + // mock helper methods + sandbox = sinon.createSandbox() + sandbox.stub(helper, 'postMessageViaWebhook').callsFake((webhook, message) => { + logger.debug({ component: 'helper', context: 'postMessageToZapier (stub)', message: `message: ${JSON.stringify({ webhook, message })}` }) + }) + sandbox.stub(helper, 'getM2MToken').callsFake(() => { + const token = 'dummy-token' + logger.debug({ component: 'helper', context: 'getM2MToken (stub)', message: token }) + return token + }) + }) + + beforeEach(() => { // clear es storage testData.esStorage.content = {} }) - beforeEach(() => { + after(() => { // clear es storage testData.esStorage.content = {} + + sandbox.restore() }) for (const [index, model] of [ @@ -38,7 +59,7 @@ describe('UBahn - Elasticsearch Data Processor Unit Test', () => { await services[`${model}ProcessorService`].processCreate(testData.messages[model].create.message, transactionId) should.deepEqual( testData.esStorage.content[testData.messages[model].create.message.payload.id], - _.omit(testData.messages[model].create.message.payload, ['id']) + testData.messages[model].create.message.payload ) }) @@ -46,13 +67,13 @@ describe('UBahn - Elasticsearch Data Processor Unit Test', () => { await testHelper.esClient.create({ index, id: testData.messages[model].create.message.payload.id, - body: _.omit(testData.messages[model].create.message.payload, ['id']), + body: testData.messages[model].create.message.payload, refresh: 'true' }) await services[`${model}ProcessorService`].processUpdate(testData.messages[model].update.message, transactionId) should.deepEqual( - _.omit(testData.esStorage.content[testData.messages[model].create.message.payload.id], ['createdAt', 'createdBy']), - _.omit(testData.messages[model].update.message.payload, ['id']) + testData.esStorage.content[testData.messages[model].create.message.payload.id], + testData.messages[model].update.message.payload ) }) @@ -60,7 +81,7 @@ describe('UBahn - Elasticsearch Data Processor Unit Test', () => { await testHelper.esClient.create({ index, id: testData.messages[model].create.message.payload.id, - body: _.omit(testData.messages[model].create.message.payload, ['id']), + body: testData.messages[model].create.message.payload, refresh: 'true' }) await services[`${model}ProcessorService`].processDelete(testData.messages[model].delete.message, transactionId) @@ -71,7 +92,7 @@ describe('UBahn - Elasticsearch Data Processor Unit Test', () => { await testHelper.esClient.create({ index, id: testData.messages[model].create.message.payload.id, - body: _.omit(testData.messages[model].create.message.payload, ['id']), + body: testData.messages[model].create.message.payload, refresh: 'true' }) try { @@ -101,3 +122,155 @@ describe('UBahn - Elasticsearch Data Processor Unit Test', () => { }) } }) + +describe('Zapier Logic Tests', () => { + let sandbox + + beforeEach(() => { + // clear es storage + testData.esStorage.content = {} + + // mock helper methods + sandbox = sinon.createSandbox() + sandbox.stub(helper, 'postMessageViaWebhook').callsFake((webhook, message) => { + logger.debug({ component: 'helper', context: 'postMessageToZapier (stub)', message: `message: ${JSON.stringify({ webhook, message })}` }) + }) + sandbox.stub(helper, 'getM2MToken').callsFake(() => { + const token = 'dummy-token' + logger.debug({ component: 'helper', context: 'getM2MToken (stub)', message: token }) + return token + }) + }) + + afterEach(() => { + // clear es storage + testData.esStorage.content = {} + + // reset mocked methods + sandbox.restore() + }) + + it('should have Zapier switched ON during testing Zapier logic', () => { + // to enable Job Candidates Zapier logic + should.equal(config.zapier.ZAPIER_JOB_CANDIDATE_SWITCH, constants.Zapier.Switch.ON) + // to enable Jobs Zapier logic + should.equal(config.zapier.ZAPIER_SWITCH, constants.Zapier.Switch.ON) + }) + + describe('Job Candidate Update', () => { + it('should post to Zapier if status is changed to "rejected"', async () => { + const previousData = _.assign({}, testData.messages.JobCandidate.create.message.payload, { status: 'open', externalId: '123' }) + const updateMessage = _.assign({}, testData.messages.JobCandidate.update.message, { + payload: _.assign({}, testData.messages.JobCandidate.update.message.payload, { status: 'rejected', externalId: '123' }) + }) + + await testHelper.esClient.create({ + index: config.esConfig.ES_INDEX_JOB, + id: previousData.id, + body: previousData, + refresh: 'true' + }) + await testHelper.esClient.create({ + index: config.esConfig.ES_INDEX_JOB_CANDIDATE, + id: testData.messages.Job.create.message.payload.id, + body: testData.messages.Job.create.message.payload, + refresh: 'true' + }) + await services[`JobCandidateProcessorService`].processUpdate(updateMessage, transactionId) + + helper.postMessageViaWebhook.callCount.should.equal(1) + }) + + it('should post to Zapier if status is changed to "shortlist"', async () => { + const previousData = _.assign({}, testData.messages.JobCandidate.create.message.payload, { status: 'open', externalId: '123' }) + const updateMessage = _.assign({}, testData.messages.JobCandidate.update.message, { + payload: _.assign({}, testData.messages.JobCandidate.update.message.payload, { status: 'shortlist', externalId: '123' }) + }) + + await testHelper.esClient.create({ + index: config.esConfig.ES_INDEX_JOB, + id: previousData.id, + body: previousData, + refresh: 'true' + }) + await testHelper.esClient.create({ + index: config.esConfig.ES_INDEX_JOB_CANDIDATE, + id: testData.messages.Job.create.message.payload.id, + body: testData.messages.Job.create.message.payload, + refresh: 'true' + }) + await services[`JobCandidateProcessorService`].processUpdate(updateMessage, transactionId) + + helper.postMessageViaWebhook.callCount.should.equal(1) + }) + + it('should not post to Zapier if status was already "rejected"', async () => { + const previousData = _.assign({}, testData.messages.JobCandidate.create.message.payload, { status: 'rejected', externalId: '123' }) + const updateMessage = _.assign({}, testData.messages.JobCandidate.update.message, { + payload: _.assign({}, testData.messages.JobCandidate.update.message.payload, { status: 'rejected', externalId: '123' }) + }) + + await testHelper.esClient.create({ + index: config.esConfig.ES_INDEX_JOB, + id: previousData.id, + body: previousData, + refresh: 'true' + }) + await testHelper.esClient.create({ + index: config.esConfig.ES_INDEX_JOB_CANDIDATE, + id: testData.messages.Job.create.message.payload.id, + body: testData.messages.Job.create.message.payload, + refresh: 'true' + }) + await services[`JobCandidateProcessorService`].processUpdate(updateMessage, transactionId) + + helper.postMessageViaWebhook.callCount.should.equal(0) + }) + + it('should not post to Zapier if status was already "shortlist"', async () => { + const previousData = _.assign({}, testData.messages.JobCandidate.create.message.payload, { status: 'shortlist', externalId: '123' }) + const updateMessage = _.assign({}, testData.messages.JobCandidate.update.message, { + payload: _.assign({}, testData.messages.JobCandidate.update.message.payload, { status: 'shortlist', externalId: '123' }) + }) + + await testHelper.esClient.create({ + index: config.esConfig.ES_INDEX_JOB, + id: previousData.id, + body: previousData, + refresh: 'true' + }) + await testHelper.esClient.create({ + index: config.esConfig.ES_INDEX_JOB_CANDIDATE, + id: testData.messages.Job.create.message.payload.id, + body: testData.messages.Job.create.message.payload, + refresh: 'true' + }) + await services[`JobCandidateProcessorService`].processUpdate(updateMessage, transactionId) + + helper.postMessageViaWebhook.callCount.should.equal(0) + }) + + it('should not post to Zapier if status is changed to "selected" (not "rejected" or "shortlist")', async () => { + const previousData = _.assign({}, testData.messages.JobCandidate.create.message.payload, { status: 'open', externalId: '123' }) + const updateMessage = _.assign({}, testData.messages.JobCandidate.update.message, { + payload: _.assign({}, testData.messages.JobCandidate.update.message.payload, { status: 'selected', externalId: '123' }) + }) + + await testHelper.esClient.create({ + index: config.esConfig.ES_INDEX_JOB, + id: previousData.id, + body: previousData, + refresh: 'true' + }) + await testHelper.esClient.create({ + index: config.esConfig.ES_INDEX_JOB_CANDIDATE, + id: testData.messages.Job.create.message.payload.id, + body: testData.messages.Job.create.message.payload, + refresh: 'true' + }) + await services[`JobCandidateProcessorService`].processUpdate(updateMessage, transactionId) + + helper.postMessageViaWebhook.callCount.should.equal(0) + }) + }) +})