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(); } });