import test from "node:test"; import assert from "node:assert/strict"; import { registerHooks } from "node:module"; import { readFile } from "node:fs/promises"; const state = { users: [], calls: [], failUpdate: false }; const clone = value => structuredClone(value); const valueAt = (row, path) => path.split(".").reduce((value, key) => value?.[key], row); const matches = (row, query) => Object.entries(query).every(([key, value]) => value?.$ne !== undefined ? valueAt(row, key) !== value.$ne : valueAt(row, key) === value); globalThis.__welfareCloudMock = { database: () => ({ command: { neq: value => ({ $ne: value }) }, collection(name) { assert.equal(name, "users"); return { where(query) { return { async update(payload) { state.calls.push({ query: clone(query), payload: clone(payload) }); // Yield before matching so concurrent callers all reach the database operation. await Promise.resolve(); if (state.failUpdate) return { ok: false, updated: 0 }; const user = state.users.find(row => matches(row, query)); if (!user) return { ok: true, updated: 0 }; for (const [path, value] of Object.entries(payload)) { const parts = path.split("."); const key = parts.pop(); const target = parts.reduce((row, part) => row[part] ??= {}, user); target[key] = clone(value); } return { ok: true, updated: 1 }; }, async getOne() { return { data: clone(state.users.find(row => matches(row, query)) ?? null) }; }, }; }, }; }, }), }; registerHooks({ resolve(specifier, context, nextResolve) { if (specifier === "@lafjs/cloud") { return { url: "data:text/javascript,export default globalThis.__welfareCloudMock", shortCircuit: true }; } return nextResolve(specifier, context); }, }); const { default: record } = await import("../functions/miniProgramWelfare.ts"); function reset(extra = {}) { state.users = [{ _id: "u1", token: "secret", coinAmount: 123, freezeAmount: 4, hammerAmount: 5, magicAmount: 6, ...extra, }, { _id: "u2", token: "other" }]; state.calls = []; state.failUpdate = false; } const call = (extra = {}) => record({ body: { uid: "u1", token: "secret", type: "favorite", ...extra } }); test("legacy user: record each reward independently without awarding resources", async () => { for (const firstType of ["favorite", "desktop"]) { reset(); const before = clone(state.users); const first = await call({ type: firstType }); assert.equal(first.code, 1); assert.deepEqual(first.data, { miniProgramWelfare: { favorite: firstType === "favorite", desktop: firstType === "desktop" }, changed: true, }); const second = await call({ type: firstType === "favorite" ? "desktop" : "favorite" }); assert.deepEqual(second.data, { miniProgramWelfare: { favorite: true, desktop: true }, changed: true }); const { miniProgramWelfare, ...unchanged } = state.users[0]; assert.deepEqual(miniProgramWelfare, { [firstType]: true, [firstType === "favorite" ? "desktop" : "favorite"]: true }); assert.deepEqual(unchanged, before[0]); assert.deepEqual(state.users[1], before[1]); } }); test("repeat calls succeed without resetting either reward or accepting client state", async () => { reset({ miniProgramWelfare: { favorite: true, desktop: true } }); const before = clone(state.users); const result = await call({ miniProgramWelfare: { favorite: false, desktop: false }, claimed: false }); assert.deepEqual(result.data, { miniProgramWelfare: { favorite: true, desktop: true }, changed: false }); assert.deepEqual(state.users, before); }); test("simultaneous retries change the same reward exactly once", async () => { reset({ miniProgramWelfare: { favorite: false, desktop: false } }); const results = await Promise.all(Array.from({ length: 20 }, () => call())); assert.ok(results.every(result => result.code === 1 && result.data.miniProgramWelfare.favorite)); assert.equal(results.filter(result => result.data.changed).length, 1); assert.deepEqual(state.users[0].miniProgramWelfare, { favorite: true, desktop: false }); }); test("simultaneous different rewards retain both marks for a legacy user", async () => { reset(); const results = await Promise.all([call(), call({ type: "desktop" }), call(), call({ type: "desktop" })]); assert.ok(results.every(result => result.code === 1)); assert.equal(results.filter(result => result.data.changed).length, 2); assert.deepEqual(state.users[0].miniProgramWelfare, { favorite: true, desktop: true }); }); test("invalid inputs and unsupported game are rejected before any write", async () => { for (const input of [ { uid: "" }, { uid: { $ne: null } }, { uid: 1 }, { token: "" }, { token: null }, { token: { $ne: null } }, { type: "" }, { type: "reset" }, { type: "__proto__" }, { type: ["favorite"] }, { gameName: "iaa" }, ]) { reset(); const before = clone(state.users); assert.equal((await call(input)).code, 0); assert.deepEqual(state.users, before); assert.equal(state.calls.length, 0); } assert.equal((await record({})).code, 0); }); test("missing users, missing stored tokens and mismatched tokens cannot write or read state", async () => { for (const [extra, input] of [ [{}, { uid: "missing" }], [{}, { uid: "u2" }], [{}, { token: "wrong" }], [{ token: undefined }, {}], ]) { reset(extra); const before = clone(state.users); const result = await call(input); assert.equal(result.code, 0); assert.equal(result.data, null); assert.deepEqual(state.users, before); } }); test("database update failures do not report success", async () => { reset(); state.failUpdate = true; const before = clone(state.users); assert.equal((await call()).code, 0); assert.deepEqual(state.users, before); }); test("function configuration exposes POST only", async () => { const yaml = await readFile(new URL("../functions/miniProgramWelfare.yaml", import.meta.url), "utf8"); assert.match(yaml, /^name: miniProgramWelfare$/m); assert.match(yaml, /methods:\s*\n\s*- POST\s*\ntags:/); });