101 lines
7.1 KiB
JavaScript
101 lines
7.1 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { dbRun } from "../server/db.mjs";
|
|
|
|
const api = process.env.AI_DRAMA_API_BASE || "http://127.0.0.1:8787";
|
|
|
|
async function request(path, headers, options = {}) {
|
|
const response = await fetch(`${api}${path}`, {
|
|
...options,
|
|
headers: { "content-type": "application/json", ...(headers || {}), ...(options.headers || {}) }
|
|
});
|
|
const payload = await response.json().catch(() => ({}));
|
|
return { response, payload };
|
|
}
|
|
|
|
async function login(email) {
|
|
const result = await request("/api/auth/login", null, { method: "POST", body: JSON.stringify({ email, password: "Demo@123456" }) });
|
|
assert.equal(result.response.ok, true, `${email} 登录失败`);
|
|
return { authorization: `Bearer ${result.payload.session.token}` };
|
|
}
|
|
|
|
const owner = await login("producer@local.test");
|
|
const writer = await login("writer@local.test");
|
|
const createdEmail = `commercial-governance-${Date.now()}@local.test`;
|
|
let createdUserId = "";
|
|
let originalBilling = null;
|
|
let originalCostCenter = null;
|
|
|
|
try {
|
|
const deniedCreate = await request("/api/system/users", writer, { method: "POST", body: JSON.stringify({ displayName: "越权用户", email: `denied-${Date.now()}@local.test` }) });
|
|
assert.equal(deniedCreate.response.status, 403, "普通用户不应手动创建全局账号");
|
|
|
|
const created = await request("/api/system/users", owner, {
|
|
method: "POST",
|
|
body: JSON.stringify({ displayName: "治理回归用户", email: createdEmail, organizationId: "org-studio-lab", workspaceId: "ws-local-aidrama", projectId: "thunder-mouth" })
|
|
});
|
|
assert.equal(created.response.status, 201, `系统管理员创建用户失败:${JSON.stringify(created.payload)}`);
|
|
assert.ok(created.payload.temporaryPassword, "留空密码时必须返回一次性初始密码");
|
|
createdUserId = created.payload.user.id;
|
|
assert.ok(created.payload.user.organizations.some((item) => item.id === "org-studio-lab"), "创建用户时组织归属未落库");
|
|
assert.ok(created.payload.user.workspaces.some((item) => item.id === "ws-local-aidrama"), "创建用户时工作区归属未落库");
|
|
|
|
const resetPassword = await request(`/api/system/users/${encodeURIComponent(createdUserId)}/reset-password`, owner, { method: "POST", body: "{}" });
|
|
assert.equal(resetPassword.response.ok, true, "管理员重置密码失败");
|
|
assert.ok(resetPassword.payload.temporaryPassword, "自动重置密码必须返回一次性密码");
|
|
const resetMfa = await request(`/api/system/users/${encodeURIComponent(createdUserId)}/reset-mfa`, owner, { method: "POST", body: "{}" });
|
|
assert.equal(resetMfa.response.ok, true, "管理员重置 MFA 失败");
|
|
|
|
const memberships = await request(`/api/system/users/${encodeURIComponent(createdUserId)}/memberships`, owner, { method: "POST", body: JSON.stringify({ organizationId: "org-studio-lab", organizationRoleKey: "org_member", workspaceId: "ws-pilot", workspaceRoleKey: "producer", projectId: "template-original-manhua", projectRoleKey: "project_viewer" }) });
|
|
assert.equal(memberships.response.ok, true, "管理员调整用户归属失败");
|
|
assert.ok(memberships.payload.user.workspaces.some((item) => item.id === "ws-pilot"), "工作区归属调整未生效");
|
|
|
|
const commercial = await request("/api/organizations/org-studio-lab/commercial", owner);
|
|
assert.equal(commercial.response.ok, true, "读取商业运营数据失败");
|
|
assert.ok(commercial.payload.billing.billing_cycle && Array.isArray(commercial.payload.costCenters), "商业数据缺少账期或成本中心");
|
|
assert.ok(Array.isArray(commercial.payload.quotaWarnings) && Array.isArray(commercial.payload.billingHistory), "商业数据缺少预警或账单变更记录");
|
|
assert.ok(Array.isArray(commercial.payload.usageTrend), "商业数据缺少按日用量趋势");
|
|
assert.ok(commercial.payload.usageTrend.every((row) => row.day && Number.isFinite(row.units) && Number.isFinite(row.estimatedCost) && Number.isFinite(row.events)), "按日用量趋势字段不完整");
|
|
originalBilling = {
|
|
planName: commercial.payload.billing.plan_name,
|
|
billingCycle: commercial.payload.billing.billing_cycle,
|
|
currency: commercial.payload.billing.currency,
|
|
seatLimit: Number(commercial.payload.billing.seat_limit),
|
|
storageGb: Number(commercial.payload.billing.storage_gb),
|
|
monthlyClipQuota: Number(commercial.payload.billing.monthly_clip_quota),
|
|
quotaWarningPercent: Number(commercial.payload.billing.quota_warning_percent),
|
|
localRunnerOnly: Boolean(commercial.payload.billing.local_runner_only),
|
|
cloudConnectorsRequireApproval: Boolean(commercial.payload.billing.cloud_connectors_require_approval)
|
|
};
|
|
originalCostCenter = commercial.payload.costCenters[0];
|
|
const updatedBilling = await request("/api/organizations/org-studio-lab/billing", owner, { method: "PATCH", body: JSON.stringify({ ...originalBilling, billingCycle: "quarterly", quotaWarningPercent: 85 }) });
|
|
assert.equal(updatedBilling.response.ok, true, "账单周期更新失败");
|
|
assert.equal(updatedBilling.payload.billing.billing_cycle, "quarterly", "账单周期未保存");
|
|
assert.ok(Array.isArray(updatedBilling.payload.usageTrend), "保存套餐后没有返回用量趋势");
|
|
const updatedCenter = await request(`/api/organizations/org-studio-lab/cost-centers/${encodeURIComponent(originalCostCenter.id)}`, owner, { method: "PATCH", body: JSON.stringify({ monthlyBudget: Number(originalCostCenter.monthly_budget) + 1 }) });
|
|
assert.equal(updatedCenter.response.ok, true, "成本中心预算更新失败");
|
|
const exported = await request("/api/organizations/org-studio-lab/commercial/export", owner);
|
|
assert.equal(exported.response.ok, true, "商业运营数据导出失败");
|
|
assert.ok(exported.payload.costCenterDetail && exported.payload.billingHistory && Array.isArray(exported.payload.usageTrend), "导出数据不完整");
|
|
|
|
const workerStatus = await request("/api/system/worker", owner);
|
|
assert.equal(workerStatus.response.ok, true, "Worker 状态接口失败");
|
|
assert.ok(workerStatus.payload.worker.healthStatus, "Worker 状态缺少心跳健康字段");
|
|
const batch = await request("/api/admin/queue/batch", owner, { method: "POST", body: JSON.stringify({ action: "retry", jobIds: ["missing-governance-job"] }) });
|
|
assert.equal(batch.response.ok, true, "批量队列运维接口失败");
|
|
assert.equal(batch.payload.failureCount, 1, "批量队列应返回逐任务失败结果");
|
|
const deniedBatch = await request("/api/admin/queue/batch", writer, { method: "POST", body: JSON.stringify({ action: "retry", jobIds: ["missing-governance-job"] }) });
|
|
assert.equal(deniedBatch.response.status, 403, "普通用户不应执行批量队列运维");
|
|
|
|
console.log(`commercial governance smoke passed: ${api}`);
|
|
} finally {
|
|
if (originalBilling) {
|
|
await request("/api/organizations/org-studio-lab/billing", owner, { method: "PATCH", body: JSON.stringify(originalBilling) });
|
|
}
|
|
if (originalCostCenter) {
|
|
await request(`/api/organizations/org-studio-lab/cost-centers/${encodeURIComponent(originalCostCenter.id)}`, owner, { method: "PATCH", body: JSON.stringify({ monthlyBudget: Number(originalCostCenter.monthly_budget) }) });
|
|
}
|
|
if (createdUserId) {
|
|
dbRun("DELETE FROM users WHERE id = ?", [createdUserId]);
|
|
}
|
|
}
|