Files
Jyotisha/frontend/tests/feature-pricing-contract.test.ts
T
jesse-uxandCursor 006b067e82
Independent Staging Quality Gate / validate (push) Failing after 14m48s
Independent Staging Quality Gate / publish (push) Skipped
fix(synastry): open the match page from the live flag and price it at 19 credits
The closed sentence was prerendered when the build had no database. The page now asks for the flag in the browser, and synastry.full is published at 19 credits.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-20 12:52:28 +08:00

87 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import test from "node:test";
const management = readFileSync(
fileURLToPath(new URL("../src/components/admin/feature-pricing-management.tsx", import.meta.url)),
"utf8",
);
const simulator = readFileSync(
fileURLToPath(new URL("../src/components/admin/pricing-simulator.tsx", import.meta.url)),
"utf8",
);
const labels = readFileSync(
fileURLToPath(new URL("../src/lib/admin/feature-pricing-labels.ts", import.meta.url)),
"utf8",
);
const readPolicyMigration = readFileSync(
fileURLToPath(new URL("../supabase/migrations/20260831020000_feature_pricing_admin_runtime_read_policy.sql", import.meta.url)),
"utf8",
);
const source = readFileSync(
fileURLToPath(new URL("../src/lib/feature-pricing.ts", import.meta.url)),
"utf8",
);
test("feature pricing resolver calls the server-side RPC and validates its row", () => {
assert.match(source, /rpc\("resolve_feature_pricing"/);
assert.match(source, /p_feature_key: featureKey/);
assert.match(source, /p_model_id: modelId/);
assert.match(source, /feature_pricing_invalid_response/);
});
test("feature pricing resolver has explicit fail-closed errors", () => {
assert.match(source, /error\.message/);
assert.match(source, /feature_pricing_unavailable/);
assert.match(source, /class FeaturePricingError/);
});
test("feature pricing admin uses readable labels while preserving stable values", () => {
for (const [value, label] of [
["chat.standard", "标准咨询"],
["chat.premium", "高级咨询"],
["rectification", "生时校正"],
["report.full", "完整报告"],
["synastry.full", "完整合盘报告"],
["report.export", "报告导出"],
["profile.extra", "额外档案"],
["standard", "标准模型"],
["premium", "高级模型"],
["internal", "内部模型"],
]) {
assert.match(labels, new RegExp(`value: "${value.replace(".", "\\.")}"`));
assert.match(labels, new RegExp(label));
}
assert.match(management, /FEATURE_PRICING_OPTIONS/);
assert.match(management, /MODEL_TIER_OPTIONS/);
assert.match(management, /featurePricingLabel/);
assert.match(management, /modelTierLabel/);
assert.match(management, /草稿(draft)/);
assert.match(management, /已发布(published)/);
assert.match(simulator, /featurePricingLabel/);
assert.match(simulator, /modelTierLabel/);
});
test("synastry.full is published at 19 credits for every model tier", () => {
const pricing = readFileSync(
fileURLToPath(new URL("../supabase/migrations/20261020010000_synastry_full_pricing.sql", import.meta.url)),
"utf8",
);
assert.match(pricing, /'synastry\.full'/);
assert.match(pricing, /19/);
for (const tier of ["standard", "premium", "internal"]) {
assert.match(pricing, new RegExp(`'${tier}'`));
}
assert.match(pricing, /'published'/);
assert.match(pricing, /where not exists/);
});
test("admin runtime can list feature pricing rows through an explicit read-only RLS policy", () => {
assert.match(readPolicyMigration, /grant select on table public\.feature_pricing to admin_runtime/);
assert.match(readPolicyMigration, /create policy feature_pricing_admin_read on public\.feature_pricing/);
assert.match(readPolicyMigration, /for select to admin_runtime using \(true\)/);
assert.doesNotMatch(readPolicyMigration, /for (?:insert|update|delete|all)/);
});