Skip to content

test: keep the project-root test inside its fixture when TMPDIR is under $HOME - #148

Merged
acidkill merged 1 commit into
acidkill:mainfrom
RobertSigmundsson:fix/project-root-detection-test-isolation
Aug 3, 2026
Merged

test: keep the project-root test inside its fixture when TMPDIR is under $HOME#148
acidkill merged 1 commit into
acidkill:mainfrom
RobertSigmundsson:fix/project-root-detection-test-isolation

Conversation

@RobertSigmundsson

Copy link
Copy Markdown
Contributor

Second of the three small things from our 3.0.3 migration. Test-only, and it cost us a
puzzled half-hour before we spotted what was going on — which is usually a sign it is worth
fixing for whoever hits it next.

Summary

  • One unit test fails on any machine whose TMPDIR sits under the home directory, because the
    code under test walks out of the fixture and into the real filesystem.
  • Two lines of the fixture change; the assertion is untouched.
  • Test-only. No production code changes.

Why

detect_project_root() walks up from Path.cwd() and stops only when it reaches Path.home()
(src/surreal_memory/surface/resolver.py:195-205):

for _ in range(20):
    if current != home and _is_project_root(current, global_dir):
        return current
    parent = current.parent
    if parent == current or current == home:
        break

test_relocated_global_config_dir_is_not_a_project_root patches cwd to tmp_path/srv/data and
home to tmp_path/home. That home is a sibling of the walk, not an ancestor of it, so the walk
never meets it. It climbs out of tmp_path, into the real home directory, finds a marker there
and returns it:

    assert detect_project_root() is None
E   AssertionError: assert PosixPath('<$HOME>') is None

The marker it finds is usually .surrealmemory — checked first in the marker list, and created
in the home directory by this tool itself — or package.json. Removing one does not help,
because the walk then matches the other.

pytest puts tmp_path under TMPDIR, so whether the test passes depends entirely on whether
the developer's TMPDIR happens to be outside their home directory. The sibling test at line 48
is unaffected because its cwd is the patched home, so it breaks out on the first iteration.

Changes

  • tests/unit/test_surface_path_and_decay.py — anchor the patched home at tmp_path so the
    walk terminates inside the fixture, with a comment stating why. The assertion, the global-dir
    patching and what the test proves are all unchanged.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature
  • Breaking change
  • Documentation update
  • Refactoring (no functional changes)
  • Tests

Test plan

  • pytest tests/ -m "not stress" -n auto passes locally — and now under both worlds:

    TMPDIR=/tmp             ->  6719 passed, 119 skipped, 1 xfailed
    TMPDIR under $HOME      ->  6719 passed, 119 skipped, 1 xfailed   (before: 1 failed)
    
    the single test file:
    TMPDIR under $HOME, before  ->  1 failed, 22 passed
    TMPDIR under $HOME, after   ->  23 passed
    TMPDIR=/tmp, after          ->  23 passed
    
  • ruff check src/ tests/ clean on 0.16.1. On 0.15.20 the tree still reports the
    pre-existing S310 in mcp/version_check_handler.py, which this branch does not touch.

  • mypy src/ --ignore-missing-imports clean — Success: no issues found in 351 source files.

  • Verified in both directions, because a test that cannot fail proves nothing. With the
    global-config-dir guard in surface/resolver.py deliberately removed —

    ```python
    if marker == _NM_DIR and global_dir is not None:
        try:
            if marker_path.resolve() == global_dir:
                continue
    ```
    
    — the fixed test fails again under **both** `TMPDIR` settings (`1 failed, 1 passed`), and
    passes again once the guard is restored (`2 passed`). So the fix did not make it
    vacuously green; it still catches the regression it was written for.
    
  • Manual check: applies cleanly onto a pristine v3.0.3 tree.

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have added tests that prove my fix is effective — the change is to a test; its power is
    demonstrated by the guard-removal run above.
  • New and existing unit tests pass locally with my changes
  • I have updated the documentation accordingly — not applicable.
  • I have updated CHANGELOG.md — deliberately not: test-only, no user-visible effect,
    matching #137 and #140. Say the word and I will add one.

Related Issues

None.

Verified by

@RobertSigmundsson

…der $HOME

`test_relocated_global_config_dir_is_not_a_project_root` in
tests/unit/test_surface_path_and_decay.py fails on any machine whose TMPDIR
sits under the home directory:

    assert detect_project_root() is None
    E  AssertionError: assert PosixPath('<$HOME>') is None

`detect_project_root` walks up from `Path.cwd()` and stops only when it
reaches `Path.home()` (surface/resolver.py:195-205). The test patches cwd to
`tmp_path/srv/data` and home to `tmp_path/home` -- a sibling of the walk, not
an ancestor of it -- so the walk never meets the patched home. It leaves
tmp_path, keeps climbing into the real home directory, finds a marker there
and returns it.

The marker it finds is usually `.surrealmemory`, which `detect_project_root`
checks first and which this tool creates in the home directory itself;
`package.json` is another one. Deleting either does not help, because the walk
then matches the other.

Anchoring the patched home at `tmp_path` keeps the walk inside the fixture.
The assertion is untouched and still says the same thing: the directory that
holds SURREAL_MEMORY_DIR must not be promoted to a project root.

Verified in both directions, because a test that cannot fail proves nothing:

    TMPDIR under home, before   ->  1 failed, 22 passed
    TMPDIR under home, after    ->  23 passed
    TMPDIR=/tmp, after          ->  23 passed
    full suite, both TMPDIRs    ->  6719 passed, 119 skipped, 1 xfailed

    with the global-dir guard in surface/resolver.py deliberately removed, the
    fixed test fails again under both TMPDIRs -- so it still catches the
    regression it was written for.

No production code changes: walking up to the home directory is documented and
intended behaviour.
@acidkill
acidkill merged commit f6b85ee into acidkill:main Aug 3, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants