mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 06:28:59 +02:00
test: regression pin for legacy remove mixed-entry filtering + ownership negatives
Coverage-audit iron rule: the rewritten legacy `remove` action filters per-item (pre-v1.67.2 it dropped the whole entry, destroying a user's co-located SessionStart hook) — modified existing behavior, previously untested. Also pins two ownership negatives: an owned basename+relpath under the WRONG matcher stays foreign, and prune-stale on an absent settings file exits 0 with removed 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
d85fbccf7a
commit
d5626653ac
@@ -470,6 +470,53 @@ describe('add-event: per-item identity re-point', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('legacy remove: per-item (regression)', () => {
|
||||
test('mixed SessionStart entry: user item survives in place, gstack item removed', () => {
|
||||
// REGRESSION pin: the pre-v1.67.2 legacy `remove` dropped the ENTIRE
|
||||
// entry when any item matched gstack-session-update, destroying a user's
|
||||
// co-located hook. The rewrite filters per-item; this is the only test of
|
||||
// that branch (team-mode.test.ts covers single-item entries only).
|
||||
fs.writeFileSync(settingsFile, JSON.stringify({
|
||||
hooks: {
|
||||
SessionStart: [{
|
||||
hooks: [
|
||||
{ type: 'command', command: '/Users/me/my-own-session-hook' },
|
||||
{ type: 'command', command: '/old/install/bin/gstack-session-update' },
|
||||
],
|
||||
}],
|
||||
},
|
||||
}, null, 2));
|
||||
const r = runIso(['remove', '/old/install/bin/gstack-session-update']);
|
||||
expect(r.exitCode).toBe(0);
|
||||
const s = settings();
|
||||
expect(s.hooks.SessionStart).toHaveLength(1);
|
||||
expect(s.hooks.SessionStart[0].hooks).toHaveLength(1);
|
||||
expect(s.hooks.SessionStart[0].hooks[0].command).toBe('/Users/me/my-own-session-hook');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ownership negatives', () => {
|
||||
test('owned basename+relpath under the WRONG matcher stays foreign (not re-pointed)', () => {
|
||||
const canon = mkCanon(tmpDir);
|
||||
fs.writeFileSync(settingsFile, JSON.stringify({
|
||||
hooks: {
|
||||
PostToolUse: [hookEntry('/dead/wt/hosts/claude/hooks/question-log-hook', 'Bash')],
|
||||
},
|
||||
}, null, 2));
|
||||
const before = fs.readFileSync(settingsFile, 'utf-8');
|
||||
const r = runIso(['prune-stale', '--repoint', canon]);
|
||||
expect(r.stdout).toMatch(/removed 0 gstack hook entries \(repointed 0\)/);
|
||||
expect(fs.readFileSync(settingsFile, 'utf-8')).toBe(before);
|
||||
});
|
||||
|
||||
test('prune-stale on an absent settings file exits 0 with removed 0', () => {
|
||||
const r = runIso(['prune-stale', '--repoint', '/nonexistent-root']);
|
||||
expect(r.exitCode).toBe(0);
|
||||
expect(r.stdout).toMatch(/removed 0 gstack hook entries \(repointed 0\)/);
|
||||
expect(fs.existsSync(settingsFile)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove-source: per-item', () => {
|
||||
test('mixed tagged entry: gstack item removed, user item survives, tag dropped', () => {
|
||||
fs.writeFileSync(settingsFile, JSON.stringify({
|
||||
|
||||
Reference in New Issue
Block a user