f4a2ba86ae
Report pages now scroll inside the chat shell lock, and admin product save forks a draft or retires a published plan instead of rejecting with a generic constraint error. Co-authored-by: Cursor <cursoragent@cursor.com>
126 lines
5.8 KiB
TypeScript
126 lines
5.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { fileURLToPath } from "node:url";
|
|
import test from "node:test";
|
|
|
|
import { startPostgresFixture } from "./helpers/postgres-fixture.ts";
|
|
|
|
const runnerPath = fileURLToPath(new URL("../scripts/db-migrate.mjs", import.meta.url));
|
|
const ids = {
|
|
billing: "81000000-0000-4000-8000-000000000001",
|
|
support: "81000000-0000-4000-8000-000000000002",
|
|
};
|
|
const publishedMonthly = "00000000-0000-4000-8000-000000000902";
|
|
const publishedYearly = "00000000-0000-4000-8000-000000000903";
|
|
const userPayload = JSON.stringify([
|
|
{
|
|
featureKey: "chat.standard",
|
|
allowanceType: "unlimited",
|
|
allowanceCount: null,
|
|
resetPeriod: "billing_period",
|
|
modelTier: "standard",
|
|
fairUsePolicyId: "standard_monthly",
|
|
metadata: { dayLimit: 100, minuteLimit: 6, billingLimit: 2000 },
|
|
},
|
|
{
|
|
featureKey: "rectification",
|
|
allowanceType: "quota",
|
|
allowanceCount: 1,
|
|
resetPeriod: "billing_period",
|
|
modelTier: "standard",
|
|
fairUsePolicyId: null,
|
|
metadata: {},
|
|
},
|
|
{
|
|
featureKey: "report.full",
|
|
allowanceType: "quota",
|
|
allowanceCount: 1,
|
|
resetPeriod: "billing_period",
|
|
modelTier: "standard",
|
|
fairUsePolicyId: null,
|
|
metadata: {},
|
|
},
|
|
]).replaceAll("'", "''");
|
|
|
|
test("saving a published product forks a draft and delete retires or removes it", async () => {
|
|
const fixture = startPostgresFixture();
|
|
const sql = (statement: string) => fixture.psql(statement);
|
|
const sqlAsOwner = (statement: string) => fixture.psqlAs(
|
|
"schema_owner",
|
|
"schema-owner-test-password",
|
|
statement,
|
|
);
|
|
const expectSqlError = (statement: string, pattern: RegExp) => {
|
|
assert.throws(() => sqlAsOwner(statement), pattern);
|
|
};
|
|
|
|
try {
|
|
const migration = spawnSync(process.execPath, [runnerPath], {
|
|
encoding: "utf8",
|
|
env: {
|
|
...process.env,
|
|
SCHEMA_DATABASE_URL: fixture.connectionUrl("schema_owner", "schema-owner-test-password"),
|
|
},
|
|
});
|
|
assert.equal(migration.status, 0, migration.stderr);
|
|
assert.match(migration.stdout, /applied 20260818010000_admin_product_catalog_mutations\.sql/);
|
|
|
|
fixture.psqlAs(
|
|
"identity_runtime",
|
|
"identity-runtime-test-password",
|
|
`insert into identity.users (id,name,email,email_verified,email_verified_at,role) values
|
|
('${ids.billing}','Billing','billing-product@example.com',true,now(),'admin'),
|
|
('${ids.support}','Support','support-product@example.com',true,now(),'user')`,
|
|
);
|
|
sql(`
|
|
insert into public.admin_users(user_id,created_by) values
|
|
('${ids.billing}','${ids.billing}'),('${ids.support}','${ids.billing}');
|
|
insert into public.admin_user_roles(admin_user_id,role_id,assigned_by)
|
|
select v.user_id,r.id,'${ids.billing}'::uuid
|
|
from (values ('${ids.billing}'::uuid,'billing_admin'),('${ids.support}'::uuid,'support')) v(user_id,role_code)
|
|
join public.admin_roles r on r.code=v.role_code
|
|
`);
|
|
|
|
expectSqlError(
|
|
`select public.admin_save_product_draft('${ids.support}','${publishedMonthly}','standard_monthly','标准月卡','会员期内标准 AI 咨询不限点数,受合理使用规则约束','subscription','month',1,9900,'CNY',false,20,false,'${userPayload}'::jsonb,'support must not write','product-denied')`,
|
|
/admin_permission_denied/,
|
|
);
|
|
|
|
const firstDraft = sql(`
|
|
select public.admin_save_product_draft(
|
|
'${ids.billing}','${publishedMonthly}','standard_monthly','标准月卡','会员期内标准 AI 咨询不限点数,受合理使用规则约束',
|
|
'subscription','month',1,9900,'CNY',false,20,false,'${userPayload}'::jsonb,'disable published monthly','product-save-published'
|
|
)
|
|
`);
|
|
assert.notEqual(firstDraft, publishedMonthly);
|
|
assert.equal(sql(`select status||':'||enabled||':'||version from public.billing_products where id='${publishedMonthly}'`), "published:true:1");
|
|
assert.equal(sql(`select status||':'||enabled||':'||version||':'||code from public.billing_products where id='${firstDraft}'`), "draft:f:2:standard_monthly");
|
|
assert.equal(sql(`select count(*) from public.product_entitlements where product_id='${firstDraft}'`), "3");
|
|
assert.equal(sql(`select count(*) from public.billing_products where code='standard_monthly' and status='draft'`), "1");
|
|
assert.equal(
|
|
sql(`select after_value ? 'code' from audit.admin_audit_logs where request_id='product-save-published'`),
|
|
"f",
|
|
"product audit payloads must not use the forbidden code key",
|
|
);
|
|
|
|
const secondSave = sql(`
|
|
select public.admin_save_product_draft(
|
|
'${ids.billing}','${publishedMonthly}','standard_monthly','标准月卡改名','会员期内标准 AI 咨询不限点数,受合理使用规则约束',
|
|
'subscription','month',1,9900,'CNY',false,20,false,'${userPayload}'::jsonb,'update existing draft','product-save-published-again'
|
|
)
|
|
`);
|
|
assert.equal(secondSave, firstDraft);
|
|
assert.equal(sql(`select name from public.billing_products where id='${firstDraft}'`), "标准月卡改名");
|
|
assert.equal(sql(`select count(*) from public.billing_products where code='standard_monthly' and status='draft'`), "1");
|
|
|
|
assert.equal(sql(`select public.admin_delete_product('${ids.billing}','${firstDraft}','remove unused draft','product-delete-draft')`), firstDraft);
|
|
assert.equal(sql(`select count(*) from public.billing_products where id='${firstDraft}'`), "0");
|
|
assert.equal(sql(`select count(*) from public.product_entitlements where product_id='${firstDraft}'`), "0");
|
|
|
|
assert.equal(sql(`select public.admin_delete_product('${ids.billing}','${publishedYearly}','take yearly off sale','product-retire-published')`), publishedYearly);
|
|
assert.equal(sql(`select status||':'||enabled from public.billing_products where id='${publishedYearly}'`), "retired:f");
|
|
} finally {
|
|
fixture.stop();
|
|
}
|
|
});
|