import importlib.util
import sys
import tempfile
import unittest
from pathlib import Path
SCRIPT = Path(__file__).parents[1] / "scripts" / "enrich_metadata.py"
def load_script():
spec = importlib.util.spec_from_file_location("tsl_enrich_metadata", SCRIPT)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
class SourceCatalogTest(unittest.TestCase):
def setUp(self):
self.module = load_script()
self.temp_dir = tempfile.TemporaryDirectory()
self.docs = Path(self.temp_dir.name)
def tearDown(self):
self.temp_dir.cleanup()
def write_corpus(self, corpus, rows, descriptions):
root = self.docs / corpus
pages = root / "pages"
pages.mkdir(parents=True)
manifest = [
"id\ttitle\turl\tpath\tsource_bytes\tutf8_bytes\tsha256\tstatus"
]
for page_id, title in rows:
manifest.append(
f"{page_id}\t{title}\thttp://example/{page_id}\t"
f"pages/{page_id}.html\t1\t1\tsha\tok"
)
description = descriptions[page_id]
(pages / f"{page_id}.html").write_text(
"
"
+ title
+ "
"
+ f"
{description}
",
encoding="utf-8",
)
(root / "manifest.tsv").write_text(
"\n".join(manifest) + "\n", encoding="utf-8"
)
def test_exact_matching_preserves_underscores_and_prefers_scope_corpus(self):
self.write_corpus(
"tsl_base",
[("1", "alpha_test")],
{"1": "基础解释器说明"},
)
self.write_corpus(
"net_function",
[("2", "alpha_test"), ("3", "alphaTest")],
{"2": "NET 说明", "3": "无下划线的另一个函数"},
)
catalog = self.module.SourceCatalog.from_docs_root(self.docs)
builtin = self.module.EntryContext(
name="alpha_test",
signature="alpha_test()",
kind="function",
scope="builtin",
page="builtin/demo.md",
summary="原说明",
)
dotnet = self.module.EntryContext(
name="alpha_test",
signature="alpha_test()",
kind="function",
scope="dotnet",
page="dotnet/demo.md",
summary="原说明",
)
self.assertEqual("tsl_base", catalog.match(builtin)[0].corpus)
self.assertEqual("net_function", catalog.match(dotnet)[0].corpus)
self.assertNotIn(
"alphaTest", {record.title for record in catalog.match(dotnet)}
)
def test_duplicate_titles_remain_visible_for_audit(self):
self.write_corpus(
"net_function",
[("1", "same_name"), ("2", "same_name")],
{"1": "说明一", "2": "说明二"},
)
catalog = self.module.SourceCatalog.from_docs_root(self.docs)
entry = self.module.EntryContext(
name="same_name",
signature="same_name()",
kind="function",
scope="dotnet",
page="dotnet/demo.md",
summary="原说明",
)
self.assertEqual(["1", "2"], [item.page_id for item in catalog.match(entry)])
class DescriptionQualityTest(unittest.TestCase):
def setUp(self):
self.module = load_script()
def entry(self, **overrides):
values = {
"name": "profitRatio",
"signature": "profitRatio(report_date)",
"kind": "function",
"scope": "dotnet",
"page": "dotnet/financial/profitability.md",
"summary": "总资产收益率(%)。",
}
values.update(overrides)
return self.module.EntryContext(**values)
def test_removes_only_terminal_sentence_punctuation(self):
entry = self.entry(summary="总资产收益率(%)。")
self.assertEqual(
"返回总资产收益率(%)",
self.module.improve_description(entry, []),
)
def test_preserves_formula_and_identifiers(self):
entry = self.entry(
summary="总资产报酬率(%)=利润总额/平均资产总额*100。"
)
self.assertEqual(
"计算总资产报酬率(%),公式为利润总额/平均资产总额*100",
self.module.improve_description(entry, []),
)
def test_rejects_source_process_prose(self):
entry = self.entry(summary="返回指定值")
source = self.module.SourceRecord(
corpus="net_function",
page_id="1",
title="profitRatio",
page=Path("1.html"),
description="Windows 已验证通过,Linux 返回 not found",
)
self.assertEqual(
"返回指定值", self.module.improve_description(entry, [source])
)
def test_current_environment_can_be_real_api_semantics(self):
self.assertEqual(
"获取当前环境时间系统参数",
self.module.safe_source_description("获取当前环境时间系统参数。"),
)
def test_property_access_is_stated_explicitly(self):
entry = self.entry(
name="Host",
signature="Host",
kind="property",
scope="builtin",
page="builtin/object/ftp.md",
summary="远程服务器地址。",
access="read/write",
owner="FTP",
)
self.assertEqual(
"获取或设置远程服务器地址",
self.module.improve_description(entry, []),
)
def test_boolean_property_description_is_idempotent(self):
entry = self.entry(
name="UseTLS",
signature="UseTLS",
kind="property",
scope="builtin",
page="builtin/object/imap.md",
summary="是否采用 SSL 连接",
access="read/write",
owner="IMAP",
)
first = self.module.improve_description(entry, [])
repeated = self.module.improve_description(
self.module.EntryContext(
**{**entry.__dict__, "summary": first}
),
[],
)
self.assertEqual("控制是否采用 SSL 连接", first)
self.assertEqual(first, repeated)
def test_low_confidence_sentence_is_kept_except_terminal_punctuation(self):
entry = self.entry(
name="opaqueApi",
page="dotnet.md",
summary="根据调用上下文处理结果,具体规则取决于输入。",
)
self.assertEqual(
"根据调用上下文处理结果,具体规则取决于输入",
self.module.improve_description(entry, []),
)
def test_short_description_is_not_replaced_by_ambiguous_source_text(self):
entry = self.entry(
name="clRed",
page="builtin/color.md",
summary="红色",
)
sources = [
self.module.SourceRecord(
corpus="net_function",
page_id="1",
title="clRed",
page=Path("1.html"),
description="定义",
)
]
self.assertEqual("红色", self.module.improve_description(entry, sources))
def test_existing_action_word_is_normalized_without_duplicate_verb(self):
getter = self.entry(
name="getCValue",
page="builtin/color.md",
summary="得到颜色的 CMYK 模式 C 值。",
)
writer = self.entry(
name="write",
page="builtin/cgi.md",
summary="输出字符串。",
)
self.assertEqual(
"获取颜色的 CMYK 模式 C 值",
self.module.improve_description(getter, []),
)
self.assertEqual("输出字符串", self.module.improve_description(writer, []))
def test_equation_inside_algorithm_sentence_is_not_rewritten_as_formula(self):
entry = self.entry(
name="se_Gauss",
page="builtin/optimization.md",
summary="用高斯消去法求解线性方程组 AX = B",
)
self.assertEqual(
"用高斯消去法求解线性方程组 AX = B",
self.module.improve_description(entry, []),
)
def test_builtin_class_gets_a_purpose_description(self):
entry = self.entry(
name="TStringList",
signature="TStringList",
kind="class",
scope="builtin",
page="builtin/object/tstringlist.md",
summary="TStringList 内置对象",
)
self.assertEqual(
"提供字符串集合存储、查找、排序和名称值管理能力的内置对象",
self.module.improve_description(entry, []),
)
def test_long_property_explanation_is_not_wrapped_in_access_boilerplate(self):
entry = self.entry(
name="CommaTextW",
signature="CommaTextW",
kind="property",
scope="builtin",
page="builtin/object/tstringlist.md",
summary=(
"功能同 CommaText,区别是在读取时返回宽字节字符串,"
"而 CommaText 返回多字节字符串"
),
access="read/write",
owner="TStringList",
)
self.assertEqual(
entry.summary, self.module.improve_description(entry, [])
)
def test_short_read_and_load_phrases_are_normalized(self):
read_entry = self.entry(
name="read",
page="builtin/object/tstream.md",
summary="读出内容",
)
load_entry = self.entry(
name="loadFromFile",
page="builtin/object/tstringlist.md",
summary="从指定的文件中装载内容",
)
self.assertEqual(
"读取内容", self.module.improve_description(read_entry, [])
)
self.assertEqual(
"从指定的文件中加载内容",
self.module.improve_description(load_entry, []),
)
def test_embedded_read_out_is_normalized_without_duplicate_prefix(self):
entry = self.entry(
name="readExcelSheets",
page="dotnet.md",
summary="从Excel文件中读出Sheets列表。",
)
self.assertEqual(
"从Excel文件中读取Sheets列表",
self.module.improve_description(entry, []),
)
def test_existing_decomposition_verb_is_not_prefixed(self):
entry = self.entry(
name="decodeGraphGroup",
page="builtin/graph.md",
summary="分解图形组合并写入输出参数",
)
self.assertEqual(
entry.summary, self.module.improve_description(entry, [])
)
def test_comparison_expression_is_not_rewritten_as_formula(self):
entry = self.entry(
name="stockStepAmount",
page="dotnet/financial/stock-capital-flow.md",
summary="分档区间 V1<=Value