Files
gbrain/test/repo-root.test.ts
Garry Tan dcd13dd638 feat: v0.16.4 — gbrain check-resolvable CLI + skillify-check wiring (#325)
* Merge origin/master into garrytan/check-resolvable-v1

Resolves CHANGELOG.md conflict: preserved v0.16.1/v0.16.2/v0.16.3 upstream
entries and added v0.16.4 (check-resolvable ship) above them.

* refactor: extract findRepoRoot to src/core/repo-root.ts

Moves findRepoRoot() from private in doctor.ts to a zero-dependency shared
module with a parameterized startDir for test hermeticity. Doctor imports
the shared version; no behavior change (default arg matches prior semantics).

The new gbrain check-resolvable CLI needs findRepoRoot too; importing from
doctor.ts would drag in DB/progress dependencies.

* feat: gbrain check-resolvable CLI wrapper

Standalone CLI gate over checkResolvable(). Exits 1 on any issue (warnings
or errors) per the README:259 contract, stricter than doctor's resolver_health
which ignores warnings. Doctor has 15 other checks to lean on; the standalone
command has nowhere to hide.

- Stable JSON envelope: {ok, skillsDir, report, autoFix, deferred, error, message}
- --fix auto-applies DRY fixes via autoFixDryViolations before re-checking
- --dry-run with --fix previews without writing; autoFix.fixed shows diff
- --verbose prints the deferred-checks note (Checks 5 + 6)
- --skills-dir PATH for hermetic test runs
- Permissive on unknown flags, matching lint/orphans/publish convention

Checks 5 (trigger routing eval) and 6 (brain filing) are tracked as separate
GitHub issues and surfaced via the deferred[] field in --json output.

Covered by 17 new test cases (flag parsing, JSON envelope shape, exit-code
regression gates, --fix wiring, --verbose output).

* chore: bump version and changelog (v0.16.4)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: track check-resolvable issue-URL swap in TODOS

Defers the filing of GitHub tracking issues for Checks 5 (trigger routing
eval) and 6 (brain filing) plus the TBD-check-5/TBD-check-6 URL replacement
in src/commands/check-resolvable.ts. Unblocks merging PR #325.

* test: fix repo-root CI failure — assert parity, not path contents

The 'default arg uses process.cwd()' test asserted the returned path
matched /honolulu/, which is the local workspace name but not the CI
runner's checkout path (/home/runner/work/gbrain/gbrain). The test's
real purpose is behavioral parity: findRepoRoot() === findRepoRoot(cwd).
Assert that directly instead of pattern-matching paths.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 02:07:00 -07:00

54 lines
1.8 KiB
TypeScript

import { describe, it, expect, afterEach } from 'bun:test';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { findRepoRoot } from '../src/core/repo-root.ts';
describe('findRepoRoot', () => {
const created: string[] = [];
afterEach(() => {
while (created.length) {
const p = created.pop()!;
try { rmSync(p, { recursive: true, force: true }); } catch { /* ignore */ }
}
});
function scratch(): string {
const dir = mkdtempSync(join(tmpdir(), 'repo-root-'));
created.push(dir);
return dir;
}
function seedRepo(dir: string): void {
mkdirSync(join(dir, 'skills'), { recursive: true });
writeFileSync(join(dir, 'skills', 'RESOLVER.md'), '# RESOLVER\n');
}
it('finds skills/RESOLVER.md in the passed startDir on first iteration', () => {
const root = scratch();
seedRepo(root);
expect(findRepoRoot(root)).toBe(root);
});
it('walks up N directories to find the repo root', () => {
const root = scratch();
seedRepo(root);
const nested = join(root, 'a', 'b', 'c');
mkdirSync(nested, { recursive: true });
expect(findRepoRoot(nested)).toBe(root);
});
it('returns null when no skills/RESOLVER.md exists up to filesystem root', () => {
const empty = scratch();
// Deliberately no seedRepo — empty dir; walk terminates at filesystem root.
expect(findRepoRoot(empty)).toBeNull();
});
it('default arg uses process.cwd() (behavioral parity with prior doctor-private impl)', () => {
// The default arg must match calling with an explicit process.cwd().
// Don't assert on the path contents — it varies between local checkouts
// and CI runners. What matters is parity: no-arg === cwd-arg.
expect(findRepoRoot()).toBe(findRepoRoot(process.cwd()));
});
});