Skip to content

fix(ai): post-remediation cleanup and test coverage#414

Merged
ianwhitedeveloper merged 2 commits intoai-testing-framework-implementation-consolidationfrom
pr/ai-test-quality-cleanup
Feb 24, 2026
Merged

fix(ai): post-remediation cleanup and test coverage#414
ianwhitedeveloper merged 2 commits intoai-testing-framework-implementation-consolidationfrom
pr/ai-test-quality-cleanup

Conversation

@ianwhitedeveloper
Copy link
Collaborator

@ianwhitedeveloper ianwhitedeveloper commented Feb 23, 2026

Context

Follow-up to #412. These are the remaining local changes that were kept out of that PR to maintain a focused review surface — squashed into a single commit for easier review.

Changes

Source

File Change
source/ai-errors.js Export allNoop helper — maps all registered error keys to noop, eliminating boilerplate in handleAIErrors tests
source/extraction-parser.js Use ...ValidationError spread instead of bare name: 'ValidationError' string — bug fix
source/tap-yaml.js Use ...ParseError spread instead of bare name: 'ParseError' string — bug fix

Tests

File Change
source/constants.test.js Replace Zod safeParse API leakage with parse()+Try(); trim to 2 high-value behavioral tests only
source/aggregation.test.js Upgrade 3 partial assertions to full objects; fix 2 duplicate test.each labels; remove logger assertion; simplify mock logger; remove unused vi import
source/extraction-parser.test.js Add resolveImportPaths describe block (success + ValidationError); add non-object branch test for parseExtractionResult
source/execute-agent.test.js Add 3 missing error path tests: spawn failure, malformed JSON fallback, ParseError on non-string envelope
source/agent-parser.test.js allNoop adoption; remove createMockLogger factory; remove 7 logger assertion blocks
source/ai-errors.test.js allNoop adoption
source/tap-yaml.test.js allNoop adoption + assertion consolidation

Decision notes for reviewers

Why allNoop lives in ai-errors.js (a production file)

handleAIErrors requires an exhaustive handler map — a constraint imposed by the error-causes registry we configured. allNoop is derived directly from Object.keys(aiErrors), so it automatically stays in sync when new error types are added. Co-locating it with the registry means adding a 10th error type touches exactly one file instead of propagating a stale 9-entry block across every test file. A shared test-util would require a separate import chain and risk the map going stale without any signal.

The production spread fixes are silent routing bugs, not style changes

Both extraction-parser.js and tap-yaml.js used bare strings (name: 'ParseError', name: 'ValidationError') when throwing errors. If either type were renamed in the aiErrors registry, the mismatch would cause handleAIErrors to silently skip the handler — the error would be swallowed, not re-thrown and not routed. Using ...ParseError / ...ValidationError spread couples the throw site to the registry so any rename is caught immediately.

Why 7 logger assertion blocks were removed from agent-parser.test.js

Each affected test previously had two assert calls: one on the return value, one on logger.logs.some(log => log.includes('...')). The logger assertions were removed because:

  • Log output is not observable behavior at the function's public API boundary. Log messages are implementation details — a valid refactor that rewrites message wording would fail tests without breaking any contract.
  • The return value assertion already fully proves correctness. If parseStringResult returns { passed: true } for a given input, the function worked. There is no additional correctness signal in knowing it also logged 'Successfully parsed string as JSON'.
  • Coupling tests to log wording makes them brittle. The 7 removed assertions each pinned an exact substring, meaning any log message change (even an improvement) would break a test unrelated to the behavior under test.

The createMockLogger factory (which captured logs into an array for inspection) was also removed and replaced with an inline { log: () => {} } at each call site, since the factory existed solely to support log capture.

The same principle applied to aggregation.test.js

The "defaults missing actual and expected with warning log" test was renamed to "defaults missing actual and expected" and its logger.log.mock.calls[0][0] assertion was removed for the same reason. The existing assertion on the full returned object already proves the function normalised the fields correctly. As a consequence, createMockLogger in that describe block was simplified from () => ({ log: vi.fn() }) to () => ({ log: () => {} }), and the now-unused vi import was removed.

What was dropped from constants.test.js and why

The six sub-schema describe blocks (runsSchema, thresholdSchema, etc.) were already removed in #412 — those tested Zod's own validation behavior. Of the remaining six aiTestOptionsSchema tests, four "rejects" tests were collapsed: the invariant that invalid inputs are rejected is already exercised by aggregation.test.js's ValidationError path, which drives the same schemas through their consuming function. The two tests kept are the behaviorally meaningful ones: "applies defaults" (asserts the concrete parsed output) and "applies lazy cwd default at parse time" (asserts runtime evaluation).

resolveImportPaths had 0% coverage before this PR

The ...ValidationError bug fix in extraction-parser.js was shipped in the previous commit without any test coverage for the affected path. The new resolveImportPaths describe block closes that gap — it directly exercises the error branch that the bug fix touched.

Coverage delta

