bab0718700
Web export now calls the same full pack as the long skill report and caches an owner-only Markdown download. Appendix failure stays unavailable and does not change the main report status. Co-authored-by: Cursor <cursoragent@cursor.com>
182 lines
7.7 KiB
TypeScript
182 lines
7.7 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { readFileSync } from "node:fs";
|
|
import { fileURLToPath } from "node:url";
|
|
import test from "node:test";
|
|
|
|
const routeSource = readFileSync(
|
|
new URL("../src/app/api/reports/[reportId]/professional-reference/route.ts", import.meta.url),
|
|
"utf8",
|
|
);
|
|
|
|
const frontendRoot = fileURLToPath(new URL("../", import.meta.url));
|
|
|
|
function executeReadyReportExport() {
|
|
const script = String.raw`
|
|
import { mock } from "node:test";
|
|
import { pathToFileURL } from "node:url";
|
|
import { Agent } from "@mastra/core/agent";
|
|
|
|
const moduleUrl = (path) => pathToFileURL(process.cwd() + "/" + path).href;
|
|
// Node 22 ignores the unsupported exports mock option; keep the route aliases explicit.
|
|
mock.module("@/lib/personal-report-entitlement", { namedExports: {
|
|
checkSameOrigin: () => ({ ok: true }),
|
|
resolveAllowedReportOrigins: () => [],
|
|
}});
|
|
mock.module("@/lib/personal-report-service", { namedExports: {
|
|
createSupabasePersonalReportService: () => ({
|
|
getOwnedById: async () => ({ status: "ready", requestId: "123e4567-e89b-12d3-a456-426614174111" }),
|
|
}),
|
|
}});
|
|
mock.module("@/lib/supabase/admin", { namedExports: {
|
|
createAdminSupabaseClient: () => { throw new Error("admin unused in this unit test"); },
|
|
}});
|
|
mock.module("@/lib/report-candidate-range", { namedExports: {
|
|
loadReportCandidateRange: async () => null,
|
|
}});
|
|
mock.module("@/lib/personal-report-longform-appendix", { namedExports: {
|
|
LONGFORM_APPENDIX_TABLE: "personal_report_longform_appendices",
|
|
parseLongformAppendixRow: () => null,
|
|
nextLongformAppendixState: () => ({ status: "ready", attemptCount: 0, markdown: "# Professional reference", contentSha256: "ab", lastErrorCode: null }),
|
|
}});
|
|
mock.module("@/lib/supabase/config", { namedExports: {
|
|
isSupabaseConfigurationError: () => false,
|
|
}});
|
|
const profileQuery = {
|
|
select() { return this; },
|
|
eq() { return this; },
|
|
async maybeSingle() { return { data: {
|
|
birth_date: "1990-01-02",
|
|
reported_birth_time: "03:04",
|
|
latitude: 39.9,
|
|
longitude: 116.4,
|
|
timezone_offset: 8,
|
|
ayanamsa: "lahiri",
|
|
}, error: null }; },
|
|
};
|
|
mock.module("@/lib/supabase/server", { namedExports: {
|
|
createServerSupabaseClient: async () => ({
|
|
auth: { getUser: async () => ({ data: { user: { id: "user-1" } }, error: null }) },
|
|
from: () => profileQuery,
|
|
}),
|
|
}});
|
|
|
|
let modelCalls = 0;
|
|
Agent.prototype.generate = async () => {
|
|
modelCalls += 1;
|
|
throw new Error("professional export must not call a model");
|
|
};
|
|
let telemetryEvents = 0;
|
|
console.info = (...args) => {
|
|
if (args[0] === "[personal-report-agent]") telemetryEvents += 1;
|
|
};
|
|
const upstream = [];
|
|
globalThis.fetch = async (input, init) => {
|
|
upstream.push({
|
|
url: String(input),
|
|
method: init?.method,
|
|
body: JSON.parse(String(init?.body)),
|
|
});
|
|
return new Response(JSON.stringify({ format: "markdown", markdown: "# Professional reference" }), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
};
|
|
|
|
const { POST } = await import(moduleUrl("src/app/api/reports/[reportId]/professional-reference/route.ts"));
|
|
const response = await POST(
|
|
new Request("https://staging.jyotisha.chat/api/reports/123e4567-e89b-12d3-a456-426614174000/professional-reference", {
|
|
method: "POST",
|
|
headers: { origin: "https://staging.jyotisha.chat" },
|
|
}),
|
|
{ params: Promise.resolve({ reportId: "123e4567-e89b-12d3-a456-426614174000" }) },
|
|
);
|
|
console.log(JSON.stringify({
|
|
status: response.status,
|
|
body: await response.json(),
|
|
modelCalls,
|
|
telemetryEvents,
|
|
upstream,
|
|
}));
|
|
`;
|
|
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);
|
|
return JSON.parse(result.stdout.trim()) as {
|
|
status: number;
|
|
body: { format: string; markdown: string };
|
|
modelCalls: number;
|
|
telemetryEvents: number;
|
|
upstream: Array<{ url: string; method: string; body: Record<string, unknown> }>;
|
|
};
|
|
}
|
|
|
|
test("professional reference route authenticates, checks origin, owner and ready status", () => {
|
|
assert.match(routeSource, /createServerSupabaseClient/);
|
|
assert.match(routeSource, /supabase\.auth\.getUser\(\)/);
|
|
assert.match(routeSource, /checkSameOrigin/);
|
|
assert.match(routeSource, /resolveAllowedReportOrigins/);
|
|
assert.match(routeSource, /getOwnedById\(user\.id, reportId\)/);
|
|
assert.match(routeSource, /report\.status !== "ready"/);
|
|
assert.match(routeSource, /status: 401/);
|
|
assert.match(routeSource, /status: 403/);
|
|
assert.match(routeSource, /status: 404/);
|
|
assert.match(routeSource, /status: 409/);
|
|
});
|
|
|
|
test("birth data stays server-owned and the route calls only the public Python export", () => {
|
|
assert.match(routeSource, /\.from\("profiles"\)/);
|
|
assert.match(routeSource, /select\(ACCOUNT_BIRTH_SELECT\)/);
|
|
assert.match(routeSource, /globalBirthProfileFromAccountRow/);
|
|
assert.match(routeSource, /\/api\/professional_report_reference/);
|
|
assert.match(routeSource, /format: "markdown"/);
|
|
assert.match(routeSource, /packs: \["full"\]/);
|
|
assert.match(routeSource, /target_year/);
|
|
assert.match(routeSource, /birth_time_accuracy/);
|
|
assert.match(routeSource, /loadReportCandidateRange/);
|
|
assert.match(routeSource, /LONGFORM_APPENDIX_TABLE/);
|
|
assert.match(routeSource, /from "@\/lib\/personal-report-longform-appendix"/);
|
|
assert.doesNotMatch(routeSource, /request\.json\(/);
|
|
assert.doesNotMatch(routeSource, /mastra|writer|billing|personal_report_sections/i);
|
|
assert.doesNotMatch(routeSource, /from\("personal_reports"\)/);
|
|
});
|
|
|
|
test("ready report export calls the Python endpoint without model calls or writer telemetry", () => {
|
|
const result = executeReadyReportExport();
|
|
assert.equal(result.status, 200);
|
|
assert.deepEqual(result.body, { format: "markdown", markdown: "# Professional reference" });
|
|
assert.equal(result.modelCalls, 0);
|
|
assert.equal(result.telemetryEvents, 0);
|
|
assert.equal(result.upstream.length, 1);
|
|
assert.equal(result.upstream[0].url, "http://127.0.0.1:5200/api/professional_report_reference");
|
|
assert.equal(result.upstream[0].method, "POST");
|
|
assert.equal(result.upstream[0].body.year, 1990);
|
|
assert.equal(result.upstream[0].body.month, 1);
|
|
assert.equal(result.upstream[0].body.day, 2);
|
|
assert.equal(result.upstream[0].body.hour, 3);
|
|
assert.equal(result.upstream[0].body.minute, 4);
|
|
assert.equal(result.upstream[0].body.lat, 39.9);
|
|
assert.equal(result.upstream[0].body.lon, 116.4);
|
|
assert.equal(result.upstream[0].body.tz, 8);
|
|
assert.equal(result.upstream[0].body.ayanamsa, "lahiri");
|
|
assert.equal(result.upstream[0].body.format, "markdown");
|
|
assert.deepEqual(result.upstream[0].body.packs, ["full"]);
|
|
assert.match(String(result.upstream[0].body.today), /^\d{4}-\d{2}-\d{2}$/);
|
|
assert.equal(result.upstream[0].body.target_year, Number(String(result.upstream[0].body.today).slice(0, 4)));
|
|
assert.equal(result.upstream[0].body.age, Number(result.upstream[0].body.target_year) - 1990);
|
|
assert.equal(result.upstream[0].body.birth_time_accuracy, "confirmed");
|
|
assert.equal(result.upstream[0].body.candidate_range, undefined);
|
|
});
|
|
|
|
test("busy upstream responses preserve 429 and Retry-After", () => {
|
|
assert.match(routeSource, /upstream\.status/);
|
|
assert.match(routeSource, /upstream\.headers\.get\("retry-after"\)/);
|
|
assert.match(routeSource, /"Retry-After": retryAfter/);
|
|
});
|