const test = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const os = require('node:os') const path = require('node:path') const { createMockContext } = require('../src/context/createMockContext') function withEnv(overrides, fn) { const previousValues = new Map() for (const [key, value] of Object.entries(overrides)) { previousValues.set(key, process.env[key]) if (value === undefined) { delete process.env[key] } else { process.env[key] = value } } try { return fn() } finally { for (const [key, value] of previousValues.entries()) { if (value === undefined) { delete process.env[key] } else { process.env[key] = value } } } } test('uses a timestamp seed when MOCK_PROMOTION_REDEMPTION_ID_START is unset', () => withEnv( { MOCK_PROMOTION_REDEMPTION_ID_START: undefined, MOCK_REDEEMED_PROMOTION_RECORDING_ENABLED: 'false', }, () => { const originalDateNow = Date.now Date.now = () => 1760000000123 try { const context = createMockContext() assert.equal(context.nextMockPromotionRedemptionId(), 1760000000) assert.equal(context.nextMockPromotionRedemptionId(), 1760000001) } finally { Date.now = originalDateNow } }, )) test('falls back to an int-safe timestamp seed when configured redemption id start is out of range', () => withEnv( { MOCK_PROMOTION_REDEMPTION_ID_START: '1774243846923', MOCK_REDEEMED_PROMOTION_RECORDING_ENABLED: 'false', }, () => { const originalDateNow = Date.now Date.now = () => 1760000000123 try { const context = createMockContext() assert.equal(context.nextMockPromotionRedemptionId(), 1760000000) } finally { Date.now = originalDateNow } }, )) test('fallback redeemed promotion detail consumes the same incrementing redemption id sequence', () => withEnv( { MOCK_PROMOTION_REDEMPTION_ID_START: '910000', MOCK_REDEEMED_PROMOTION_RECORDING_ENABLED: 'false', }, () => { const context = createMockContext() const firstOutcome = context.buildMockRedeemedPromotionOutcome({ data: { loginId: 'tester' }, schema: {}, }) const secondOutcome = context.buildMockRedeemedPromotionOutcome({ data: { loginId: 'tester' }, schema: {}, }) assert.equal(firstOutcome.redemptionId, 910000) assert.equal(secondOutcome.redemptionId, 910001) }, )) test('uses the birthday promotion fixture as the default promotion source when no override is configured', () => withEnv( { MOCK_PROMOTION_CAPTURE_FILE: undefined, MOCK_REDEEMED_PROMOTION_RECORDING_ENABLED: 'false', }, () => { const context = createMockContext() const promotions = context.getPromotionLists('910700001') assert.equal(promotions.eligiblePromotionList.length, 1) assert.equal(promotions.eligiblePromotionList[0].code, 'PROMO-BIRTHDAY-2026') assert.equal(promotions.eligiblePromotionList[0].ruleId, 3800801) assert.equal(promotions.eligiblePromotionList[0].outcomeList.length, 3) assert.equal(promotions.eligiblePromotionList[0].isEntitled, true) }, )) test('backdate promotions returned by the fixed fixture satisfy the frontend redeemable contract', () => withEnv( { MOCK_REDEEMED_PROMOTION_RECORDING_ENABLED: 'false', }, () => { const context = createMockContext() const promotions = context.getBackdatePromotionLists('800038008') assert.ok(promotions.entitledPromotionList.length > 0) promotions.entitledPromotionList.forEach((item) => { assert.equal(item.isEntitled, true) assert.equal(item.isRedeemCompleted, false) assert.ok(Number(item.awardsEntitled) > Number(item.awardsCount)) }) }, )) test('backdate promotions can be resolved through the default 80038008 member alias', () => withEnv( { MOCK_REDEEMED_PROMOTION_RECORDING_ENABLED: 'false', }, () => { const context = createMockContext() const promotions = context.getBackdatePromotionLists('80038008') assert.ok(promotions.entitledPromotionList.length > 0) assert.equal(promotions.entitledPromotionList[0].ruleId, 3800801) assert.equal(promotions.entitledPromotionList[0].isEntitled, true) }, )) test('persists runtime recorded redeemed promotions and resumes redemption id sequence', () => { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mock-gateway-recording-')) const recordingFile = path.join(tempDir, 'redeemed-promotions.recorded.json') try { withEnv( { MOCK_PROMOTION_REDEMPTION_ID_START: '910000', MOCK_REDEEMED_PROMOTION_RECORDING_ENABLED: 'true', MOCK_REDEEMED_PROMOTION_RECORDING_FILE: recordingFile, }, () => { const firstContext = createMockContext() firstContext.recordRedeemedPromotionOutcome('80010991', { redemptionId: 910005, promotionId: 11225, outcome: { redeemedList: [ { outcomeRemark: 'qa-ebonus-auto', transId: 'tx-910005', }, ], }, }) const persisted = JSON.parse(fs.readFileSync(recordingFile, 'utf8')) assert.equal(persisted.accounts['80010991'][0].redemptionId, 910005) const secondContext = createMockContext() assert.equal(secondContext.getRecordedRedeemedPromotionOutcomes('80010991')[0].redemptionId, 910005) assert.equal(secondContext.nextMockPromotionRedemptionId(), 910006) }, ) } finally { fs.rmSync(tempDir, { recursive: true, force: true }) } }) test('account-specific redeemed promotions fixture preserves voidedByEmployeeId for the 2026-04-06 voided record', () => { const context = createMockContext() const fixture = context.getAccountSpecificRedeemedPromotionsFixture('100000059') const targetOutcome = fixture.data.outcomeList.find( (item) => item.voidedDtm === '2026-04-06T16:11:05.584548' && item.outcome?.redeemedList?.[0]?.transId === '43a9ab81-bbff-4812-bd64-bfd4eaa50d72', ) assert.ok(targetOutcome) assert.equal(targetOutcome.voidedBy, 'qa-randyng') assert.equal(targetOutcome.voidedByEmployeeId, '171853') })