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); });