Skip to content

Commit 34aa113

Browse files
author
NagyVikt
committed
Prove cockpit backend selection stays explicit
The parser and resolver already support tmux, kitty, and auto backend choices. This change locks the operator-facing gx cockpit surfaces so future cockpit work cannot quietly collapse explicit tmux, Kitty preference, auto fallback, or invalid-backend errors. Constraint: Existing gx cockpit without --backend must remain on the tmux path. Rejected: Switch the default backend to auto | that would change existing gx cockpit behavior without an explicit flag. Confidence: high Scope-risk: narrow Directive: Keep explicit tmux and auto fallback coverage when changing cockpit backend resolution. Tested: node --test test/cockpit-terminal-backend.test.js test/cockpit-command.test.js Tested: openspec validate --specs
1 parent c1ae473 commit 34aa113

3 files changed

Lines changed: 106 additions & 20 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# cockpit backend selection CLI coverage
2+
3+
## Intent
4+
5+
Lock the existing `gx cockpit --backend` parser and terminal resolver behavior with focused regression coverage.
6+
7+
## Scope
8+
9+
- Keep default `gx cockpit` behavior on the tmux path.
10+
- Prove explicit `--backend tmux` bypasses Kitty even when Kitty is available.
11+
- Prove explicit `--backend kitty` and `--backend auto` route through the selected terminal backend.
12+
- Prove invalid backend names fail with `--backend requires auto, kitty, or tmux`.
13+
14+
## Verification
15+
16+
- `node --test test/cockpit-terminal-backend.test.js test/cockpit-command.test.js`
17+
- `openspec validate --specs`

test/cockpit-command.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,12 @@ test('cockpit reports a helpful error when tmux is unavailable', () => {
9393
assert.equal(result.status, 1);
9494
assert.match(result.stderr, /tmux is required for gx cockpit\. Install tmux and retry\./);
9595
});
96+
97+
test('cockpit reports a clear error for an invalid backend', () => {
98+
const repoDir = initRepo();
99+
100+
const result = runNodeWithEnv(['cockpit', '--backend', 'screen', '--target', repoDir], repoDir, {});
101+
102+
assert.equal(result.status, 1);
103+
assert.match(result.stderr, /--backend requires auto, kitty, or tmux/);
104+
});

test/cockpit-terminal-backend.test.js

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,41 @@ const {
1010
kitty,
1111
} = require('../src/terminal');
1212

13+
function cockpitBackendHarness({ kittyAvailable = true } = {}) {
14+
const stdout = [];
15+
const calls = {
16+
kitty: [],
17+
tmux: [],
18+
};
19+
20+
const backend = (name) => ({
21+
name,
22+
isAvailable: () => (name === 'kitty' ? kittyAvailable : true),
23+
openCockpitLayout(options) {
24+
calls[name].push(options);
25+
return { action: 'created' };
26+
},
27+
});
28+
29+
return {
30+
stdout,
31+
calls,
32+
deps: {
33+
resolveRepoRoot: (target) => target,
34+
toolName: 'gx',
35+
stdout: {
36+
write(chunk) {
37+
stdout.push(String(chunk));
38+
},
39+
},
40+
terminalBackends: {
41+
kitty: backend('kitty'),
42+
tmux: backend('tmux'),
43+
},
44+
},
45+
};
46+
}
47+
1348
test('backend selection prefers kitty for auto when remote control is available', () => {
1449
const backend = selectTerminalBackend('auto', {
1550
kittyBackend: {
@@ -113,31 +148,14 @@ test('kitty command construction is stable', () => {
113148
});
114149

115150
test('cockpit --backend kitty opens through the selected backend', () => {
116-
const stdout = [];
117-
const calls = [];
151+
const { stdout, calls, deps } = cockpitBackendHarness();
118152
const result = cockpit.openCockpit(['--backend', 'kitty', '--session', 'guardex-dev', '--target', '/repo/gitguardex'], {
119-
resolveRepoRoot: (target) => target,
120-
toolName: 'gx',
121-
stdout: {
122-
write(chunk) {
123-
stdout.push(String(chunk));
124-
},
125-
},
126-
terminalBackends: {
127-
kitty: {
128-
name: 'kitty',
129-
isAvailable: () => true,
130-
openCockpitLayout(options) {
131-
calls.push(options);
132-
return { action: 'created' };
133-
},
134-
},
135-
},
153+
...deps,
136154
});
137155

138156
assert.equal(result.backend, 'kitty');
139157
assert.equal(result.sessionName, 'guardex-dev');
140-
assert.deepEqual(calls, [
158+
assert.deepEqual(calls.kitty, [
141159
{
142160
repoRoot: '/repo/gitguardex',
143161
sessionName: 'guardex-dev',
@@ -148,3 +166,45 @@ test('cockpit --backend kitty opens through the selected backend', () => {
148166
assert.match(stdout.join(''), /Created kitty cockpit window 'guardex-dev'/);
149167
assert.match(stdout.join(''), /Control pane: gx cockpit control --target '\/repo\/gitguardex'/);
150168
});
169+
170+
test('cockpit --backend tmux opens through the tmux backend when kitty is available', () => {
171+
const { stdout, calls, deps } = cockpitBackendHarness();
172+
const result = cockpit.openCockpit(['--backend=tmux', '--session', 'guardex-dev', '--target', '/repo/gitguardex'], {
173+
...deps,
174+
});
175+
176+
assert.equal(result.backend, 'tmux');
177+
assert.equal(result.sessionName, 'guardex-dev');
178+
assert.deepEqual(calls.kitty, []);
179+
assert.deepEqual(calls.tmux, [
180+
{
181+
repoRoot: '/repo/gitguardex',
182+
sessionName: 'guardex-dev',
183+
command: "gx cockpit control --target '/repo/gitguardex'",
184+
attach: false,
185+
},
186+
]);
187+
assert.match(stdout.join(''), /Created tmux session 'guardex-dev'/);
188+
});
189+
190+
test('cockpit --backend auto prefers kitty through the CLI option', () => {
191+
const { calls, deps } = cockpitBackendHarness({ kittyAvailable: true });
192+
const result = cockpit.openCockpit(['--backend', 'auto', '--target', '/repo/gitguardex'], {
193+
...deps,
194+
});
195+
196+
assert.equal(result.backend, 'kitty');
197+
assert.equal(calls.kitty.length, 1);
198+
assert.equal(calls.tmux.length, 0);
199+
});
200+
201+
test('cockpit --backend auto falls back to tmux when kitty is unavailable', () => {
202+
const { calls, deps } = cockpitBackendHarness({ kittyAvailable: false });
203+
const result = cockpit.openCockpit(['--backend', 'auto', '--target', '/repo/gitguardex'], {
204+
...deps,
205+
});
206+
207+
assert.equal(result.backend, 'tmux');
208+
assert.equal(calls.kitty.length, 0);
209+
assert.equal(calls.tmux.length, 1);
210+
});

0 commit comments

Comments
 (0)