"""场景切片构建与读取:授权来源入池,用途撤回即退出候选;切片可全量重建。""" import pytest import test_模型修订任务 as 修订测试 from muse.资料研究.场景切片 import 计算切片 from muse.资料研究.接口 import ( 导入请求, 构建场景索引, 读取场景切片, 资料服务, ) pytestmark = pytest.mark.数据库 生成环境 = 修订测试.生成环境 def _导入(env, 连, *, 标题: str, origin: str, 用途: tuple[str, ...]) -> str: 来源 = 资料服务().导入( 连, env["作者"].作者, 导入请求( kind="reference", title=标题, origin=origin, content="刀锋擦过装甲,反震顺着掌心爬上来。\n火花在真空中凝成细线。\n" * 400, authorized_uses=用途, ), ) return str(来源.source_id) @pytest.fixture def 切片环境(生成环境): env = 生成环境 with env["装配"].要求数据库().连接() as 连: env["授权来源"] = _导入( env, 连, 标题="机甲打斗参考", origin="seed://slice-ref", 用途=("reference_reading",) ) env["未授权来源"] = _导入( env, 连, 标题="分析专用资料", origin="seed://slice-analysis", 用途=("analysis",) ) return env @pytest.mark.case_id( "NC-w33-a10029", environment="隔离PG与生成环境", given="一个 reference_reading 授权来源与一个仅 analysis 来源", when="构建场景索引并读取切片", then=[ "仅授权来源入池,切片数与确定性计算一致", "切片行携带书名、定位与来源版本内容哈希", "重复构建为幂等重建,索引身份一致", ], contract="docs/系统架构/新版设计/模块设计/B03-资料研究.md#场景片段切片与索引", ) def test_构建场景索引只纳入授权来源__a10029(切片环境): env = 切片环境 库 = env["装配"].要求数据库() with 库.连接() as 连: 回执 = 构建场景索引(连, env["作者"].作者) assert 回执["来源数"] == 1 assert 回执["切片数"] > 0 assert 回执["embedder"].startswith("hash-bigram-") 再次 = 构建场景索引(连, env["作者"].作者) assert 再次["切片数"] == 回执["切片数"] assert 再次["索引身份"] == 回执["索引身份"] raw = 连.execute("SELECT slice_id FROM muse_source_slice").fetchall() 切片集 = [str(r[0]) for r in raw] 明细 = 读取场景切片(连, env["作者"].作者, 切片集) assert len(明细) == 回执["切片数"] assert all(条["书名"] == "机甲打斗参考" for 条 in 明细) assert all(条["内容哈希"] for 条 in 明细) # 未授权来源切片不出现。 assert all(条["source_id"] == env["授权来源"] for 条 in 明细) @pytest.mark.case_id( "NC-w33-a10030", environment="隔离PG与生成环境", given="已入池的授权来源", when="撤回 reference_reading 用途后读取切片", then=["读取返回空,切片行保留(派生缓存不删历史),重建后来源数归零"], contract="docs/系统架构/新版设计/模块设计/B03-资料研究.md#场景片段切片与索引", ) def test_用途撤回后切片退出候选__a10030(切片环境): env = 切片环境 库 = env["装配"].要求数据库() with 库.连接() as 连: 构建场景索引(连, env["作者"].作者) 当前 = 连.execute("SELECT slice_id FROM muse_source_slice LIMIT 1").fetchone() # 测试关注读侧授权过滤,直接置空用途模拟撤回后的来源状态。 连.execute( "UPDATE muse_source SET authorized_uses='[]'::jsonb WHERE source_id=%s", (env["授权来源"],), ) 明细 = 读取场景切片(连, env["作者"].作者, [str(当前[0])]) assert 明细 == [] 保留 = 连.execute("SELECT COUNT(*) FROM muse_source_slice").fetchone()[0] assert 保留 > 0 回执 = 构建场景索引(连, env["作者"].作者) assert 回执["来源数"] == 0 and 回执["切片数"] == 0 @pytest.mark.case_id( "NC-w33-a10031", environment="隔离PG与生成环境", given="他人导入的授权来源", when="以当前作者构建场景索引", then=["来源不计入切片池:切片只对导入者视角构建"], contract="docs/系统架构/新版设计/模块设计/B03-资料研究.md#场景片段切片与索引", ) def test_非导入者来源不入池__a10031(切片环境): env = 切片环境 with env["装配"].要求数据库().连接() as 连: 回执 = 构建场景索引(连, "other-author") assert 回执["来源数"] == 0 and 回执["切片数"] == 0 assert 计算切片("有字。\n", env["授权来源"], 1) @pytest.mark.case_id( "NC-w33-a10032", environment="隔离PG与生成环境", given="已构建的授权来源切片索引", when="以场景相关查询执行场景检索", then=["两路融合返回切片:带书名、预览与双路得分;授权来源独占结果"], contract="docs/系统架构/新版设计/模块设计/B09-上下文.md#场景片段检索", ) def test_场景检索返回知情候选__a10032(切片环境): env = 切片环境 库 = env["装配"].要求数据库() from muse.上下文.接口 import 场景检索 with 库.连接() as 连: 构建场景索引(连, env["作者"].作者) 回执 = 场景检索(连, "刀锋擦过装甲 反震", 数量=3) assert 回执["empty_reason"] is None assert 1 <= len(回执["结果"]) <= 3 首 = 回执["结果"][0] assert 首["书名"] == "机甲打斗参考" assert "刀锋" in 首["预览"] assert "关键词得分" in 首 and "向量得分" in 首 and 首["融合得分"] > 0 @pytest.mark.case_id( "NC-w33-a10033", environment="隔离PG与生成环境", given="尚未构建切片索引的库", when="执行场景检索", then=["拒绝为索引陈旧,不静默返回空结果"], contract="docs/系统架构/新版设计/模块设计/B09-上下文.md#场景片段检索", ) def test_未建索引拒绝__a10033(生成环境): env = 生成环境 from muse.上下文.接口 import 场景检索 from muse.上下文.模型 import 上下文错误 with env["装配"].要求数据库().连接(只读=True) as 连: with pytest.raises(上下文错误): 场景检索(连, "任意查询") @pytest.mark.case_id( "NC-w33-a10034", environment="隔离PG与生成环境", given="授权来源用途被撤回后的切片索引", when="执行场景检索", then=["结果为空且说明可读,不扩大为索引不可用"], contract="docs/系统架构/新版设计/模块设计/B09-上下文.md#场景片段检索", ) def test_用途撤回后检索为空__a10034(切片环境): env = 切片环境 库 = env["装配"].要求数据库() from muse.上下文.接口 import 场景检索 with 库.连接() as 连: 构建场景索引(连, env["作者"].作者) 连.execute( "UPDATE muse_source SET authorized_uses='[]'::jsonb WHERE source_id=%s", (env["授权来源"],), ) 回执 = 场景检索(连, "刀锋", 数量=3) assert 回执["结果"] == []