Skip to content

Commit 0310cd7

Browse files
committed
sessions: 显式标记摘要回放的已知缺陷
07_summary_create_update 和 08_summary_truncation 继续在差异报告中保留 status: different 和 allowed: false,不将事件顺序、author 或 text 等业务字段声明为允许差异。 为两个用例增加 strict xfail,明确正确行为应为跨后端 match,但当前受 InMemorySessionService 在 update_session 后共享 events 引用的已知缺陷影响。本 PR 只负责检测和稳定复现,不修改生产实现;缺陷修复后测试将产生 XPASS 并使 CI 失败,提醒移除 xfail 和更新报告。 同时整理 Mock Redis 命令分支,使未知 key 的类型返回 none,并补充 string、hash、list 和 unknown 类型测试。 Fixes #89 RELEASE NOTES: 明确标记 Session Summary 回放中的已知 InMemory 一致性缺陷。
1 parent 8d8b2cf commit 0310cd7

1 file changed

Lines changed: 40 additions & 3 deletions

File tree

tests/sessions/test_replay_consistency.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async def execute_command(self, _session: Any, command: Any) -> Any:
7070
if method == "set":
7171
self._strings[args[0]] = args[1]
7272
result = True
73-
if method == "get":
73+
elif method == "get":
7474
return self._strings.get(args[0])
7575
elif method == "keys":
7676
return self._keys(args[0])
@@ -85,10 +85,16 @@ async def execute_command(self, _session: Any, command: Any) -> Any:
8585
result = len(self._lists[args[0]])
8686
elif method == "type":
8787
key = args[0]
88-
return "string" if key in self._strings else "hash" if key in self._hashes else "list"
88+
if key in self._strings:
89+
return "string"
90+
if key in self._hashes:
91+
return "hash"
92+
if key in self._lists:
93+
return "list"
94+
return "none"
8995
elif method == "lrange":
9096
return list(self._lists.get(args[0], []))
91-
elif method != "set":
97+
else:
9298
raise ValueError(f"Unsupported mock Redis command: {method}")
9399
await self.expire(_session, command.expire)
94100
return result
@@ -606,6 +612,19 @@ async def test_all_public_cases_detect_injected_difference(self):
606612
diffs = _diff(source, injected, set())
607613
assert expected_path in {diff["path"] for diff in diffs if not diff["allowed"]}, case["id"]
608614

615+
@pytest.mark.parametrize("case_id", ["07_summary_create_update", "08_summary_truncation"])
616+
@pytest.mark.xfail(
617+
strict=True,
618+
reason="Known InMemory session aliasing after update_session duplicates post-summary events",
619+
)
620+
async def test_summary_replay_matches_default_backends(self, case_id, monkeypatch):
621+
monkeypatch.delenv("REPLAY_LIGHTWEIGHT", raising=False)
622+
monkeypatch.delenv("REPLAY_SQL_URL", raising=False)
623+
monkeypatch.delenv("REPLAY_REDIS_URL", raising=False)
624+
case = next(case for case in _load_cases() if case["id"] == case_id)
625+
result = await _compare_case(case)
626+
assert result["status"] == "match"
627+
609628
async def test_equivalent_snapshots_have_no_false_positives(self, monkeypatch):
610629
monkeypatch.delenv("REPLAY_LIGHTWEIGHT", raising=False)
611630
monkeypatch.delenv("REPLAY_SQL_URL", raising=False)
@@ -629,6 +648,24 @@ async def test_lightweight_mode_runs_without_a_persistent_backend(self, monkeypa
629648

630649
class TestReplayComparison:
631650

651+
async def test_mock_redis_type_matches_stored_value_type(self):
652+
storage = _MockRedisStorage()
653+
await storage.execute_command(None, RedisCommand(method="set", args=("string:key", "value")))
654+
await storage.execute_command(None, RedisCommand(method="hset", args=("hash:key", "field", "value")))
655+
await storage.execute_command(None, RedisCommand(method="rpush", args=("list:key", "value")))
656+
657+
results = {
658+
key: await storage.execute_command(None, RedisCommand(method="type", args=(key, )))
659+
for key in ["string:key", "hash:key", "list:key", "missing:key"]
660+
}
661+
662+
assert results == {
663+
"string:key": "string",
664+
"hash:key": "hash",
665+
"list:key": "list",
666+
"missing:key": "none",
667+
}
668+
632669
async def test_mock_redis_query_returns_string_and_hash_values(self):
633670
storage = _MockRedisStorage()
634671
await storage.execute_command(None, RedisCommand(method="set", args=("value:key", '"stored"')))

0 commit comments

Comments
 (0)