Skip to content

test: enforce test catalog consistency and Jest discovery#85

Merged
Mehdi-Bl merged 4 commits into
mainfrom
feat/main-process-observability-increment-1
Feb 11, 2026
Merged

test: enforce test catalog consistency and Jest discovery#85
Mehdi-Bl merged 4 commits into
mainfrom
feat/main-process-observability-increment-1

Conversation

@Mehdi-Bl

@Mehdi-Bl Mehdi-Bl commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a test catalog validator script to ensure tests/catalog.md references existing paths
  • fail when executable Jest test files are missing from the catalog or when cataloged test files fall outside Jest discovery
  • wire the guard into npm run lint and expand tests/catalog.md to include currently executed suites

Validation

  • npm run lint
  • npm test -- --runInBand

Summary by Sourcery

Enforce consistency between the Jest test suite and tests/catalog.md via a new validation script wired into linting and test workflows.

New Features:

  • Add a test catalog validation script that checks cataloged paths exist and align with Jest discovery patterns.

Enhancements:

  • Update the test catalog to document additional Jest suites and reference the new catalog validation command in the core commands table.
  • Integrate catalog validation into the standard lint task and expose it as an npm script.

Tests:

  • Add unit tests for the test catalog validation script covering path extraction, missing paths, unlisted tests, and mismatches with Jest discovery.

Summary by CodeRabbit

  • New Features
    • Added an automated validation step to ensure the test catalog stays consistent with discovered tests.
  • Documentation
    • Reformatted and expanded the test catalog with clearer tables, broader command coverage, and updated entries (including a new unit test).
  • Chores
    • Integrated catalog validation into the lint workflow to surface discrepancies during CI.
  • Tests
    • Added comprehensive unit tests for the catalog validator, covering discovery patterns, ignore rules, path safety, and mismatch detection.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@sourcery-ai

sourcery-ai Bot commented Feb 11, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds an automated validator to keep tests/catalog.md consistent with actual Jest-discovered test files, wires it into the lint pipeline, and expands the catalog to cover all currently executed script and unit test suites.

Sequence diagram for validate test catalog execution and Jest discovery

sequenceDiagram
  actor Developer
  participant Npm as npm_cli
  participant Lint as npm_script_lint
  participant CatalogScript as validate_test_catalog_js
  participant FS as file_system
  participant Jest as jest_runner

  Developer->>Npm: run npm run lint
  Npm->>Lint: execute lint script pipeline
  Lint->>CatalogScript: run npm run test:catalog

  CatalogScript->>FS: read tests/catalog.md
  FS-->>CatalogScript: catalog_entries

  CatalogScript->>Jest: run jest with config jest.config.js
  Jest-->>CatalogScript: discovered_test_files

  CatalogScript->>CatalogScript: build_catalog_path_set
  CatalogScript->>CatalogScript: build_jest_path_set
  CatalogScript->>CatalogScript: diff sets for missing_or_extra_tests

  alt catalog consistent with Jest discovery
    CatalogScript-->>Lint: exit code 0
  else catalog inconsistent
    CatalogScript-->>Lint: exit non_zero
    Lint-->>Npm: propagate failure
    Npm-->>Developer: lint command fails
  end
Loading

Flow diagram for updated lint pipeline with test catalog validation

flowchart LR
  subgraph NpmScripts
    A[npm_run_lint]
    B[npm_run_format_check]
    C[eslint_src_and_tests]
    D[npm_run_lint_md]
    E[npm_run_test_catalog]
    F[npm_run_changelog_validate]
  end

  A --> B --> C --> D --> E --> F

  subgraph TestCatalogValidation
    E --> G[node_scripts_validate_test_catalog_js]
    G --> H[read_tests_catalog_md]
    G --> I[run_jest_discovery_config_jest_config_js]
    H --> J[build_catalog_test_path_set]
    I --> K[build_jest_discovered_test_path_set]
    J --> L[compare_catalog_and_jest_sets]
    K --> L
    L --> M{catalog_matches_jest?}
    M -->|yes| N[exit_code_0]
    M -->|no| O[exit_with_failure_non_zero]
  end
Loading

File-Level Changes

