Skip to content

Commit 9459275

Browse files
committed
feat(cli): add runtime Node.js version check with clear error message
1 parent 18f6e6e commit 9459275

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

src/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
#!/usr/bin/env node
2+
3+
// Guard: exit early with a clear message on unsupported Node.js versions.
4+
// This runs before any ESM/modern-syntax imports that would crash with cryptic errors.
5+
const major = Number(process.versions.node.split('.')[0]);
6+
if (major < 20) {
7+
process.stderr.write(
8+
`Error: testsprite requires Node.js >= 20 (found ${process.versions.node}).\nInstall the latest LTS from https://nodejs.org\n`,
9+
);
10+
process.exit(1);
11+
}
12+
213
import { Command, CommanderError } from 'commander';
314
import { createAgentCommand } from './commands/agent.js';
415
import { createAuthCommand } from './commands/auth.js';

src/version-guard.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
/**
4+
* The version guard logic extracted for testability.
5+
* The real guard lives at the top of src/index.ts as imperative code.
6+
* These tests verify the parsing and threshold logic.
7+
*/
8+
9+
function parseMajorVersion(nodeVersion: string): number {
10+
return Number(nodeVersion.split('.')[0]);
11+
}
12+
13+
function shouldRejectVersion(nodeVersion: string): boolean {
14+
return parseMajorVersion(nodeVersion) < 20;
15+
}
16+
17+
describe('Node.js version guard logic', () => {
18+
it('rejects Node 18.x', () => {
19+
expect(shouldRejectVersion('18.19.1')).toBe(true);
20+
});
21+
22+
it('rejects Node 16.x', () => {
23+
expect(shouldRejectVersion('16.20.2')).toBe(true);
24+
});
25+
26+
it('rejects Node 14.x', () => {
27+
expect(shouldRejectVersion('14.21.3')).toBe(true);
28+
});
29+
30+
it('accepts Node 20.x', () => {
31+
expect(shouldRejectVersion('20.11.0')).toBe(false);
32+
});
33+
34+
it('accepts Node 22.x', () => {
35+
expect(shouldRejectVersion('22.1.0')).toBe(false);
36+
});
37+
38+
it('accepts Node 21.x', () => {
39+
expect(shouldRejectVersion('21.0.0')).toBe(false);
40+
});
41+
42+
it('parses major version correctly from semver string', () => {
43+
expect(parseMajorVersion('20.11.1')).toBe(20);
44+
expect(parseMajorVersion('18.0.0')).toBe(18);
45+
expect(parseMajorVersion('22.3.0')).toBe(22);
46+
});
47+
48+
it('the running Node satisfies the guard (meta-test)', () => {
49+
// This test is running on Node >= 20, so it must pass the guard.
50+
expect(shouldRejectVersion(process.versions.node)).toBe(false);
51+
});
52+
});

0 commit comments

Comments
 (0)