Files
Jyotisha/frontend/tests/application-billing-contract.test.ts
T
Jesse_Chen 32b683ebef
Staging Backend Quality Gate / validate (push) Successful in 12m47s
Staging Backend Quality Gate / publish (push) Successful in 31m1s
fix(consult): preserve streaming across disconnects
2026-08-08 21:34:03 +08:00

76 lines
4.8 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");
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"\)/);
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:\$\{sessionId\}`/);
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("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.equal(consultRoute.match(/result\.totalUsage/g)?.length, 4);
});
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,/);
});