# -*- coding: utf-8 -*- """ file_reader 单元测试(标准库 unittest,零额外依赖) 运行:在项目根目录执行 python -m unittest discover tests -v 或直接 python tests/test_file_attach.py """ import os import sys import tempfile import unittest # 保证直接运行(python tests/xxx.py)时也能 import 到项目根下的包 sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) from ui.views.system_tools.file_reader import BINARY_EXTS, read_text_file # noqa: E402 class ReadTextFileTest(unittest.TestCase): """read_text_file:编码探测 / 二进制探测 / 大小守卫""" def _write(self, data: bytes, suffix: str = ".txt") -> str: fd, path = tempfile.mkstemp(suffix=suffix) with os.fdopen(fd, "wb") as f: f.write(data) self.addCleanup(os.remove, path) return path def test_utf8_file(self): path = self._write("你好,世界\nhello".encode("utf-8")) content, enc, size_kb, lines = read_text_file(path) self.assertIn("你好,世界", content) self.assertIn("hello", content) self.assertEqual(lines, 2) self.assertIn(enc, ("utf-8-sig", "utf-8")) self.assertGreater(size_kb, 0) def test_utf8_bom_file(self): path = self._write(b"\xef\xbb\xbf" + "带BOM".encode("utf-8")) content, enc, _, _ = read_text_file(path) self.assertEqual(content, "带BOM") # utf-8-sig 会吃掉 BOM self.assertEqual(enc, "utf-8-sig") def test_gbk_file_falls_back_to_gb18030(self): path = self._write("中文GBK内容".encode("gbk")) content, enc, _, _ = read_text_file(path) self.assertEqual(content, "中文GBK内容") self.assertEqual(enc, "gb18030") def test_binary_file_rejected(self): path = self._write(b"\x00\x01\x02\x03binary-payload") with self.assertRaises(ValueError): read_text_file(path) def test_oversize_file_rejected(self): path = self._write(b"a" * 100) with self.assertRaises(ValueError): read_text_file(path, max_bytes=10) def test_latin1_fallback_never_fails(self): # 0xFF 既非合法 UTF-8 也非合法 GB18030 引导字节,应由 latin-1 兜底 path = self._write(b"\xff\xfe\xfd plain text") content, enc, _, _ = read_text_file(path) self.assertEqual(enc, "latin-1") self.assertIn("plain text", content) class BinaryExtBlacklistTest(unittest.TestCase): """黑名单分类:Word/Excel 等被拒,常见代码/文本文件放行""" def test_office_and_binary_blocked(self): for ext in (".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".zip", ".exe", ".dll", ".mp4", ".ttf", ".db", ".psd"): self.assertIn(ext, BINARY_EXTS, f"{ext} 应在黑名单中") def test_pdf_not_blacklisted(self): # PDF 改由专用分支(pdf_reader)处理,不再走二进制黑名单 self.assertNotIn(".pdf", BINARY_EXTS, ".pdf 应由 PDF 专用分支处理,不应在黑名单中") def test_text_and_code_allowed(self): for ext in (".py", ".js", ".ts", ".java", ".c", ".cpp", ".go", ".rs", ".md", ".txt", ".json", ".yaml", ".html", ".css", ".sql", ".sh", ".csv", ".log", ".ipynb", ""): self.assertNotIn(ext, BINARY_EXTS, f"{ext} 不应在黑名单中") if __name__ == "__main__": unittest.main()