The second review still failed on missing database and route coverage, an unmarked relationship structure, a stuck English lease, and a sidebar that fetched the product switch itself. Co-Authored-By: Grok (agent loop) Co-authored-by: Cursor <cursoragent@cursor.com>
152 lines
7.0 KiB
TypeScript
152 lines
7.0 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { fileURLToPath } from "node:url";
|
|
import test from "node:test";
|
|
|
|
const frontendRoot = fileURLToPath(new URL("../", import.meta.url));
|
|
const otherId = "11111111-1111-4111-8111-111111111111";
|
|
const selfRow = {
|
|
name: "虚构甲",
|
|
birth_date: "1991-04-07",
|
|
reported_birth_time: "09:15:00",
|
|
birth_time_status: "reported",
|
|
latitude: 31.2,
|
|
longitude: 121.4,
|
|
timezone_offset: 8,
|
|
timezone_id: "Asia/Shanghai",
|
|
ayanamsa: "lahiri",
|
|
};
|
|
|
|
function runRoute(script: string): { status: number; body: Record<string, unknown>; charged: boolean } {
|
|
const result = spawnSync(process.execPath, [
|
|
"--experimental-test-module-mocks",
|
|
"--import",
|
|
"tsx",
|
|
"--input-type=module",
|
|
"--eval",
|
|
script,
|
|
], { cwd: frontendRoot, encoding: "utf8" });
|
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
const line = result.stdout.trim().split("\n").at(-1) ?? "{}";
|
|
return JSON.parse(line) as { status: number; body: Record<string, unknown>; charged: boolean };
|
|
}
|
|
|
|
function script(input: { product: boolean; same: boolean; missingOther: boolean; reused: boolean }): string {
|
|
return String.raw`
|
|
import { mock } from "node:test";
|
|
import { pathToFileURL } from "node:url";
|
|
const moduleUrl = (path) => pathToFileURL(process.cwd() + "/" + path).href;
|
|
const selfRow = ${JSON.stringify(selfRow)};
|
|
const otherId = ${JSON.stringify(otherId)};
|
|
let charged = false;
|
|
mock.module("server-only", { namedExports: {} });
|
|
mock.module("@/mastra", { namedExports: { runConsultationWorkflow() { throw new Error("mastra"); } } });
|
|
mock.module("@/mastra/personal-report", { namedExports: {
|
|
PersonalReportAgentOutputError: class PersonalReportAgentOutputError extends Error {},
|
|
createPersonalReportAgent() { throw new Error("agent"); },
|
|
} });
|
|
mock.module("@/lib/product-access", { namedExports: { isProductEnabled: async () => ${input.product ? "true" : "false"} } });
|
|
mock.module("@/lib/consultation-billing", { namedExports: {
|
|
authorizeUsage() { charged = true; return { success: true }; },
|
|
completeUsage() { charged = true; },
|
|
releaseUsage() {},
|
|
} });
|
|
mock.module("@/lib/personal-report-service", { namedExports: {
|
|
createSupabasePersonalReportService: () => ({ getOwnedById: async () => null }),
|
|
createPersonalReportDataClient: () => ({ from: () => ({}) }),
|
|
} });
|
|
function query(table) {
|
|
const filters = [];
|
|
const q = {
|
|
select() { return q; },
|
|
eq(column, value) { filters.push([column, value]); return q; },
|
|
in() { return q; },
|
|
gte() { return q; },
|
|
is() { return q; },
|
|
or() { return q; },
|
|
order() { return q; },
|
|
limit() { return q; },
|
|
maybeSingle() {
|
|
if (table === "profiles") return Promise.resolve({ data: selfRow, error: null });
|
|
if (table === "chart_profiles") {
|
|
return Promise.resolve({ data: ${input.missingOther ? "null" : "{ ...selfRow, id: otherId, user_id: 'user-1', role: 'other' }"}, error: null });
|
|
}
|
|
if (table === "personal_reports") {
|
|
return Promise.resolve({ data: ${input.reused ? "{ id: 'ready-1', request_id: 'req-1', status: 'ready', created_at: '2026-10-19T00:00:00Z' }" : "null"}, error: null });
|
|
}
|
|
return Promise.resolve({ data: null, error: null });
|
|
},
|
|
then(resolve, reject) { return Promise.resolve({ data: [], error: null }).then(resolve, reject); },
|
|
};
|
|
return q;
|
|
}
|
|
const client = { auth: { getUser: async () => ({ data: { user: { id: "user-1" } }, error: null }) }, from: (table) => query(table) };
|
|
mock.module("@/lib/supabase/server", { namedExports: { createServerSupabaseClient: async () => client } });
|
|
mock.module("@/lib/supabase/admin", { namedExports: { createAdminSupabaseClient: () => client } });
|
|
const reports = await import(moduleUrl("src/app/api/reports/route.ts"));
|
|
const response = await reports.POST(new Request("https://staging.jyotisha.chat/api/reports", {
|
|
method: "POST",
|
|
headers: { "content-type": "application/json" },
|
|
body: JSON.stringify({
|
|
reportType: "synastry_full",
|
|
subjectA: "self",
|
|
subjectB: ${input.same ? '"self"' : "otherId"},
|
|
}),
|
|
}));
|
|
console.log(JSON.stringify({ status: response.status, body: await response.json(), charged }));
|
|
`;
|
|
}
|
|
|
|
test("the same person is rejected before billing", () => {
|
|
const result = runRoute(script({ product: true, same: true, missingOther: false, reused: false }));
|
|
assert.equal(result.status, 400);
|
|
assert.equal(result.body.code, "same_subject");
|
|
assert.equal(result.charged, false);
|
|
});
|
|
|
|
test("someone else's id is rejected before billing", () => {
|
|
const result = runRoute(script({ product: true, same: false, missingOther: true, reused: false }));
|
|
assert.equal(result.status, 409);
|
|
assert.equal(result.charged, false);
|
|
assert.match(JSON.stringify(result.body), /不会扣点/);
|
|
});
|
|
|
|
test("an unchanged pair is reused without a new reservation", () => {
|
|
const result = runRoute(script({ product: true, same: false, missingOther: false, reused: true }));
|
|
assert.equal(result.status, 200);
|
|
assert.equal(result.body.reused, true);
|
|
assert.equal(result.charged, false);
|
|
});
|
|
|
|
test("a closed product switch rejects the report before billing", () => {
|
|
const result = runRoute(script({ product: false, same: false, missingOther: true, reused: false }));
|
|
assert.equal(result.status, 503);
|
|
assert.equal(result.body.code, "compatibility_product_disabled");
|
|
assert.equal(result.charged, false);
|
|
});
|
|
|
|
test("the english route stays closed when the product switch is off", () => {
|
|
const scriptText = String.raw`
|
|
import { mock } from "node:test";
|
|
import { pathToFileURL } from "node:url";
|
|
const moduleUrl = (path) => pathToFileURL(process.cwd() + "/" + path).href;
|
|
mock.module("server-only", { namedExports: {} });
|
|
mock.module("@/lib/product-access", { namedExports: { isProductEnabled: async () => false } });
|
|
mock.module("@/lib/supabase/server", { namedExports: {
|
|
createServerSupabaseClient: async () => ({ auth: { getUser: async () => ({ data: { user: { id: "user-1" } } }) } }),
|
|
} });
|
|
mock.module("@/lib/supabase/admin", { namedExports: { createAdminSupabaseClient: () => ({}) } });
|
|
mock.module("@/lib/personal-report-service", { namedExports: {
|
|
createSupabasePersonalReportService: () => ({ getOwnedById: async () => null }),
|
|
} });
|
|
const route = await import(moduleUrl("src/app/api/reports/[reportId]/english/route.ts"));
|
|
const response = await route.POST(new Request("https://staging.jyotisha.chat/api/reports/11111111-1111-4111-8111-111111111111/english", { method: "POST" }), {
|
|
params: Promise.resolve({ reportId: "11111111-1111-4111-8111-111111111111" }),
|
|
});
|
|
console.log(JSON.stringify({ status: response.status, body: await response.json(), charged: false }));
|
|
`;
|
|
const result = runRoute(scriptText);
|
|
assert.equal(result.status, 503);
|
|
assert.equal(result.body.code, "compatibility_product_disabled");
|
|
});
|