const test = require('node:test') const assert = require('node:assert/strict') const registerPlayerPromotionRedeemRoute = require('../src/routes/ewl/bal/playerPromotionRedeem') const { ISSUE_TARGET_TO_WALLET_HEADER, bridgePromotionRedeemToEwallet, createMockPromotionRedeemResult, isBackdatePromotionRedeemRequest, registerableRoutes, mergeIssueTargetToWalletOverrides, mergeRuntimeIssueTargetToWalletOverrides, resolveIssueTargetToWallet, } = registerPlayerPromotionRedeemRoute.__internal test('registers both normal and backdate promotion redeem routes', () => { assert.deepEqual(registerableRoutes, [ '/ewl/promotion/v1/playerpromotion/redeem', '/ewl/promotion/v1/playerpromotion/redeemBackDate', '/ewl/bal/v1/playerpromotion/redeem', '/ewl/bal/v1/playerpromotion/redeemBackDate', ]) }) test('detects backdate redeem requests from route and payload', () => { assert.equal( isBackdatePromotionRedeemRequest('/ewl/promotion/v1/playerpromotion/redeemBackDate', { data: {}, }), true, ) assert.equal( isBackdatePromotionRedeemRequest('/ewl/promotion/v1/playerpromotion/redeem', { data: { backdate: '2026-03-23' }, }), true, ) assert.equal( isBackdatePromotionRedeemRequest('/ewl/promotion/v1/playerpromotion/redeem', { data: {}, }), false, ) }) test('creates a local redeemed outcome for backdate requests without bridging back to backend', () => { const mockResult = createMockPromotionRedeemResult({ context: { resolvePromotionRedeemSelection(accountNum, promotionId, promotionRuleId, outcomeIds, options) { assert.equal(accountNum, '800038008') assert.equal(promotionId, 71768004) assert.equal(promotionRuleId, 71768026) assert.deepEqual(outcomeIds, [71768072, 71768073]) assert.deepEqual(options, { backdate: true }) return { promotion: { code: 'LP-EE-CNYGS26', name: 'CNY Gift Set Earn & Get-2026', ruleName: 'CNYGS-188pts-Martell + LCH-Can Abalone', }, selectedOutcomes: [ { value: 'lp-ee-cnygs26-188pts-martellnoblige-me', prize: { prizeCode: 'lp-ee-cnygs26-188pts-martellnoblige-me', prizeCategory: 'PROMOTION', }, }, { value: 'lp-ee-cnygs26-188pts-lchabalone-me', prize: { prizeCode: 'lp-ee-cnygs26-188pts-lchabalone-me', prizeCategory: 'PROMOTION', }, }, ], } }, nextMockPromotionRedemptionId() { return 900001 }, generateToken(prefix) { return `${prefix}_001` }, }, requestBody: { schema: {}, data: { accountNum: '800038008', promotionId: 71768004, promotionRuleId: 71768026, outcomeId: [71768072, 71768073], quantity: 1, backdate: '2026-03-23', }, }, accountNum: '800038008', backdate: true, }) assert.equal(mockResult.redeemedOutcome.redemptionId, 900001) assert.equal(mockResult.redeemedOutcome.promotionId, 71768004) assert.equal(mockResult.redeemedOutcome.prizeRuleId, 71768026) assert.equal(mockResult.redeemedOutcome.outcome.redeemedList.length, 2) assert.equal( mockResult.redeemedOutcome.outcome.redeemedList[0].transId, 'mock_backdate_redemption_001', ) }) test('bridges backdate requests to backend when promotion bridge is enabled', async (t) => { const originalFetch = global.fetch t.after(() => { global.fetch = originalFetch }) const fetchCalls = [] global.fetch = async (url, options = {}) => { const parsedBody = options.body ? JSON.parse(options.body) : null fetchCalls.push({ url, method: options.method, headers: options.headers, body: parsedBody, }) if (url === 'http://backend.test/api/v1/auth/login') { return { ok: true, status: 200, statusText: 'OK', text: async () => JSON.stringify({ success: true, data: { accessToken: 'token-123' } }), } } if (url === 'http://backend.test/api/v1/prize-redemptions') { assert.equal(parsedBody.customerId, '80038008') assert.equal(parsedBody.metadata.redemption_id, 900002) assert.equal(parsedBody.metadata.promotion_id, 71768004) assert.equal(parsedBody.targetPrizes.length, 2) assert.deepEqual( parsedBody.targetPrizes.map((targetPrize) => targetPrize.issueTargetToWallet), [true, false], ) return { ok: true, status: 200, statusText: 'OK', text: async () => JSON.stringify({ success: true, data: { publicId: 'backend-redemption-001', overallStatus: 'COMPLETED', }, }), } } throw new Error(`Unexpected fetch URL: ${url}`) } const bridgeResult = await bridgePromotionRedeemToEwallet({ context: { resolvePromotionRedeemSelection(accountNum, promotionId, promotionRuleId, outcomeIds, options) { assert.equal(accountNum, '80038008') assert.equal(promotionId, 71768004) assert.equal(promotionRuleId, 71768024) assert.deepEqual(outcomeIds, [71768070, 71768069]) assert.deepEqual(options, { backdate: true }) return { promotion: { code: 'LP-EE-CNYGS26', name: 'CNY Gift Set Earn & Get-2026', ruleName: 'CNYGS-188pts-Hennessy + LCH-Scallop', }, selectedOutcomes: [ { key: 71768070, value: 'lp-ee-cnygs26-188pts-lchscallop-me', prize: { prizeCode: 'lp-ee-cnygs26-188pts-lchscallop-me', prizeCategory: 'PROMOTION', }, }, { key: 71768069, value: 'lp-ee-cnygs26-188pts-hennessyvsop-me', prize: { prizeCode: 'lp-ee-cnygs26-188pts-hennessyvsop-me', prizeCategory: 'PROMOTION', }, }, ], } }, nextMockPromotionRedemptionId() { return 900002 }, promotionRedemptionBridgeLoginUsername: 'mock.gateway', promotionRedemptionBridgeLoginPassword: 'secret', promotionRedemptionBridgeBaseUrl: 'http://backend.test', promotionRedemptionBridgeTimeoutMs: 5000, promotionRedemptionSourceBucket: 'POINT', promotionRedemptionInitiateSource: 'PROMOTION', generateToken(prefix) { return `${prefix}_001` }, }, requestBody: { schema: { computerName: 'BOH-DT200244', ipAddress: '10.20.30.40', transactionId: 'mock-backdate-request-001', }, data: { accountNum: '80038008', promotionId: 71768004, promotionRuleId: 71768024, outcomeId: [71768070, 71768069], issueTargetToWallet: [true, false], quantity: 1, loginId: 'admin', casinoCode: 'GLP', locationCode: 'GLP01', backdate: '2026-04-21', }, }, accountNum: '80038008', backdate: true, }) assert.equal(bridgeResult.redeemedOutcome.redemptionId, 900002) assert.equal(bridgeResult.redeemedOutcome.outcome.redeemedList.length, 2) assert.equal( bridgeResult.redeemedOutcome.outcome.redeemedList[0].transId, 'backend-redemption-001', ) assert.equal(fetchCalls.length, 2) assert.equal(fetchCalls[0].url, 'http://backend.test/api/v1/auth/login') assert.equal(fetchCalls[1].url, 'http://backend.test/api/v1/prize-redemptions') }) test('header overrides are merged into request data for backend passthrough', () => { const normalizedRequestBody = mergeIssueTargetToWalletOverrides( { data: { outcomeId: [11330011, 11330012], }, }, { [ISSUE_TARGET_TO_WALLET_HEADER]: '{"11330011":false,"11330012":true}', }, ) assert.deepEqual(normalizedRequestBody.data.issueTargetToWalletByOutcomeId, { '11330011': false, '11330012': true, }) }) test('body overrides win over header overrides when both are present', () => { const normalizedRequestBody = mergeIssueTargetToWalletOverrides( { data: { issueTargetToWalletByOutcomeId: { '11330011': true, }, }, }, { [ISSUE_TARGET_TO_WALLET_HEADER]: '{"11330011":false,"11330012":true}', }, ) assert.deepEqual(normalizedRequestBody.data.issueTargetToWalletByOutcomeId, { '11330011': true, '11330012': true, }) }) test('runtime overrides are merged for matching promotion outcomes', () => { const normalizedRequestBody = mergeRuntimeIssueTargetToWalletOverrides( { data: { promotionId: 11330, promotionRuleId: 1133001, outcomeId: [11330011, 11330012], }, }, { resolvePromotionRedeemFulfillmentOverrideByOutcomeId(input) { assert.deepEqual(input, { accountNum: '80010991', promotionId: 11330, promotionRuleId: 1133001, outcomeIds: [11330011, 11330012], }) return { '11330011': false, '11330012': true, } }, }, '80010991', ) assert.deepEqual(normalizedRequestBody.data.issueTargetToWalletByOutcomeId, { '11330011': false, '11330012': true, }) }) test('request overrides still win over runtime overrides', () => { const normalizedRequestBody = mergeRuntimeIssueTargetToWalletOverrides( { data: { promotionId: 11330, promotionRuleId: 1133001, outcomeId: [11330011, 11330012], issueTargetToWalletByOutcomeId: { '11330011': true, }, }, }, { resolvePromotionRedeemFulfillmentOverrideByOutcomeId() { return { '11330011': false, '11330012': false, } }, }, '80010991', ) assert.deepEqual(normalizedRequestBody.data.issueTargetToWalletByOutcomeId, { '11330011': true, '11330012': false, }) }) test('defaults USER_CHOICE promotions to wallet when no explicit override is provided', () => { const resolved = resolveIssueTargetToWallet({ requestData: {}, outcome: { key: 11330011, prize: { deliveryMode: 'USER_CHOICE', }, }, index: 0, prizeIdentifier: null, }) assert.equal(resolved, true) }) test('uses non-empty gisPrizeIdentifier to resolve USER_CHOICE promotions as external', () => { const resolved = resolveIssueTargetToWallet({ requestData: {}, outcome: { key: 11330011, prize: { deliveryMode: 'USER_CHOICE', }, }, index: 0, prizeIdentifier: 'GIS-CODE-001', }) assert.equal(resolved, false) }) test('request outcome override takes precedence over delivery mode inference', () => { const resolved = resolveIssueTargetToWallet({ requestData: { issueTargetToWalletByOutcomeId: { '11330011': false, }, }, outcome: { key: 11330011, prize: { deliveryMode: 'USER_CHOICE', }, }, index: 0, prizeIdentifier: null, }) assert.equal(resolved, false) }) test('fixture-level mockIssueTargetToWallet override is honored', () => { const resolved = resolveIssueTargetToWallet({ requestData: {}, outcome: { key: 11330011, mockIssueTargetToWallet: false, prize: { deliveryMode: 'USER_CHOICE', }, }, index: 0, prizeIdentifier: null, }) assert.equal(resolved, false) }) test('request array override is honored by outcome index', () => { const resolved = resolveIssueTargetToWallet({ requestData: { issueTargetToWallet: [true, false], }, outcome: { key: 11330012, prize: { deliveryMode: 'WALLET_ONLY', }, }, index: 1, prizeIdentifier: null, }) assert.equal(resolved, false) }) test('request array override can distinguish duplicate outcome ids by position', () => { const firstResolved = resolveIssueTargetToWallet({ requestData: { issueTargetToWallet: [true, false, true], }, outcome: { key: 11330011, prize: { deliveryMode: 'USER_CHOICE', }, }, index: 0, prizeIdentifier: null, }) const secondResolved = resolveIssueTargetToWallet({ requestData: { issueTargetToWallet: [true, false, true], }, outcome: { key: 11330011, prize: { deliveryMode: 'USER_CHOICE', }, }, index: 1, prizeIdentifier: null, }) assert.equal(firstResolved, true) assert.equal(secondResolved, false) })