Skip to content

fix: await runLogParser in async test callbacks#43316

Merged
pelikhan merged 1 commit into
mainfrom
copilot/fix-log-parser-tests
Jul 4, 2026
Merged

fix: await runLogParser in async test callbacks#43316
pelikhan merged 1 commit into
mainfrom
copilot/fix-log-parser-tests

Conversation

Copilot AI commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

12 tests in log_parser_bootstrap.test.cjs were failing because the it() callbacks were synchronous but runLogParser is async. core.setFailed() and core.warning() are invoked after await core.summary.addRaw(...).write() inside runLogParser, so they hadn't been called by the time assertions ran — yielding "Number of calls: 0" for all 12.

Changes

  • log_parser_bootstrap.test.cjs — made the 12 affected it() callbacks async and added await before each runLogParser() call:

    // before
    it("should handle MCP failures", () => {
      runLogParser({ ... });
      expect(mockCore.setFailed).toHaveBeenCalledWith(...);
    });
    
    // after
    it("should handle MCP failures", async () => {
      await runLogParser({ ... });
      expect(mockCore.setFailed).toHaveBeenCalledWith(...);
    });
  • pkg/workflow/arc_dind_artifacts.go — fixed a pre-existing modernize lint failure (HasPrefix+TrimPrefixstrings.CutPrefix) that blocked validation.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title fix: await runLogParser in async tests fix: await runLogParser in async test callbacks Jul 4, 2026
Copilot AI requested a review from pelikhan July 4, 2026 05:28
@pelikhan pelikhan marked this pull request as ready for review July 4, 2026 05:34
Copilot AI review requested due to automatic review settings July 4, 2026 05:34
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #43316 does not have the 'implementation' label and has only 2 new lines of code in business logic directories (threshold: 100).

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Copilot AI 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.

Pull request overview

This PR fixes flaky/failing JS unit tests by properly awaiting the async runLogParser() helper, and unblocks Go validation by addressing a pre-existing modernize lint issue in ARC/DinD artifact path rewriting.

Changes:

  • Updated 12 it() test callbacks in log_parser_bootstrap.test.cjs to be async and to await runLogParser(...), ensuring assertions run after post-summary side effects (e.g., core.setFailed(), core.warning()).
  • Replaced strings.HasPrefix + strings.TrimPrefix with strings.CutPrefix in rewriteTmpGhAwPathsForArcDind to satisfy modernization linting.
Show a summary per file
File Description
pkg/workflow/arc_dind_artifacts.go Modernizes prefix handling via strings.CutPrefix while preserving the existing path rewrite behavior.
actions/setup/js/log_parser_bootstrap.test.cjs Fixes async test timing by awaiting runLogParser() before asserting side effects.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Low

@pelikhan pelikhan merged commit 23337a0 into main Jul 4, 2026
77 of 97 checks passed
@pelikhan pelikhan deleted the copilot/fix-log-parser-tests branch July 4, 2026 05:38
@github-actions github-actions Bot mentioned this pull request Jul 4, 2026
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100 — Excellent

Analyzed 12 test(s): 12 design, 0 implementation, 0 violation(s).

📊 Metrics (12 tests)
Metric Value
Analyzed 12 (Go: 0, JS: 12)
✅ Design 12 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 12 (100%)
Duplicate clusters 0
Inflation No (test fix — no new production lines)
🚨 Violations 0
Test File Classification Issues
should fail Claude runs when no structured log entries are parsed log_parser_bootstrap.test.cjs:66 design_test / behavioral_contract None
should handle MCP failures log_parser_bootstrap.test.cjs:108 design_test / behavioral_contract None
should warn instead of failing MCP failures when safe outputs exist log_parser_bootstrap.test.cjs:118 design_test / behavioral_contract None
should warn (non-fatal) when MCP fails but agent ran turns (legacy result entry) log_parser_bootstrap.test.cjs:137 design_test / behavioral_contract None
should warn (non-fatal) when MCP fails but agent ran turns (Copilot event session.result) log_parser_bootstrap.test.cjs:163 design_test / behavioral_contract None
should still fail when MCP fails and agent ran no turns log_parser_bootstrap.test.cjs:189 design_test / behavioral_contract None
should warn (non-fatal) when Claude has empty logEntries but safe outputs exist log_parser_bootstrap.test.cjs:209 design_test / behavioral_contract None
should fail when Claude has empty logEntries and no safe outputs (startup failure) log_parser_bootstrap.test.cjs:231 design_test / behavioral_contract None
should not print parsed successfully for Claude when logEntries is empty log_parser_bootstrap.test.cjs:246 design_test / behavioral_contract None
should treat logEntries: null as missing for Claude (no safe outputs → setFailed) log_parser_bootstrap.test.cjs:268 design_test / edge_case None
should treat logEntries: null as missing for Claude (safe outputs → warning) log_parser_bootstrap.test.cjs:284 design_test / edge_case None
should handle max-turns limit reached log_parser_bootstrap.test.cjs:305 design_test / behavioral_contract None

