初始化

This commit is contained in:
DESKTOP-U5J7A7B\Willtech007
2026-10-12 17:55:59 +08:00
parent 54b4d8f8c5
commit 633690b0c4
52 changed files with 15478 additions and 0 deletions
+463
View File
@@ -0,0 +1,463 @@
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)
})