Skip to content

fix(tests): make test_doctor_journal_modes collectable and green on Windows - #81926

Open
hyqqx wants to merge 1 commit into
NousResearch:mainfrom
hyqqx:fix/win-doctor-journal-collect
Open

fix(tests): make test_doctor_journal_modes collectable and green on Windows#81926
hyqqx wants to merge 1 commit into
NousResearch:mainfrom
hyqqx:fix/win-doctor-journal-collect

Conversation

@hyqqx

@hyqqx hyqqx commented Aug 8, 2026

Copy link
Copy Markdown

What does this PR do?

tests/hermes_cli/test_doctor_journal_modes.py is new in v0.20.0 (658329708) and aborts collection on native Windows. pytest stops the entire run on a collection error, so no test in the invocation gets to execute:

ERROR collecting tests/hermes_cli/test_doctor_journal_modes.py
E   AttributeError: module 'os' has no attribute 'geteuid'

The Windows guard is already there — it just never runs

@pytest.mark.skipif(os.name == "nt", reason="chmod is a no-op on Windows")
@pytest.mark.skipif(os.geteuid() == 0, reason="root ignores file permissions")
def test_read_only_directory_is_still_readable(self, tmp_path):

The first decorator is correct and does exactly the right thing. The second one evaluates os.geteuid() at import time, before pytest looks at either marker — and os.geteuid does not exist on Windows. The author's own guard is annulled by the line directly beneath it.

Short-circuiting on hasattr fixes it, and it is the form already used for this same pair in tests/test_hermes_state_readonly_preflight.py:31:

@pytest.mark.skipif(
    hasattr(os, "geteuid") and os.geteuid() == 0,
    reason="root ignores file permissions",
)

POSIX behaviour is byte-identical — hasattr(os, "geteuid") is True there, so the euid check runs exactly as before.

One more failure becomes reachable once collection is unblocked

test_lists_every_managed_database asserts a POSIX path:

assert "kanban/boards/myboard/kanban.db is in WAL mode" in out

doctor names nested databases with str(board_db.relative_to(hermes_home)) (hermes_cli/doctor.py:114), which uses the platform separator — so on Windows the output carries backslashes. The product is correct on both platforms; only the expectation was POSIX-only. It is now built with os.path.join so it follows whatever separator the host uses.

No production code is touched.

How to test

On native Windows, from PowerShell:

python -m pytest tests/hermes_cli/test_doctor_journal_modes.py -q -rs

Before:

ERROR collecting tests/hermes_cli/test_doctor_journal_modes.py
E   AttributeError: module 'os' has no attribute 'geteuid'
Interrupted: 1 error during collection

After:

25 passed, 2 skipped in 1.95s

SKIPPED [1] test_doctor_journal_modes.py:116: chmod is a no-op on Windows
SKIPPED [1] test_doctor_journal_modes.py:218: chmod is a no-op on Windows

The two skips carry the author's own reason — now actually firing.

On Linux/macOS nothing changes: both markers evaluate as before and all 27 tests run.

Scope

I collected all 230 test files added since v2026.7.30 on this host. This was the only collection error among them, and the bare skipif(os.geteuid()...) pattern appears nowhere else under tests/.

Why CI never caught this

All CI jobs run on ubuntu-latest (the only other runner in the tree is ubuntu-24.04-arm in docker.yml). There is no Windows runner, so os.geteuid always exists in CI and the import-time evaluation never raises.

Platforms tested

Native Windows 11 Home, build 26200 · Python 3.11.6 · PowerShell.

Related

…indows

This file is new in v0.20.0 (6583297) and aborts collection on native
Windows, which takes the whole run down with it -- pytest stops on a
collection error, so no test in the invocation gets to run.

  ERROR collecting tests/hermes_cli/test_doctor_journal_modes.py
  E   AttributeError: module 'os' has no attribute 'geteuid'

The Windows guard is already there and already correct:

  @pytest.mark.skipif(os.name == "nt", reason="chmod is a no-op on Windows")
  @pytest.mark.skipif(os.geteuid() == 0, reason="root ignores file permissions")