Verdict

Passed. 0% implementation tests (threshold: 30%). No guideline violations.

Note: This PR is a correctness fix — 12 async test callbacks were calling runLogParser without await, causing assertions to fire before the Promise resolved. The fix ensures tests actually validate the async behavior they claim to test. All tests cover distinct failure and degradation scenarios (MCP failures, startup errors, safe-output fallbacks, max-turns, null log entries) with meaningful error and negative assertions.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 41.6 AIC · ⌖ 16.2 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot 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.

✅ Test Quality Sentinel: 100/100. 0% implementation tests (threshold: 30%). All 12 modified async tests now correctly await runLogParser, ensuring assertions fire after Promise resolution.

@github-actions github-actions Bot 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.

Skills-Based Review 🧠

Applied /tdd and /diagnosing-bugs — no blocking issues, but three tests have a cleanup-safety problem worth addressing.

📋 Key Themes & Highlights

Key Themes

  • Correct async fix: Adding async/await to the 12 test callbacks is exactly the right fix for the race condition. The root cause (synchronous callbacks for an async function) is fully addressed.
  • Inconsistent test structure: Three tests (lines 113, 130, 310) still use the comma-chain pattern — the same tests that were already tricky to read without async. After the async fix, if an expect assertion in those chains throws, cleanup code (file unlinks, env var deletion) is silently skipped. The surrounding tests in this file already use the correct try/finally pattern — these three should match.
  • No regression test: The PR description explains the bug clearly, but there is no test that would fail if someone accidentally writes a synchronous it() callback calling runLogParser in the future. A JSDoc @returns {Promise<void>} on runLogParser in the source file would help IDEs surface this immediately.
  • Go change is clean: The strings.CutPrefix modernisation in arc_dind_artifacts.go is a nice, idiomatic improvement.

Positive Highlights

  • ✅ Root cause fully addressed across all 12 affected tests
  • ✅ PR description is thorough and accurate
  • ✅ Consistent adoption of async/await without introducing any new patterns
  • ✅ The Go modernisation reduces double-traversal of the string prefix

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 54.6 AIC · ⌖ 5.63 AIC · ⊞ 6.6K
Comment /matt to run again

(fs.writeFileSync(logFile, "content"), (process.env.GH_AW_AGENT_OUTPUT = logFile));
const mockParseLog = vi.fn().mockReturnValue({ markdown: "## Result\n", mcpFailures: ["server1", "server2"], maxTurnsHit: !1 });
(runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }), expect(mockCore.setFailed).toHaveBeenCalledWith(`${ERR_API}: MCP server(s) failed to launch: server1, server2`), fs.unlinkSync(logFile), fs.rmdirSync(tmpDir));
(await runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }),

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.

[/tdd] Comma-chain async test skips cleanup on failure — if the expect assertion throws, fs.unlinkSync and fs.rmdirSync never run, leaving temp dirs behind.

💡 Suggested refactor (try/finally)

Other tests in this same file (e.g. lines 137–162) already use the safe pattern:

