Files
ai-drama-platform/scripts/smoke-organization-policies.mjs
T
2026-09-01 13:22:04 +08:00

189 lines
9.1 KiB
JavaScript

import assert from "node:assert/strict";
import { rm } from "node:fs/promises";
import { resolve } from "node:path";
import { dbRun, withTransaction } from "../server/db.mjs";
const api = process.env.AI_DRAMA_API_BASE || "http://127.0.0.1:8787";
const projectRoot = resolve(import.meta.dirname, "..");
const organizationId = "org-studio-lab";
const workspaceId = "ws-local-aidrama";
const projectId = "thunder-mouth";
const createdJobIds = [];
const evaluationSubjectIds = [];
async function request(path, options = {}) {
const response = await fetch(`${api}${path}`, {
...options,
headers: { "content-type": "application/json", ...(options.headers || {}) }
});
const payload = await response.json().catch(() => ({}));
return { response, payload };
}
function expectOk(result, label) {
assert.equal(result.response.ok, true, `${label}: ${result.response.status} ${result.payload.detail || result.payload.error || ""}`);
return result.payload;
}
async function login(email) {
const result = expectOk(await request("/api/auth/login", {
method: "POST",
body: JSON.stringify({ email, password: "Demo@123456" })
}), `${email} login`);
return { authorization: `Bearer ${result.session.token}` };
}
const ownerAuth = await login("producer@local.test");
const writerAuth = await login("writer@local.test");
const headers = { ...ownerAuth, "x-organization-id": organizationId, "x-workspace-id": workspaceId, "x-project-id": projectId };
const writerHeaders = { ...writerAuth, "x-organization-id": organizationId, "x-workspace-id": workspaceId, "x-project-id": projectId };
let originalRetentionPolicy = null;
try {
const initial = expectOk(await request(`/api/organizations/${organizationId}/policies`, { headers }), "list organization policies");
assert.ok(initial.policies.length >= 8, "组织策略必须包含默认商业硬门");
assert.equal(initial.effective.generation.imageOutputCount, 1, "组织策略必须锁定单张图片输出");
assert.equal(initial.effective.generation.batchSize, 1, "组织策略必须锁定 batchSize=1");
assert.equal(initial.effective.voice.forbidRandomNativeVoice, true, "组织策略必须禁止随机原生声线作为最终方案");
assert.equal(initial.effective.model.externalApprovalRequired, true, "组织策略必须要求外部连接器审批");
const writerRead = await request(`/api/organizations/${organizationId}/policies`, { headers: writerHeaders });
assert.equal(writerRead.response.status, 200, "编剧可以读取组织策略用于生产自检");
const writerPatch = await request(`/api/organizations/${organizationId}/policies/retention.production_evidence`, {
method: "PATCH",
headers: writerHeaders,
body: JSON.stringify({ value: { temporaryMediaDays: 90 } })
});
assert.equal(writerPatch.response.status, 403, "编剧不能修改组织策略");
const disableHardGate = await request(`/api/organizations/${organizationId}/policies/generation.single_frame_contract`, {
method: "PATCH",
headers,
body: JSON.stringify({ status: "disabled", enforcement: "off" })
});
assert.equal(disableHardGate.response.status, 409, "一图一完整单画面硬门不能被关闭");
assert.equal(disableHardGate.payload.error, "policy_hard_gate_locked", "硬门降级错误码必须稳定");
originalRetentionPolicy = initial.policies.find((policy) => policy.policyKey === "retention.production_evidence");
const patched = expectOk(await request(`/api/organizations/${organizationId}/policies/retention.production_evidence`, {
method: "PATCH",
headers,
body: JSON.stringify({
...originalRetentionPolicy,
value: { ...originalRetentionPolicy.value, temporaryMediaDays: 45 }
})
}), "update editable retention policy");
assert.equal(patched.policies.find((policy) => policy.policyKey === "retention.production_evidence").value.temporaryMediaDays, 45, "可编辑策略必须落库");
const passSubjectId = `policy-pass-${Date.now()}`;
evaluationSubjectIds.push(passSubjectId);
const passEvaluation = expectOk(await request(`/api/organizations/${organizationId}/policies/evaluate`, {
method: "POST",
headers,
body: JSON.stringify({
subjectType: "generation_request",
subjectId: passSubjectId,
kind: "单画面关键帧",
prompt: "国漫 2D 动画风格,雨夜地铁口,一个完整单画面。",
costMode: "local",
endpoint: "http://127.0.0.1:8000/v1",
imageOutputCount: 1,
batchSize: 1,
randomNativeVoice: false,
rightsEvidenceApproved: true
})
}), "evaluate passing request");
assert.equal(passEvaluation.evaluation.result, "pass", "本地单画面生成请求应通过组织策略");
const blockedSubjectId = `policy-block-${Date.now()}`;
evaluationSubjectIds.push(blockedSubjectId);
const blockedEvaluation = expectOk(await request(`/api/organizations/${organizationId}/policies/evaluate`, {
method: "POST",
headers,
body: JSON.stringify({
subjectType: "generation_request",
subjectId: blockedSubjectId,
kind: "单画面关键帧",
prompt: "请生成 split-screen 对比图和漫画多格拼图。",
costMode: "local",
imageOutputCount: 2,
batchSize: 2,
randomNativeVoice: true,
rightsEvidenceApproved: true
})
}), "evaluate blocked request");
assert.equal(blockedEvaluation.evaluation.result, "block", "分屏/多图/随机声线请求必须被组织策略阻断");
assert.ok(blockedEvaluation.evaluation.blockers.some((item) => item.policyKey === "generation.single_frame_contract"), "阻断结果必须包含单画面硬门");
assert.ok(blockedEvaluation.evaluation.blockers.some((item) => item.policyKey === "voice.fixed_voice_required"), "阻断结果必须包含固定声线硬门");
const cloudSubjectId = `policy-cloud-${Date.now()}`;
evaluationSubjectIds.push(cloudSubjectId);
const cloudEvaluation = expectOk(await request(`/api/organizations/${organizationId}/policies/evaluate`, {
method: "POST",
headers,
body: JSON.stringify({
subjectType: "connector_request",
subjectId: cloudSubjectId,
kind: "图生视频",
prompt: "原创国漫单画面",
costMode: "cloud",
endpoint: "https://api.example.com/v1",
imageOutputCount: 1,
batchSize: 1,
randomNativeVoice: false,
rightsEvidenceApproved: true
})
}), "evaluate cloud connector request");
assert.equal(cloudEvaluation.evaluation.result, "block", "默认商业策略不能直接放行 cloud 成本模式");
const preview = expectOk(await request("/api/jobs/preview", {
method: "POST",
headers,
body: JSON.stringify({ kind: "单画面关键帧", shotId: "shot-01", adapter: "owned-model-platform" })
}), "preview job with organization policy");
assert.equal(preview.preview.organizationPolicy.generation.batchSize, 1, "生成预览合同必须包含组织策略快照");
assert.equal(preview.preview.policyEvaluation.result, "pass", "生成预览必须返回策略评估结果");
assert.ok(preview.preview.constraints.negativePromptTerms.includes("contact sheet"), "生成合同必须注入组织级单画面禁止词");
const created = expectOk(await request("/api/jobs", {
method: "POST",
headers,
body: JSON.stringify({ kind: "单画面关键帧", shotId: "shot-01", adapter: "owned-model-platform", output: "qa/policy-contract/frame.json" })
}), "create job with organization policy");
createdJobIds.push(created.job.id);
assert.equal(created.job.request.organizationPolicy.generation.imageOutputCount, 1, "创建任务必须持久化组织策略快照");
assert.equal(created.job.request.policyEvaluation.result, "pass", "创建任务必须持久化组织策略评估");
const afterCreate = expectOk(await request(`/api/organizations/${organizationId}/policies`, { headers }), "read policy evaluations after job");
assert.ok(afterCreate.evaluations.some((item) => item.subjectId === created.job.id), "创建任务后必须写入策略评估 ledger");
console.log(`organization policy smoke passed: ${api}`);
} finally {
if (originalRetentionPolicy) {
await request(`/api/organizations/${organizationId}/policies/retention.production_evidence`, {
method: "PATCH",
headers,
body: JSON.stringify(originalRetentionPolicy)
}).catch(() => {});
}
withTransaction(() => {
for (const jobId of createdJobIds) {
dbRun("DELETE FROM job_dependencies WHERE job_id = ? OR depends_on_job_id = ?", [jobId, jobId]);
dbRun("DELETE FROM job_attempts WHERE job_id = ?", [jobId]);
dbRun("DELETE FROM usage_events WHERE metadata_json LIKE ?", [`%${jobId}%`]);
dbRun("DELETE FROM audit_logs WHERE target_id = ?", [jobId]);
dbRun("DELETE FROM organization_policy_evaluations WHERE subject_id = ?", [jobId]);
dbRun("DELETE FROM generation_jobs WHERE id = ?", [jobId]);
}
for (const subjectId of evaluationSubjectIds) {
dbRun("DELETE FROM organization_policy_evaluations WHERE subject_id = ?", [subjectId]);
dbRun("DELETE FROM audit_logs WHERE target_id = ?", [subjectId]);
}
});
for (const jobId of createdJobIds) {
await rm(resolve(projectRoot, "storage", "jobs", jobId), { recursive: true, force: true });
}
}