LearnNewsExamplesServices
Frontmatter
id10881
titleAuthorization.spec.mjs: resolve listener watches wrong stream (stdout vs stderr)
stateClosed
labels
bugaitesting
assigneesneo-opus-4-7
createdAtMay 7, 2026, 9:42 AM
updatedAtMay 9, 2026, 11:30 PM
githubUrlhttps://github.com/neomjs/neo/issues/10881
authorneo-opus-4-7
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 7, 2026, 9:56 AM

Authorization.spec.mjs: resolve listener watches wrong stream (stdout vs stderr)

Closedbugaitesting
neo-opus-4-7
neo-opus-4-7 commented on May 7, 2026, 9:42 AM

Context

Surfaced 2026-05-07 while implementing #10878 (post-#10877 NEO_-env-var fixture rename) — the env-var rename was applied + verified via node --check + grep, but npm run test-unit -- test/playwright/unit/ai/mcp/Authorization.spec.mjs still timed out. Investigation revealed a pre-existing bug in the test's startup-wait promise resolution logic: it listens for the SSE-startup log message on stdout, but the server emits this message via logger.info which writes to stderr.

The bug existed before #10877; this ticket exists because #10878's Evidence claim documents that the fixture rename does NOT make the test pass end-to-end (timeout is independent of env-var contract).

The Problem

test/playwright/unit/ai/mcp/Authorization.spec.mjs:88-105 (test.beforeAll startup wait):

await new Promise((resolve, reject) => {
    mcpServerProcess.stdout.on('data', (data) => {
        const msg = data.toString();
        if (msg.includes('Server started on SSE transport')) {
            resolve();
        }
    });
    mcpServerProcess.stderr.on('data', (data) => {
        const msg = data.toString();
        console.error(`[MCP Server Error] ${msg}`);
        // Often SSE server logs to stderr
    });
    setTimeout(() => reject(new Error('MCP Server startup timeout')), 20000);
});

Note the comment "Often SSE server logs to stderr" — the author was aware of the channel mismatch but didn't add resolve() to the stderr listener. The startup-wait therefore times out at 20s regardless of whether the server actually started successfully.

The same shape exists at lines ~199-215 for discoveryProcess.

The Architectural Reality

  • Server emits the startup log via: ai/mcp/server/shared/services/TransportService.mjs:202logger.info(\[${resourceName}] Server started on SSE transport (Port: ${port})`);`
  • logger.info (Memory Core's logger module) writes to stderr by convention; verified by the captured [MCP Server Error] [INFO] [neo-knowledge-base MCP] Available tools loaded from OpenAPI spec line in test output (test wraps stderr with [MCP Server Error] prefix).
  • The test's stdout listener pattern would only match if the server's logger emitted to stdout — which it does NOT for info-level messages.

The Fix

Move the resolve() check into the stderr listener (or include both):

await new Promise((resolve, reject) => {
    const checkStarted = (data) => {
        const msg = data.toString();
        if (msg.includes('Server started on SSE transport')) {
            resolve();
        }
    };
    mcpServerProcess.stdout.on('data', checkStarted);
    mcpServerProcess.stderr.on('data', (data) => {
        checkStarted(data);
        console.error(`[MCP Server Error] ${data.toString()}`);
    });
    setTimeout(() => reject(new Error('MCP Server startup timeout')), 20000);
});

Apply the same fix to the discoveryProcess startup wait (around line ~199-215).

Acceptance Criteria

  • Both mcpServerProcess and discoveryProcess startup-wait promises resolve when the server emits Server started on SSE transport on either stdout OR stderr.
  • npm run test-unit -- test/playwright/unit/ai/mcp/Authorization.spec.mjs reaches the actual test bodies (no MCP Server startup timeout reject in test.beforeAll).
  • Existing test bodies pass OR fail for their actual content reasons — not for setup-promise timeout.
  • console.error([MCP Server Error] ...) echo behavior preserved (operator visibility into stderr stream maintained).

Out of Scope

  • Refactoring the spawned-server-startup pattern into a shared helper (defer until pattern recurs in a third spec file).
  • Changing the server's logger output channel (logger.info → stderr is canonical convention).
  • The 20-second timeout itself — keep as-is; only the resolve condition needs fixing.

Related

  • #10878 / PR #10879 (env-var fixture rename — the parent fix surfacing this bug)
  • #10877 (NEO_ env-var prefix institutionalization — adjacent context)
  • #9929/#9930 (last commit on this test file, predates the listener-channel bug surfacing)

Origin Session ID: 78a3272e-847b-4799-ad6c-ce334464844c

Retrieval Hint: query_raw_memories(query="Authorization.spec.mjs startup wait stderr stdout listener bug")