- core:配置(prod 拒绝默认密钥 / dev 短信 / 假模型)、租户事务 + 行级安全策略、 problem+json 错误码、信封加密与签名下载、对象存储、事务内入队、Valkey stream 事件流、权限点 - identity:手机号 HMAC 存储、注册、登录(挤下线需确认,API3)、服务端会话 - billing:额度流水只追加,预扣 / 结算 / 退回,一笔预扣只有一个结局 - document:分片上传、按文件头识别格式、Gotenberg 转换、文字层与稳定句子编号 - checklist:标注规则识别、模型抽取、三道校验、标注类条款规则兜底、重跑对齐 - ai:提示词注册表(extract.substantive v1 来自 S4 v2)、假模型、OpenAI 兼容供应商、ai_call 流水 - 迁移:data-model 基线、套餐、Procrastinate、会话状态函数 - 测试 30 个:租户隔离、额度、失败退回、对象加密、契约一致性 - Compose(含 migrate)、镜像、CI Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
3.4 KiB
Python
76 lines
3.4 KiB
Python
"""不需要数据库的单元测试:证据校验、格式识别、文字层。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from bid_doctor.checklist.extract import check_basis, check_evidence, is_marked
|
||
from bid_doctor.core.errors import AppError
|
||
from bid_doctor.core.events import _sid # pyright: ignore[reportPrivateUsage]
|
||
from bid_doctor.document.formats import detect
|
||
from bid_doctor.document.pipeline.text_layer import Sentence, extract
|
||
from tests.conftest import FIXTURES
|
||
|
||
|
||
def sent(text: str, runs: list[tuple[int, int, str]] | None = None, seq: int = 1) -> Sentence:
|
||
return Sentence(key=f"d:p1:s{seq}", page_no=1, seq=seq, text=text, quads=[[0, 0, 1, 1]], fmt_runs=runs or [])
|
||
|
||
|
||
def test_evidence_checks() -> None:
|
||
s = sent("★投标人须具备有效的信息系统集成服务资质证书。")
|
||
by_id = {"p1s1": s}
|
||
assert check_evidence({"sid": "p1s1", "quote": "投标人须具备有效的信息"}, by_id) is None
|
||
assert check_evidence({"sid": "p9s9", "quote": "投标人须具备有效的信息"}, by_id) == "id"
|
||
assert check_evidence({"sid": "p1s1", "quote": "投标人须具备"}, by_id) == "quote" # 少于 8 个字
|
||
assert check_evidence({"sid": "p1s1", "quote": "这句话是模型改写的"}, by_id) == "quote"
|
||
|
||
|
||
def test_basis_check() -> None:
|
||
star = sent("★服务器内存不低于32GB。")
|
||
plain = sent("投标人应按要求提供售后服务方案。")
|
||
iu = sent("投标人须提供原厂授权书。", [(0, 12, "iu")])
|
||
assert check_basis("star", [star], [star], set())
|
||
assert not check_basis("star", [plain], [plain], set())
|
||
# 斜体下划线只在文件声明过时才算(07 F1)
|
||
assert not check_basis("italic_underline", [iu], [iu], set())
|
||
assert check_basis("italic_underline", [iu], [iu], {"italic_underline"})
|
||
invalid = sent("未提供的,其投标无效。", seq=2)
|
||
assert check_basis("explicit_invalid", [plain], [plain, invalid], set())
|
||
assert not check_basis("unknown", [star], [star], set())
|
||
|
||
|
||
def test_marker_fallback() -> None:
|
||
assert is_marked(sent("★采用国产化CPU芯片。"), set()) == "star"
|
||
iu = sent("投标人须提供原厂授权书。", [(0, 12, "iu")])
|
||
assert is_marked(iu, set()) is None
|
||
assert is_marked(iu, {"italic_underline"}) == "italic_underline"
|
||
|
||
|
||
def test_marked_text_for_model() -> None:
|
||
s = sent("须提供授权书,否则无效", [(3, 6, "iu"), (9, 11, "u")])
|
||
assert s.marked == "须提供<iu>授权书</iu>,否则<u>无效</u>"
|
||
|
||
|
||
def test_detect_formats() -> None:
|
||
pdf = (FIXTURES / "tender-mini.pdf").read_bytes()
|
||
d = detect(pdf, "采购文件.pdf")
|
||
assert (d.format, d.page_count, d.extension_mismatch) == ("pdf", 1, False)
|
||
assert detect(pdf, "采购文件.doc").extension_mismatch
|
||
with pytest.raises(AppError) as e:
|
||
detect(b"hello", "a.doc")
|
||
assert e.value.code == "file_format_unsupported"
|
||
|
||
|
||
def test_text_layer_normalizes_radicals_and_keys() -> None:
|
||
pages = extract("doc1", (FIXTURES / "tender-mini.pdf").read_bytes())
|
||
texts = [s.text for s in pages[0].sentences]
|
||
assert any("项目" in t for t in texts) and not any("⽬" in t for t in texts) # 「目」不是康熙部首
|
||
assert any("彩页" in t for t in texts)
|
||
keys = [s.key for s in pages[0].sentences]
|
||
assert keys[0] == "doc1:p1:s1" and len(set(keys)) == len(keys)
|
||
|
||
|
||
def test_stream_id_compare() -> None:
|
||
assert _sid("1700000000000-1") > _sid("999-5")
|
||
assert _sid("bad") == (0, 0)
|