mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
test(ignore-service): guard the twin build-output ignore lists against drift
_next now lives in two lists in two packages — the analyzer's DEFAULT_IGNORE_LIST and the browser upload filter's EXCLUDED_DIRS — with nothing tying them together. This is the seventh twin-list pair in this repo; the header of receiver-twin-list-drift.test.ts records that the previous ones each shipped a bug when one side moved. Containment runs web -> CLI only, and that is the load-bearing direction: the browser filter decides what the server ever sees, and it reads no .gitnexusignore, so a name it drops that the analyzer would have indexed is silent source loss with no recovery. The reverse is not an error — the analyzer prunes far more aggressively than an upload needs to. .gitnexus is the one exemption and has a mechanism: the walker passes dot: false to glob, so it never enumerates dot-directories. Asserted in both directions so re-adding it to the CLI list or dropping it from the web list both fail. Both sides are source-parsed through the shared helper. DEFAULT_IGNORE_LIST is module-private, and no test in this package imports across the package boundary — every cross-package precedent reads source instead. Also corrects the documentation this PR's comments got wrong: the guard test is cited by path rather than as "below", the unreproducible per-repo percentage is gone, the reason _next is deliberately unanchored is recorded next to the entry (no <web-root>/_next form matches a root-level _next/static/…), and the upload filter now states that it consults no repository ignore rules — so unlike the CLI, a negation cannot recover what it drops. Verified by mutation: a web-only addition and a CLI removal each turn the guard red. 194 targeted tests pass; tsc clean in both packages.
This commit is contained in:
parent
00c2fe81be
commit
1dc5328af5
3 changed files with 118 additions and 5 deletions
|
|
@ -27,6 +27,11 @@ export const EXCLUDED_DIRS = new Set([
|
|||
// `<platform>/app/src/main/assets/public/_next/`, so without this the whole
|
||||
// minified tree is uploaded against the server's file/byte caps only to be
|
||||
// discarded by the analyzer's own ignore list (#3007).
|
||||
//
|
||||
// This pre-filter reads no repository ignore rules, so unlike the CLI walker
|
||||
// a `.gitnexusignore` negation cannot recover anything dropped here. Names
|
||||
// added below must therefore stay a subset of the analyzer's own list; see
|
||||
// `gitnexus/test/unit/upload-filter-ignore-drift.test.ts`.
|
||||
'_next',
|
||||
'.nuxt',
|
||||
'.cache',
|
||||
|
|
|
|||
|
|
@ -61,9 +61,15 @@ const DEFAULT_IGNORE_LIST = new Set([
|
|||
// `.next` is Next.js's build CACHE; `_next` is the EMITTED output, and the two
|
||||
// are different directories. A Capacitor/Cordova shell copies the emitted
|
||||
// bundle to `<platform>/app/src/main/assets/public/_next/static/…`, where none
|
||||
// of the path segments hit this list — so a mobile-wrapped Next.js app had 40%
|
||||
// of its indexed files come from minified chunks, and every Route node it
|
||||
// produced pointed at a webpack bundle rather than source (#3007).
|
||||
// of the path segments hit this list — so a mobile-wrapped Next.js app had its
|
||||
// shipped bundle indexed as source, and every Route node it produced pointed at
|
||||
// a webpack chunk rather than code anyone wrote (#3007).
|
||||
//
|
||||
// The name is deliberately unanchored. No `<web-root>/_next` form matches a
|
||||
// root-level `_next/static/…`, which is the shape the reported repo has, so
|
||||
// anchoring it would miss the case it was added for. The accepted cost is a
|
||||
// hand-written directory literally named `_next`; recover one with a bare
|
||||
// `!_next/` line in `.gitnexusignore`.
|
||||
'_next',
|
||||
'.nuxt',
|
||||
'.output',
|
||||
|
|
@ -75,8 +81,10 @@ const DEFAULT_IGNORE_LIST = new Set([
|
|||
// time, and `isHardcodedIgnoredDirectory(name)` takes a bare directory name,
|
||||
// so a slash-containing member could never match either — it was inert. Its
|
||||
// paths were never unignored though: bare `'build'` above already prunes
|
||||
// `public/build/**`, so removing the entry changes no behavior (#3007). The
|
||||
// guard test below keeps the next slash-bearing entry from dying the same way.
|
||||
// `public/build/**`, so removing the entry changes no behavior (#3007).
|
||||
// `test/unit/ignore-build-output.test.ts` keeps the next slash-bearing entry
|
||||
// in this set — or in IGNORED_FILES, ROOT_ARTIFACT_DIRECTORIES or
|
||||
// IGNORED_EXTENSIONS — from dying the same way.
|
||||
'.parcel-cache',
|
||||
'.turbo',
|
||||
'.svelte-kit',
|
||||
|
|
|
|||
100
gitnexus/test/unit/upload-filter-ignore-drift.test.ts
Normal file
100
gitnexus/test/unit/upload-filter-ignore-drift.test.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* The drift guard for the twin build-output ignore lists (#3007 follow-up).
|
||||
*
|
||||
* TWO lists spell "do not index this directory", in two packages:
|
||||
*
|
||||
* - `DEFAULT_IGNORE_LIST` — gitnexus `src/config/ignore-service.ts`. The
|
||||
* analyzer's own list, consulted for every path during the repository walk.
|
||||
* - `EXCLUDED_DIRS` — gitnexus-web `src/lib/upload-filter.ts`. A client-side
|
||||
* pre-filter that decides what a browser folder upload sends at all.
|
||||
*
|
||||
* `_next` was added to both in the same PR, one commit apart. Before it, neither
|
||||
* carried the name — so #3007 was a shared omission rather than drift between
|
||||
* them. What this test guards is the divergence that becomes possible now that
|
||||
* the same name lives in two places with nothing tying them together.
|
||||
*
|
||||
* The containment runs web -> CLI only, and that direction is the load-bearing
|
||||
* one: the browser filter decides what the server ever sees, so a name it drops
|
||||
* that the analyzer would have indexed is silent source loss with no recovery —
|
||||
* this pre-filter reads no `.gitnexusignore`, so a negation cannot bring the
|
||||
* files back. The reverse direction is not an error: roughly sixty CLI-only
|
||||
* names exist because the analyzer prunes far more aggressively than an upload
|
||||
* needs to, and the walker's own `dot: false` already hides dot-directories from
|
||||
* it. That asymmetry is why equality is not the assertion.
|
||||
*
|
||||
* `.gitnexus` is the one deliberate exception, and it has a mechanism rather
|
||||
* than being an oversight: the CLI walker passes `dot: false` to glob
|
||||
* (`src/core/ingestion/filesystem-walker.ts`), so it never enumerates
|
||||
* dot-directories and does not need the name in its list. The browser filter has
|
||||
* no equivalent and must name it. That exemption is asserted explicitly in both
|
||||
* directions, so re-adding `.gitnexus` to the CLI list or dropping it from the
|
||||
* web list both fail loudly.
|
||||
*
|
||||
* Structural (source-parsed) rather than value-imported: `DEFAULT_IGNORE_LIST`
|
||||
* is module-private and exporting it purely to be testable would widen a
|
||||
* production surface to satisfy a test. `EXCLUDED_DIRS` is exported, but no test
|
||||
* in this package imports across the package boundary — every cross-package
|
||||
* precedent here reads source instead — so both sides use the same parser.
|
||||
*/
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { setEntries } from '../helpers/ignore-set-source.js';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
|
||||
|
||||
const cliSource = readFileSync(
|
||||
path.join(REPO_ROOT, 'gitnexus', 'src', 'config', 'ignore-service.ts'),
|
||||
'utf8',
|
||||
);
|
||||
const webSource = readFileSync(
|
||||
path.join(REPO_ROOT, 'gitnexus-web', 'src', 'lib', 'upload-filter.ts'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
const cliNames = () => setEntries(cliSource, 'const DEFAULT_IGNORE_LIST = new Set([');
|
||||
const webNames = () => setEntries(webSource, 'export const EXCLUDED_DIRS = new Set([');
|
||||
|
||||
/**
|
||||
* Names the browser filter may drop that the analyzer does not list.
|
||||
*
|
||||
* Only `.gitnexus`, and only because the CLI walker's `dot: false` makes the
|
||||
* entry unnecessary there. A name added here must cite a comparable structural
|
||||
* reason in the walker — this list is not a place to park a failing assertion.
|
||||
*/
|
||||
const WEB_ONLY_ALLOWLIST = ['.gitnexus'];
|
||||
|
||||
describe('build-output ignore lists stay in agreement across packages', () => {
|
||||
it('parses both lists — neither is silently empty', () => {
|
||||
// Guards the guard: a parser that read nothing would make every containment
|
||||
// assertion below vacuously true.
|
||||
expect(cliNames().length).toBeGreaterThan(20);
|
||||
expect(webNames().length).toBeGreaterThan(5);
|
||||
});
|
||||
|
||||
it('every name the browser filter drops is one the analyzer also ignores', () => {
|
||||
const cli = new Set(cliNames());
|
||||
const unmatched = webNames().filter(
|
||||
(name) => !cli.has(name) && !WEB_ONLY_ALLOWLIST.includes(name),
|
||||
);
|
||||
expect(unmatched).toEqual([]);
|
||||
});
|
||||
|
||||
it('carries the documented web-only exemption, and only that one', () => {
|
||||
// Asserted separately from the containment above so the intent survives if
|
||||
// that assertion is ever relaxed.
|
||||
expect(webNames()).toContain('.gitnexus');
|
||||
expect(cliNames()).not.toContain('.gitnexus');
|
||||
});
|
||||
|
||||
it('shares the build-output names the reported bug was about', () => {
|
||||
const cli = new Set(cliNames());
|
||||
const web = new Set(webNames());
|
||||
for (const name of ['_next', '.next', 'dist', 'build', 'out']) {
|
||||
expect(web.has(name), `${name} missing from the browser upload filter`).toBe(true);
|
||||
expect(cli.has(name), `${name} missing from the analyzer ignore list`).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue