Skip to content

Commit 712e25f

Browse files
committed
test: add integration tests for global install and symlink invocation
1 parent 87baae6 commit 712e25f

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* Integration tests for global install / symlink invocation
3+
* Verifies the built binary works when invoked through a symlink,
4+
* as happens with `npm install -g` or `npm link`.
5+
*/
6+
7+
import { test, describe, beforeEach, afterEach } from 'node:test';
8+
import assert from 'node:assert';
9+
import { spawnSync } from 'node:child_process';
10+
import { mkdirSync, rmSync, symlinkSync, existsSync } from 'node:fs';
11+
import { join, resolve } from 'node:path';
12+
import { tmpdir } from 'node:os';
13+
14+
describe('Global Install (symlink) Integration Tests', () => {
15+
const projectRoot = resolve(import.meta.dirname, '..', '..');
16+
const distEntry = join(projectRoot, 'dist', 'index.js');
17+
let tempBinDir: string;
18+
let symlinkPath: string;
19+
20+
beforeEach(() => {
21+
tempBinDir = join(tmpdir(), `c8ctl-global-test-${Date.now()}`);
22+
mkdirSync(tempBinDir, { recursive: true });
23+
symlinkPath = join(tempBinDir, 'c8ctl');
24+
});
25+
26+
afterEach(() => {
27+
if (existsSync(tempBinDir)) {
28+
rmSync(tempBinDir, { recursive: true, force: true });
29+
}
30+
});
31+
32+
test('dist/index.js must exist (run npm run build first)', () => {
33+
assert.ok(
34+
existsSync(distEntry),
35+
`dist/index.js not found at ${distEntry}. Run "npm run build" before running this test.`,
36+
);
37+
});
38+
39+
test('binary works when invoked directly with node', () => {
40+
const result = spawnSync('node', [distEntry, 'help'], {
41+
encoding: 'utf-8',
42+
timeout: 10_000,
43+
});
44+
45+
assert.strictEqual(result.status, 0, `Expected exit 0, got ${result.status}. stderr: ${result.stderr}`);
46+
assert.ok(result.stdout.includes('c8ctl'), 'help output should mention c8ctl');
47+
assert.ok(result.stdout.includes('Usage:'), 'help output should contain Usage');
48+
});
49+
50+
test('binary works when invoked through a symlink (simulates npm link / npm install -g)', () => {
51+
symlinkSync(distEntry, symlinkPath);
52+
53+
const result = spawnSync(symlinkPath, ['help'], {
54+
encoding: 'utf-8',
55+
timeout: 10_000,
56+
});
57+
58+
assert.strictEqual(result.status, 0, `Expected exit 0, got ${result.status}. stderr: ${result.stderr}`);
59+
assert.ok(result.stdout.includes('c8ctl'), 'help output should mention c8ctl');
60+
assert.ok(result.stdout.includes('Usage:'), 'help output should contain Usage');
61+
});
62+
63+
test('binary shows version when invoked through a symlink', () => {
64+
symlinkSync(distEntry, symlinkPath);
65+
66+
const result = spawnSync(symlinkPath, ['help'], {
67+
encoding: 'utf-8',
68+
timeout: 10_000,
69+
});
70+
71+
assert.strictEqual(result.status, 0, `Expected exit 0, got ${result.status}. stderr: ${result.stderr}`);
72+
// Version line appears in help output header
73+
assert.match(result.stdout, /v\d+\.\d+\.\d+/, 'output should contain a version string');
74+
});
75+
76+
test('binary works through a double symlink (symlink → symlink → dist/index.js)', () => {
77+
// Simulates npm global bin → node_modules symlink → project dist
78+
const intermediateDir = join(tempBinDir, 'node_modules');
79+
mkdirSync(intermediateDir, { recursive: true });
80+
const intermediatePath = join(intermediateDir, 'index.js');
81+
82+
// First symlink: intermediate → real file
83+
symlinkSync(distEntry, intermediatePath);
84+
// Second symlink: bin entry → intermediate
85+
symlinkSync(intermediatePath, symlinkPath);
86+
87+
const result = spawnSync(symlinkPath, ['help'], {
88+
encoding: 'utf-8',
89+
timeout: 10_000,
90+
});
91+
92+
assert.strictEqual(result.status, 0, `Expected exit 0, got ${result.status}. stderr: ${result.stderr}`);
93+
assert.ok(result.stdout.includes('c8ctl'), 'help output should mention c8ctl');
94+
});
95+
});

0 commit comments

Comments
 (0)