Files
AgentLab/backend/tests/test_report_datetime_display.py
T
ThearchyHelios 470f8b6641 报告里单元格的 ISO 日期时间显示成「2026-06-30 08:21:39」,只改显示
报告表格里的时间以前照快照原样显示成 2026-06-30T08:21:39,读起来费劲。[[v:]] 和 [[table:]] 渲染出来的 ISO 日期时间改成用空格隔开(小数秒、时区照留),写作目录和流式输出同一套写法。快照里的值、引用记下的 value、eid、证据下钻都不变。

复核按文档生成时的规则:老文档存的是带 T 的字,拿新规则重新渲染会对不上、被当成文档被改过。所以显示规则也记版本:新文档里真有日期时间被改写时在顶层记 cell_display: 2(参与内容哈希),没有被改写的文档和以前一字不差;没有这个字段的按 1 版(原样)复核,记了认不出的值报 bad_schema。出口复核、期 4 的金样(test_provenance_unchanged)都照旧通过;裁判摘录是给模型看的,不改
2026-10-04 11:33:09 +08:00

119 lines
5.3 KiB
Python
Raw 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"
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