-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun-tests.ts
More file actions
235 lines (189 loc) · 9.32 KB
/
Copy pathrun-tests.ts
File metadata and controls
235 lines (189 loc) · 9.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
import { Shell, SpawnStatus, VerbosityLevel } from '@codifycli/plugin-core';
import { testSpawn, TestUtils } from '@codifycli/plugin-test';
import { Command } from 'commander';
import { spawn } from 'node:child_process';
import * as inspector from 'node:inspector';
import os from 'node:os';
import path from 'node:path';
import { codifySpawn } from '../src/utils/codify-spawn';
const IP_REGEX = /VM was assigned with (.*) IP/;
const program = new Command();
program
.option('--launchPersistent', 'Launches a persistent VM for testing', false)
.option('--operatingSystem <operatingSystem>', 'Operating system to run tests on', os.platform())
.option('--persistent', 'Runs tests on a persistent VM (reuse) to skip the overhead of launching a new VM each time', false)
.argument('[file]', 'File to run')
.action(main)
.parse()
async function main(argument: string, args: {
operatingSystem: string;
persistent: boolean;
launchPersistent: boolean
}): Promise<void> {
const debug = isInDebugMode();
if (debug) {
console.log('Running in debug mode!')
}
if (args.launchPersistent) {
await launchPersistentVm(args.operatingSystem);
return process.exit(0);
}
if (args.persistent) {
// if (!argument) {
// throw new Error('No test specified for persistent mode');
// }
await launchPersistentTest(argument, debug, args.operatingSystem);
return process.exit(0);
}
if (!argument) {
await launchTestAll(debug, args.operatingSystem)
return process.exit(0);
}
await launchSingleTest(argument, debug, args.operatingSystem);
process.exit(0);
}
async function launchTestAll(debug: boolean, operatingSystem: string): Promise<void> {
const image = operatingSystem === 'darwin' ? 'integration_test_dev_macos' : 'integration_test_dev_linux';
// const tests = await glob('./test/**/*.test.ts');
// for (const test of tests) {
// console.log(`Running test ${test}`)
// await run(`cirrus run --lazy-pull ${image} -e FILE_NAME="${test}" ${ debug ? '-o simple' : ''}`, debug, false)
// }
await run(`cirrus run --lazy-pull ${image} -o simple`, debug, false);
}
async function launchSingleTest(test: string, debug: boolean, operatingSystem: string) {
const image = operatingSystem === 'darwin' ? 'integration_individual_test_macos' : 'integration_individual_test_linux';
console.log(`Running test: ${test}`)
await run(`cirrus run --lazy-pull ${image} -e FILE_NAME="${test}" -o simple`, debug)
}
async function launchPersistentTest(test: string, debug: boolean, operatingSystem: string) {
const shell = operatingSystem === 'darwin' ? 'zsh' : 'bash';
// if (operatingSystem === 'darwin') {
const { data: vmList } = await codifySpawn('tart list --format json');
console.log(vmList);
const parsedVmList = JSON.parse(vmList);
const runningVm = parsedVmList.find(vm => vm.Name.startsWith('codify-test-vm') && vm.Running === true);
if (!runningVm) {
throw new Error('No persistent VM found');
}
const vmName = runningVm.Name;
const dir = '~/codify-homebrew-plugin';
// const dir = '/Volumes/My\\ Shared\\ Files/plugin'
const debugFlag = debug ? ' -e DEBUG="--inspect-brk=9229"' : ''
console.log('Refreshing files on VM...');
const { data: ipAddr } = await testSpawn(`tart ip ${vmName}`);
await testSpawn(`tart exec ${vmName} rm -rf ${dir}/src ${dir}/test`, { throws: true });
await testSpawn(`sshpass -p "admin" scp -r -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${path.join(process.cwd(), 'test')} ${path.join(process.cwd(), 'src')} ${path.join(process.cwd(), 'package.json')} ${path.join(process.cwd(), 'package-lock.json')} admin@${ipAddr}:${dir}`, { throws: true });
console.log('Done refreshing files on VM. Starting tests...');
VerbosityLevel.set(3);
await codifySpawn(`tart exec -i ${vmName} ${shell} -c -i 'cd ${dir} && XDG_RUNTIME_DIR="/run/user/$(id -u)" DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus" FORCE_COLOR=true npm run test -- ${test ? test : ''} --disable-console-intercept ${debugFlag} --no-file-parallelism'`, { throws: false });
// }
}
async function launchPersistentVm(operatingSystem: string) {
const newVmName = `codify-test-vm-${Date.now()}`;
const shell = operatingSystem === 'darwin' ? 'zsh' : 'bash';
console.log(`Cloning new VM... ${newVmName}`);
const image = (operatingSystem === 'darwin') ? 'codify-test-vm' : 'codify-test-vm-linux';
await testSpawn(`tart clone ${image} ${newVmName}`);
testSpawn(`tart run ${newVmName}`)
.then(cleanupVm)
process.on('exit', cleanupVm);
process.on('SIGINT', cleanupVm);
process.on('SIGHUP', cleanupVm);
process.on('SIGTERM', cleanupVm);
await sleep(5000);
await waitUntilVmIsReady(newVmName);
const { data: ipAddr } = await testSpawn(`tart ip ${newVmName}`);
await testSpawn(`sshpass -p "admin" rsync -avz -e 'ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' --exclude 'node_modules' --exclude '.git' --exclude 'dist' --exclude '.fleet' ${process.cwd()} admin@${ipAddr}:~`);
if (operatingSystem === 'darwin') {
await testSpawn(`tart exec ${newVmName} ${shell} -i -c "mv ~/.zprofile ~/.zshenv"`);
} else {
await testSpawn(`tart exec ${newVmName} ${shell} -i -c "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash"`)
await testSpawn(`tart exec ${newVmName} ${shell} -i -c "nvm install 24; nvm alias default 24"`)
// XDG_RUNTIME_DIR is required by systemd user services (e.g. `systemctl --user`). On desktop
// Linux it is set automatically by PAM/logind on graphical login, but non-interactive SSH/tart
// sessions skip that path, so we set it explicitly to the canonical location /run/user/<uid>.
await testSpawn(`tart exec ${newVmName} ${shell} -i -c "echo 'export XDG_RUNTIME_DIR=/run/user/$(id -u)' >> ~/.bashrc"`)
// enable-linger keeps the user's systemd session alive after logout. Without it, user-scoped
// systemd units (like syncthing.service) are stopped when the tart session ends, which causes
// `systemctl --user enable --now` to fail during integration tests.
await testSpawn(`tart exec ${newVmName} ${shell} -i -c "loginctl enable-linger admin"`)
}
await testSpawn(`tart exec ${newVmName} ${shell} -i -c "cd ~/codify-homebrew-plugin && npm ci"`);
console.log('Finished installing dependencies. Start tests in a new terminal window.');
await sleep(1_000_000_000);
// This is effective the end just without a return
async function cleanupVm() {
console.log('Deleting VM after...')
await testSpawn(`tart delete ${newVmName}`);
process.exit(0);
}
}
async function run(cmd: string, debug: boolean, simple = true) {
const messageBuffer: string[] = [];
cmd += (debug ? ' -e DEBUG="--inspect-brk=9229"' : '');
const cp = spawn(
'source ~/.zshrc; ' + cmd,
[],
{ stdio: simple || debug ? 'pipe' : 'inherit', shell: 'zsh' });
cp.stderr?.on('data', data => {
console.log(data.toString());
messageBuffer.push(data.toString());
});
cp.stdout?.on('data', data => {
console.log(data.toString());
messageBuffer.push(data.toString());
});
// If debug then open ssh tunnel
// if (debug) {
// cp.stdout!.on('data', data => {
// if (data.toString().includes('VM was assigned with')) {
// const [_, ip] = data.toString().match(IP_REGEX);
//
// console.log(`Copying ssh keys to ${ip}`)
// // spawnSync(`source $HOME/.zshrc; sshpass -p admin ssh-copy-id -o "StrictHostKeyChecking=no" admin@${ip}`, { stdio: 'inherit', shell: 'zsh' });
//
// console.log('Enabling port forwarding')
// const sshStatement1 = `ssh${ Array.from({ length: 20 }, (i: number) => i + 9000).map((i) => ` -L ${i}:localhost:${i}`)} admin@${ip}`;
// const sshStatement2 = `source $HOME/.zshrc; sshpass -p admin ssh -L 9221:localhost:9221 -L 9229:localhost:9229 -N -o "StrictHostKeyChecking=no" admin@${ip}`
//
// const portForward1 = spawn(sshStatement2, { stdio: 'pipe', shell: 'zsh' });
// portForward1.stderr.pipe(process.stdout)
// portForward1.stdout.on('data', data => {
// console.log(data.toString());
// if (data.toString().includes('Address already in use')) {
// throw new Error('Port 9229 already in use!')
// }
// })
// // console.log('Enabled on port 9229')
//
// // const portForward2 = spawn(`ssh -L 9221:localhost:9221 -Nf admin@${ip}`, { stdio: 'pipe', shell: 'zsh' });
// // portForward2.stdout.on('data', data => {
// // console.log(data.toString());
// // if (data.toString().includes('Address already in use')) {
// // throw new Error('Port 9221 already in use!')
// // }
// // });
// // console.log('Enabled on port 9221')
// }
// })
// }
return new Promise((resolve) =>
cp.on('exit', () => resolve(messageBuffer.join('\n'))) // We never want to reject here even if the test fails
);
}
function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function isInDebugMode() {
return inspector.url() !== undefined;
}
async function waitUntilVmIsReady(vmName: string): Promise<void> {
while (true) {
const result = await testSpawn(`tart exec ${vmName} pwd`, { interactive: true })
if (result.status === SpawnStatus.SUCCESS) {
return;
}
await sleep(1000);
}
}