From 486a10b1a9da08ab4da88bffcd8a1f00d97c1a86 Mon Sep 17 00:00:00 2001 From: maestro-relay Date: Fri, 5 Jun 2026 23:58:54 -0500 Subject: [PATCH] test: make auto-run-command and attachments tests OS-agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four tests in the suite failed on Windows but passed on Linux/macOS, encoding platform assumptions in the test data and assertions: - src/__tests__/auto-run-command.test.ts:77, 104, 131 — three tests mocked autoRunFolderPath as '/agents/auto-run-docs' and asserted the result of path.join(folder, doc). On Linux that yields '/agents/auto-run-docs/plan.md'; on Windows, path.isAbsolute('/...') is true so the resolved path is 'C:\\\\agents\\\\auto-run-docs\\\\...', and path.join without resolve produces '\\\\agents\\\\...' (no drive). Production calls path.resolve first, so the test's expected value drifted from the actual on Windows. - src/__tests__/attachments.test.ts:80 — checked savedPath.includes(DEFAULT_FILES_SUBDIR) where the constant is the forward-slash literal '.maestro/discord-files'. path.join on Windows produces backslashes, so the substring never matches. Fixes: - Replace the mocked folder with a constant built from path.join, giving a relative path that path.isAbsolute returns false for on every platform. The absolute-path test inputs are built with path.resolve against the same relative folder so they round-trip identically. - Compute expected docs values via resolveContainedDocPath, the same OS-agnostic helper production uses, so the assertion is exact regardless of how path.resolve normalizes the CWD prefix. - The /etc/passwd test now uses path.resolve(os.tmpdir(), 'evil.txt') for a portable 'guaranteed outside the folder' absolute path. - For the DEFAULT_FILES_SUBDIR assertion, normalize the constant to path.sep before the .includes() check. Verified on Windows: 211 pass, 0 fail (was 207/4 on main). Production code untouched. --- src/__tests__/attachments.test.ts | 7 ++++- src/__tests__/auto-run-command.test.ts | 36 +++++++++++++++++++------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/__tests__/attachments.test.ts b/src/__tests__/attachments.test.ts index 1d3c429..de248a6 100644 --- a/src/__tests__/attachments.test.ts +++ b/src/__tests__/attachments.test.ts @@ -77,7 +77,12 @@ test('downloadAttachments saves files with UUID-prefixed names', async () => { assert.equal(downloaded.length, 1); assert.deepEqual(failed, []); assert.equal(downloaded[0].originalName, 'photo.png'); - assert.ok(downloaded[0].savedPath.includes(DEFAULT_FILES_SUBDIR)); + // `DEFAULT_FILES_SUBDIR` is the literal string '.maestro/discord-files', + // but the joined `savedPath` uses `path.sep`. Normalize to the platform + // separator before checking containment so the test passes on Windows + // (where path.join produces backslashes) and Unix (forward slashes). + const expectedSubdir = DEFAULT_FILES_SUBDIR.split('/').join(path.sep); + assert.ok(downloaded[0].savedPath.includes(expectedSubdir)); const basename = path.basename(downloaded[0].savedPath); assert.match(basename, /^[0-9a-f-]{36}-photo\.png$/); diff --git a/src/__tests__/auto-run-command.test.ts b/src/__tests__/auto-run-command.test.ts index b7cf02f..848ded5 100644 --- a/src/__tests__/auto-run-command.test.ts +++ b/src/__tests__/auto-run-command.test.ts @@ -50,6 +50,12 @@ test('auto-run start rejects channels not connected to an agent', async () => { assert.ok(reply.content.includes('not connected to an agent')); }); +// OS-agnostic relative path for the mocked Auto Run folder. Avoids the +// Windows pitfall where `path.isAbsolute('/agents/...')` is true and +// `path.join('/...', x)` prepends `C:\` instead of producing a +// relative join. +const MOCK_AUTO_RUN_FOLDER = path.join('agents', 'auto-run-docs'); + test('auto-run start resolves a bare filename against the agent Auto Run folder', async () => { const { channelDb } = await import('../providers/discord/channelsDb'); mock.method(channelDb, 'get', () => ({ @@ -64,7 +70,7 @@ test('auto-run start resolves a bare filename against the agent Auto Run folder' name: 'TestBot', toolType: 'claude', cwd: '/proj', - autoRunFolderPath: '/agents/auto-run-docs', + autoRunFolderPath: MOCK_AUTO_RUN_FOLDER, })); const startMock = mock.method(maestro, 'startAutoRun', async () => ''); @@ -74,7 +80,10 @@ test('auto-run start resolves a bare filename against the agent Auto Run folder' assert.equal(startMock.mock.callCount(), 1); const opts = startMock.mock.calls[0].arguments[0] as { docs: string[] }; - assert.deepEqual(opts.docs, [path.join('/agents/auto-run-docs', 'plan.md')]); + // Compute the expected value via the same OS-agnostic helper the + // production code uses internally — guarantees the assertion holds + // regardless of which platform's `path.resolve` semantics apply. + assert.deepEqual(opts.docs, [resolveContainedDocPath(MOCK_AUTO_RUN_FOLDER, 'plan.md')]); }); test('auto-run start resolves a relative subpath against the agent Auto Run folder', async () => { @@ -91,7 +100,7 @@ test('auto-run start resolves a relative subpath against the agent Auto Run fold name: 'TestBot', toolType: 'claude', cwd: '/proj', - autoRunFolderPath: '/agents/auto-run-docs', + autoRunFolderPath: MOCK_AUTO_RUN_FOLDER, })); const startMock = mock.method(maestro, 'startAutoRun', async () => ''); @@ -101,7 +110,7 @@ test('auto-run start resolves a relative subpath against the agent Auto Run fold assert.equal(startMock.mock.callCount(), 1); const opts = startMock.mock.calls[0].arguments[0] as { docs: string[] }; - assert.deepEqual(opts.docs, [path.join('/agents/auto-run-docs', 'subdir/doc.md')]); + assert.deepEqual(opts.docs, [resolveContainedDocPath(MOCK_AUTO_RUN_FOLDER, 'subdir/doc.md')]); }); test('auto-run start accepts an absolute path that lives inside the agent folder', async () => { @@ -118,11 +127,14 @@ test('auto-run start accepts an absolute path that lives inside the agent folder name: 'TestBot', toolType: 'claude', cwd: '/proj', - autoRunFolderPath: '/agents/auto-run-docs', + autoRunFolderPath: MOCK_AUTO_RUN_FOLDER, })); const startMock = mock.method(maestro, 'startAutoRun', async () => ''); - const inside = '/agents/auto-run-docs/subdir/doc.md'; + // Build the absolute doc path the same way production does — via + // `path.resolve` against the (relative) folder — so the input + // round-trips identically on Linux and Windows. + const inside = path.join(path.resolve(MOCK_AUTO_RUN_FOLDER), 'subdir', 'doc.md'); const i = makeInteraction({ doc: inside }); await execute(i as unknown as Parameters[0]); @@ -145,11 +157,15 @@ test('auto-run start rejects an absolute path outside the agent folder', async ( name: 'TestBot', toolType: 'claude', cwd: '/proj', - autoRunFolderPath: '/agents/auto-run-docs', + autoRunFolderPath: MOCK_AUTO_RUN_FOLDER, })); const startMock = mock.method(maestro, 'startAutoRun', async () => ''); - const i = makeInteraction({ doc: '/etc/passwd' }); + // Build an absolute path that's guaranteed not to live under the + // resolved mock folder on any platform. `os.tmpdir()` is portable + // and resolvable via path.resolve on both OSes. + const outside = path.resolve(os.tmpdir(), 'evil.txt'); + const i = makeInteraction({ doc: outside }); await execute(i as unknown as Parameters[0]); assert.equal(startMock.mock.callCount(), 0); @@ -172,7 +188,7 @@ test('auto-run start rejects relative paths that escape the folder via traversal name: 'TestBot', toolType: 'claude', cwd: '/proj', - autoRunFolderPath: '/agents/auto-run-docs', + autoRunFolderPath: MOCK_AUTO_RUN_FOLDER, })); const startMock = mock.method(maestro, 'startAutoRun', async () => ''); @@ -284,7 +300,7 @@ test('auto-run start surfaces errors from startAutoRun', async () => { name: 'TestBot', toolType: 'claude', cwd: '/proj', - autoRunFolderPath: '/agents/auto-run-docs', + autoRunFolderPath: MOCK_AUTO_RUN_FOLDER, })); mock.method(maestro, 'startAutoRun', async () => { throw new Error('boom');