57 lines
3.0 KiB
JavaScript
57 lines
3.0 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFile } from 'node:fs/promises';
|
|
import { stripTypeScriptTypes } from 'node:module';
|
|
|
|
// Exercise the real login recovery function without contacting the payment provider.
|
|
const source = await readFile(new URL('../functions/login.ts', import.meta.url), 'utf8');
|
|
const recoverySource = source.slice(source.indexOf('async function getIosOrder('), source.indexOf('async function sead('));
|
|
const buildRecovery = new Function('db', 'getOrder', 'sead', 'getClientIp', 'JungleConfig', `return (${stripTypeScriptTypes(recoverySource)});`);
|
|
|
|
function fixture(itemid, extra = {}) {
|
|
const user = { _id: 'user-1', openid: 'openid-1', rebornGiftTime: 0, rebornGiftCount: 3, pay_user: false };
|
|
const order = { outTradeNo: 'order-1', openid: user.openid, state: 0, type: 'ios', itemid, ...extra };
|
|
const providerCalls = [];
|
|
const db = { collection(name) {
|
|
const documents = name === 'users' ? [user] : [order];
|
|
return { where(query) {
|
|
const matched = documents.filter(row => Object.entries(query).every(([key, value]) => row[key] === value));
|
|
return {
|
|
async get() { return { data: structuredClone(matched) }; },
|
|
async getOne() { return { data: structuredClone(matched[0]) }; },
|
|
async update(patch) { matched.forEach(row => Object.assign(row, patch)); },
|
|
async remove() { assert.fail('recoverable activity orders must not be deleted'); },
|
|
};
|
|
} };
|
|
} };
|
|
const recover = buildRecovery(db, async orderId => {
|
|
providerCalls.push(orderId);
|
|
return { trade_state: 'SUCCESS' };
|
|
}, () => {}, () => '', { getJunglePaidTierByProductId: () => null });
|
|
return { user, order, providerCalls, recover };
|
|
}
|
|
|
|
for (const itemid of ['reborn_Gift', 'reborn_Gift_2', 'reborn_Gift_3', 'reborn_Gift_4', 'reborn_Gift_5']) {
|
|
test(`login preserves main iOS recovery for ${itemid} until client confirms delivery`, async () => {
|
|
const { user, order, providerCalls, recover } = fixture(itemid);
|
|
const before = Date.now();
|
|
const pending = await recover(user.openid, {});
|
|
assert.equal(user.rebornGiftCount, 0);
|
|
assert(user.rebornGiftTime >= before);
|
|
assert.equal(user.pay_user, true);
|
|
assert.equal(order.state, 1, 'confirmed payment stays pending for client reward delivery');
|
|
assert.deepEqual(pending.map(row => row.outTradeNo), ['order-1']);
|
|
assert.deepEqual(providerCalls, ['order-1']);
|
|
assert.deepEqual(await recover(user.openid, {}), [], 'state=1 goes through ordinary login recovery next time');
|
|
assert.deepEqual(providerCalls, ['order-1'], 'no second provider query for confirmed payment');
|
|
});
|
|
}
|
|
|
|
test('disabled gold miner orders never enter legacy iOS recovery', async () => {
|
|
const { user, order, providerCalls, recover } = fixture('gold_miner', { goldMiner: { confirmedAt: 0 }, time: 1 });
|
|
assert.deepEqual(await recover(user.openid, {}), []);
|
|
assert.deepEqual(providerCalls, []);
|
|
assert.equal(order.state, 0);
|
|
assert.equal(user.rebornGiftCount, 3);
|
|
});
|