Skip to content

Commit

Permalink
feat(mojaloop/#2796): duplicate transaction not getting callback for …
Browse files Browse the repository at this point in the history
…post /bulkTransfers (#332)

feat(mojaloop/#2796): duplicate transaction not getting callback for post /bulkTransfers - mojaloop/project#2796
- updated duplicateCheckComparator to support overriding the generatedHash calculation to accomodate Bulk Transfer Prepare/Fulfil use-cases by adding optional config param to duplicateCheckComparator
- enhanced duplicateCheckComparator to also return get and save results
- updated unit tests to validate the above change

chore: maintenance
- updated dependencies
- fixed minor test-case wording
  • Loading branch information
mdebarros authored Aug 11, 2022
1 parent 7316727 commit 5427a53
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 28 deletions.
32 changes: 23 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -27,6 +27,10 @@
"engines": {
"node": "=16.x"
},
"imports": {
"#src/*": "./src/*.js",
"#test/*": "./test/*.js"
},
"pre-commit": [
"lint",
"dep:check",
Expand Down Expand Up @@ -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"
},
Expand Down
25 changes: 17 additions & 8 deletions src/util/comparators/duplicateCheckComparator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
190 changes: 181 additions & 9 deletions test/unit/util/comparators/duplicateCheckComparator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})

Expand Down

0 comments on commit 5427a53

Please sign in to comment.