Skip to content
This repository was archived by the owner on Apr 21, 2026. It is now read-only.

test: add bin-entries regression guard#40

Merged
LucasSantana-Dev merged 2 commits into
mainfrom
test/bin-entries-regression-guard
Apr 8, 2026
Merged

test: add bin-entries regression guard#40
LucasSantana-Dev merged 2 commits into
mainfrom
test/bin-entries-regression-guard

Conversation

@LucasSantana-Dev
Copy link
Copy Markdown
Member

@LucasSantana-Dev LucasSantana-Dev commented Apr 7, 2026

Summary

Adds a parameterised Jest test suite that validates all package.json bin entries:

  • Target file exists on disk
  • Target starts with a node shebang (#!/usr/bin/env node)
  • Target lives under a directory declared in pkg.files

Why This Matters

Forge-Space/core shipped v1.4.0 through v1.14.0 with bin.forge-patterns pointing at a non-existent dist/cli.js file — an error that went undetected for over a year because there was no regression guard. Every npx @forgespace/core forge-patterns invocation failed silently.

See Forge-Space/core#202 for the original incident and test implementation.

Test Plan

  • Build runs successfully before test (npm run build)
  • Test file matches Jest pattern (**/tests/**/*.test.ts)
  • 4 test cases pass: 1 "has bin entry" + 3 assertions per entry
  • Mutation test: reverting bin.forge-ai-init to non-existent path → test fails on existence assertion
  • Original bin.forge-ai-init -> ./dist/index.js restored → all tests pass

Notes

This PR references Forge-Space/core#202 as the pattern implementation source. Rolling out to all Forge Space npm packages with bin blocks.

Summary by CodeRabbit

  • Tests
    • Added a test suite validating package bin declarations, confirming executable files exist, contain proper Node.js shebangs, and reference correct file paths.
  • Chores
    • Ensure the project is built automatically before running the test suite by adding a pre-test build step.

Parameterised test (tests/bin-entries.test.ts) that iterates over
every pkg.bin entry and asserts:
- target file exists on disk
- target starts with a node shebang
- target lives under a directory declared in pkg.files

This prevents the regression class that shipped in @forgespace/core
v1.4.0-v1.14.0, where bin.forge-patterns pointed at a non-existent
dist/cli.js for over a year. See Forge-Space/core#202 for the
original incident.

Mutation-tested locally: reverting bin.forge-ai-init to point at a
non-existent path makes the test fail on the existence assertion,
proving the guard catches real breakage.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2026

Forge AI Quality Gate

Passed — Score 0/100 (F) · Delta +0 · 0 new findings

Category scores
Category Score
security 0
error-handling 0
architecture 34
engineering 0
async 91
hardcoded-values 98
accessibility 99
type-safety 0
scalability 99

Powered by Forge AI Action · forgespace.co

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 837bc85a-40cc-407e-84f3-5947053c8592

📥 Commits

Reviewing files that changed from the base of the PR and between 67c5533 and e4eff0d.

📒 Files selected for processing (1)
  • package.json
✅ Files skipped from review due to trivial changes (1)
  • package.json

📝 Walkthrough

Walkthrough

Added a test that validates package.json bin declarations: it ensures declared bin entries exist, have a Node shebang, and are located under directories covered by the files field. Also added a pretest npm script to run build before tests.

Changes

Cohort / File(s) Summary
Binary Entries Validation Tests
tests/bin-entries.test.ts
New test suite that normalizes bin entries, asserts at least one bin exists, checks each bin file exists, verifies first-line Node shebang when present, and ensures bin paths fall under directories listed in pkg.files.
NPM script update
package.json
Added "pretest": "npm run build" so build runs automatically before npm test / npm run test:coverage.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I hopped through package bins with care,

Searching shebangs hidden there,
Files must live where files declare,
A tiny check, a gentle stare —
The build runs first, then tests prepare.

🚥 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 directly and concisely describes the main change: adding a bin-entries regression guard test. It accurately reflects the primary objective of this PR, which is to add Jest tests validating package.json bin declarations to prevent regressions.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/bin-entries-regression-guard

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.17.0)
package.json

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m


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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/bin-entries.test.ts (1)

4-9: Use temp-directory fixtures for this filesystem-heavy test suite.

