diff --git a/package-lock.json b/package-lock.json index 5ea553c4..b7292cbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mojaloop/central-services-shared", - "version": "17.1.0", + "version": "17.2.0-snapshot.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@mojaloop/central-services-shared", - "version": "17.1.0", + "version": "17.2.0-snapshot.1", "license": "Apache-2.0", "dependencies": { "@hapi/catbox": "12.0.0", @@ -23,7 +23,7 @@ "raw-body": "2.5.1", "rc": "1.2.8", "shins": "2.6.0", - "uuid4": "2.0.2", + "uuid4": "2.0.3", "widdershins": "^4.0.1", "yaml": "2.1.1" }, @@ -1546,6 +1546,12 @@ } } }, + "node_modules/@mojaloop/event-sdk/node_modules/uuid4": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uuid4/-/uuid4-2.0.2.tgz", + "integrity": "sha512-TzsQS8sN1B2m9WojyNp0X/3JL8J2RScnrAJnooNPL6lq3lA02/XdoWysyUgI6rAif0DzkkWk51N6OggujPy2RA==", + "peer": true + }, "node_modules/@mojaloop/sdk-standard-components": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/@mojaloop/sdk-standard-components/-/sdk-standard-components-17.0.1.tgz", @@ -14146,9 +14152,9 @@ } }, "node_modules/uuid4": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/uuid4/-/uuid4-2.0.2.tgz", - "integrity": "sha512-TzsQS8sN1B2m9WojyNp0X/3JL8J2RScnrAJnooNPL6lq3lA02/XdoWysyUgI6rAif0DzkkWk51N6OggujPy2RA==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid4/-/uuid4-2.0.3.tgz", + "integrity": "sha512-CTpAkEVXMNJl2ojgtpLXHgz23dh8z81u6/HEPiQFOvBc/c2pde6TVHmH4uwY0d/GLF3tb7+VDAj4+2eJaQSdZQ==" }, "node_modules/v8-compile-cache": { "version": "2.3.0", @@ -16185,6 +16191,14 @@ "tslib": "2.4.0", "uuid4": "2.0.2", "winston": "3.7.2" + }, + "dependencies": { + "uuid4": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uuid4/-/uuid4-2.0.2.tgz", + "integrity": "sha512-TzsQS8sN1B2m9WojyNp0X/3JL8J2RScnrAJnooNPL6lq3lA02/XdoWysyUgI6rAif0DzkkWk51N6OggujPy2RA==", + "peer": true + } } }, "@mojaloop/sdk-standard-components": { @@ -25922,9 +25936,9 @@ "dev": true }, "uuid4": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/uuid4/-/uuid4-2.0.2.tgz", - "integrity": "sha512-TzsQS8sN1B2m9WojyNp0X/3JL8J2RScnrAJnooNPL6lq3lA02/XdoWysyUgI6rAif0DzkkWk51N6OggujPy2RA==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid4/-/uuid4-2.0.3.tgz", + "integrity": "sha512-CTpAkEVXMNJl2ojgtpLXHgz23dh8z81u6/HEPiQFOvBc/c2pde6TVHmH4uwY0d/GLF3tb7+VDAj4+2eJaQSdZQ==" }, "v8-compile-cache": { "version": "2.3.0", diff --git a/package.json b/package.json index 42be6bc4..5fec9b19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mojaloop/central-services-shared", - "version": "17.1.0", + "version": "17.2.0-snapshot.1", "description": "Shared code for mojaloop central services", "license": "Apache-2.0", "author": "ModusBox", @@ -27,6 +27,10 @@ "engines": { "node": "=16.x" }, + "imports": { + "#src/*": "./src/*.js", + "#test/*": "./test/*.js" + }, "pre-commit": [ "lint", "dep:check", @@ -65,7 +69,7 @@ "raw-body": "2.5.1", "rc": "1.2.8", "shins": "2.6.0", - "uuid4": "2.0.2", + "uuid4": "2.0.3", "widdershins": "^4.0.1", "yaml": "2.1.1" }, diff --git a/src/util/comparators/duplicateCheckComparator.js b/src/util/comparators/duplicateCheckComparator.js index 7595cb90..ee5284a3 100644 --- a/src/util/comparators/duplicateCheckComparator.js +++ b/src/util/comparators/duplicateCheckComparator.js @@ -30,32 +30,41 @@ const Hash = require('../hash') -const duplicateCheckComparator = async (id, object, getDuplicateDataFuncOverride, saveHashFuncOverride) => { +const duplicateCheckComparator = async (id, objectOrHashOverride, getDuplicateDataFuncOverride, saveHashFuncOverride, options = { + hashOverride: false +}) => { + // lets check if we can actually do the comparison + if (objectOrHashOverride == null) throw Error('objectOrHashOverride arg is null') + let hasDuplicateId = false let hasDuplicateHash = false - const generatedHash = Hash.generateSha256(object) - let duplicateHashRecord + let duplicateHashRecordResult = null + let saveHashFuncOverrideResult = false + + const generatedHash = options?.hashOverride ? objectOrHashOverride : Hash.generateSha256(objectOrHashOverride) // options.hashOverride is useful for cases where the hash is already provided, such as Bulk Transfers Prepare/Fulfil Use-Case - the hash is calculated in the Bulk-API-Adapter. const compareById = async () => { - duplicateHashRecord = await getDuplicateDataFuncOverride(id) - hasDuplicateId = duplicateHashRecord !== null + duplicateHashRecordResult = await getDuplicateDataFuncOverride(id) + hasDuplicateId = duplicateHashRecordResult !== null return hasDuplicateId } const compareByHash = () => { - hasDuplicateHash = duplicateHashRecord.hash === generatedHash + hasDuplicateHash = duplicateHashRecordResult.hash === generatedHash return hasDuplicateHash } if (await compareById()) { compareByHash() } else { - await saveHashFuncOverride(id, generatedHash) + saveHashFuncOverrideResult = await saveHashFuncOverride(id, generatedHash) } return { hasDuplicateId, - hasDuplicateHash + hasDuplicateHash, + duplicateHashRecordResult, + saveHashFuncOverrideResult } } diff --git a/test/unit/util/comparators/duplicateCheckComparator.test.js b/test/unit/util/comparators/duplicateCheckComparator.test.js index 6c0207ec..9c7c4335 100644 --- a/test/unit/util/comparators/duplicateCheckComparator.test.js +++ b/test/unit/util/comparators/duplicateCheckComparator.test.js @@ -45,58 +45,230 @@ Test('Duplicate check comparator', dccTest => { dccTest.test('duplicateCheckComparator should', duplicateCheckComparatorTest => { duplicateCheckComparatorTest.test('compare hashes when id exists', async test => { try { + // Arrange const hash = 'helper.hash' - const duplicateCheckComparator = Proxyquire('../../../../src/util/comparators/duplicateCheckComparator', { + const duplicateCheckComparator = Proxyquire('#src/util/comparators/duplicateCheckComparator', { '../hash': { generateSha256: sandbox.stub().returns(hash) } }) const id = 1 const object = { key: 'value' } - const getDuplicateDataFuncOverride = async (id) => { return Promise.resolve({ id, hash }) } - const saveHashFuncOverride = async () => { return true } + const getDuplicateDataFuncOverrideResult = { id, hash } + const getDuplicateDataFuncOverride = sandbox.stub().resolves(getDuplicateDataFuncOverrideResult) + const saveHashFuncOverride = sandbox.stub().resolves(true) const expected = { hasDuplicateId: true, - hasDuplicateHash: true + hasDuplicateHash: true, + saveHashFuncOverrideResult: false, + duplicateHashRecordResult: getDuplicateDataFuncOverrideResult } + + // Act const result = await duplicateCheckComparator(id, object, getDuplicateDataFuncOverride, saveHashFuncOverride) + + // Assert test.deepEqual(result, expected, 'hash matched') + test.ok(saveHashFuncOverride.called === false) test.end() } catch (err) { + // Assert Logger.error(`duplicateCheckComparator failed with error - ${err}`) test.fail() test.end() } }) - duplicateCheckComparatorTest.test('save hashe when id not found', async test => { + duplicateCheckComparatorTest.test('save hash when id not found', async test => { try { + // Arrange const hash = 'helper.hash' - const duplicateCheckComparator = Proxyquire('../../../../src/util/comparators/duplicateCheckComparator', { + const duplicateCheckComparator = Proxyquire('#src/util/comparators/duplicateCheckComparator', { '../hash': { generateSha256: sandbox.stub().returns(hash) } }) const id = 1 const object = { key: 'value' } - const getDuplicateDataFuncOverride = async () => { return Promise.resolve(null) } - const saveHashFuncOverride = async () => { return true } + const getDuplicateDataFuncOverride = sandbox.stub().resolves(null) + const saveHashFuncOverride = sandbox.stub().resolves(true) const expected = { hasDuplicateId: false, - hasDuplicateHash: false + hasDuplicateHash: false, + saveHashFuncOverrideResult: true, + duplicateHashRecordResult: null } + + // Act const result = await duplicateCheckComparator(id, object, getDuplicateDataFuncOverride, saveHashFuncOverride) + + // Assert test.deepEqual(result, expected, 'hash saved') + test.ok(saveHashFuncOverride.calledOnceWith(id, hash)) + test.end() + } catch (err) { + // Assert + Logger.error(`duplicateCheckComparator failed with error - ${err}`) + test.fail() + test.end() + } + }) + + duplicateCheckComparatorTest.test('compare hashes when id exists and generatedHashOverride is injected', async test => { + try { + // Arrange + const hash = 'helper.hash' + const generatedHashOverride = 'helper.hash.override' + const duplicateCheckComparator = Proxyquire('#src/util/comparators/duplicateCheckComparator', { + '../hash': { + generateSha256: sandbox.stub().returns(hash) + } + }) + const id = 1 + const getDuplicateDataFuncOverrideResult = { id, hash: generatedHashOverride } + const getDuplicateDataFuncOverride = sandbox.stub().resolves(getDuplicateDataFuncOverrideResult) + const saveHashFuncOverride = sandbox.stub().resolves(true) + + const expected = { + hasDuplicateId: true, + hasDuplicateHash: true, + duplicateHashRecordResult: getDuplicateDataFuncOverrideResult, + saveHashFuncOverrideResult: false + } + + // Act + const result = await duplicateCheckComparator(id, generatedHashOverride, getDuplicateDataFuncOverride, saveHashFuncOverride, { + hashOverride: true + }) + + // Assert + test.deepEqual(result, expected, 'hash matched') + test.ok(saveHashFuncOverride.called === false) test.end() } catch (err) { + // Assert Logger.error(`duplicateCheckComparator failed with error - ${err}`) test.fail() test.end() } }) + duplicateCheckComparatorTest.test('save hash when id not found and generatedHashOverride is injected', async test => { + try { + // Arrange + const hash = 'helper.hash' + const generatedHashOverride = 'helper.hash.override' + const duplicateCheckComparator = Proxyquire('#src/util/comparators/duplicateCheckComparator', { + '../hash': { + generateSha256: sandbox.stub().returns(hash) + } + }) + const id = 1 + const getDuplicateDataFuncOverride = sandbox.stub().resolves(null) + const saveHashFuncOverride = sandbox.stub().resolves(true) + + const expected = { + hasDuplicateId: false, + hasDuplicateHash: false, + saveHashFuncOverrideResult: true, + duplicateHashRecordResult: null + } + + // Act + const result = await duplicateCheckComparator(id, generatedHashOverride, getDuplicateDataFuncOverride, saveHashFuncOverride, { + hashOverride: true + }) + + // Assert + test.deepEqual(result, expected, 'hash saved') + test.ok(saveHashFuncOverride.calledOnceWith(id, generatedHashOverride)) + test.end() + } catch (err) { + // Assert + Logger.error(`duplicateCheckComparator failed with error - ${err}`) + test.fail() + test.end() + } + }) + + duplicateCheckComparatorTest.test('save hash when id not found with empty object and generatedHashOverride is injected', async test => { + try { + // Arrange + const hash = 'helper.hash' + const generatedHashOverride = 'helper.hash.override' + const duplicateCheckComparator = Proxyquire('#src/util/comparators/duplicateCheckComparator', { + '../hash': { + generateSha256: sandbox.stub().returns(hash) + } + }) + const id = 1 + const object = null // We don't actually care about the object when we override the hash by setting the generatedHashOverride + const getDuplicateDataFuncOverride = sandbox.stub().resolves(null) + const saveHashFuncOverride = sandbox.stub().resolves(true) + + const expected = { + hasDuplicateId: false, + hasDuplicateHash: false + } + + // Act + const result = await duplicateCheckComparator(id, object, getDuplicateDataFuncOverride, saveHashFuncOverride, { + hashOverride: false + }) + + // Assert + test.deepEqual(result, expected, 'hash saved') + test.ok(saveHashFuncOverride.calledOnceWith(id, generatedHashOverride)) + test.fail() + test.end() + } catch (err) { + // Assert + Logger.error(`duplicateCheckComparator failed with error - ${err}`) + test.ok(err) + test.end() + } + }) + + duplicateCheckComparatorTest.test('save hash when id not found with empty object and generatedHashOverride is injected', async test => { + try { + // Arrange + const hash = 'helper.hash' + const generatedHashOverride = 'helper.hash.override' + const duplicateCheckComparator = Proxyquire('#src/util/comparators/duplicateCheckComparator', { + '../hash': { + generateSha256: sandbox.stub().returns(hash) + } + }) + const id = 1 + const object = null // We don't actually care about the object when we override the hash by setting the generatedHashOverride + const getDuplicateDataFuncOverride = sandbox.stub().resolves(null) + const saveHashFuncOverride = sandbox.stub().resolves(true) + + const expected = { + hasDuplicateId: false, + hasDuplicateHash: false + } + + // Act + const result = await duplicateCheckComparator(id, object, getDuplicateDataFuncOverride, saveHashFuncOverride, { + hashOverride: true + }) + + // Assert + test.deepEqual(result, expected, 'hash saved') + test.ok(saveHashFuncOverride.calledOnceWith(id, generatedHashOverride)) + test.fail() + test.end() + } catch (err) { + // Assert + Logger.error(`duplicateCheckComparator failed with error - ${err}`) + test.ok(err) + test.end() + } + }) + duplicateCheckComparatorTest.end() })