Files
dingtalk-workspace-cli/scripts/dev/verify_tool_search_eval_manifest_test.py
T
玉澜 2dc3147cda refactor(scripts): decouple tool search eval manifest from source hashes and seal gate
- drop the algorithm/gate source SHA256 freeze: every ranking change
  required a manual manifest hash sync, coupling code evolution to the
  manifest
- drop the --require-sealed release gate (sealed qrels hash, dual
  signatures, coverage minima, independent-result thresholds) and its
  tool-search-independent-data-gate Makefile target
- behavior regression protection stays with the Go evaluation sentinels
  that run the real assembled catalog
2026-08-13 16:09:19 +08:00

119 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Unit tests for verify_tool_search_eval_manifest.py."""
from __future__ import annotations
import importlib.util
import sys
import unittest
from pathlib import Path
SCRIPT = Path(__file__).with_name("verify_tool_search_eval_manifest.py")
SPEC = importlib.util.spec_from_file_location("verify_tool_search_eval_manifest", SCRIPT)
if SPEC is None or SPEC.loader is None: # pragma: no cover
raise RuntimeError(f"cannot import {SCRIPT}")
verify = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = verify
SPEC.loader.exec_module(verify)
class ToolSearchEvalManifestTest(unittest.TestCase):
def test_language_slice(self) -> None:
self.assertEqual(verify.language_slice("给群里发文件"), "chinese_only")
self.assertEqual(
verify.language_slice("读取 baseId 对应的表格"),
"mixed_chinese_ascii",
)
self.assertEqual(verify.language_slice("send a file to the group"), "english")
def test_collecting_manifest_is_valid(self) -> None:
self.assertEqual(verify.validate_manifest(verify.DEFAULT_MANIFEST), [])
def test_graded_equivalent_qrels_are_accepted(self) -> None:
catalog = {
"dev.search_open_platform_docs_rag",
"devdoc.search_open_platform_docs_rag",
}
case = {
"id": "devdoc-search",
"query": "查 OAuth2 接入文档",
"language": "mixed_chinese_ascii",
"qrels": [
{
"canonical": "devdoc.search_open_platform_docs_rag",
"relevance": 3,
},
{
"canonical": "dev.search_open_platform_docs_rag",
"relevance": 2,
},
],
}
problems: list[str] = []
language, workflow = verify.validate_case(case, 0, catalog, problems)
self.assertEqual(language, "mixed_chinese_ascii")
self.assertFalse(workflow)
self.assertEqual(problems, [])
def test_forbidden_requires_alternative_gold(self) -> None:
case = {
"id": "unsafe",
"query": "删除记录",
"language": "chinese_only",
"qrels": [{"canonical": "safe.read", "relevance": 3}],
"forbidden": ["danger.delete"],
}
problems: list[str] = []
verify.validate_case(case, 0, {"safe.read", "danger.delete"}, problems)
self.assertTrue(any("alternative_gold" in problem for problem in problems))
def test_forbidden_and_alternative_gold_are_catalog_bound_and_disjoint(self) -> None:
case = {
"id": "unsafe",
"query": "删除记录",
"language": "chinese_only",
"qrels": [{"canonical": "safe.read", "relevance": 3}],
"forbidden": ["danger.delete", "unknown.tool"],
"alternative_gold": ["danger.delete"],
}
problems: list[str] = []
verify.validate_case(case, 0, {"safe.read", "danger.delete"}, problems)
self.assertTrue(any("unknown Catalog tool" in problem for problem in problems), problems)
self.assertTrue(any("must be disjoint" in problem for problem in problems), problems)
def test_release_coverage_is_derived_not_self_asserted(self) -> None:
metadata = {
"chat.send": {"product_id": "chat", "effect": "write", "interface_ref": {"rpc_name": "send"}},
"chat.delete": {"product_id": "chat", "effect": "destructive", "interface_ref": {"rpc_name": "delete"}},
"doc.read": {"product_id": "doc", "effect": "read", "interface_ref": {"rpc_name": "get"}},
"doc.fetch": {"product_id": "devdoc", "effect": "read", "interface_ref": {"rpc_name": "get"}},
}
cases = [
{
"qrels": [{"canonical": "chat.send", "relevance": 3}],
"forbidden": ["chat.delete"],
"alternative_gold": ["chat.send"],
"confusion_family": ["chat.delete", "chat.send.other"],
"workflow": {"required": ["chat.send", "doc.read"]},
},
{
"qrels": [
{"canonical": "doc.read", "relevance": 3},
{"canonical": "doc.fetch", "relevance": 2},
]
},
{"qrels": [{"canonical": "chat.delete", "relevance": 3}]},
]
problems: list[str] = []
coverage = verify.derive_independent_coverage(cases, metadata, problems)
self.assertIn("all_reviewed_products", coverage)
self.assertIn("read_write_destructive", coverage)
self.assertIn("graded_equivalent_entrypoints", coverage)
self.assertNotIn("sibling_confusion", coverage)
self.assertTrue(any("confusion_family" in problem for problem in problems))
if __name__ == "__main__":
unittest.main()