Change Details Files
Introduce a reusable test catalog validation script used both as a CLI and as a test helper.
  • Add scripts/validate-test-catalog.js which parses tests/catalog.md for path references, crawls tests/ for executable Jest test files, and loads Jest config to understand discovery patterns and ignore rules.
  • Implement helpers for path normalization, recursive file collection, Jest testMatch/ignore pattern handling, and matching via minimatch.
  • Expose validateTestCatalog and related utilities for programmatic use and provide a CLI entrypoint that exits non‑zero on validation failures and logs a short success summary on pass.
scripts/validate-test-catalog.js
Add unit coverage for the test catalog validator and its main behaviors.
  • Create tests/unit/scripts/validate-test-catalog.test.js to cover catalog extraction, happy-path validation, and failure cases for missing paths, unlisted discovered tests, and cataloged-but-not-discovered tests.
  • Use temporary workspaces and synthetic Jest configs in tests to exercise various discovery and ignore pattern scenarios.
tests/unit/scripts/validate-test-catalog.test.js
Wire test catalog validation into the developer workflow and update the human-readable catalog to match current suites.
  • Add npm run test:catalog script and integrate it into npm run lint so catalog inconsistencies fail lint.
  • Extend tests/catalog.md with the new test:catalog command and additional rows for existing Jest script/unit suites so that the catalog aligns with actual Jest discovery.
package.json
tests/catalog.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Feb 11, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

A new test catalog validation script is introduced alongside an updated test catalog and comprehensive test suite. The script verifies consistency between test catalog references and Jest-discovered tests, integrated into the lint workflow to run validation checks during the build process.

Changes

Cohort / File(s) Summary
Build Configuration
package.json
Added test:catalog npm script and integrated it into the lint script to validate test catalog consistency during linting.
Catalog Validation Script
scripts/validate-test-catalog.js
New script that discovers executable test files, loads Jest configuration, extracts catalog references, matches files against Jest patterns while respecting ignore rules, and reports discrepancies between catalog entries and discovered tests.
Test Catalog Documentation
tests/catalog.md
Restructured catalog with expanded commands list and reformatted unit/integration test entries from markdown lists to pipe-delimited tables; added new SourceTab test entry.
Validation Script Tests
tests/unit/scripts/validate-test-catalog.test.js
Comprehensive test suite covering catalog extraction, Jest pattern matching, ignore rule handling, path resolution safety, and validation of catalog-to-discovery mismatches across multiple scenarios.

Sequence Diagram

sequenceDiagram
    participant Lint as Lint Workflow
    participant Val as validate-test-catalog.js
    participant FS as File System
    participant Jest as Jest Config
    participant Cat as Catalog.md
    
    Lint->>Val: Execute validation script
    Val->>Cat: Read catalog.md
    Cat-->>Val: Return catalog content
    Val->>Val: Extract catalog references
    Val->>Jest: Load Jest configuration
    Jest-->>Val: Return testMatch & ignorePatterns
    Val->>FS: Recursively list test files
    FS-->>Val: Return executable test files
    Val->>Val: Compile ignore patterns
    Val->>Val: Match files against Jest patterns<br/>while respecting ignore rules
    Val->>Val: Compute discrepancies<br/>(missing refs, unlisted, not discovered)
    Val-->>Lint: Exit with status & report
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

Poem

🐰 A catalog keeper, precise and keen,
Validates tests to keep things clean,
Jest patterns match, ignore rules hide,
Discrepancies surfaced, nowhere to hide! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'test: enforce test catalog consistency and Jest discovery' clearly and specifically describes the main changes: a new validation mechanism that ensures the test catalog aligns with Jest test discovery patterns.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/main-process-observability-increment-1

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
tests/unit/scripts/validate-test-catalog.test.js (1)

249-263: Test assumes process.cwd() equals the repository root.

