159 lines
5.1 KiB
JavaScript
159 lines
5.1 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { registerHooks } from "node:module";
|
|
|
|
const monthlyCardUrl = new URL("../functions/monthlyCard.ts", import.meta.url);
|
|
const MONTH_CARD_DURATION = 30 * 24 * 60 * 60 * 1000;
|
|
const NOW = 1_800_000_000_000;
|
|
const state = { user: null, orders: [] };
|
|
|
|
globalThis.__monthlyCardCloudMock = {
|
|
database() {
|
|
return {
|
|
collection(name) {
|
|
return {
|
|
where(query) {
|
|
return {
|
|
async getOne() {
|
|
if (name === "order") {
|
|
return { data: state.orders.find((order) => matches(order, query)) || null };
|
|
}
|
|
return { data: state.user && matches(state.user, query) ? state.user : null };
|
|
},
|
|
async update(payload) {
|
|
if (name === "order") {
|
|
const orders = state.orders.filter((order) => matches(order, query));
|
|
orders.forEach((order) => Object.assign(order, payload));
|
|
return { updated: orders.length };
|
|
}
|
|
if (state.user && matches(state.user, query)) {
|
|
Object.assign(state.user, payload);
|
|
return { updated: 1 };
|
|
}
|
|
return { updated: 0 };
|
|
},
|
|
};
|
|
},
|
|
};
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
globalThis.__monthlyCardUtilsMock = {
|
|
checkToken(received, expected) {
|
|
return received === expected;
|
|
},
|
|
};
|
|
|
|
registerHooks({
|
|
resolve(specifier, context, nextResolve) {
|
|
if (specifier === "@lafjs/cloud") {
|
|
return { url: "data:text/javascript,export default globalThis.__monthlyCardCloudMock", shortCircuit: true };
|
|
}
|
|
if (specifier === "@/Utils") {
|
|
return { url: "data:text/javascript,export default globalThis.__monthlyCardUtilsMock", shortCircuit: true };
|
|
}
|
|
return nextResolve(specifier, context);
|
|
},
|
|
});
|
|
|
|
const { default: monthlyCard } = await import(monthlyCardUrl);
|
|
const realDateNow = Date.now;
|
|
Date.now = () => NOW;
|
|
test.after(() => { Date.now = realDateNow; });
|
|
|
|
function matches(target, query) {
|
|
return Object.entries(query || {}).every(([key, value]) => target?.[key] === value);
|
|
}
|
|
|
|
function reset({ monthCardTime = 0, monthCardReward = 0, granted = false } = {}) {
|
|
state.user = { _id: "user-1", openid: "openid-1", monthCardTime, monthCardReward };
|
|
state.orders = [{
|
|
outTradeNo: "order-1",
|
|
openid: "openid-1",
|
|
itemid: "month_Card",
|
|
state: 1,
|
|
monthCardGranted: granted,
|
|
}];
|
|
}
|
|
|
|
function save(outTradeNo = "order-1") {
|
|
return monthlyCard({ body: { action: "save", uid: "user-1", outTradeNo } });
|
|
}
|
|
|
|
test("first purchase starts a 30-day month card", async () => {
|
|
reset();
|
|
const result = await save();
|
|
assert.equal(result.code, 1);
|
|
assert.equal(state.user.monthCardTime, NOW + MONTH_CARD_DURATION);
|
|
assert.equal(state.user.monthCardReward, 0);
|
|
assert.equal(state.orders[0].state, 2);
|
|
assert.equal(state.orders[0].monthCardGranted, true);
|
|
});
|
|
|
|
test("renewal extends from the existing expiry and preserves today's claim state", async () => {
|
|
const currentExpiry = NOW + 12 * 24 * 60 * 60 * 1000;
|
|
const rewardTime = NOW - 60_000;
|
|
reset({ monthCardTime: currentExpiry, monthCardReward: rewardTime });
|
|
await save();
|
|
assert.equal(state.user.monthCardTime, currentExpiry + MONTH_CARD_DURATION);
|
|
assert.equal(state.user.monthCardReward, rewardTime);
|
|
});
|
|
|
|
test("three different paid orders accumulate 30, 60, then 90 days", async () => {
|
|
reset();
|
|
await save("order-1");
|
|
assert.equal(state.user.monthCardTime, NOW + MONTH_CARD_DURATION);
|
|
|
|
for (const [outTradeNo, days] of [["order-2", 60], ["order-3", 90]]) {
|
|
state.orders.push({
|
|
outTradeNo,
|
|
openid: "openid-1",
|
|
itemid: "month_Card",
|
|
state: 1,
|
|
monthCardGranted: false,
|
|
});
|
|
await save(outTradeNo);
|
|
assert.equal(state.user.monthCardTime, NOW + days * 24 * 60 * 60 * 1000);
|
|
}
|
|
});
|
|
|
|
test("expired card restarts from now and resets the claim state", async () => {
|
|
reset({ monthCardTime: NOW - 1, monthCardReward: NOW - 60_000 });
|
|
await save();
|
|
assert.equal(state.user.monthCardTime, NOW + MONTH_CARD_DURATION);
|
|
assert.equal(state.user.monthCardReward, 0);
|
|
});
|
|
|
|
test("the same paid order cannot extend the card twice", async () => {
|
|
reset();
|
|
await save();
|
|
const firstExpiry = state.user.monthCardTime;
|
|
const result = await save();
|
|
assert.equal(result.code, 1);
|
|
assert.equal(result.data.alreadyGranted, true);
|
|
assert.equal(state.user.monthCardTime, firstExpiry);
|
|
});
|
|
|
|
test("a legacy completed order without a grant marker cannot be replayed", async () => {
|
|
reset({ monthCardTime: NOW + MONTH_CARD_DURATION });
|
|
state.orders[0].state = 2;
|
|
delete state.orders[0].monthCardGranted;
|
|
const expiry = state.user.monthCardTime;
|
|
|
|
const result = await save();
|
|
|
|
assert.equal(result.code, 1);
|
|
assert.equal(result.data.alreadyGranted, true);
|
|
assert.equal(state.user.monthCardTime, expiry);
|
|
});
|
|
|
|
test("saving without a paid order number is rejected", async () => {
|
|
reset();
|
|
const result = await save("");
|
|
assert.equal(result.code, 0);
|
|
assert.match(result.msg, /订单号/);
|
|
assert.equal(state.user.monthCardTime, 0);
|
|
});
|