Skip to content

Commit 2737eb3

Browse files
NagyViktNagyVikt
andauthored
Prove cockpit backend selection stays explicit (#501)
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 Co-authored-by: NagyVikt <nagy.viktordp@gmail.com>
1 parent c1ae473 commit 2737eb3

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)