resolvePathWithinRoot resolves relative paths against process.cwd() but validates containment against ROOT_DIR (derived from the script's __dirname). This test will pass when Jest runs from the repo root (the typical case), but is fragile if cwd ever differs. Not blocking, but worth noting.

scripts/validate-test-catalog.js (3)

31-50: collectFilesRecursively: spread in recursive push is fine for test directories but worth noting.

files.push(...collectFilesRecursively(absolutePath)) at Line 42 passes the recursive result as spread arguments to push. For V8, Function.prototype.apply / spread has an argument-count limit (~100k–200k). A tests/ directory won't realistically approach this, so no action needed, but if this utility is ever reused on larger trees, consider Array.prototype.push.apply with chunking or files = files.concat(...).


201-211: resolvePathWithinRoot relies on process.cwd() while containment is checked against ROOT_DIR.

When invoked via npm run test:catalog, cwd will match ROOT_DIR, so this works correctly. However, if the script is ever invoked from a different working directory (e.g., a subdirectory or CI with a custom cwd), a valid relative path like tests/catalog.md could resolve outside ROOT_DIR and be rejected.

Consider resolving against ROOT_DIR instead of process.cwd() for robustness:

Proposed fix
 function resolvePathWithinRoot(inputPath, defaultPath, label) {
-  const resolvedPath = inputPath ? path.resolve(process.cwd(), inputPath) : defaultPath;
+  const resolvedPath = inputPath ? path.resolve(ROOT_DIR, inputPath) : defaultPath;
   const relativeToRoot = path.relative(ROOT_DIR, resolvedPath);

254-264: Large public API surface — consider trimming exports to what tests actually need.

The module exports 9 symbols, but the test file only imports 4 (compileIgnorePatterns, extractCatalogPathReferences, resolvePathWithinRoot, validateTestCatalog). Exporting internal helpers like isMatchedByJest, listExecutableTestFiles, normalizeTestMatchPatterns increases the public surface that needs to remain stable. Not blocking, but trimming unused exports would reduce coupling.


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.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Enforce test catalog consistency and Jest discovery validation

🧪 Tests ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add test catalog validator script to enforce consistency between tests/catalog.md and actual
  test files
• Integrate catalog validation into npm run lint workflow
• Expand tests/catalog.md with newly added test suite entries
• Implement Jest discovery checks to prevent unlisted or out-of-scope test files
Diagram
flowchart LR
  A["validate-test-catalog.js"] -- "validates" --> B["tests/catalog.md"]
  B -- "references" --> C["test files"]
  A -- "checks against" --> D["Jest config"]
  D -- "discovers" --> C
  A -- "integrated into" --> E["npm run lint"]
Loading

Grey Divider

File Changes

1. scripts/validate-test-catalog.js 🧪 Tests +224/-0

Create test catalog validator with Jest discovery checks

• Add script to validate test catalog references against filesystem paths
• Implement Jest config parsing to check test discovery patterns
• Detect unlisted test files and cataloged files outside Jest scope
• Export validation functions for testing and CLI execution

scripts/validate-test-catalog.js


2. tests/unit/scripts/validate-test-catalog.test.js 🧪 Tests +139/-0

Add comprehensive unit tests for catalog validator

• Add unit tests for catalog reference extraction logic
• Test validation scenarios: missing paths, unlisted tests, out-of-scope files
• Use temporary workspace for isolated filesystem testing

tests/unit/scripts/validate-test-catalog.test.js


3. package.json ⚙️ Configuration changes +2/-1

Wire catalog validation into lint workflow

• Add test:catalog script to run catalog validation
• Integrate test:catalog into npm run lint workflow

package.json


View more (1)
4. tests/catalog.md 📝 Documentation +27/-20

Update catalog with new test suites and commands

• Add test:catalog command documentation
• Expand unit test table with five new test suite entries
• Include newly added script tests (sonar, stress metrics, perf, catalog validator)

tests/catalog.md


Grey Divider

Qodo Logo

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @Mehdi-Bl, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a test catalog validation script to ensure consistency between the test catalog and Jest's test discovery. It enhances the linting process by verifying that all test files are correctly referenced and discovered, improving the reliability and maintainability of the test suite.

Highlights

  • Test Catalog Validation: Introduced a new script to validate the test catalog (tests/catalog.md) ensuring that all referenced test paths exist and are correctly discovered by Jest.
  • Jest Discovery Enforcement: The validation process now fails if executable Jest test files are missing from the catalog or if cataloged test files fall outside Jest's discovery patterns.
  • Lint Integration: Wired the test catalog validation script into the npm run lint command to ensure consistency as part of the linting process.
  • Expanded Test Catalog: Expanded the tests/catalog.md file to include currently executed test suites, providing a more comprehensive overview of the test landscape.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • package.json
    • Added test:catalog script to package.json.
    • Integrated test:catalog into the lint script.
  • scripts/validate-test-catalog.js
    • Added validate-test-catalog.js script to validate test catalog consistency.
  • tests/catalog.md
    • Updated tests/catalog.md to include currently executed suites.
  • tests/unit/scripts/validate-test-catalog.test.js
    • Added validate-test-catalog.test.js to test the validate-test-catalog script.
Activity
  • Added a test catalog validator script.
  • Ensured that the linter fails when executable Jest test files are missing from the catalog or when cataloged test files fall outside Jest discovery.
  • Wired the guard into npm run lint.
  • Expanded tests/catalog.md to include currently executed suites.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions

github-actions Bot commented Feb 11, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • In normalizeTestMatchPatterns, the rootDir parameter is unused—either remove it from the signature or incorporate it into the normalization logic to avoid confusion.
  • compileIgnorePatterns silently discards invalid regex patterns; consider surfacing which pattern failed (e.g., via a warning) so Jest ignore misconfigurations don’t go unnoticed.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `normalizeTestMatchPatterns`, the `rootDir` parameter is unused—either remove it from the signature or incorporate it into the normalization logic to avoid confusion.
- `compileIgnorePatterns` silently discards invalid regex patterns; consider surfacing which pattern failed (e.g., via a warning) so Jest ignore misconfigurations don’t go unnoticed.

## Individual Comments

### Comment 1
<location> `tests/unit/scripts/validate-test-catalog.test.js:20` </location>
<code_context>
+  return fs.mkdtempSync(path.join(os.tmpdir(), 'validate-test-catalog-'));
+}
+
+describe('validate-test-catalog script', () => {
+  test('extracts unique test catalog references from markdown content', () => {
+    const content = `
</code_context>

<issue_to_address>
**suggestion (testing):** Add coverage for catalog read failures (missing or unreadable catalog.md)

Since the function returns a specific failure shape when `fs.readFileSync` throws (e.g., missing file, permission error), please add a test that calls `validateTestCatalog` with a non-existent `catalogPath` (and/or one made unreadable via `fs.chmodSync`) and asserts that `isValid === false`, `errors` includes `Unable to read test catalog`, and the other arrays are empty. This will lock in the error-handling behavior for these failure cases.

Suggested implementation:

```javascript
describe('validate-test-catalog script', () => {
  test('extracts unique test catalog references from markdown content', () => {
    const content = `
# Test Catalog
- \`tests/unit/a.test.ts\`
- \`tests/unit/a.test.ts\`
- \`tests/integration/b.test.ts\`
`;

    expect(extractCatalogPathReferences(content)).toEqual([
      'tests/integration/b.test.ts',
      'tests/unit/a.test.ts',
    ]);
  });

  test('returns failure result when catalog cannot be read', () => {
    const workspaceRoot = createWorkspace();
    const nonExistentCatalogPath = path.join(
      workspaceRoot,
      'does-not-exist',
      'catalog.md',
    );

    const result = validateTestCatalog({
      catalogPath: nonExistentCatalogPath,
      workspaceRoot,
    });

    expect(result.isValid).toBe(false);
    expect(result.errors).toEqual(
      expect.arrayContaining([
        expect.stringContaining('Unable to read test catalog'),
      ]),
    );
    expect(result.missingInCatalog).toEqual([]);
    expect(result.missingInRepo).toEqual([]);
    expect(result.extraInCatalog).toEqual([]);

```

Because only part of the file is visible, you may need to:

1. **Adjust the `validateTestCatalog` call signature**  
   If `validateTestCatalog` is not invoked with an options object elsewhere, update the new test to match the existing pattern, e.g.:
   - `const result = validateTestCatalog(nonExistentCatalogPath, workspaceRoot);` or
   - `const result = validateTestCatalog(nonExistentCatalogPath);`

2. **Align result shape property names**  
   If the returned object uses different property names for the arrays, update the expectations accordingly. For example, if they are `missing`, `extra`, etc., replace:
   - `missingInCatalog`, `missingInRepo`, `extraInCatalog`
   with the actual property names from the implementation.

3. **Ensure imports are present**  
   Confirm that `validateTestCatalog`, `path`, `fs`, and `os` are already imported at the top of the test file. If `validateTestCatalog` is not yet imported, add:
   ```js
   const { validateTestCatalog } = require('../../../scripts/validate-test-catalog');
   ```
   or whatever path/name matches the rest of the file.

4. **Placement in the file**  
   Make sure the inserted `test('returns failure result when catalog cannot be read', ...)` sits within the same top-level `describe('validate-test-catalog script', ...)` block and does not accidentally nest inside another test if the surrounding structure differs slightly from the snippet above.
</issue_to_address>

### Comment 2
<location> `tests/unit/scripts/validate-test-catalog.test.js:21` </location>
<code_context>
+}
+
+describe('validate-test-catalog script', () => {
+  test('extracts unique test catalog references from markdown content', () => {
+    const content = `
+# Test Catalog
</code_context>

<issue_to_address>
**suggestion (testing):** Extend `extractCatalogPathReferences` tests to cover wildcards and non-test file references

Since wildcard catalog entries are skipped by the validator’s existence checks and paths are extracted purely from markdown patterns, please add coverage for entries like `tests/unit/*.test.ts` and `tests/README.md`. The assertions should verify that both the wildcard and non-test paths are returned as-is, confirming that `extractCatalogPathReferences` doesn’t filter by extension or glob semantics.
</issue_to_address>

### Comment 3
<location> `tests/unit/scripts/validate-test-catalog.test.js:36-45` </location>
<code_context>
+  });
+
+  test('fails when catalog references missing paths', () => {
+    const rootDir = createWorkspace();
+
+    writeFile(rootDir, 'tests/catalog.md', '`tests/unit/missing.test.ts`');
+
+    const report = validateTestCatalog({
+      rootDir,
+      catalogPath: path.join(rootDir, 'tests/catalog.md'),
+      jestConfig: {
+        testMatch: ['<rootDir>/tests/**/*.{js,jsx,ts,tsx}'],
+        testPathIgnorePatterns: [],
+      },
+    });
+
+    expect(report.isValid).toBe(false);
+    expect(report.missingCatalogPaths).toEqual(['tests/unit/missing.test.ts']);
+    expect(report.errors[0]).toContain('Catalog references missing paths');
+
+    fs.rmSync(rootDir, { recursive: true, force: true });
+  });
+
</code_context>

<issue_to_address>
**suggestion (testing):** Refactor workspace cleanup into `afterEach` or `try/finally` to avoid leaks on test failure

If an assertion fails before `fs.rmSync(rootDir, ...)`, the temporary workspace is left behind, which clutters local runs. Centralize cleanup in an `afterEach` hook or a helper that wraps the test body in `try/finally` so `rmSync` always executes, even on failure.

Suggested implementation:

```javascript
  test('fails when catalog references missing paths', () => {
    return withWorkspace((rootDir) => {
      writeFile(rootDir, 'tests/catalog.md', '`tests/unit/missing.test.ts`');

      const report = validateTestCatalog({
        rootDir,
        catalogPath: path.join(rootDir, 'tests/catalog.md'),
        jestConfig: {
          testMatch: ['<rootDir>/tests/**/*.{js,jsx,ts,tsx}'],
          testPathIgnorePatterns: [],
        },
      });

      expect(report.isValid).toBe(false);
      expect(report.missingCatalogPaths).toEqual(['tests/unit/missing.test.ts']);
      expect(report.errors[0]).toContain('Catalog references missing paths');
    });
  });

```

```javascript
function createWorkspace() {
  return fs.mkdtempSync(path.join(os.tmpdir(), 'validate-test-catalog-'));
}

function withWorkspace(testFn) {
  const rootDir = createWorkspace();
  const cleanup = () =>
    fs.rmSync(rootDir, {
      recursive: true,
      force: true,
    });

  try {
    const result = testFn(rootDir);

    // Support both sync and async test bodies
    if (result && typeof result.then === 'function') {
      return result.finally(cleanup);
    }

    cleanup();
    return result;
  } catch (error) {
    cleanup();
    throw error;
  }
}

```

Scan the rest of `tests/unit/scripts/validate-test-catalog.test.js` for other tests that:
1. Call `createWorkspace()` directly, and
2. Manually invoke `fs.rmSync(rootDir, ...)` at the end.

Refactor those tests to use `return withWorkspace((rootDir) => { ... })` in the same way, and remove their manual `rmSync` calls, so all temporary workspaces are consistently cleaned up via `try/finally`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread tests/unit/scripts/validate-test-catalog.test.js
Comment thread tests/unit/scripts/validate-test-catalog.test.js
Comment thread tests/unit/scripts/validate-test-catalog.test.js Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a valuable script for validating the test catalog against Jest's discovery, a positive step towards better test documentation and maintenance. However, a security audit revealed critical vulnerabilities in the new validate-test-catalog.js script: a high-severity Path Traversal vulnerability and a critical-severity Code Injection vulnerability, both stemming from improper handling of command-line arguments. Immediate remediation is crucial to prevent potential compromise. Additionally, the code review suggests enhancing the script's validation logic for better alignment with Jest's behavior and improving the test setup for reliability and proper cleanup.

Comment thread scripts/validate-test-catalog.js Outdated
Comment on lines +194 to +196
jestConfigPath: jestConfigArg
? path.resolve(process.cwd(), jestConfigArg)
: DEFAULT_JEST_CONFIG_PATH,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-critical critical

The script is vulnerable to arbitrary code execution. The jestConfigArg variable, which is derived from user-supplied command-line input (process.argv[3]), is used to construct a file path for a Jest configuration file. This path is then passed to require() on line 51. An attacker can provide a path to a malicious JavaScript file, leading to its execution with the privileges of the running process. This is a critical vulnerability, especially if the script is run in a CI/CD environment or any automated context. The user-provided path for the Jest config should be validated to ensure it is a valid and trusted file within the project's directory. Ideally, the script should only allow loading files from a specific, hardcoded directory.

Comment thread scripts/validate-test-catalog.js Outdated
const catalogArg = process.argv[2];
const jestConfigArg = process.argv[3];
const result = validateTestCatalog({
catalogPath: catalogArg ? path.resolve(process.cwd(), catalogArg) : DEFAULT_CATALOG_PATH,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-high high

The script is vulnerable to Path Traversal. The catalogArg variable, which is derived from user-supplied command-line input (process.argv[2]), is used to construct a file path using path.resolve() without proper sanitization. An attacker could provide a malicious path (e.g., ../../../../etc/passwd) to read arbitrary files from the filesystem where the script is executed. Before using the user-provided path, it should be validated to ensure it resolves to a location within the intended project directory.

Comment thread scripts/validate-test-catalog.js Outdated
Comment on lines +79 to +81
if (testMatchPatterns.length === 0) {
return true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

When testMatchPatterns is empty, the function currently returns true, implying all files are matched. This doesn't align with Jest's default behavior. If testMatch is not specified in the Jest configuration, Jest uses a default set of patterns (**/__tests__/**/*.[jt]s?(x) and **/?(*.)+(spec|test).[jt]s?(x)). To make this script more robust and align with Jest's behavior, it should apply these default patterns when testMatchPatterns is empty.

  if (testMatchPatterns.length === 0) {
    const JEST_DEFAULT_PATTERNS = [
      '**/__tests__/**/*.[jt]s?(x)',
      '**/?(*.)+(spec|test).[jt]s?(x)',
    ];
    return JEST_DEFAULT_PATTERNS.some((pattern) => minimatch(relativeFilePath, pattern, { dot: true }));
  }

Comment on lines +150 to +157
.filter((relativePath) => {
const absolutePath = path.join(rootDir, relativePath);
if (isIgnoredByJest(absolutePath, ignorePatterns)) {
return false;
}

return !discoveredTestFileSet.has(relativePath);
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The current logic for identifying listedButNotDiscoveredTestFiles explicitly ignores files that are listed in the catalog but are ignored by Jest's testPathIgnorePatterns. This seems to contradict the goal of ensuring the catalog is consistent with Jest's discovery, as ignored files are not discovered or run by Jest. If a test is listed in the catalog, it should probably be discovered by Jest. If it's intentionally ignored, it might be better to remove it from the catalog to avoid confusion. Consider changing this logic to flag cataloged tests that are ignored by Jest.

    .filter((relativePath) => !discoveredTestFileSet.has(relativePath))

return fs.mkdtempSync(path.join(os.tmpdir(), 'validate-test-catalog-'));
}

describe('validate-test-catalog script', () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The tests create and clean up a temporary directory inside each test block. If an assertion fails, the cleanup code (fs.rmSync) will not be reached, potentially leaving temporary files on the file system. It's a better practice to use beforeEach to create the workspace and afterEach to clean it up. This ensures that cleanup always runs, regardless of whether the test passes or fails.

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Feb 11, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

✅ 1. compileIgnorePatterns() swallows regex errors 📘 Rule violation ⛯ Reliability
Description
Invalid testPathIgnorePatterns entries are silently dropped when new RegExp(pattern) fails,
which can cause incorrect catalog validation results without indicating why. This reduces
diagnosability and violates the requirement to handle failure points with meaningful context.
Code

scripts/validate-test-catalog.js[R61-70]

+function compileIgnorePatterns(patterns = []) {
+  return patterns
+    .map((pattern) => {
+      try {
+        return new RegExp(pattern);
+      } catch (_error) {
+        return null;
+      }
+    })
+    .filter(Boolean);
Evidence
The compliance rule requires robust error handling with actionable context; the added code catches
regex compilation failures and returns null without recording an error or surfacing the
misconfiguration, creating a silent failure mode.

Rule 3: Generic: Robust Error Handling and Edge Case Management
scripts/validate-test-catalog.js[61-70]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`compileIgnorePatterns()` silently ignores invalid regex patterns in `testPathIgnorePatterns` by catching errors and returning `null` without surfacing the failure. This can lead to incorrect validation output and makes debugging configuration issues difficult.
## Issue Context
This script is intended to enforce consistency between `tests/catalog.md` and Jest discovery. Dropping invalid ignore patterns silently changes what is considered “discovered,” undermining the validator.
## Fix Focus Areas
- scripts/validate-test-catalog.js[61-70]
- scripts/validate-test-catalog.js[96-187]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


✅ 2. Validator mis-models Jest discovery 🐞 Bug ✓ Correctness
Description
The validator only treats *.test.*/*.spec.* files as “discovered”, but this repo’s Jest
testMatch includes all tests/**/*.{js,jsx,ts,tsx}; Jest will execute files like tests/setup.ts
that the validator ignores. This can allow real Jest-executed suites/files to be missing from
tests/catalog.md without npm run lint failing, contradicting the intended enforcement.
Code

scripts/validate-test-catalog.js[R86-94]

+function listExecutableTestFiles(rootDir = ROOT_DIR) {
+  const testsRoot = path.join(rootDir, 'tests');
+  const files = collectFilesRecursively(testsRoot);
+
+  return files
+    .map((absolutePath) => toPosixPath(path.relative(rootDir, absolutePath)))
+    .filter((relativePath) => EXECUTABLE_TEST_FILE_PATTERN.test(relativePath))
+    .sort();
+}
Evidence
Jest is configured to discover/execute any JS/TS file under tests/, not just
*.test.*/*.spec.*. Meanwhile, the validator constructs discoveredTestFiles from a recursively
collected file list that is first filtered by a hard-coded EXECUTABLE_TEST_FILE_PATTERN, so files
like tests/setup.ts are excluded from discovery checks even though Jest will run them under the
current testMatch.

scripts/validate-test-catalog.js[10-12]
scripts/validate-test-catalog.js[86-94]
jest.config.js[8-18]
tests/setup.ts[1-8]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The catalog validator only considers files matching `\.(test|spec)\.(js|jsx|ts|tsx)$` as “Jest discovered”, but the repo’s `jest.config.js` discovers **all** `tests/**/*.{js,jsx,ts,tsx}`. This mismatch means `npm run test:catalog` can pass even while Jest executes non-`.test`/`.spec` files (e.g. `tests/setup.ts`) that are not enforced in `tests/catalog.md`.
### Issue Context
- Current Jest config `testMatch` is broader than the validator’s `EXECUTABLE_TEST_FILE_PATTERN`.
- `tests/setup.ts` contains a real Jest suite and is also included in `setupFilesAfterEnv`, so it will be executed under the current `testMatch`.
### Fix Focus Areas
Choose one approach and make the validator + Jest consistent.
**Approach A (recommended): narrow Jest discovery to match validator intent**
- Update Jest `testMatch` to only include actual test files (e.g. default Jest pattern within `tests/`).
- Remove the “dummy test” from `tests/setup.ts` (it will still run as a setup file via `setupFilesAfterEnv`, but should not itself be a test suite).
**Approach B: broaden validator discovery to match Jest config**
- Treat any file under `tests/` that matches Jest `testMatch` (and isn’t ignored) as discovered; then decide whether helper files should be required in the catalog.
Add/adjust unit tests to cover whichever approach you choose.
Fix locations:
- scripts/validate-test-catalog.js[10-12]
- scripts/validate-test-catalog.js[86-138]
- jest.config.js[8-18]
- tests/setup.ts[1-10]
- tests/unit/scripts/validate-test-catalog.test.js[35-139]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment thread scripts/validate-test-catalog.js Outdated
Comment on lines +61 to +70
function compileIgnorePatterns(patterns = []) {
return patterns
.map((pattern) => {
try {
return new RegExp(pattern);
} catch (_error) {
return null;
}
})
.filter(Boolean);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. compileignorepatterns() swallows regex errors 📘 Rule violation ⛯ Reliability

Invalid testPathIgnorePatterns entries are silently dropped when new RegExp(pattern) fails,
which can cause incorrect catalog validation results without indicating why. This reduces
diagnosability and violates the requirement to handle failure points with meaningful context.
Agent Prompt
## Issue description
`compileIgnorePatterns()` silently ignores invalid regex patterns in `testPathIgnorePatterns` by catching errors and returning `null` without surfacing the failure. This can lead to incorrect validation output and makes debugging configuration issues difficult.

## Issue Context
This script is intended to enforce consistency between `tests/catalog.md` and Jest discovery. Dropping invalid ignore patterns silently changes what is considered “discovered,” undermining the validator.

## Fix Focus Areas
- scripts/validate-test-catalog.js[61-70]
- scripts/validate-test-catalog.js[96-187]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +86 to +94
function listExecutableTestFiles(rootDir = ROOT_DIR) {
const testsRoot = path.join(rootDir, 'tests');
const files = collectFilesRecursively(testsRoot);

return files
.map((absolutePath) => toPosixPath(path.relative(rootDir, absolutePath)))
.filter((relativePath) => EXECUTABLE_TEST_FILE_PATTERN.test(relativePath))
.sort();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Validator mis-models jest discovery 🐞 Bug ✓ Correctness

The validator only treats *.test.*/*.spec.* files as “discovered”, but this repo’s Jest
testMatch includes all tests/**/*.{js,jsx,ts,tsx}; Jest will execute files like tests/setup.ts
that the validator ignores. This can allow real Jest-executed suites/files to be missing from
tests/catalog.md without npm run lint failing, contradicting the intended enforcement.
Agent Prompt
### Issue description
The catalog validator only considers files matching `\.(test|spec)\.(js|jsx|ts|tsx)$` as “Jest discovered”, but the repo’s `jest.config.js` discovers **all** `tests/**/*.{js,jsx,ts,tsx}`. This mismatch means `npm run test:catalog` can pass even while Jest executes non-`.test`/`.spec` files (e.g. `tests/setup.ts`) that are not enforced in `tests/catalog.md`.

### Issue Context
- Current Jest config `testMatch` is broader than the validator’s `EXECUTABLE_TEST_FILE_PATTERN`.
- `tests/setup.ts` contains a real Jest suite and is also included in `setupFilesAfterEnv`, so it will be executed under the current `testMatch`.

### Fix Focus Areas
Choose one approach and make the validator + Jest consistent.

**Approach A (recommended): narrow Jest discovery to match validator intent**
- Update Jest `testMatch` to only include actual test files (e.g. default Jest pattern within `tests/`).
- Remove the “dummy test” from `tests/setup.ts` (it will still run as a setup file via `setupFilesAfterEnv`, but should not itself be a test suite).

**Approach B: broaden validator discovery to match Jest config**
- Treat any file under `tests/` that matches Jest `testMatch` (and isn’t ignored) as discovered; then decide whether helper files should be required in the catalog.

Add/adjust unit tests to cover whichever approach you choose.

Fix locations:
- scripts/validate-test-catalog.js[10-12]
- scripts/validate-test-catalog.js[86-138]
- jest.config.js[8-18]
- tests/setup.ts[1-10]
- tests/unit/scripts/validate-test-catalog.test.js[35-139]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@sonarqubecloud

Copy link
Copy Markdown

@Mehdi-Bl Mehdi-Bl merged commit a4ca4fc into main Feb 11, 2026
18 checks passed
@Mehdi-Bl Mehdi-Bl deleted the feat/main-process-observability-increment-1 branch February 11, 2026 15:10
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