The suite currently reads real repo state (package.json, dist/*) directly, which makes it environment/order dependent. Prefer temp-dir fixtures for deterministic behavior.

As per coding guidelines **/*.test.ts: "Use temp directories for filesystem-heavy tests".

Also applies to: 26-49

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/bin-entries.test.ts` around lines 4 - 9, The test reads the real
repository via process.cwd()/repoRoot and package.json (pkg), making it order-
and environment-dependent; change the tests to create an isolated temp directory
fixture for filesystem operations (use a temp dir, write a minimal package.json
with the expected bin/files entries and create any required dist/* files), then
point repoRoot or mock process.cwd() to that temp directory (so the code under
test reads from the fixture), and apply the same pattern to the other
filesystem-heavy blocks referenced around lines 26-49; update any cleanup to
remove the temp fixture after each test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/bin-entries.test.ts`:
- Around line 17-23: The pkgFilesCoversPath function is too strict by only
comparing the top-level segment; update pkgFilesCoversPath to treat package
"files" entries as covering a path if any entry (1) exactly equals the cleaned
relPath, (2) equals the top-level segment (current behavior), (3) is a directory
prefix that matches the start of the cleaned relPath (e.g., entry "dist/" covers
"dist/index.js"), or (4) matches common glob patterns—use a glob/match library
(e.g., minimatch) or simple suffix/prefix checks to handle entries like
"dist/*.js" and explicit file paths so valid package configs are not rejected.
- Around line 39-44: The test currently early-returns when the computed bin path
(abs) is missing, masking failures; instead assert the file exists inside the
test by replacing the early return with an explicit assertion (e.g.,
expect(existsSync(abs)).toBeTruthy()/toBe(true)) before reading firstLine, then
proceed to read the file and assert the shebang; update the test in the
it.each(entries) block that computes abs from repoRoot/rel and uses firstLine so
missing bin targets cause a test failure rather than being skipped.
- Around line 34-37: The test relies on prebuilt dist artifacts and should be
made self-contained: in tests/bin-entries.test.ts update the suite that uses
it.each(entries) (and the repoRoot/existsSync check) to create a temporary test
fixture directory in a beforeAll (using fs.mkdtempSync or fs.promises.mkdtemp)
and write dummy files for each rel from entries into that temp dir (preserving
the same relative paths), then point repoRoot to that temp dir for the
assertions; remove dependence on global build artifacts and clean up the temp
directory in afterAll. Ensure references to entries, repoRoot, and the it.each
test remain but change the path resolution to use the new temp fixture
directory.

---

Nitpick comments:
In `@tests/bin-entries.test.ts`:
- Around line 4-9: The test reads the real repository via process.cwd()/repoRoot
and package.json (pkg), making it order- and environment-dependent; change the
tests to create an isolated temp directory fixture for filesystem operations
(use a temp dir, write a minimal package.json with the expected bin/files
entries and create any required dist/* files), then point repoRoot or mock
process.cwd() to that temp directory (so the code under test reads from the
fixture), and apply the same pattern to the other filesystem-heavy blocks
referenced around lines 26-49; update any cleanup to remove the temp fixture
after each test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d8f0e49-b69b-42db-ad56-1083cca480b4

📥 Commits

Reviewing files that changed from the base of the PR and between 7ad1490 and 67c5533.

📒 Files selected for processing (1)
  • tests/bin-entries.test.ts

Comment thread tests/bin-entries.test.ts
Comment on lines +17 to +23
function pkgFilesCoversPath(files: string[], relPath: string): boolean {
const clean = relPath.replace(/^\.\//, '');
const top = clean.split('/')[0];
return files.some((f) => {
const c = f.replace(/\/$/, '').replace(/^\.\//, '');
return c === top;
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

pkg.files coverage check is too narrow and can reject valid package configs.

Matching only the top-level directory misses valid files entries like explicit file paths (e.g., dist/index.js) and common pattern forms. That can create false failures when this guard is rolled out across packages.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/bin-entries.test.ts` around lines 17 - 23, The pkgFilesCoversPath
function is too strict by only comparing the top-level segment; update
pkgFilesCoversPath to treat package "files" entries as covering a path if any
entry (1) exactly equals the cleaned relPath, (2) equals the top-level segment
(current behavior), (3) is a directory prefix that matches the start of the
cleaned relPath (e.g., entry "dist/" covers "dist/index.js"), or (4) matches
common glob patterns—use a glob/match library (e.g., minimatch) or simple
suffix/prefix checks to handle entries like "dist/*.js" and explicit file paths
so valid package configs are not rejected.

Comment thread tests/bin-entries.test.ts
Comment on lines +34 to +37
it.each(entries)('bin "%s" -> "%s" exists on disk', (_name, rel) => {
const abs = join(repoRoot, rel.replace(/^\.\//, ''));
expect(existsSync(abs)).toBe(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.

⚠️ Potential issue | 🟠 Major

Build-order dependency is currently breaking CI.

This assertion depends on prebuilt dist artifacts, and CI is already failing on this exact check. Make this suite self-contained (fixture/temp artifact) or guarantee npm run build always runs before this test job.

🧰 Tools
🪛 GitHub Actions: CI

[error] 34-36: Jest failed: package.json bin entries expects "forge-ai-init" -> "./dist/index.js" to exist on disk. expect(existsSync(abs)).toBe(true) received false.

🪛 GitHub Actions: SonarCloud Analysis

[error] 34-36: Jest test failed: package.json bin entries expected the target file to exist on disk. Failed assertion expect(existsSync(abs)).toBe(true) for bin "forge-ai-init" -> "./dist/index.js". Received false.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/bin-entries.test.ts` around lines 34 - 37, The test relies on prebuilt
dist artifacts and should be made self-contained: in tests/bin-entries.test.ts
update the suite that uses it.each(entries) (and the repoRoot/existsSync check)
to create a temporary test fixture directory in a beforeAll (using
fs.mkdtempSync or fs.promises.mkdtemp) and write dummy files for each rel from
entries into that temp dir (preserving the same relative paths), then point
repoRoot to that temp dir for the assertions; remove dependence on global build
artifacts and clean up the temp directory in afterAll. Ensure references to
entries, repoRoot, and the it.each test remain but change the path resolution to
use the new temp fixture directory.

Comment thread tests/bin-entries.test.ts
Comment on lines +39 to +44
it.each(entries)('bin "%s" -> "%s" starts with a node shebang', (_name, rel) => {
const abs = join(repoRoot, rel.replace(/^\.\//, ''));
if (!existsSync(abs)) return;
const firstLine = readFileSync(abs, 'utf-8').split('\n', 1)[0] ?? '';
expect(firstLine).toMatch(/^#!.*\bnode\b/);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Shebang test can pass even when the bin target is missing.

Early-return on missing file masks the failure when this test is run in isolation. Assert file existence inside this test too, then check the shebang.

Suggested fix
 it.each(entries)('bin "%s" -> "%s" starts with a node shebang', (_name, rel) => {
   const abs = join(repoRoot, rel.replace(/^\.\//, ''));
-  if (!existsSync(abs)) return;
+  expect(existsSync(abs)).toBe(true);
   const firstLine = readFileSync(abs, 'utf-8').split('\n', 1)[0] ?? '';
   expect(firstLine).toMatch(/^#!.*\bnode\b/);
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it.each(entries)('bin "%s" -> "%s" starts with a node shebang', (_name, rel) => {
const abs = join(repoRoot, rel.replace(/^\.\//, ''));
if (!existsSync(abs)) return;
const firstLine = readFileSync(abs, 'utf-8').split('\n', 1)[0] ?? '';
expect(firstLine).toMatch(/^#!.*\bnode\b/);
});
it.each(entries)('bin "%s" -> "%s" starts with a node shebang', (_name, rel) => {
const abs = join(repoRoot, rel.replace(/^\.\//, ''));
expect(existsSync(abs)).toBe(true);
const firstLine = readFileSync(abs, 'utf-8').split('\n', 1)[0] ?? '';
expect(firstLine).toMatch(/^#!.*\bnode\b/);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/bin-entries.test.ts` around lines 39 - 44, The test currently
early-returns when the computed bin path (abs) is missing, masking failures;
instead assert the file exists inside the test by replacing the early return
with an explicit assertion (e.g.,
expect(existsSync(abs)).toBeTruthy()/toBe(true)) before reading firstLine, then
proceed to read the file and assert the shebang; update the test in the
it.each(entries) block that computes abs from repoRoot/rel and uses firstLine so
missing bin targets cause a test failure rather than being skipped.

Without pretest=build, bin-entries.test.ts asserts against dist/index.js
that does not exist yet in CI (jest runs before the build step), producing
a false-positive failure. A pretest hook guarantees dist/ exists in every
invocation path (CI, local, validate).
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Apr 8, 2026

@LucasSantana-Dev LucasSantana-Dev merged commit 98f8936 into main Apr 8, 2026
18 checks passed
@LucasSantana-Dev LucasSantana-Dev deleted the test/bin-entries-regression-guard branch April 8, 2026 17:14
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Engineering Board Apr 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant