Skip to content

fix(tests): make tests cross-platform for Windows, Linux, and macOS#565

Open
jSydorowicz21 wants to merge 5 commits intoRunMaestro:mainfrom
jSydorowicz21:fix/windows-tests
Open

fix(tests): make tests cross-platform for Windows, Linux, and macOS#565
jSydorowicz21 wants to merge 5 commits intoRunMaestro:mainfrom
jSydorowicz21:fix/windows-tests

Conversation

@jSydorowicz21
Copy link

@jSydorowicz21 jSydorowicz21 commented Mar 12, 2026

Summary

  • Replace hardcoded Unix path separators in 57 test assertions with portable path module functions (path.join(), path.resolve(), path.normalize(), path.isAbsolute(), path.delimiter) so tests pass on Windows, Linux, and macOS
  • Fix ESM/CJS interop in the os mock for session-storage.test.ts (handle default export ambiguity, add missing tmpdir)
  • Increase timeout on 4 SSH integration tests from 10s to 20s these make real SSH connection attempts to non-existent hosts and can exceed the default timeout under load on Linux

Files changed (10 test files, 0 production files)

  • cli/services/storage.test.ts (11 tests)
  • cli/services/agent-sessions.test.ts (9 tests)
  • main/agents/session-storage.test.ts (10 tests + 4 timeout fixes)
  • main/ipc/handlers/attachments.test.ts (13 tests)
  • main/ipc/handlers/system.test.ts (3 tests)
  • main/ipc/handlers/marketplace.test.ts (1 test)
  • main/stats/paths.test.ts (5 tests)
  • main/utils/cliDetection.test.ts (2 tests)
  • shared/pathUtils.test.ts (1 test)
  • process-listeners/wakatime-listener.test.ts (3 tests)

Test plan

  • All tests pass on Windows
  • All tests pass on Linux (WSL Ubuntu)
  • No production code modified
  • No tests skipped all run on every platform

Summary by CodeRabbit

  • Tests
    • Improved cross-platform test robustness by standardizing and normalizing filesystem path handling and related assertions across multiple test suites.
  • Documentation
    • Minor formatting and reflow updates to release notes and reference docs.
  • Bug Fixes / Chores
    • Removed a redundant page-crash event handler and its reporting to third-party error tracking, consolidating crash handling.

Replace hardcoded Unix paths with path.join(), path.resolve(), and
path.normalize() so assertions match native path separators on all
platforms. Use path.isAbsolute() instead of regex for portability
and path.delimiter instead of hardcoded ':' for PATH splitting.

Affected test files (10):
- cli/services/storage.test.ts (11 tests)
- cli/services/agent-sessions.test.ts (9 tests)
- main/agents/session-storage.test.ts (10 tests)
- main/ipc/handlers/attachments.test.ts (13 tests)
- main/ipc/handlers/system.test.ts (3 tests)
- main/ipc/handlers/marketplace.test.ts (1 test)
- main/stats/paths.test.ts (5 tests)
- main/utils/cliDetection.test.ts (2 tests)
- shared/pathUtils.test.ts (1 test)
- process-listeners/wakatime-listener.test.ts (3 tests)
These tests make real SSH connection attempts to non-existent hosts,
which can exceed the default 10s timeout under load on Linux.
Two instances of the old startsWith('/') || match(/^[A-Z]:\/) pattern
were missed in the initial cross-platform fix. Now all 5 instances
consistently use path.isAbsolute().
@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 36a732aa-343e-4374-8fd9-8f88360dc635

📥 Commits

Reviewing files that changed from the base of the PR and between 49cfd57 and 232a56e.

📒 Files selected for processing (1)
  • src/main/app-lifecycle/window-manager.ts
💤 Files with no reviewable changes (1)
  • src/main/app-lifecycle/window-manager.ts

📝 Walkthrough

Walkthrough

Applies cross-platform path handling across many tests (using path.join/resolve/normalize/delimiter) and removes the webContents 'crashed' event handler from the window manager; changes are limited to tests, docs, and one production file edit (handler removal).

Changes

Cohort / File(s) Summary
CLI Services Tests
src/__tests__/cli/services/agent-sessions.test.ts, src/__tests__/cli/services/storage.test.ts
Imported path and replaced hard-coded path literals with path.join/path.normalize/path.sep for cross-platform assertions.
Main Agents & Session Storage Tests
src/__tests__/main/agents/session-storage.test.ts
Refactored os mock to handle default export, added tmpdir mock, standardized test timeouts, and replaced fragile path checks with path.isAbsolute()/path.resolve().
IPC Handler Tests
src/__tests__/main/ipc/handlers/attachments.test.ts, src/__tests__/main/ipc/handlers/marketplace.test.ts, src/__tests__/main/ipc/handlers/system.test.ts
Added path imports and switched direct string path expectations to path.resolve/path.normalize in mocks and assertions.
Stats & Path Utilities Tests
src/__tests__/main/stats/paths.test.ts, src/__tests__/shared/pathUtils.test.ts
Normalized filesystem paths in expectations and mocks and replaced hard-coded PATH delimiter with path.delimiter.
Process Listeners Tests
src/main/process-listeners/__tests__/wakatime-listener.test.ts
Replaced literal file paths in test payloads with path.resolve(...) to normalize file_path expectations.
CLI Detection Tests
src/__tests__/main/utils/cliDetection.test.ts
Made Windows PATH containment check case-insensitive by lowercasing PATH before assertions.
Window manager (production)
src/main/app-lifecycle/window-manager.ts
Removed the mainWindow.webContents 'crashed' event handler (logging and Sentry reporting removed).
Docs & Misc
AGENTS.md, docs/releases.md
Minor formatting and content reflow; small non-functional edits (newline, release note reformat).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Security Hardening #413 — Modifies src/main/app-lifecycle/window-manager.ts (adds sandbox/webContents security handlers); likely related to the removed webContents 'crashed' handler.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: making tests cross-platform by fixing Unix path separators across multiple test files for Windows, Linux, and macOS compatibility.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR replaces 57+ hardcoded Unix path separators across 10 test files with portable path module calls (path.join, path.resolve, path.normalize, path.isAbsolute, path.delimiter, path.sep), so the test suite runs correctly on Windows, Linux, and macOS. No production code is modified.

