muse-agent-example/web/tests/旅程/规则改进.spec.ts
zizi 9e6f1c4481 R2 改造交付:新版模块化单体全量成果
- src/muse 新版全模块(装配/共享/上下文/任务运行/作品规划/故事世界/正文写作/审校修订/知识方法/作者经验/效果评测/交付连载/资料研究/正式变更/元数据/接入/基础设施/编排)+ 测试树(单元/契约/集成/架构/迁移/端到端/夹具)
- 129 项功能全部实现与自动验证(功能覆盖.json/矩阵),含 W31 补齐的规则与代价/节奏安排/伏笔与承诺
- 旧实现按处置清单退出(702 条中 324 删,保护合同与未迁移条目留存有据);web/app.py 旧工作台退役,新工作台为唯一写入口
- 数据库/旧库迁移:真实旧库内容批次迁移链(端点守卫/PG作品正文映射/质量资产缺省投影)
- 运行手册 docs/运行手册.md;W30 本机服务阶段一已运行(infra PG 为正式内容权威)
- R2 执行证据与私有运行材料在 .agents.local/改造/R2-20260909/(不入库)
2026-09-15 12:47:42 +08:00

148 lines
5.2 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 { execFileSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";
import { expect, test } from "@playwright/test";
test("NC-browser-rule-improvement:反馈提案经实际验证后明确启停并读回诊断", async ({
page,
}, info) => {
const env = process.env;
const password = env.MUSE_AUTHOR_PASSWORD_FILE;
const feedback = env.MUSE_FEEDBACK_ID;
const python = env.MUSE_TEST_PYTHON;
const production = env.MUSE_PRODUCTION_CONFIG;
const evaluation = env.MUSE_EVALUATION_CONFIG;
const maintenance = env.MUSE_MAINTENANCE_CONFIG;
const dataset = env.MUSE_RULE_DATASET_ID;
const target = env.MUSE_RULE_BODY_TARGET;
if (
!password ||
!feedback ||
!python ||
!production ||
!evaluation ||
!maintenance ||
!dataset ||
!target
)
throw new Error("需要独立测试配置、反馈、确切规则样本及正文");
const errors: string[] = [];
page.on("pageerror", (error) => errors.push(error.message));
await page.goto("/");
await page.getByLabel("访问口令").fill(readFileSync(password, "utf8").trim());
await page.getByRole("button", { name: "进入工作台" }).click();
await page.goto(`/improvements/new?feedback=${feedback}`);
await page.getByRole("combobox", { name: "改进目标", exact: true }).selectOption("rule");
await page.getByLabel("改进标题").fill("提示语规则的反馈验证");
await page
.getByLabel("需要验证的改变")
.fill("识别提示语,同时保留人物有意的口头禅。");
await page.getByLabel("具体规则版本").selectOption("l002");
const created = page.waitForResponse(
(response) =>
response.url().endsWith("/api/v1/rule-improvements") &&
response.request().method() === "POST",
);
await page.getByRole("button", { name: "保存规则改进提案" }).click();
const response = await created;
expect(response.status()).toBe(201);
const row = await response.json();
await expect(
page.getByRole("heading", { name: "提示语规则的反馈验证", exact: true }),
).toBeVisible();
await expect(page.getByText("本提案规则尚未启用。")).toBeVisible();
await expect(
page.getByRole("button", { name: "核对凭据并打开启用审阅" }),
).toBeDisabled();
// 评测用途由测试驱动显式调用真实CLI,作者服务不持有或推导评测凭据。
const validationFile = info.outputPath("验证请求.json");
writeFileSync(
validationFile,
JSON.stringify({
command_id: "browser-rule-improvement-validate",
selection: {
improvement_id: row.improvement_id,
revision: row.revision,
content_hash: row.content_hash,
},
request: {
dataset_version_id: dataset,
rule_id: row.payload.rule_id,
rule_version: row.payload.rule_version,
rule_hash: row.payload.rule_hash,
},
}),
);
const result = JSON.parse(
execFileSync(
python,
[
"-I",
"-m",
"muse",
"经验",
production,
"申请规则验证",
validationFile,
"--评测配置",
evaluation,
],
{ encoding: "utf8", timeout: 30000 },
),
);
const exportFile = info.outputPath("导出请求.json");
writeFileSync(
exportFile,
JSON.stringify({
run_id: result.receipt.run_id,
approval_ref: "fixture-rule-improvement-browser",
valid_until: new Date(Date.now() + 3600000).toISOString(),
}),
);
const proof = JSON.parse(
execFileSync(
python,
["-I", "-m", "muse", "评测", maintenance, "导出规则凭据", exportFile],
{ encoding: "utf8", timeout: 30000 },
),
);
await page.reload();
await expect(
page.getByText("实际验证结果已关联", { exact: true }),
).toBeVisible();
await page.getByLabel("规则验证凭据编号").fill(proof.receipt_id);
await page.getByRole("button", { name: "核对凭据并打开启用审阅" }).click();
await expect(page.getByText("本提案规则尚未启用。")).toBeVisible();
await page.getByRole("button", { name: "确认启用此规则版本" }).click();
await expect(
page.getByText("本提案第1版已启用,可用于只读诊断。"),
).toBeVisible();
const diagnosis = await page.evaluate(async (body) => {
const response = await fetch("/api/v1/diagnoses", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ target: JSON.parse(body) }),
});
if (response.status !== 201) throw new Error(await response.text());
return response.json();
}, target);
await page.reload();
await expect(
page.getByRole("link", { name: `查看诊断 ${diagnosis.diagnosis_id}` }),
).toBeVisible();
await page.screenshot({
path: info.outputPath("规则改进与实际诊断.png"),
fullPage: true,
});
await page.getByRole("button", { name: "核对停用依据" }).click();
await page.getByRole("button", { name: "确认停用此规则版本" }).click();
await expect(
page.getByText("本提案规则已停用。", { exact: true }),
).toBeVisible();
await page.reload();
await expect(
page.getByText("本提案规则已停用。", { exact: true }),
).toBeVisible();
expect(errors).toEqual([]);
});