# -*- coding: utf-8 -*- """模板化(templating)单元测试:知识源分类 / 命名推导 / YAML 配置加载。""" import os import sys import unittest sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import _bootstrap # noqa: F401 from rag_kb.templating import ( # noqa: E402 KbTemplateConfig, KbTemplateNaming, KnowledgeSourceKind, SOURCE_KINDS, load_kb_config, sanitize, ) CONFIG_PATH = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "config", "kb.template.yaml", ) class SanitizeTest(unittest.TestCase): def test_lower_and_replace_unsafe(self): # 小写 + 空格等非安全字符替换为 `_`;RAG 命名**保留中文**(文档对象键) self.assertEqual(sanitize("Ti-Cl4 模板"), "ti-cl4_模板") self.assertEqual(sanitize("A.B-c_d"), "a.b-c_d") def test_chinese_kept(self): self.assertEqual(sanitize("沸腾氯化工艺规范"), "沸腾氯化工艺规范") # `/` 属非安全字符,替换为 `_` self.assertEqual(sanitize("GB/T 氯气安全使用标准"), "gb_t_氯气安全使用标准") def test_empty_fallback(self): self.assertEqual(sanitize(""), "kb") self.assertEqual(sanitize("..."), "kb") class SourceKindTest(unittest.TestCase): def test_kinds_registered(self): # 三类知识源固定注册(PRD 7.3:工艺规范 / SOP / 国标) self.assertEqual( set(SOURCE_KINDS), {"process", "sop", "standard"}, ) def test_labels(self): self.assertEqual(KnowledgeSourceKind.PROCESS.label, "工艺规范") self.assertEqual(KnowledgeSourceKind.SOP.label, "SOP/操作手册") self.assertEqual(KnowledgeSourceKind.STANDARD.label, "国标/标准") class NamingTest(unittest.TestCase): def setUp(self): self.naming = KbTemplateNaming(template="ti-cl4") def test_collection(self): # 向量库 collection:下划线形态(对齐 data-bus `tpl_{tpl}` schema) self.assertEqual(self.naming.collection(), "ti_cl4_kb") def test_index_and_namespace(self): self.assertEqual(self.naming.index_name(), "ti_cl4_kb_idx") self.assertEqual(self.naming.namespace(), "tpl-ti-cl4-kb") def test_doc_object_key(self): self.assertEqual( self.naming.doc_object_key("沸腾氯化工艺规范"), "kb/沸腾氯化工艺规范.md", ) def test_default_template(self): self.assertEqual(KbTemplateNaming("").collection(), "kb_kb") class YamlLoadTest(unittest.TestCase): def test_load_example_config(self): config = load_kb_config(CONFIG_PATH) self.assertEqual(config.template, "ti-cl4") self.assertEqual(config.version, "1.0.0") # 三类知识源各至少声明一份文档 self.assertIn(KnowledgeSourceKind.PROCESS, {s.kind for s in config.sources}) self.assertIn(KnowledgeSourceKind.SOP, {s.kind for s in config.sources}) self.assertIn(KnowledgeSourceKind.STANDARD, {s.kind for s in config.sources}) self.assertGreaterEqual(len(config.all_documents()), 3) def test_documents_for_kind(self): config = load_kb_config(CONFIG_PATH) docs = config.documents_for(KnowledgeSourceKind.PROCESS) self.assertIn("沸腾氯化工艺规范", docs) self.assertEqual( config.documents_for(KnowledgeSourceKind.STANDARD), ["GB/T 氯气安全使用标准", "GB/T 钛及钛合金加工标准"], ) def test_unknown_kind_rejected(self): # 未知知识源类别必须报错(杜绝配置拼写漂移) import tempfile with tempfile.NamedTemporaryFile("w", suffix=".yaml", delete=False, encoding="utf-8") as fh: fh.write("template: ti-cl4\nsources:\n - kind: hmm\n documents: [a]\n") path = fh.name try: with self.assertRaises(ValueError): load_kb_config(path) finally: os.unlink(path) def test_missing_template_rejected(self): import tempfile with tempfile.NamedTemporaryFile("w", suffix=".yaml", delete=False, encoding="utf-8") as fh: fh.write("version: 1.0.0\nsources: []\n") path = fh.name try: with self.assertRaises(ValueError): load_kb_config(path) finally: os.unlink(path) if __name__ == "__main__": unittest.main()