but the second decorator evaluates `os.geteuid()` at import time, before
pytest looks at either marker -- so the first one never gets a chance to
apply. `os.geteuid` does not exist on Windows.

Short-circuiting on `hasattr` fixes it and is the form already used for the
same pair in tests/test_hermes_state_readonly_preflight.py:31. POSIX
behaviour is byte-identical: `hasattr(os, "geteuid")` is True there, so the
euid check runs exactly as before.

With collection unblocked, one more Windows failure becomes reachable in the
same file: test_lists_every_managed_database asserts a POSIX path. doctor
names nested databases with `str(board_db.relative_to(hermes_home))`
(hermes_cli/doctor.py:114), which uses the platform separator, so the
expectation is built the same way instead of hardcoding forward slashes. No
production code is touched -- doctor's output is already correct on both
platforms.

Verified on native Windows 11 (build 26200), Python 3.11.6, from PowerShell:

  before:  ERROR at collection, 0 tests runnable
  after:   25 passed, 2 skipped   (exit 0)

The two skips are the author's own `chmod is a no-op on Windows` reason,
now actually firing.

Across the 230 test files added since v2026.7.30 this was the only
collection error, and the bare `skipif(os.geteuid()...)` pattern appears
nowhere else in tests/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@alt-glitch alt-glitch added type/test Test coverage or test infrastructure P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Aug 8, 2026

@monerostar monerostar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Native Win11 verification (monerostar)

Host: Windows 11 (build 10.0.26200), Python 3.11.15, pytest 9.1.1
SHA reviewed: c8c3ae8cd

Baseline (upstream/main @ 222465d84)

python -m pytest tests/hermes_cli/test_doctor_journal_modes.py -o addopts= --collect-only
# ERROR collecting tests/hermes_cli/test_doctor_journal_modes.py
# E   AttributeError: module 'os' has no attribute 'geteuid'
# (decorator line: @pytest.mark.skipif(os.geteuid() == 0, ...))

Bare os.geteuid() is evaluated at import/class-body time, so the existing os.name == "nt" skip never gets a chance to run. Confirmed live: hasattr(os, "geteuid") is False on this host.

This PR

python -m pytest tests/hermes_cli/test_doctor_journal_modes.py -o addopts=
# 27 collected → 25 passed, 2 skipped in ~2.3s

Both chmod-gated tests correctly skip on nt. test_lists_every_managed_database passes with the os.path.join(...) expectation (backslash path on Windows).

Verdict

Matches the stated bug class and the existing hasattr(os, "geteuid") pattern elsewhere. Test-only, focused, green on native Win11. LGTM from a Windows contributor host.

@monerostar

Copy link
Copy Markdown
Contributor

Sibling note (Win11 verify pass tonight): #84073 covers the same geteuid collection abort plus the test_run_tests_parallel.py /tmptempfile.gettempdir() handoff (issue #83935). #74083 is handoff-only. Maintainers can land any one complete path; no need for all three.

kshitijk4poor pushed a commit that referenced this pull request Aug 17, 2026
…rows

read_header_bytes_preopen answers None for a live connection, a missing
file and an unreadable file alike, so the error string doctor prints is
now chosen rather than inherited from the OSError. These cases pin that
choice: the missing file keeps its errno text, and the chmod-000 file is
still reported as a permission problem rather than collapsing into the
generic message — the behaviour the raw open() gave before.

test_reason_does_not_open_the_file is the load-bearing one. It patches
builtins.open to raise and asserts _unreadable_reason still answers,
which fixes the constraint that makes the helper safe to call on a
database path at all: stat() and access() read metadata and take no file
descriptor, so no close() of ours can cancel the file's advisory locks. A
future edit that reached for open() here to get a better message would
reintroduce the original bug on the error path, and this test fails
loudly if it does.

The root check is written as hasattr(os, "geteuid") and os.geteuid() == 0
rather than the bare call the surrounding tests use. skipif conditions are
evaluated at collection time and os.geteuid is POSIX-only, so the bare
form raises AttributeError and takes the whole module down on Windows.
The pre-existing occurrences are left alone — #81926 and #84073 are
already open against exactly those lines, and this only avoids adding a
third instance of the same defect.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/test Test coverage or test infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants