start-command 0.20.3 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -0
- package/package.json +1 -1
- package/src/lib/args-parser.js +220 -45
- package/src/lib/command-builder.js +130 -0
- package/src/lib/docker-utils.js +3 -1
- package/src/lib/isolation.js +31 -4
- package/src/lib/sequence-parser.js +231 -0
- package/test/args-parser.test.js +6 -6
- package/test/isolation-stacking.test.js +366 -0
- package/test/output-blocks.test.js +28 -0
- package/test/sequence-parser.test.js +237 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for sequence-parser.js
|
|
3
|
+
* Isolation stacking feature for issue #77
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { describe, it, expect } = require('bun:test');
|
|
7
|
+
const {
|
|
8
|
+
parseSequence,
|
|
9
|
+
formatSequence,
|
|
10
|
+
shiftSequence,
|
|
11
|
+
isSequence,
|
|
12
|
+
distributeOption,
|
|
13
|
+
getValueAtLevel,
|
|
14
|
+
formatIsolationChain,
|
|
15
|
+
buildNextLevelOptions,
|
|
16
|
+
} = require('../src/lib/sequence-parser');
|
|
17
|
+
|
|
18
|
+
describe('Sequence Parser', () => {
|
|
19
|
+
describe('parseSequence', () => {
|
|
20
|
+
it('should parse single value', () => {
|
|
21
|
+
expect(parseSequence('docker')).toEqual(['docker']);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should parse space-separated sequence', () => {
|
|
25
|
+
expect(parseSequence('screen ssh docker')).toEqual([
|
|
26
|
+
'screen',
|
|
27
|
+
'ssh',
|
|
28
|
+
'docker',
|
|
29
|
+
]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should parse sequence with underscores as null', () => {
|
|
33
|
+
expect(parseSequence('_ ssh _ docker')).toEqual([
|
|
34
|
+
null,
|
|
35
|
+
'ssh',
|
|
36
|
+
null,
|
|
37
|
+
'docker',
|
|
38
|
+
]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should handle all underscores', () => {
|
|
42
|
+
expect(parseSequence('_ _ _')).toEqual([null, null, null]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle empty string', () => {
|
|
46
|
+
expect(parseSequence('')).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should handle null/undefined', () => {
|
|
50
|
+
expect(parseSequence(null)).toEqual([]);
|
|
51
|
+
expect(parseSequence(undefined)).toEqual([]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should trim whitespace', () => {
|
|
55
|
+
expect(parseSequence(' screen ssh ')).toEqual(['screen', 'ssh']);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should handle multiple spaces between values', () => {
|
|
59
|
+
expect(parseSequence('screen ssh docker')).toEqual([
|
|
60
|
+
'screen',
|
|
61
|
+
'ssh',
|
|
62
|
+
'docker',
|
|
63
|
+
]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('formatSequence', () => {
|
|
68
|
+
it('should format array with values', () => {
|
|
69
|
+
expect(formatSequence(['screen', 'ssh', 'docker'])).toBe(
|
|
70
|
+
'screen ssh docker'
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should format array with nulls as underscores', () => {
|
|
75
|
+
expect(formatSequence([null, 'ssh', null, 'docker'])).toBe(
|
|
76
|
+
'_ ssh _ docker'
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should handle empty array', () => {
|
|
81
|
+
expect(formatSequence([])).toBe('');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should handle non-array', () => {
|
|
85
|
+
expect(formatSequence(null)).toBe('');
|
|
86
|
+
expect(formatSequence(undefined)).toBe('');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('shiftSequence', () => {
|
|
91
|
+
it('should remove first element', () => {
|
|
92
|
+
expect(shiftSequence(['screen', 'ssh', 'docker'])).toEqual([
|
|
93
|
+
'ssh',
|
|
94
|
+
'docker',
|
|
95
|
+
]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should handle nulls', () => {
|
|
99
|
+
expect(shiftSequence([null, 'ssh', null])).toEqual(['ssh', null]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should handle single element', () => {
|
|
103
|
+
expect(shiftSequence(['docker'])).toEqual([]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should handle empty array', () => {
|
|
107
|
+
expect(shiftSequence([])).toEqual([]);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('isSequence', () => {
|
|
112
|
+
it('should return true for space-separated values', () => {
|
|
113
|
+
expect(isSequence('screen ssh docker')).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should return false for single value', () => {
|
|
117
|
+
expect(isSequence('docker')).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should return false for non-string', () => {
|
|
121
|
+
expect(isSequence(null)).toBe(false);
|
|
122
|
+
expect(isSequence(undefined)).toBe(false);
|
|
123
|
+
expect(isSequence(123)).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('distributeOption', () => {
|
|
128
|
+
it('should replicate single value for all levels', () => {
|
|
129
|
+
expect(distributeOption('ubuntu:22.04', 3, '--image')).toEqual([
|
|
130
|
+
'ubuntu:22.04',
|
|
131
|
+
'ubuntu:22.04',
|
|
132
|
+
'ubuntu:22.04',
|
|
133
|
+
]);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should parse sequence with matching length', () => {
|
|
137
|
+
expect(distributeOption('_ _ ubuntu:22.04', 3, '--image')).toEqual([
|
|
138
|
+
null,
|
|
139
|
+
null,
|
|
140
|
+
'ubuntu:22.04',
|
|
141
|
+
]);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should throw on length mismatch', () => {
|
|
145
|
+
expect(() => distributeOption('_ _', 3, '--image')).toThrow();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should handle null/undefined value', () => {
|
|
149
|
+
expect(distributeOption(null, 3, '--image')).toEqual([null, null, null]);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('getValueAtLevel', () => {
|
|
154
|
+
it('should get value at valid index', () => {
|
|
155
|
+
expect(getValueAtLevel(['a', 'b', 'c'], 1)).toBe('b');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should handle nulls', () => {
|
|
159
|
+
expect(getValueAtLevel([null, 'b', null], 0)).toBe(null);
|
|
160
|
+
expect(getValueAtLevel([null, 'b', null], 1)).toBe('b');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should return null for out of bounds', () => {
|
|
164
|
+
expect(getValueAtLevel(['a', 'b'], 5)).toBe(null);
|
|
165
|
+
expect(getValueAtLevel(['a', 'b'], -1)).toBe(null);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should handle non-array', () => {
|
|
169
|
+
expect(getValueAtLevel(null, 0)).toBe(null);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('formatIsolationChain', () => {
|
|
174
|
+
it('should format simple chain', () => {
|
|
175
|
+
expect(formatIsolationChain(['screen', 'tmux', 'docker'])).toBe(
|
|
176
|
+
'screen → tmux → docker'
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should add SSH endpoint', () => {
|
|
181
|
+
const options = { endpointStack: [null, 'user@host', null] };
|
|
182
|
+
expect(formatIsolationChain(['screen', 'ssh', 'docker'], options)).toBe(
|
|
183
|
+
'screen → ssh@user@host → docker'
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should add Docker image short name', () => {
|
|
188
|
+
const options = { imageStack: [null, null, 'oven/bun:latest'] };
|
|
189
|
+
expect(formatIsolationChain(['screen', 'ssh', 'docker'], options)).toBe(
|
|
190
|
+
'screen → ssh → docker:bun'
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should handle placeholders', () => {
|
|
195
|
+
expect(formatIsolationChain([null, 'ssh', null])).toBe('_ → ssh → _');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should handle empty array', () => {
|
|
199
|
+
expect(formatIsolationChain([])).toBe('');
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('buildNextLevelOptions', () => {
|
|
204
|
+
it('should shift all stacks', () => {
|
|
205
|
+
const options = {
|
|
206
|
+
isolated: 'screen',
|
|
207
|
+
isolatedStack: ['screen', 'ssh', 'docker'],
|
|
208
|
+
image: null,
|
|
209
|
+
imageStack: [null, null, 'ubuntu:22.04'],
|
|
210
|
+
endpoint: null,
|
|
211
|
+
endpointStack: [null, 'user@host', null],
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const next = buildNextLevelOptions(options);
|
|
215
|
+
|
|
216
|
+
expect(next.isolated).toBe('ssh');
|
|
217
|
+
expect(next.isolatedStack).toEqual(['ssh', 'docker']);
|
|
218
|
+
expect(next.image).toBe(null);
|
|
219
|
+
expect(next.imageStack).toEqual([null, 'ubuntu:22.04']);
|
|
220
|
+
expect(next.endpoint).toBe('user@host');
|
|
221
|
+
expect(next.endpointStack).toEqual(['user@host', null]);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should handle last level', () => {
|
|
225
|
+
const options = {
|
|
226
|
+
isolated: 'docker',
|
|
227
|
+
isolatedStack: ['docker'],
|
|
228
|
+
imageStack: ['ubuntu:22.04'],
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const next = buildNextLevelOptions(options);
|
|
232
|
+
|
|
233
|
+
expect(next.isolated).toBe(null);
|
|
234
|
+
expect(next.isolatedStack).toEqual([]);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
});
|