Files
AgentLab/backend/tests/test_report_datetime_display.py

120 lines
5.5 KiB
Python
Raw Permalink 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.
"""报告里单元格的 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"
# 记的是渲染得出这些格子的最低一版:3 版(整表截行)不改格子的写法,只有日期时间被改写的照旧记 2
assert doc["cell_display"] == 2 and doc_cell_display(doc) == 2 and CELL_DISPLAY >= 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