Files
Jyotisha/frontend/tests/application-billing-contract.test.ts
Jesse_Chen 0e5575a8a9
Independent Staging Quality Gate / validate (push) Has been cancelled
Independent Staging Quality Gate / publish (push) Has been cancelled
test: close local release gate regressions
2026-08-15 09:32:28 +08:00

101 lines
6.5 KiB
TypeScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { completeUsage } from "../src/lib/consultation-billing.ts";
const root = new URL("../", import.meta.url);
const rectificationRoute = readFileSync(new URL("src/app/api/rectification/agent/route.ts", root), "utf8");
const consultRoute = readFileSync(new URL("src/app/api/consult/route.ts", root), "utf8");
const packagesRoute = readFileSync(new URL("src/app/api/admin/packages/route.ts", root), "utf8");
function sourceBetween(source: string, start: string, end: string): string {
const startIndex = source.indexOf(start);
const endIndex = source.indexOf(end, startIndex + start.length);
assert.ok(startIndex >= 0, `missing start marker: ${start}`);
assert.ok(endIndex > startIndex, `missing end marker: ${end}`);
return source.slice(startIndex, endIndex);
}
test("Agentic rectification reuses one case-level usage authorization and the session-pinned model version", () => {
assert.match(rectificationRoute, /select\("id,messages,session_type,model_id,model_config_version,agentic_rectification_case_id"\)/);
assert.match(rectificationRoute, /resolveSessionLanguageModel\(\s*chatSession\.model_id,\s*chatSession\.model_config_version,?\s*\)/);
assert.match(rectificationRoute, /modelConfigVersion: selectedModel\.configVersion/);
assert.doesNotMatch(rectificationRoute, /loadLanguageModelCatalog|resolveLanguageModelFromCatalog|\bresolveLanguageModel\(|\bdefaultLanguageModel\(/);
assert.match(rectificationRoute, /const billingRequestPrefix = `rectification:case:\$\{caseId\}`/);
assert.match(rectificationRoute, /from\("usage_reservations"\)[\s\S]*\.eq\("feature_key", "rectification"\)[\s\S]*\.like\("request_id", `\$\{billingRequestPrefix\}%`\)/);
assert.match(rectificationRoute, /return reservations.length === 0[\s\S]*`\$\{billingRequestPrefix\}:retry:\$\{reservations.length\}`/);
assert.match(rectificationRoute, /authorizeUsage\(accounting, \{[\s\S]*requestId: billingRequestId/);
assert.match(rectificationRoute, /completeUsage\(accounting, userId, billingRequestId,/);
assert.match(rectificationRoute, /releaseUsage\(accounting, userId, billingRequestId,/);
});
test("free Agentic rectification turns bypass reservation, completion, and cancellation settlement", () => {
const reserveBilling = sourceBetween(rectificationRoute, "async reserve() {", "async complete(usage) {");
const completeBilling = sourceBetween(rectificationRoute, "async complete(usage) {", "async release() {");
const releaseBilling = sourceBetween(rectificationRoute, "async release() {", " };\n\n try {");
assert.match(rectificationRoute, /action: z\.enum\(\["opening", "message", "read_only"\]\)/);
assert.equal(rectificationRoute.match(/if \(action !== "message"\)/g)?.length, 3);
assert.match(reserveBilling, /if \(action !== "message"\) \{[\s\S]*return \{ success: true, status: 200 \};/);
assert.match(reserveBilling, /authorizeUsage\(/);
assert.match(completeBilling, /if \(action !== "message"\) return true;/);
assert.match(completeBilling, /completeUsage\(/);
assert.match(releaseBilling, /if \(action !== "message"\) return true;/);
assert.match(releaseBilling, /releaseUsage\(/);
});
test("standard consultation resolves and settles the session-pinned model version", () => {
assert.match(consultRoute, /sessionId: z\.string\(\)\.uuid\(\)/);
assert.match(consultRoute, /select\("id,model_id,model_config_version,session_type"\)/);
assert.match(consultRoute, /resolveSessionLanguageModel\(\s*chatSession\.model_id,\s*chatSession\.model_config_version,?\s*\)/);
assert.match(consultRoute, /actualModelId: selectedModel\.id/);
assert.match(consultRoute, /modelConfigVersion: selectedModel\.configVersion/);
});
test("standard consultation awaits real usage before durable response settlement", () => {
assert.doesNotMatch(consultRoute, /recordActualUsage|void usage\.then/);
assert.doesNotMatch(consultRoute, /inputTokens: 0,[\s\S]*outputTokens: 0,[\s\S]*costMicrousd: 0/);
assert.match(consultRoute, /async function usagePayload\(usage: Promise<\{ inputTokens\?: number; outputTokens\?: number \}>\)/);
assert.match(consultRoute, /const resolved = await usage;/);
assert.match(consultRoute, /const actualUsage = await usagePayload\(usage\);[\s\S]*p_actual_usage: actualUsage/);
assert.match(consultRoute, /function mergeUsage\(usages: Promise<Usage>\[\]\): Promise<Usage> \{[\s\S]*Promise\.all\(usages\)/);
assert.equal(consultRoute.match(/usages\.push\(result\.totalUsage\)/g)?.length, 2);
assert.equal(consultRoute.match(/usages\.push\(retried\.totalUsage\)/g)?.length, 2);
});
test("standard consultation forwards its stable reservation request as the usage event key", async () => {
const eventKey = "00000000-0000-4000-8000-000000000002";
const calls: Record<string, unknown>[] = [];
const accounting = {
async rpc(_rpcName: string, args: Record<string, unknown>) {
calls.push(args);
return {
data: { success: true, reservation_id: "00000000-0000-4000-8000-000000000003", credits: 9, error_code: null },
error: null,
};
},
};
const usage = { eventKey, actualModelId: "model", inputTokens: 1, outputTokens: 2, costMicrousd: 3, durationMs: 4 };
await completeUsage(accounting, "00000000-0000-4000-8000-000000000001", eventKey, usage);
await completeUsage(accounting, "00000000-0000-4000-8000-000000000001", eventKey, usage);
assert.deepEqual(calls.map((call) => (call.p_actual_usage as { eventKey: string }).eventKey), [eventKey, eventKey]);
assert.match(consultRoute, /eventKey: requestId,/);
assert.match(consultRoute, /const actualUsage = await usagePayload\(usage\);[\s\S]*p_actual_usage: actualUsage/);
assert.doesNotMatch(consultRoute, /eventKey:\s*(?:globalThis\.)?crypto\.randomUUID\(\)/);
});
test("legacy admin packages cannot mutate detached payment_packages", () => {
assert.doesNotMatch(packagesRoute, /payment_packages|queryAdminRows|insert into|update public/);
assert.match(packagesRoute, /export function GET\(request: Request\)/);
assert.match(packagesRoute, /const replacement = "\/api\/admin\/products"/);
assert.match(packagesRoute, /NextResponse\.redirect\(new URL\(replacement, request\.url\), 308\)/);
for (const method of ["POST", "PATCH", "DELETE"]) {
assert.match(packagesRoute, new RegExp(`export function ${method}\\(\\)`));
}
assert.match(packagesRoute, /status: 410/);
assert.match(packagesRoute, /error: "旧套餐写接口已停用,请使用统一商品管理。",[\s\S]*replacement,/);
});