mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 14:52:02 +00:00
470f8b6641
报告表格里的时间以前照快照原样显示成 2026-06-30T08:21:39,读起来费劲。[[v:]] 和 [[table:]] 渲染出来的 ISO 日期时间改成用空格隔开(小数秒、时区照留),写作目录和流式输出同一套写法。快照里的值、引用记下的 value、eid、证据下钻都不变。 复核按文档生成时的规则:老文档存的是带 T 的字,拿新规则重新渲染会对不上、被当成文档被改过。所以显示规则也记版本:新文档里真有日期时间被改写时在顶层记 cell_display: 2(参与内容哈希),没有被改写的文档和以前一字不差;没有这个字段的按 1 版(原样)复核,记了认不出的值报 bad_schema。出口复核、期 4 的金样(test_provenance_unchanged)都照旧通过;裁判摘录是给模型看的,不改
119 lines
5.3 KiB
Python
119 lines
5.3 KiB
Python
"""报告里单元格的 ISO 日期时间显示成「2026-06-30 08:21:39」,不再是「2026-06-30T08:21:39」。
|
||
|
||
只改显示:快照里的值、引用记下的 value、eid、证据下钻都不变。复核要按文档生成时的规则:老文档里存的是带 T 的字,
|
||
拿新规则重新渲染会对不上,被当成文档被改过(render_mismatch)——所以新文档在顶层记 cell_display: 2(只在真有日期
|
||
时间被改写时记,没有的文档和以前一字不差),没有这个字段的老文档按 1 版(原样)复核。期 4 的金样
|
||
(test_provenance_unchanged)照旧逐字不变。
|
||
|
||
夹具合成:门店、卡券。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from app.core.artifact_store import canonical_json
|
||
from app.engine import evidence as ev
|
||
from app.engine.evidence import (
|
||
CELL_DISPLAY,
|
||
StreamRenderer,
|
||
build_catalog,
|
||
cell_eid,
|
||
compose_doc,
|
||
doc_cell_display,
|
||
iter_segments,
|
||
verify_doc,
|
||
writer_catalog,
|
||
)
|
||
|
||
ART = "d" * 64
|
||
SNAP = {"columns": ["store_name", "redeemed_at", "biz_date", "amount", "note"],
|
||
"column_types": {"store_name": "text", "redeemed_at": "datetime", "biz_date": "date", "amount": "number",
|
||
"note": "text"},
|
||
"rows": [["门店01", "2026-06-30T08:21:39", "2026-06-30", 120, "T 开头的备注"],
|
||
["门店02", "2026-06-29T23:05:00.250000+08:00", "2026-06-29", 80, "2026-06-29T10:00:00Z"]],
|
||
"truncated": False}
|
||
STORE = {ART: SNAP}
|
||
|
||
|
||
def load(artifact):
|
||
return STORE.get(artifact)
|
||
|
||
|
||
@pytest.fixture
|
||
def catalog():
|
||
ledger = [{"kind": "query", "node_id": "fetch", "exec": 1, "artifact": ART, "tool": "db_query__shop",
|
||
"columns": SNAP["columns"], "rows": 2, "truncated": False}]
|
||
return build_catalog(nodes={}, ledger=ledger)
|
||
|
||
|
||
def cells(doc):
|
||
return [s for s in iter_segments(doc) if s.get("ref", "").startswith("v:")]
|
||
|
||
|
||
def test_iso_datetimes_read_with_a_space(catalog):
|
||
doc = compose_doc("最近一笔核销在 [[v:Q1.r0.redeemed_at]],门店 [[v:Q1.r0.store_name]]。[[see:Q1]]", catalog, loader=load)
|
||
[when, store] = cells(doc)
|
||
assert when["text"] == "2026-06-30 08:21:39" and "2026-06-30 08:21:39" in doc["markdown"]
|
||
# 只改显示:引用记下的值、证据标识照旧
|
||
assert when["cite"]["value"] == "2026-06-30T08:21:39"
|
||
assert when["cite"]["eid"] == cell_eid(ART, 0, "redeemed_at")
|
||
assert store["text"] == "门店01"
|
||
assert doc["cell_display"] == CELL_DISPLAY == 2 and doc_cell_display(doc) == 2
|
||
assert '"cell_display":2' in canonical_json(doc), "在文档顶层,参与内容哈希"
|
||
assert verify_doc(doc, catalog, loader=load)["ok"]
|
||
|
||
|
||
def test_fractions_and_offsets_are_kept_and_dates_are_untouched(catalog):
|
||
doc = compose_doc("时间 [[v:Q1.r1.redeemed_at]],日期 [[v:Q1.r1.biz_date]],备注 [[v:Q1.r1.note]]。", catalog,
|
||
loader=load)
|
||
assert [s["text"] for s in cells(doc)] == ["2026-06-29 23:05:00.250000+08:00", "2026-06-29", "2026-06-29 10:00:00Z"]
|
||
assert verify_doc(doc, catalog, loader=load)["ok"]
|
||
|
||
|
||
def test_tables_read_the_same_way(catalog):
|
||
doc = compose_doc("[[table:Q1 cols=store_name,redeemed_at]]", catalog, loader=load)
|
||
assert "| 门店01 | 2026-06-30 08:21:39 |" in doc["markdown"]
|
||
assert "T08:21:39" not in doc["markdown"]
|
||
assert verify_doc(doc, catalog, loader=load)["ok"]
|
||
|
||
|
||
def test_documents_without_datetimes_are_unchanged(catalog):
|
||
doc = compose_doc("门店 [[v:Q1.r0.store_name]],金额 [[v:Q1.r0.amount]]。", catalog, loader=load)
|
||
assert "cell_display" not in doc, "没有被改写的日期时间:文档和以前一字不差"
|
||
assert doc_cell_display(doc) == 1
|
||
|
||
|
||
def test_old_documents_still_verify_as_they_were(catalog, monkeypatch):
|
||
"""改动前组装的文档:存的是带 T 的字、没有 cell_display。按 1 版复核,不报 render_mismatch。"""
|
||
monkeypatch.setattr(ev, "CELL_DISPLAY", 1)
|
||
old = compose_doc("最近一笔核销在 [[v:Q1.r0.redeemed_at]]。", catalog, loader=load)
|
||
monkeypatch.undo()
|
||
assert "cell_display" not in old and "2026-06-30T08:21:39" in old["markdown"]
|
||
checked = verify_doc(old, catalog, loader=load)
|
||
assert checked["ok"], checked["violations"]
|
||
|
||
|
||
def test_a_document_edited_back_is_caught(catalog):
|
||
doc = compose_doc("最近一笔核销在 [[v:Q1.r0.redeemed_at]]。", catalog, loader=load)
|
||
[seg] = cells(doc)
|
||
start = seg["span"][0]
|
||
doc["markdown"] = doc["markdown"].replace("2026-06-30 08:21:39", "2026-06-30T08:21:39")
|
||
seg["text"] = "2026-06-30T08:21:39"
|
||
seg["span"] = [start, start + len(seg["text"])]
|
||
codes = {v["code"] for v in verify_doc(doc, catalog, loader=load)["violations"]}
|
||
assert "render_mismatch" in codes
|
||
|
||
|
||
def test_an_unknown_display_version_is_reported(catalog):
|
||
doc = compose_doc("最近一笔核销在 [[v:Q1.r0.redeemed_at]]。", catalog, loader=load)
|
||
doc["cell_display"] = 9
|
||
assert "bad_schema" in {v["code"] for v in verify_doc(doc, catalog, loader=load)["violations"]}
|
||
|
||
|
||
def test_the_writer_and_the_stream_see_what_the_report_will_show(catalog):
|
||
text = writer_catalog(catalog, loader=load).text
|
||
assert "redeemed_at=2026-06-30 08:21:39" in text and "T08:21:39" not in text
|
||
stream = StreamRenderer(catalog, loader=load)
|
||
shown = stream.feed("最近一笔在 [[v:Q1.r0.redeemed_at]]。") + stream.flush()
|
||
assert "2026-06-30 08:21:39" in shown
|