fix(telegram): make Windows-unmasked tests cross-platform
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.
This commit is contained in:
+8
-1
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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": {}})
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user