Files
server/laf-cloud/tests/stamina-pack.test.mjs
COMPUTER\EDY aba163f5b5 Merge origin/main while preserving StaminaPack payment support
Retain upstream pyramid and payment changes. Adapt StaminaPack test mocks to the added pyramid route. StaminaPack tests pass; other tested failures reproduce on origin/main.
2026-10-12 18:47:22 +08:00

85 lines
4.3 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { registerHooks } from 'node:module';
import crypto from 'node:crypto';
const state = { orders: [], requests: [] };
globalThis.__staminaCloud = {
database: () => ({ collection: name => ({
add: async data => { state.orders.push({ ...data, collection: name }); },
where: () => ({
getOne: async () => ({ data: { _id: 'u', openid: 'o', session_key: 'test-session' } }),
update: async () => ({ updated: 1 }),
}),
}) }),
shared: { get: () => 'test-token' },
storage: { bucket: () => ({ readFile: async () => ({ Body: { transformToString: async () => 'test-key' } }) }) },
};
globalThis.__staminaCrypto = {
...crypto, createSign: () => ({ update() {}, sign: () => 'test-signature' }),
};
registerHooks({
resolve(specifier, context, nextResolve) {
const mocks = {
'@lafjs/cloud': 'export default globalThis.__staminaCloud;',
'@/limitedTimeEvent': 'export const starterPackOrderMetadata = () => ({});',
'@/pyramid/api': 'export const isPyramid = () => false; export const createOrder = () => { throw new Error("Unexpected pyramid order"); };',
'@/goldMiner/payment': 'export const createNativeOrder = () => { throw new Error("Unexpected gold miner order"); };',
'@/goldMiner/legacyPayment': 'export const recordCustomerLink = () => { throw new Error("Unexpected gold miner link"); };',
'@/passCheckUpgrade': 'export const passOrderFields = async () => ({}); export const PASS_PRICES = {};',
'@/jungleConfig': 'export const getJunglePaidTierByProductId = () => null;',
crypto: 'export default globalThis.__staminaCrypto; export const createHmac = globalThis.__staminaCrypto.createHmac;',
};
if (Object.hasOwn(mocks, specifier)) return { url: 'data:text/javascript,' + encodeURIComponent(mocks[specifier]), shortCircuit: true };
return nextResolve(specifier, context);
},
});
const [{ default: android }, { default: ios }, { default: customerService }] = await Promise.all([
import('../functions/wx/orderPaySig.ts'), import('../functions/wx/iosorderPaySig.ts'), import('../functions/wx/KeFuInfo.ts'),
]);
test('StaminaPack native orders enforce 800 fen and one pack on Android and iOS', async () => {
const oldEnv = process.env.PAYMENT_APP_ENV, oldKey = process.env.WX_MIDAS_PAY_SIGN_KEY;
process.env.PAYMENT_APP_ENV = 'test'; process.env.WX_MIDAS_PAY_SIGN_KEY = 'test-only';
try {
for (const create of [android, ios]) {
const result = await create({ body: { uid: 'u', itemid: 'StaminaPack', itemPrice: 1, itemCount: 99 } });
assert.equal(result.code, 1);
const signed = JSON.parse(result.data.signData);
assert.equal(signed.productId, 'StaminaPack');
assert.equal(signed.goodsPrice, 800);
assert.equal(signed.buyQuantity, 1);
assert.equal(state.orders.at(-1).goodsPrice, 800);
assert.equal(state.orders.at(-1).itemCount, 1);
}
} finally {
if (oldEnv === undefined) delete process.env.PAYMENT_APP_ENV; else process.env.PAYMENT_APP_ENV = oldEnv;
if (oldKey === undefined) delete process.env.WX_MIDAS_PAY_SIGN_KEY; else process.env.WX_MIDAS_PAY_SIGN_KEY = oldKey;
}
});
test('legacy iOS customer service creates an 8-yuan StaminaPack order and payment link', async () => {
state.orders = []; state.requests = [];
const oldFetch = globalThis.fetch;
globalThis.fetch = async (url, options) => {
state.requests.push({ url, body: JSON.parse(options.body) });
return { ok: true, json: async () => ({ prepay_id: 'test-prepay', errcode: 0 }) };
};
try {
await customerService({ body: { FromUserName: 'o', SessionFrom: JSON.stringify({
tpye: 'ios', propName: 'StaminaPack', price: 1, count: 99, outTradeNo: 'test-stamina-order',
}) } });
const order = state.orders.at(-1);
assert.equal(order.itemid, 'StaminaPack');
assert.equal(order.goodsPrice, 800);
assert.equal(order.itemCount, 1);
assert.equal(state.requests.find(r => r.url.includes('/v3/pay/transactions/jsapi')).body.amount.total, 800);
const link = state.requests.find(r => r.body.msgtype === 'link').body.link;
assert.equal(link.description, '金额:8.00元');
const data = JSON.parse(decodeURIComponent(link.url.split('#data=')[1]));
assert.equal(data.price, 800);
assert.equal(data.quantity, 1);
} finally { globalThis.fetch = oldFetch; }
});