From 7e9c0cfca7f18ab8dce01e5cd496b8e0ca63b474 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Thu, 25 Jun 2026 03:44:26 -0700 Subject: [PATCH] fix(telegram): make Windows-unmasked tests cross-platform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guarding the fcntl import let Windows collection succeed, which surfaced 3 telegram tests that had never run on Windows — all test-portability bugs: - log_streamer byte-count broke on CRLF -> fixture writes newline='' - bot_registry write-failure used Unix-only /proc -> file-as-parent (all OS) - validate_bot_config rejected POSIX work_dir on Windows (Path.is_absolute is host-dependent) -> test absoluteness under PurePosixPath OR PureWindowsPath 493 telegram tests green on Linux; ruff clean. --- CHANGELOG.md | 9 ++++++++- .../skills/lib/telegram/apps/handlers/config.py | 9 ++++++--- .../skills/lib/telegram/tests/test_bot_registry.py | 13 ++++++++----- .../skills/lib/telegram/tests/test_log_streamer.py | 10 ++++++---- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9424d786..04092360 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,14 @@ PyPI version — not the changelog header. last several PRs. Guarded the import (`try/except ImportError → fcntl = None`, the established hooks/daemon convention) and routed the three flock call-sites through no-op-on-Windows `_lock`/`_unlock` helpers — advisory locking still - applies on POSIX, is skipped where unavailable. 246 telegram tests green. + applies on POSIX, is skipped where unavailable. Fixing collection then + *unmasked* three telegram tests that had never actually run on Windows, all + test-portability bugs (not product bugs): a log-streamer byte-count broke on + CRLF translation (fixture now writes `newline=""`); a registry write-failure + test used the Unix-only `/proc` path (now a cross-platform file-as-directory + parent); and `validate_bot_config` rejected valid POSIX `work_dir`s on Windows + because `Path.is_absolute()` is host-dependent (now tests POSIX *and* Windows + absoluteness). 493 telegram tests green. - **prax-monitor service feedback loop** — the unit wrote its own stdout into `system_logs/`, the very directory the monitor tails *and* @trigger watches, diff --git a/src/aipass/skills/lib/telegram/apps/handlers/config.py b/src/aipass/skills/lib/telegram/apps/handlers/config.py index af21e76d..22bd6135 100644 --- a/src/aipass/skills/lib/telegram/apps/handlers/config.py +++ b/src/aipass/skills/lib/telegram/apps/handlers/config.py @@ -1,5 +1,5 @@ # Standard library -from pathlib import Path +from pathlib import PurePosixPath, PureWindowsPath from typing import Optional, List # Logging @@ -208,8 +208,11 @@ def validate_bot_config(config: object) -> tuple[bool, str]: return False, "bot_token must be a string in format 'id:hash'" if "work_dir" in config and config["work_dir"] is not None: - work_dir = Path(config["work_dir"]) - if not work_dir.is_absolute(): + # work_dir is a deployment-target (Linux) path. Test absoluteness under + # POSIX *and* Windows semantics so validation is platform-independent — + # a bare host Path().is_absolute() would reject "/home/..." on Windows. + work_dir = str(config["work_dir"]) + if not (PurePosixPath(work_dir).is_absolute() or PureWindowsPath(work_dir).is_absolute()): return False, "work_dir must be an absolute path" if "allowed_user_ids" in config: diff --git a/src/aipass/skills/lib/telegram/tests/test_bot_registry.py b/src/aipass/skills/lib/telegram/tests/test_bot_registry.py index 3e4da190..1cd02a06 100644 --- a/src/aipass/skills/lib/telegram/tests/test_bot_registry.py +++ b/src/aipass/skills/lib/telegram/tests/test_bot_registry.py @@ -6,7 +6,6 @@ No external dependencies beyond pytest. """ import json -from pathlib import Path import pytest from apps.handlers import bot_registry # type: ignore[import-not-found] @@ -191,11 +190,15 @@ class TestSaveRegistry: assert result is True assert bot_registry.REGISTRY_DIR.is_dir() - def test_returns_false_on_write_failure(self, monkeypatch): + def test_returns_false_on_write_failure(self, monkeypatch, tmp_path): """Should return False when writing fails (e.g. permission error).""" - # Point to an impossible path - monkeypatch.setattr(bot_registry, "REGISTRY_DIR", Path("/proc/nonexistent/impossible")) - monkeypatch.setattr(bot_registry, "REGISTRY_FILE", Path("/proc/nonexistent/impossible/_registry.json")) + # Put a regular file where a directory is expected. mkdir(parents=True) + # then fails on every OS (NotADirectoryError/FileExistsError), so this is + # cross-platform — unlike a hardcoded Unix-only path such as /proc/... + blocker = tmp_path / "blocker" + blocker.write_text("x", encoding="utf-8") + monkeypatch.setattr(bot_registry, "REGISTRY_DIR", blocker / "sub") + monkeypatch.setattr(bot_registry, "REGISTRY_FILE", blocker / "sub" / "_registry.json") result = bot_registry.save_registry({"bots": {}, "metadata": {}}) diff --git a/src/aipass/skills/lib/telegram/tests/test_log_streamer.py b/src/aipass/skills/lib/telegram/tests/test_log_streamer.py index ce2d6ec8..dbb188be 100644 --- a/src/aipass/skills/lib/telegram/tests/test_log_streamer.py +++ b/src/aipass/skills/lib/telegram/tests/test_log_streamer.py @@ -62,11 +62,13 @@ def streamer(logs_dir, tmp_path): @pytest.fixture def streamer_with_files(logs_dir, tmp_path): """Create a LogStreamer after pre-populating matching and non-matching log files.""" - # Create matching log files with content - (logs_dir / "api_main.log").write_text("line1\nline2\n", encoding="utf-8") - (logs_dir / "api_error.log").write_text("err1\n", encoding="utf-8") + # Create matching log files with content. newline="" disables newline + # translation so byte counts match len() on Windows too (\n stays 1 byte, + # not \r\n) — real log files are LF-terminated. + (logs_dir / "api_main.log").write_text("line1\nline2\n", encoding="utf-8", newline="") + (logs_dir / "api_error.log").write_text("err1\n", encoding="utf-8", newline="") # Create a non-matching file (should be ignored) - (logs_dir / "trigger_main.log").write_text("other\n", encoding="utf-8") + (logs_dir / "trigger_main.log").write_text("other\n", encoding="utf-8", newline="") return _make_streamer(logs_dir, tmp_path)