mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-14 12:27:18 +01:00
fix: a removed test is named by any whole-word mention of its name without the extension (pregate lanes)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
39fce52f2d
commit
f943239692
4 files changed
+26
-5
No files matched your search
+1
-1
@@ -12,7 +12,7 @@
|
||||
- Only removed tests are waived. These still fail PREGATE_NO_LANE until a test or a `dependencies` entry covers them: any removed non-test path, a renamed source file's old name included; a removed test that remaining code imports; a deleted or renamed Go or Ruby test (it may define helpers its package still uses); and a deleted test whose merge-base text cannot be read (a symlink, invalid UTF-8, over 1 MiB). A remaining test that reads a deleted file or directory is still selected.
|
||||
|
||||
### Gate integrity
|
||||
- New `test/pregate-removed-paths.test.ts` (42 cases) covers the waived tests and the fail-closed negative controls; no existing test, threshold or allowlist changed.
|
||||
- New `test/pregate-removed-paths.test.ts` (45 cases) covers the waived tests and the fail-closed negative controls; no existing test, threshold or allowlist changed.
|
||||
|
||||
## [1.91.291.0] - 2026-10-28
|
||||
|
||||
|
||||
+3
-2
@@ -200,8 +200,9 @@ Two stages with separate clocks, both printed:
|
||||
<why>` (the `lanes` detail adds `N removed path(s)`), when it is a JS, TS,
|
||||
Python, Dart or bats test and no remaining file imports it (directly or
|
||||
through a directory it reads; otherwise `still imported by …`). It runs
|
||||
the tests of each remaining non-Markdown file that mentions its basename,
|
||||
such as a curation list (`files naming it run: …`), and fails closed when
|
||||
the tests of each remaining non-Markdown file that mentions its name
|
||||
without the final extension as a whole word (`alpha.test`, `test_alpha`:
|
||||
a path with or without the extension, or a dotted module), such as a curation list (`files naming it run: …`), and fails closed when
|
||||
such a file has no test (`still named by …, which no test covers`). A test
|
||||
renamed to another test path then defers to its new name (`old name of
|
||||
<new>`). A deleted test, or one renamed to a non-test path, also runs its
|
||||
|
||||
@@ -105,9 +105,15 @@ function readWorkingTree(repoRoot: string, rel: string): { text: string | null;
|
||||
/** Tracked files matching a registry entry's output globs. */
|
||||
function outputsOf(all: readonly string[], globs: readonly string[]): string[] { return all.filter(f => matchesAny(globs, f)); }
|
||||
|
||||
/** Tracked files outside Markdown that mention `file`'s basename, where the import graph cannot see: a curation list, a durations manifest, a script or a workflow. */
|
||||
/**
|
||||
* Tracked files outside Markdown that mention `file`'s name without its final extension as a whole word, where the import graph
|
||||
* cannot see: a curation list, a durations manifest, a script, a workflow, an extensionless path (`node test/alpha.test`) or a
|
||||
* dotted module (`tests.test_alpha`).
|
||||
*/
|
||||
function namedBy(ctx: PregateContext, file: string): string[] {
|
||||
const r = spawnSync('git', ['grep', '--untracked', '-l', '-F', '-e', path.posix.basename(file), '--', '.', ':(exclude)*.md', ':(exclude)*.mdx'], { cwd: ctx.repoRoot, encoding: 'utf8', timeout: 60_000, maxBuffer: 16 * 1024 * 1024, env: ctx.env });
|
||||
const base = path.posix.basename(file);
|
||||
const stem = base.includes('.') ? base.slice(0, base.lastIndexOf('.')) : base;
|
||||
const r = spawnSync('git', ['grep', '--untracked', '-l', '-w', '-F', '-e', stem, '--', '.', ':(exclude)*.md', ':(exclude)*.mdx'], { cwd: ctx.repoRoot, encoding: 'utf8', timeout: 60_000, maxBuffer: 16 * 1024 * 1024, env: ctx.env });
|
||||
if (r.status === 1) return [];
|
||||
return r.status === 0 ? (r.stdout ?? '').split('\n').filter(Boolean) : ['(git grep failed)'];
|
||||
}
|
||||
|
||||
@@ -229,6 +229,20 @@ describe('removed paths in lanes (#3529)', () => {
|
||||
expect(r.removed[0]).toContain('files naming it run: test/curation.test.js');
|
||||
});
|
||||
|
||||
test.each([
|
||||
['an extensionless path in package.json', 'test/util.test.js', { 'package.json': '{ "name": "fixture", "scripts": { "smoke": "node --test test/util.test" } }\n' }],
|
||||
['a dotted module name in a Makefile', 'tests/test_alpha.py', { 'tests/__init__.py': '', 'tests/test_alpha.py': 'import unittest\nclass T(unittest.TestCase):\n def test_a(self):\n pass\n', 'Makefile': 'smoke:\n\tpython3 -m unittest tests.test_alpha\n' }],
|
||||
['a dotted module name untested code imports at run time', 'app/test_handlers.py', { 'app/__init__.py': '', 'app/test_handlers.py': 'X = 1\n', 'app/registry.py': 'import importlib\nH = importlib.import_module("app.test_handlers")\n' }],
|
||||
])('a removed test named only by %s that no test covers fails closed', (_how, removed, files) => {
|
||||
const repo = fixture({ baseFiles: files });
|
||||
commit(repo, { [removed]: null });
|
||||
const r = lanes(repo);
|
||||
expect(r.check.code).toBe('PREGATE_NO_LANE');
|
||||
expect(r.removed).toEqual([]);
|
||||
const explain = runBin(repo, 'gstack-pregate', ['--base', 'main', '--allow-repo-commands', '--stage', 'tests', '--explain']);
|
||||
expect(explain.out + explain.err).toContain(`${removed}: removed, but still named by`);
|
||||
});
|
||||
|
||||
test('a removed test a file no test covers still names fails closed', () => {
|
||||
const repo = fixture({ baseFiles: { 'scripts/skip-list.txt': 'test/util.test.js\n' } });
|
||||
commit(repo, { 'test/util.test.js': null });
|
||||
|
||||
Reference in new issue
Block a user