it('should handle MCP failures', async () => {
  const tmpDir = fs.mkdtempSync(path.join(__dirname, 'test-'));
  const logFile = path.join(tmpDir, 'test.log');
  try {
    fs.writeFileSync(logFile, 'content');
    process.env.GH_AW_AGENT_OUTPUT = logFile;
    const mockParseLog = vi.fn().mockReturnValue({ markdown: '## Result\n', mcpFailures: ['server1', 'server2'], maxTurnsHit: false });
    await runLogParser({ parseLog: mockParseLog, parserName: 'TestParser' });
    expect(mockCore.setFailed).toHaveBeenCalledWith(`${ERR_API}: MCP server(s) failed to launch: server1, server2`);
  } finally {
    fs.unlinkSync(logFile);
    fs.rmdirSync(tmpDir);
  }
});

The same pattern applies to the tests at lines 130–136 and 305–314.

@copilot please address this.

const mockParseLog = vi.fn().mockReturnValue({ markdown: "## Result\n", mcpFailures: ["server1"], maxTurnsHit: !1 });

(runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }),
(await runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }),

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.

[/tdd] Same comma-chain cleanup hazard as line 113 — env vars and temp files aren't cleaned up if expect throws mid-chain.

💡 Suggested refactor (try/finally)

Use try/finally as the surrounding tests already do (e.g. lines 118–136 already exist cleanly — but the comma-chain equivalent here leaves GH_AW_SAFE_OUTPUTS set on failure, polluting later tests):

try {
  // ... setup ...
  await runLogParser({ parseLog: mockParseLog, parserName: 'TestParser' });
  expect(mockCore.warning).toHaveBeenCalledWith(...);
  expect(mockCore.setFailed).not.toHaveBeenCalled();
} finally {
  fs.unlinkSync(logFile);
  fs.unlinkSync(safeOutputsFile);
  delete process.env.GH_AW_SAFE_OUTPUTS;
  fs.rmdirSync(tmpDir);
}

@copilot please address this.

(fs.writeFileSync(logFile, "content"), (process.env.GH_AW_AGENT_OUTPUT = logFile));
const mockParseLog = vi.fn().mockReturnValue({ markdown: "## Result\n", mcpFailures: [], maxTurnsHit: !0 });
(runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }),
(await runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }),

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.

[/tdd] Comma-chain async test: cleanup won't execute if expect throws — same structural issue as lines 113 and 130. The GH_AW_AGENT_OUTPUT env var is also never cleared, which may bleed state into subsequent tests.

💡 Suggested refactor (try/finally)
it('should handle max-turns limit reached', async () => {
  const tmpDir = fs.mkdtempSync(path.join(__dirname, 'test-'));
  const logFile = path.join(tmpDir, 'test.log');
  try {
    fs.writeFileSync(logFile, 'content');
    process.env.GH_AW_AGENT_OUTPUT = logFile;
    const mockParseLog = vi.fn().mockReturnValue({ markdown: '## Result\n', mcpFailures: [], maxTurnsHit: true });
    await runLogParser({ parseLog: mockParseLog, parserName: 'TestParser' });
    expect(mockCore.setFailed).toHaveBeenCalledWith(`${ERR_VALIDATION}: Agent execution stopped: max-turns limit reached. The agent did not complete its task successfully.`);
  } finally {
    fs.unlinkSync(logFile);
    fs.rmdirSync(tmpDir);
  }
});

@copilot please address this.

fs.rmdirSync(tmpDir));
}),
it("should fail Claude runs when no structured log entries are parsed", () => {
it("should fail Claude runs when no structured log entries are parsed", async () => {

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.

[/diagnosing-bugs] The root cause of all 12 failing tests was that runLogParser is async but the test callbacks were synchronous. This PR correctly fixes that, but a comment or a regression-guarding lint rule would prevent future callers from making the same mistake.

💡 Consider a JSDoc comment on runLogParser

Adding a JSDoc @returns {Promise<void>} annotation to runLogParser in log_parser_bootstrap.cjs makes it immediately obvious to the IDE and future test authors that the function must be awaited:

/**
 * `@param` {{ parseLog: Function, parserName: string, supportsDirectories?: boolean }} opts
 * `@returns` {Promise<void>}
 */
async function runLogParser(opts) { ... }

This is a low-cost, permanent documentation of the async contract — exactly the kind of instrumentation /diagnosing-bugs recommends to prevent regression.

@copilot please address this.

@github-actions github-actions Bot 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.

Review: fix: await runLogParser in async test callbacks

The fix is correct and complete. Both changed files are clean.

log_parser_bootstrap.test.cjs

runLogParser is declared async in the source (it awaits core.summary.addRaw().write()), and setFailed/warning calls for MCP failures, max-turns, and Claude guardrails are all issued after that await. Without await in the test callbacks, assertions ran against zero-call mocks. Adding async/await to the 12 affected tests is precisely the right fix.

Tests that remain synchronous (early-return paths and assertions on state set before the await) are correctly left unchanged.

pkg/workflow/arc_dind_artifacts.go

strings.CutPrefix is the idiomatic modern replacement for HasPrefix + TrimPrefix. The rewrite is semantically equivalent and the modernize lint fix is legitimate.

Minor observation (non-blocking)

Tests at lines 54 and 80 (should handle parser returning object with markdown, should generate plain text summary) still call runLogParser without await. They only assert on state set before the inner await write() call, so they pass today — but if logic ever moves after the await they will silently become flaky. Consider adding async/await as a consistency measure in a follow-up.

✅ No blocking issues. Approving.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 75.7 AIC · ⌖ 6.32 AIC · ⊞ 4.8K

@github-actions github-actions Bot 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.

Non-blocking: 3 tests missing try/finally cleanup

The async/await fix is correct and the Go strings.CutPrefix modernization is clean. One low-severity pattern to fix:

Details

Three tests updated in this PR use a comma-expression cleanup pattern without try/finally:

  • should handle MCP failures (line ~108)
  • should warn instead of failing MCP failures when safe outputs exist (line ~118)
  • should handle max-turns limit reached (line ~305)

If the await runLogParser() call rejects, or if any expect() assertion throws, the cleanup (fs.unlinkSync, fs.rmdirSync) in the comma expression is skipped — leaving temp directories behind. Every other async test fixed in this PR correctly uses try/finally for cleanup; these three are outliers.

This won't cause test failures in the happy path, hence non-blocking, but it will produce mysterious leftover test- directories if a test fails mid-run.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • patchdiff.githubusercontent.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "patchdiff.githubusercontent.com"

See Network Configuration for more information.

🔎 Code quality review by PR Code Quality Reviewer · 141 AIC · ⌖ 6.36 AIC · ⊞ 5.4K
Comment /review to run again

(await runLogParser({ parseLog: mockParseLog, parserName: "TestParser" }),
expect(mockCore.setFailed).toHaveBeenCalledWith(`${ERR_API}: MCP server(s) failed to launch: server1, server2`),
fs.unlinkSync(logFile),
fs.rmdirSync(tmpDir));

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.

Temp-dir leak if assertion throws: cleanup runs in a comma expression with no try/finally, so if expect(mockCore.setFailed) throws, fs.unlinkSync and fs.rmdirSync are never reached.

💡 Suggested fix

Refactor to use try/finally for cleanup, consistent with the majority of tests in this file:

it("should handle MCP failures", async () => {
  const tmpDir = fs.mkdtempSync(path.join(__dirname, "test-"));
  const logFile = path.join(tmpDir, "test.log");
  try {
    fs.writeFileSync(logFile, "content");
    process.env.GH_AW_AGENT_OUTPUT = logFile;
    const mockParseLog = vi.fn().mockReturnValue({ markdown: "## Result\n", mcpFailures: ["server1", "server2"], maxTurnsHit: false });
    await runLogParser({ parseLog: mockParseLog, parserName: "TestParser" });
    expect(mockCore.setFailed).toHaveBeenCalledWith(`${ERR_API}: MCP server(s) failed to launch: server1, server2`);
  } finally {
    fs.unlinkSync(logFile);
    fs.rmdirSync(tmpDir);
  }
});

The same issue exists in the "should warn instead of failing MCP failures when safe outputs exist" test (~line 128) and "should handle max-turns limit reached" test (~line 308). All three were touched by this PR and are outliers — every other updated test already uses try/finally.

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.

3 participants