Key changes:

  • Path assertion portability: assertions now use the same path operations as the production code they test (e.g. path.resolve in system.ts/marketplace.ts handlers, path.join in agent-sessions.ts/storage.ts), ensuring both sides produce the same string on any OS
  • path.sep in includes() checks: pathStr.includes('/history/')pathStr.includes(\${path.sep}history${path.sep}`)guards against Windows backslash paths instorage.test.ts`
  • ESM/CJS interop fix: the os mock in session-storage.test.ts now handles the default export ambiguity and adds the missing tmpdir mock, fixing test failures caused by ESM module wrapping
  • SSH test timeouts: four integration tests that make real SSH connection attempts to non-existent hosts are bumped from 10 s → 20 s to handle slower CI environments
  • Windows path case-sensitivity: cliDetection.test.ts lowercases the PATH string before asserting Windows-specific paths, accounting for mixed-case registry/environment values

One minor inconsistency to consider: in pathUtils.test.ts, the "should not duplicate paths already in PATH" test is inside a describe('Unix-like systems') block that mocks process.platform to 'darwin', but path.delimiter is not affected by that mock and is ; on Windows. The test still passes on Windows (the original : delimiter was the semantically correct choice for this Unix-specific test), though it does not cause false failures.

Confidence Score: 4/5

  • Safe to merge — test-only changes that are well-reasoned and consistent with production code behaviour.
  • All changes are in test files with zero production code impact. The path API choices correctly mirror what the production implementations use. The one conceptual inconsistency (colon-delimited PATH string split with path.delimiter in a Unix-specific test block) does not cause test failures on any platform, but slightly weakens the deduplication coverage on Windows.
  • src/tests/shared/pathUtils.test.ts (deduplication test split delimiter) and src/tests/main/agents/session-storage.test.ts (pre-existing path variable shadowing)

Important Files Changed

Filename Overview
src/tests/main/agents/session-storage.test.ts Fixes ESM/CJS os mock interop, adds missing tmpdir mock, replaces manual absolute-path regex with path.isAbsolute(), and increases SSH integration test timeouts to 20 s. Pre-existing local variable path shadowing the module import is retained (no functional impact today, but fragile).
src/tests/cli/services/storage.test.ts Replaces hardcoded Unix path strings with path.join() / path.sep constructs throughout. Both the production getConfigDirectory() and the test now call path.join with the same arguments, so results remain consistent on all platforms.
src/tests/shared/pathUtils.test.ts Single change: .split(':').split(path.delimiter) in the deduplication test. The test still passes on Windows (for a different reason — the hardcoded colon-delimited PATH string is treated as one opaque ;-separated entry on Windows), but the deduplication behaviour is no longer actually verified on Windows.
src/tests/main/ipc/handlers/system.test.ts Wraps three shell-handler assertions with path.resolve(), correctly mirroring the production code which calls path.resolve(itemPath) before passing the path to Electron's shell APIs.
src/tests/main/ipc/handlers/marketplace.test.ts Changes two path.normalize() assertions to path.resolve() to match the production validateSafePath() function that uses path.resolve(basePath, requestedFile) internally.
src/tests/main/ipc/handlers/attachments.test.ts Mock app.getPath return value wrapped with path.resolve() so the base directory used in the security boundary check (sanitizeSessionId) is an absolute path on all platforms.
src/tests/main/stats/paths.test.ts Wraps mkdirSync and getDbPath assertions with path.normalize() to match the normalization applied by path.dirname(path.join(...)) in the StatsDB constructor and initialize() method.
src/tests/main/utils/cliDetection.test.ts Normalises Windows path comparisons to lowercase before asserting, fixing case-sensitivity issues on Windows where the registry or environment may return mixed-case drive/path names.
src/tests/cli/services/agent-sessions.test.ts Single fix: replaces the hardcoded sessionsDir string with path.join(), matching the production path.join(os.homedir(), '.claude', 'projects', encodedPath) construction.
src/main/process-listeners/tests/wakatime-listener.test.ts Three heartbeat assertions updated to use path.resolve(projectDir, relativePath), correctly matching the production wakatime-listener.ts logic which calls path.resolve(projectDir, f.filePath) for relative paths.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Test runs on any platform] --> B{Which OS?}

    B -->|Windows| C[path.sep = backslash\npath.delimiter = semicolon\npath.resolve adds drive letter]
    B -->|macOS / Linux| D[path.sep = slash\npath.delimiter = colon\npath.resolve keeps Unix root]

    C --> E[path.join constructs\nnative Windows paths]
    D --> F[path.join constructs\nnative Unix paths]

    E --> G[Assertion uses same\npath.join / path.resolve\nas production code]
    F --> G

    G --> H{Assertion matches\nproduction output?}
    H -->|Yes - portable| I[Test PASSES on all platforms]
    H -->|No - hardcoded separator| J[Test FAILS on non-matching OS]

    style I fill:#22c55e,color:#fff
    style J fill:#ef4444,color:#fff
    style G fill:#3b82f6,color:#fff
Loading

Comments Outside Diff (1)

  1. src/__tests__/shared/pathUtils.test.ts, line 262-269 (link)

    path.delimiter doesn't follow process.platform mock

    This test is inside describe('Unix-like systems (macOS/Linux)') which mocks process.platform to 'darwin', but path.delimiter is a compile-time constant determined by the real OS — it is unaffected by process.platform mocking.

    On Windows:

    • process.env.PATH is set to '/opt/homebrew/bin:/usr/bin' (colon-delimited)
    • buildExpandedPath() also uses path.delimiter (i.e. ;) internally, so it treats the entire string '/opt/homebrew/bin:/usr/bin' as one opaque entry
    • The deduplication check in the production code never recognizes /opt/homebrew/bin as being present
    • So /opt/homebrew/bin is re-added as a new ;-delimited entry
    • The test then splits on ; and counts 1 occurrence — and passes, but for the wrong reason

    The original split(':') would have actually failed on Windows (count = 0 since the result string uses ; as a separator). The path.delimiter change makes the test pass on Windows, but it is no longer verifying deduplication there.

    Since this describe block is explicitly modelling Unix behaviour, consider keeping the hard-coded : separator for the split, which matches the Unix PATH format set by the test:

Last reviewed commit: 4033ecd

Comment on lines +1075 to +1076
const path = storage.getSessionPath('/project', 'session-id', fullConfig);
expect(path).toBe('~/.local/share/opencode/storage/message/session-id');
Copy link

Choose a reason for hiding this comment

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

Local variable path shadows the module import