File Stmts before Stmts after
extraction-parser.js 69.8% ~95%
execute-agent.js 90.1% ~97%
aggregation.js 100% stmts / 90.9% branch 100% stmts / 100% branch

Why Try(() => aiTestOptionsSchema.parse({...})) uses an arrow wrapper

aiTestOptionsSchema.parse is a method on a Zod object — passing it as a bare function reference loses the this binding and throws. The arrow wrapper is the minimal fix consistent with how Try is used elsewhere in the codebase (e.g., Try(parseExtractionResult, input)).

Test Results

Test Files  10 passed (10)
     Tests  112 passed (112)
  Duration  ~933ms

Made with Cursor

- ai-errors: export allNoop helper for exhaustive handleAIErrors tests
- extraction-parser: spread ...ValidationError instead of bare name string
- tap-yaml: spread ...ParseError instead of bare name string
- constants.test: replace safeParse leakage with parse()+Try(); trim to behavioral tests only
- aggregation.test: full-object assertions; fix duplicate test.each labels
- extraction-parser.test: add resolveImportPaths success/error tests; non-object branch test
- execute-agent.test: add spawn failure, malformed JSON fallback, ParseError envelope tests

Co-authored-by: Cursor <cursoragent@cursor.com>
@ianwhitedeveloper ianwhitedeveloper marked this pull request as draft February 23, 2026 17:34
@ianwhitedeveloper
Copy link
Collaborator Author

@cursor please /review

@cursor
Copy link

cursor bot commented Feb 23, 2026

Excellent work! All 112 tests pass. Let me now provide a comprehensive code review.

🔬 Code Review Summary

✅ Overall Assessment: APPROVED

This is a high-quality cleanup PR that demonstrates excellent software engineering discipline. The changes improve test coverage, eliminate code duplication, and enhance maintainability without introducing any regressions.


📋 Detailed Review

Source Changes

source/ai-errors.js - allNoop Helper Export ✅

export const allNoop = Object.fromEntries(Object.keys(aiErrors).map(k => [k, noop]));

Strengths:

  • Eliminates test boilerplate by providing a reusable base for exhaustive error handlers
  • Follows functional programming principles with clean composition
  • Clear documentation explains its purpose

Minor observation:

  • Consider adding a JSDoc comment for better IDE autocomplete support:
/**
 * Maps all registered error keys to noop handlers.
 * Spread this and override specific handlers in tests.
 * @example handleAIErrors({ ...allNoop, ParseError: () => {...} })
 */
export const allNoop = Object.fromEntries(Object.keys(aiErrors).map(k => [k, noop]));

source/extraction-parser.js & source/tap-yaml.js - Spread Syntax ✅

          ...ValidationError,
      ...ParseError,

Excellent improvement:

  • Using spread syntax (...ValidationError) instead of string literals is more maintainable
  • Better type safety and refactoring resilience
  • Follows the project's established patterns

Test Changes

source/constants.test.js - Simplified Testing ✅

Strengths:

  • Replaced safeParse with parse() + Try() - eliminates Zod API leakage
  • Reduced from multiple tests to 2 high-value behavioral tests
  • Focuses on what matters: default value application

Observation:
The lazy cwd default test is well-isolated and validates runtime behavior correctly.

