"""人工创作入口使用真实owner、元数据、S01和隔离PG;不需要模型评分。""" import pytest from muse.作品规划.接口 import 作品服务, 档案保存, 登记作品参与者 from muse.元数据.接口 import 元数据服务 from muse.共享.标识 import 任务标识 from muse.共享.调用身份 import 内容用途, 用途, 调用身份 from muse.正式变更.接口 import 参与者目录, 正式变更服务 pytestmark = pytest.mark.数据库 @pytest.fixture def 作品环境(内置结构测试库): 应用测试库 = 内置结构测试库 目录 = 参与者目录() from muse.任务运行.接口 import 任务服务, 登记任务参与者 from muse.编排.接口 import 流程登记 登记任务参与者(目录, 任务服务(应用测试库[用途.生产], 流程登记())) 登记作品参与者(目录) 正式 = 正式变更服务(应用测试库[用途.生产], 目录) 服务 = 作品服务(应用测试库[用途.生产], 正式) 作者 = 调用身份("author", 任务标识("manual-create"), 用途.生产, 内容用途.规划) 结构 = 服务.新档案结构(作者, "work-new", "work_core", 1) 输入 = 档案保存( "work-new", 0, {"名称": "新故事", "题材定位": "未定题材", "主题立意": "关于选择"}, 结构 ) return 服务, 作者, 输入, 应用测试库 @pytest.mark.case_id( "NC-work-profile-atomic", environment="隔离 PostgreSQL、真实业务owner,CLI例使用子进程和任意cwd", given="内置结构已发布,作者创建具有动态档案的作品", when="经S01保存及重试后读回", then=["档案值由B01写入,S04初始绑定同事务,命令重试不多写版本"], contract="docs/系统架构/新版设计/接口契约/正文结构与中文选区.md", ) def test_创建动态作品档案经S01且同命令不重复__aa1101(作品环境): 服务, 作者, 输入, 应用测试库 = 作品环境 回执 = 服务.保存档案(作者, "create-work", 输入) assert 服务.保存档案(作者, "create-work", 输入) == 回执 作品 = 服务.读取作品(作者, "work-new") assert 作品["revision"] == 1 assert 作品["content"] == {"名称": "新故事", "题材定位": "未定题材", "主题立意": "关于选择"} assert 作品["schema_binding"]["schema_id"] == "work_core" with 应用测试库[用途.生产].连接() as 连: assert 连.execute("SELECT count(*) FROM muse_work_version").fetchone()[0] == 1 @pytest.mark.case_id( "NC-work-directory-stable", environment="隔离 PostgreSQL、真实业务owner,CLI例使用子进程和任意cwd", given="作者作品及两个稳定身份的章节", when="重排全部章节后重试", then=["新目录版本和原章节身份正确,保留历史目录,不重复更新"], contract="docs/系统架构/新版设计/接口契约/正文结构与中文选区.md", ) def test_章节重排只改目录顺序保留稳定身份__aa1102(作品环境): 服务, 作者, 输入, _ = 作品环境 服务.保存档案(作者, "create-work", 输入) 服务.添加章节(作者, "chapter-A", "work-new", "chapter-a", "开篇", 预期目录版本=0) 服务.添加章节(作者, "chapter-B", "work-new", "chapter-b", "随后", 预期目录版本=1) 回执 = 服务.重排章节(作者, "reorder", "work-new", ("chapter-b", "chapter-a"), 预期目录版本=2) assert ( 服务.重排章节(作者, "reorder", "work-new", ("chapter-b", "chapter-a"), 预期目录版本=2) == 回执 ) 目录 = 服务.读取目录(作者, "work-new") assert 目录["revision"] == 3 assert [(v["chapter_id"], v["title"], v["position"]) for v in 目录["chapters"]] == [ ("chapter-b", "随后", 1), ("chapter-a", "开篇", 2), ] @pytest.mark.case_id( "NC-document-manual-history", environment="隔离 PostgreSQL、真实业务owner,CLI例使用子进程和任意cwd", given="真实作品章节与带组合字符、emoji、空白的文稿", when="两次人工保存、重开、回退、旧基线再次保存", then=["中文与段落身份原样,回退形成第三版,第二版仍可读,旧基线拒绝"], contract="docs/系统架构/新版设计/接口契约/正文结构与中文选区.md", ) def test_人工正文保存重开与回退保留中文和历史__aa1103(作品环境): from dataclasses import replace from muse.正式变更.接口 import 变更错误 from muse.正文写作.接口 import 可见文本, 文本节点, 正文服务, 正文草稿, 段落, 登记正文参与者 作品, 作者, 输入, 库 = 作品环境 登记正文参与者(作品.正式.目录) 作品.保存档案(作者, "create-work", 输入) 作品.添加章节(作者, "chapter-A", "work-new", "chapter-a", "开篇", 预期目录版本=0) 正文 = 正文服务(库[用途.生产], 作品.正式) 草稿 = 正文草稿( ( 段落("p1", (文本节点(" 「中文」 e\u0301 🧑‍🏽 "),)), 段落("p2", ()), 段落("p3", (文本节点("最后一段。"),)), ) ) 首次 = 正文.保存人工(作者, "save-1", "chapter-a", 0, 草稿) assert 正文.保存人工(作者, "save-1", "chapter-a", 0, 草稿) == 首次 重开 = 正文.读取正文(作者, "chapter-a") assert 重开["revision"] == 1 and 重开["visible_text"] == 可见文本(草稿) assert [p["paragraph_id"] for p in 重开["document"]["paragraphs"]] == ["p1", "p2", "p3"] 第二稿 = replace( 草稿, paragraphs=(*草稿.paragraphs[:2], 段落("p3", (文本节点("作者更新的末段。"),))) ) 正文.保存人工(作者, "save-2", "chapter-a", 1, 第二稿) 回退 = 正文.回退正文(作者, "restore-1", "chapter-a", 2, 1) assert 回退["results"][0]["revision"] == 3 assert 正文.读取正文(作者, "chapter-a")["visible_text"] == 可见文本(草稿) assert 正文.读取正文(作者, "chapter-a", 版本=2)["visible_text"] == 可见文本(第二稿) with pytest.raises(变更错误) as 捕获: 正文.保存人工(作者, "stale-save", "chapter-a", 1, 第二稿) assert 捕获.value.错误码 == "REVISION_CONFLICT" assert 正文.读取正文(作者, "chapter-a")["revision"] == 3 @pytest.mark.case_id( "NC-document-cli-owner", environment="隔离 PostgreSQL、真实业务owner,CLI例使用子进程和任意cwd", given="真实作品章节和本地作者配置", when="仓库外cwd用python -m muse保存正文、重试和读回", then=["CLI实际装配真实B01/B05/S01,结果幂等,中文和末尾空段落不损坏"], contract="docs/系统架构/新版设计/接口契约/正文结构与中文选区.md", ) def test_人工正文CLI使用实际装配并返回相同文稿__aa1104(作品环境, tmp_path): 文稿 = _核对CLI正文(作品环境, tmp_path, " CLI 原文「保持不变」🧑‍🏽\n") assert 文稿["visible_text"] == " CLI 原文「保持不变」🧑‍🏽\n" @pytest.mark.case_id( "TC-125b0ca098ec", environment="隔离PostgreSQL与仓内Python真实CLI", given="显式大文本输入:中文、双引号、LF和反斜杠组合重复5000次", when="经具名正文CLI请求文件保存到隔离PG,重复命令后重新读取", then=["全文逐字符相同,重复命令不新增版本;不恢复通用SQL写入口"], contract="docs/系统架构/新版设计/接口契约/正文结构与中文选区.md", ) def test_大段中文引号换行与反斜杠经CLI和PG往返__125b0c(作品环境, tmp_path): big = '中文"引号"\n换行\\反斜杠' * 5000 文稿 = _核对CLI正文(作品环境, tmp_path, big) assert 文稿["visible_text"] == big def _核对CLI正文(作品环境, tmp_path, 原文): import json import subprocess import sys from dataclasses import asdict from muse.正文写作.接口 import 文本节点, 正文草稿, 段落 作品, 作者, 输入, 库 = 作品环境 作品.保存档案(作者, "create-work", 输入) 作品.添加章节(作者, "chapter-A", "work-new", "chapter-a", "开篇", 预期目录版本=0) 配置文件 = tmp_path / "人工.toml" 配置文件.write_text( '["数据库"]\n"取值方式"="受控存储"\n"位置"=' + json.dumps(库[用途.生产].引用.位置) + """ ["资源"] "发布身份"="test" ["HTTP"] "口令文件"="作者口令.txt" "作者ID"="author" """ ) 草稿 = 正文草稿( tuple(段落(f"cli-p{i}", (文本节点(行),)) for i, 行 in enumerate(原文.split("\n"))) ) 请求文件 = tmp_path / "正文请求.json" 请求文件.write_text( json.dumps( { "command_id": "cli-save", "chapter_id": "chapter-a", "expected_revision": 0, "document": asdict(草稿), }, ensure_ascii=False, ) ) base = [sys.executable, "-m", "muse", "作品", str(配置文件)] def 执行(*args): p = subprocess.run([*base, *args], cwd="/", capture_output=True, text=True, timeout=15) assert p.returncode == 0, p.stderr return json.loads(p.stdout) 回执 = 执行("保存正文", str(请求文件)) assert 执行("保存正文", str(请求文件)) == 回执 文稿 = 执行("正文", "chapter-a") assert 文稿["revision"] == 1 return 文稿 @pytest.mark.case_id( "NC-work-current-binding", environment="隔离 PostgreSQL;结构升级为明确的已完成状态夹具,不代替作者升级旅程", given="作品值按结构1保存,兼容升级已将当前绑定切到结构2", when="重新打开档案并填写新增字段", then=["表单使用当前结构2和投影,原值版本仍1;新增字段通过S01保存为新数据版本"], contract="docs/系统架构/新版设计/接口契约/元数据投影与版本.md", ) def test_档案读取当前绑定而不沿用上次写值结构__aa1105(作品环境): from dataclasses import replace from muse.作品规划.接口 import 档案保存, 档案结构 from muse.元数据.接口 import 值规则, 启用命令, 字段定义 作品, 作者, 输入, 库 = 作品环境 作品.保存档案(作者, "create-work", 输入) # 这里只设置已完成的兼容升级;作者升级确认旅程由W11另验。 with 库[用途.维护].连接() as 连, 连.transaction(): 元数据 = 元数据服务(连) 旧 = 元数据.读取结构("work_core", 1) 新 = replace( 旧, schema_version=2, 父版本=1, 字段=(*旧.字段, 字段定义("work_core.读者约定", "读者约定", "读者约定", 值规则("text"))), ) 元数据.登记结构候选(新) 元数据.发布结构("work_core", 2, 新.内容哈希) 有效 = 元数据.读取有效结构("work_core", 2) 元数据.启用绑定( "work:work-new", "work-new", 有效, 启用命令( "synthetic-upgrade", 有效.effective_schema_hash, 1, "synthetic-reviewed-upgrade", "work:work-new", ), ) 当前 = 作品.读取作品(作者, "work-new") assert 当前["revision"] == 1 and 当前["schema_binding"]["base_version"] == 2 frame = 当前["schema_binding"] 结构 = 档案结构( frame["schema_id"], frame["base_version"], frame["effective_schema_hash"], 当前["projection_version"], frame["extension_version"], ) 作品.保存档案( 作者, "save-new-field", 档案保存("work-new", 1, {"读者约定": "先抛问题,再给答案"}, 结构) ) assert 作品.读取作品(作者, "work-new")["content"]["读者约定"] == "先抛问题,再给答案" @pytest.mark.case_id("NC-O09-PG-PROFILE") def test_无变化保存保留回执不消耗版本且旧标签仍可真修改(作品环境): from dataclasses import replace from muse.正式变更.接口 import 变更错误 服务, 作者, 输入, 应用测试库 = 作品环境 服务.保存档案(作者, "create-no-change", 输入) 同值 = replace(输入, expected_revision=1) 回执 = 服务.保存档案(作者, "same-new-command", 同值) assert 回执["no_change"] and not 回执["did_change"] assert 回执["results"][0]["revision"] == 1 and 回执["impact_ids"] == [] assert 服务.正式.读取回执(作者, "same-new-command") == 回执 assert 服务.保存档案(作者, "same-new-command", 同值) == 回执 with pytest.raises(变更错误): 服务.保存档案(作者, "same-new-command", replace(同值, content={"名称": "B"})) with pytest.raises(变更错误): 服务.保存档案(作者, "old-cas", 输入) 服务.保存档案(作者, "tab-B", replace(同值, content={"名称": "B"})) 服务.保存档案(作者, "return-A", replace(同值, expected_revision=2)) with 应用测试库[用途.生产].连接() as 连: assert 连.execute("SELECT count(*) FROM muse_work_version").fetchone()[0] == 3 assert ( 连.execute( "SELECT count(*) FROM muse_change_impact WHERE reason='profile_changed'" ).fetchone()[0] == 3 ) @pytest.mark.case_id("NC-O06-PROGRESS-PG-SEMANTICS") def test_进度批量摘要与正文候选实际语义一致(作品环境): from muse.任务运行.接口 import 任务服务 from muse.作品规划.接口 import 创作进度服务 from muse.正文写作.接口 import 文本节点, 正文服务, 正文草稿, 段落, 登记正文参与者 from muse.编排.接口 import 流程登记 作品, 作者, 输入, 库 = 作品环境 登记正文参与者(作品.正式.目录) 作品.保存档案(作者, "o06-create", 输入) 作品.添加章节(作者, "o06-chapter", "work-new", "chapter-a", "章", 预期目录版本=0) 正文 = 正文服务(库[用途.生产], 作品.正式) 草稿 = 正文草稿((段落("p1", (文本节点("中文 e\u0301 🧑‍🏽"),)), 段落("p2", ()))) 正文.保存人工(作者, "o06-save", "chapter-a", 0, 草稿) 正文.创建人工候选(作者, "o06-candidate", "chapter-a", 1, 草稿) 进度 = 创作进度服务(作品, 正文, 任务服务(库[用途.生产], 流程登记())) expected = 正文.读取正文(作者, "chapter-a") candidates = 正文.列出候选(作者, "chapter-a") result = 进度.查询(作者, "work-new")["chapters"][0] assert result["document_hash"] == expected["document_hash"] assert result["visible_characters"] == len(expected["visible_text"]) assert result["candidates"] == [ {key: candidate[key] for key in ("candidate_id", "revision", "decision", "stale")} for candidate in candidates ] 第二稿 = 正文草稿((段落("p1", (文本节点("正文版本已改变。"),)),)) 正文.保存人工(作者, "o06-save2", "chapter-a", 1, 第二稿) assert 进度.查询(作者, "work-new")["chapters"][0]["candidates"][0]["stale"]