Inside several test callbacks in this file, a local variable named path is declared that shadows the top-level import path from 'path':

const path = storage.getSessionPath('/project', 'session-id', fullConfig);

This occurs in the following tests (all pre-existing, reformatted by this PR):

  • OpenCodeSessionStorage – "should verify SshRemoteConfig interface is properly accepted" (line ~1075)
  • FactoryDroidSessionStorage – "should verify SshRemoteConfig interface is properly accepted" (line ~1446)
  • FactoryDroidSessionStorage – "Remote sessions directory path" inner it (line ~1475)

None of these specific callbacks call path.<method>() after the re-declaration, so there is no runtime error today. But the shadowing is fragile — if a future maintainer adds a path.isAbsolute() or similar call inside one of these callbacks they'll get a TypeError: path.isAbsolute is not a function instead of the usual path utilities. Consider renaming the local variable to avoid confusion, e.g. sessionPath or resultPath.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/releases.md (1)

1-519: ⚠️ Potential issue | 🟡 Minor

This file appears unrelated to the PR objectives.

The PR is focused on making tests cross-platform by replacing hardcoded Unix path separators with Node.js path utilities. This file contains only formatting changes to historical release notes and does not relate to the test fixes described in the PR summary.

Including unrelated documentation changes creates scope creep and makes the PR harder to review and understand. Consider moving these release note formatting changes to a separate PR focused on documentation maintenance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/releases.md` around lines 1 - 519, The release notes file
docs/releases.md contains unrelated formatting changes that do not belong in
this test-focused PR; revert or remove the modifications to docs/releases.md
from this branch (undo the commit or reset the file) so the PR only contains the
test/path-separator fixes, or move the docs changes into a separate
documentation PR; ensure the branch/PR only touches the test-related files (the
path-separator replacements) before re-submitting for review.
🧹 Nitpick comments (1)
docs/releases.md (1)

201-201: Use correct capitalization for "GitHub".

The official name of the platform is "GitHub" with a capital "H". As per coding guidelines, this is flagged by static analysis.

📝 Proposed fix
-🌳 Github Worktree support was added. Any agent bound to a Git repository has the option to enable worktrees, each of which show up as a sub-agent with their own write-lock and Auto Run capability. Now you can truly develop in parallel on the same project and issue PRs when you're ready, all from within Maestro. Huge improvement, major thanks to `@petersilberman`.
+🌳 GitHub Worktree support was added. Any agent bound to a Git repository has the option to enable worktrees, each of which show up as a sub-agent with their own write-lock and Auto Run capability. Now you can truly develop in parallel on the same project and issue PRs when you're ready, all from within Maestro. Huge improvement, major thanks to `@petersilberman`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/releases.md` at line 201, The release note uses incorrect capitalization
"Github"; update the sentence beginning with "🌳 Github Worktree support was
added." to use the correct product name "GitHub" (i.e., change "Github" to
"GitHub") so the text reads "🌳 GitHub Worktree support was added." and keep the
rest of the sentence unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@docs/releases.md`:
- Around line 1-519: The release notes file docs/releases.md contains unrelated
formatting changes that do not belong in this test-focused PR; revert or remove
the modifications to docs/releases.md from this branch (undo the commit or reset
the file) so the PR only contains the test/path-separator fixes, or move the
docs changes into a separate documentation PR; ensure the branch/PR only touches
the test-related files (the path-separator replacements) before re-submitting
for review.

---

Nitpick comments:
In `@docs/releases.md`:
- Line 201: The release note uses incorrect capitalization "Github"; update the
sentence beginning with "🌳 Github Worktree support was added." to use the
correct product name "GitHub" (i.e., change "Github" to "GitHub") so the text
reads "🌳 GitHub Worktree support was added." and keep the rest of the sentence
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f39053ea-ee79-4808-9273-23c253b3d6e6

📥 Commits

Reviewing files that changed from the base of the PR and between 4033ecd and 49cfd57.

📒 Files selected for processing (2)
  • AGENTS.md
  • docs/releases.md
✅ Files skipped from review due to trivial changes (1)
  • AGENTS.md

The crashed event was removed from Electron's WebContents API.
The render-process-gone handler at line 228 already covers this
case with better crash details and auto-reload logic.
@jSydorowicz21
Copy link
Author

All checks pass, good to go

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.

1 participant