source/aggregation.test.js - Enhanced Assertions ✅

      actual: result,
      expected: {
        passed: true,
        assertions: [
          {
            requirement: 'Given simple addition, should add correctly',
            passed: true,
            passCount: 2,
            totalRuns: 2,
            averageScore: 0,
            runResults: runResultsA
          },

Excellent improvements:

  1. Full object assertions - Instead of partial checks, tests now verify complete result structure
  2. Fixed duplicate test.each labels - Improves test output readability
  3. Comprehensive validation - Tests verify all fields: passed, assertions, runResults, totalRuns, etc.

Adherence to TDD principles:

  • Tests answer all 5 questions
  • Clear given/should descriptions
  • Tests are isolated and explicit

source/extraction-parser.test.js - New Coverage ✅

describe('resolveImportPaths()', () => {
  test('resolves and joins file contents for valid import paths', async () => {

New test coverage added:

  1. resolveImportPaths() - Success path + ValidationError on file read failure
  2. Non-object branch test for parseExtractionResult() (line 159-171)

Strengths:

  • Tests use mocks appropriately (vi.mock('fs/promises'))
  • Error path coverage is thorough
  • allNoop adoption reduces boilerplate

source/execute-agent.test.js - Critical Error Paths ✅

New tests added:

  1. Spawn failure test (lines 185-199)
  2. Malformed JSON fallback when rawOutput: true (lines 201-212)
  3. ParseError on non-string envelope (lines 214-229)

Strengths:

  • Tests critical failure modes that were previously uncovered
  • Mock implementation is clean and well-structured
  • Tests verify proper error routing through handleAIErrors

Observation: The createMockProcess helper is a well-designed test utility that makes tests readable.

source/agent-parser.test.js - Assertion Consolidation ✅

Improvements:

  • allNoop adoption throughout
  • Consolidated error assertions use the handler pattern consistently

source/ai-errors.test.js & source/tap-yaml.test.js

Both files demonstrate:

  • Clean allNoop adoption
  • Improved readability through reduced boilerplate
  • Consistent error handling patterns

📊 Code Quality Analysis

JavaScript Best Practices (javascript.mdc) ✅

  • Functional programming: ✅ Proper use of map, filter, spread operators
  • Immutability: ✅ No mutation detected
  • Naming: ✅ Clear, descriptive names (allNoop, parseExtractionResult)
  • Composition: ✅ Functions are small, focused, and composable
  • Point-free style: ✅ Used appropriately (allNoop construction)

TDD Principles (tdd.mdc) ✅

All tests properly answer the 5 questions:

  1. Unit under test: ✅ Clear from describe blocks
  2. Expected behavior:given and should are descriptive
  3. Actual output: ✅ Units are exercised correctly
  4. Expected output: ✅ Expected values are explicit
  5. Bug reproduction: ✅ Tests are isolated and reproducible

Test isolation: ✅ No shared mutable state, proper use of mocks

Test thoroughness: ✅ Edge cases covered (NaN, null, boundary conditions)

Explicit tests: ✅ All test data is inline or from clear factories


🔒 Security & Performance

  • No security issues detected
  • No performance regressions
  • Test execution time: ~933ms (within acceptable range)

🎯 Recommendations

Optional Enhancements (Not blocking approval)

  1. Add JSDoc to allNoop for better developer experience
  2. Consider extracting createMockProcess to a shared test utility if it will be reused
  3. Type definitions: Verify TypeScript types are up to date (if applicable)

✅ Final Verdict

SHIP IT! 🚀

This PR demonstrates:

  • ✅ Excellent test coverage (112 passing tests)
  • ✅ Clean code following project standards
  • ✅ No regressions or breaking changes
  • ✅ Proper TDD discipline
  • ✅ Improved maintainability through DRY principles

The allNoop helper is a particularly elegant solution to reduce test boilerplate while maintaining exhaustive error handler requirements.

Ready to merge.

Open in Web Open in Cursor 

@ianwhitedeveloper ianwhitedeveloper marked this pull request as ready for review February 23, 2026 18:11
Copy link
Collaborator

@janhesters janhesters left a comment

Choose a reason for hiding this comment

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

LGTM

- ai-errors.test.js: removed; handleAIErrors routing
  already covered by agent-parser, aggregation,
  extraction-parser, and tap-yaml test suites

Co-authored-by: Cursor <cursoragent@cursor.com>
@ianwhitedeveloper ianwhitedeveloper merged commit a96a49d into ai-testing-framework-implementation-consolidation Feb 24, 2026
@ianwhitedeveloper ianwhitedeveloper deleted the pr/ai-test-quality-cleanup branch February 24, 2026 15:46
ianwhitedeveloper added a commit that referenced this pull request Feb 25, 2026
- ai-errors: export allNoop helper for exhaustive handleAIErrors tests
- extraction-parser: spread ...ValidationError instead of bare name string
- tap-yaml: spread ...ParseError instead of bare name string
- constants.test: replace safeParse leakage with parse()+Try(); trim to behavioral tests only
- aggregation.test: full-object assertions; fix duplicate test.each labels
- extraction-parser.test: add resolveImportPaths success/error tests; non-object branch test
- execute-agent.test: add spawn failure, malformed JSON fallback, ParseError envelope tests
- ai-errors.test.js: removed; handleAIErrors routing
  already covered by agent-parser, aggregation,
  extraction-parser, and tap-yaml test suites

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
ianwhitedeveloper added a commit that referenced this pull request Feb 25, 2026
- ai-errors: export allNoop helper for exhaustive handleAIErrors tests
- extraction-parser: spread ...ValidationError instead of bare name string
- tap-yaml: spread ...ParseError instead of bare name string
- constants.test: replace safeParse leakage with parse()+Try(); trim to behavioral tests only
- aggregation.test: full-object assertions; fix duplicate test.each labels
- extraction-parser.test: add resolveImportPaths success/error tests; non-object branch test
- execute-agent.test: add spawn failure, malformed JSON fallback, ParseError envelope tests
- ai-errors.test.js: removed; handleAIErrors routing
  already covered by agent-parser, aggregation,
  extraction-parser, and tap-yaml test suites

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
ianwhitedeveloper added a commit that referenced this pull request Feb 25, 2026
- ai-errors: export allNoop helper for exhaustive handleAIErrors tests
- extraction-parser: spread ...ValidationError instead of bare name string
- tap-yaml: spread ...ParseError instead of bare name string
- constants.test: replace safeParse leakage with parse()+Try(); trim to behavioral tests only
- aggregation.test: full-object assertions; fix duplicate test.each labels
- extraction-parser.test: add resolveImportPaths success/error tests; non-object branch test
- execute-agent.test: add spawn failure, malformed JSON fallback, ParseError envelope tests
- ai-errors.test.js: removed; handleAIErrors routing
  already covered by agent-parser, aggregation,
  extraction-parser, and tap-yaml test suites

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
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