import importlib.util import subprocess import sys import tempfile import types import unittest import zipfile from pathlib import Path from unittest.mock import patch from openpyxl import Workbook ROOT = Path(__file__).resolve().parents[2] COMMON = ROOT / "config/skill-overrides/common" def load_module(name): spec = importlib.util.spec_from_file_location(name, COMMON / f"{name}.py") module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def load_recalc(): office = types.ModuleType("office.soffice") office.run_soffice = lambda *args, **kwargs: None spec = importlib.util.spec_from_file_location("market_recalc", ROOT / "config/skill-overrides/xlsx/recalc.py") module = importlib.util.module_from_spec(spec) with patch.dict(sys.modules, {"office.soffice": office}): spec.loader.exec_module(module) return module class OfficePortabilityTests(unittest.TestCase): def test_windows_install_path_and_arguments_with_spaces(self): office = load_module("soffice") with tempfile.TemporaryDirectory() as temporary: program_files = Path(temporary) / "Program Files" executable = program_files / "LibreOffice/program/soffice.com" executable.parent.mkdir(parents=True) executable.touch() with patch.object(office.platform, "system", return_value="Windows"), \ patch.object(office.shutil, "which", return_value=None), \ patch.dict(office.os.environ, {"ProgramFiles": str(program_files)}, clear=True), \ patch.object(office.subprocess, "run", return_value=subprocess.CompletedProcess([], 0)) as run: office.run_soffice(["--headless", "--convert-to", "pdf", "中文 文件.docx"]) args, kwargs = run.call_args self.assertEqual(args[0][0], str(executable)) self.assertEqual(args[0][-1], "中文 文件.docx") self.assertTrue(args[0][1].startswith("-env:UserInstallation=file:")) self.assertNotIn("LD_PRELOAD", kwargs["env"]) def test_existing_profile_is_preserved(self): office = load_module("soffice") with patch.object(office, "resolve_soffice_binary", return_value="soffice"), \ patch.object(office.subprocess, "run", return_value=subprocess.CompletedProcess([], 0)) as run: office.run_soffice(["-env:UserInstallation=file:///profile", "--headless"]) self.assertEqual(run.call_args.args[0], ["soffice", "-env:UserInstallation=file:///profile", "--headless"]) def test_package_roundtrip_and_removed_parts(self): package = load_module("office_package") with tempfile.TemporaryDirectory() as temporary: root = Path(temporary) source = root / "中文 输入.docx" with zipfile.ZipFile(source, "w") as archive: archive.writestr("[Content_Types].xml", "") archive.writestr("word/document.xml", "<正文/>") archive.writestr("word/removed.xml", "") extracted = root / "解包" package.unpack(source, extracted) (extracted / "word/removed.xml").unlink() output = root / "中文 输出.docx" package.pack(extracted, output) with zipfile.ZipFile(output) as archive: self.assertNotIn("word/removed.xml", archive.namelist()) self.assertEqual(archive.read("word/document.xml").decode(), "<正文/>") def test_package_rejects_path_escape(self): package = load_module("office_package") with tempfile.TemporaryDirectory() as temporary: root = Path(temporary) source = root / "bad.docx" with zipfile.ZipFile(source, "w") as archive: archive.writestr("../escape.txt", "bad") with self.assertRaisesRegex(ValueError, "Unsafe ZIP entry"): package.unpack(source, root / "unpacked") self.assertFalse((root / "escape.txt").exists()) def test_formula_without_cache_is_not_success(self): recalc = load_recalc() with tempfile.TemporaryDirectory() as temporary: source = Path(temporary) / "中文.xlsx" workbook = Workbook() workbook.active["A1"] = "=1+1" workbook.save(source) formulas, errors = recalc.inspect_workbook(source) self.assertEqual(formulas, {"Sheet!A1"}) self.assertEqual(errors, {"MISSING_FORMULA_CACHE": ["Sheet!A1"]}) def test_recalc_timeout_preserves_original(self): recalc = load_recalc() with tempfile.TemporaryDirectory() as temporary: source = Path(temporary) / "中文.xlsx" Workbook().save(source) before = source.read_bytes() with patch.object(recalc, "run_soffice", side_effect=subprocess.TimeoutExpired("soffice", 1)): report = recalc.recalc(source, 1) self.assertIn("error", report) self.assertEqual(source.read_bytes(), before) def test_recalc_external_links_refused_without_conversion(self): recalc = load_recalc() with tempfile.TemporaryDirectory() as temporary: source = Path(temporary) / "linked.xlsx" with zipfile.ZipFile(source, "w") as archive: archive.writestr("xl/externalLinks/externalLink1.xml", "") before = source.read_bytes() with patch.object(recalc, "run_soffice") as convert: report = recalc.recalc(source) self.assertIn("error", report) convert.assert_not_called() self.assertEqual(source.read_bytes(), before) if __name__ == "__main__": unittest.main()