-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathindex.test.ts
More file actions
164 lines (138 loc) · 4.86 KB
/
Copy pathindex.test.ts
File metadata and controls
164 lines (138 loc) · 4.86 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
import { spawnSync } from 'node:child_process';
import {
existsSync,
mkdirSync,
mkdtempSync,
rmSync,
writeFileSync,
} from 'node:fs';
import path from 'node:path';
import { afterEach, beforeEach } from 'rstack/test';
import { normalizeHelpOutput, RSTACK_BIN_PATH, test } from '#test-helpers';
const hooksPath = '.rstack/hooks/_';
let cwd: string;
let env: NodeJS.ProcessEnv;
const git = (args: string[]): string => {
const result = spawnSync('git', args, { cwd, encoding: 'utf8', env });
if (result.status !== 0) {
throw new Error(result.stderr || `Git exited with status ${result.status}`);
}
return result.stdout.trim();
};
const initRepository = (): void => {
git(['init', '--quiet']);
};
const runSetup = (args: string[], runCwd: string = cwd) =>
spawnSync(process.execPath, [RSTACK_BIN_PATH, 'setup', ...args], {
cwd: runCwd,
encoding: 'utf8',
env,
});
beforeEach(() => {
cwd = mkdtempSync(path.join(import.meta.dirname, 'test-temp-rstack setup '));
env = {
...process.env,
// Keep Git from treating the fixture as part of this repository.
GIT_CEILING_DIRECTORIES: import.meta.dirname,
GIT_CONFIG_GLOBAL: path.join(cwd, 'global.gitconfig'),
GIT_CONFIG_NOSYSTEM: '1',
};
});
afterEach(() => {
rmSync(cwd, { force: true, recursive: true });
});
test('displays setup help', ({ execCli, expect }) => {
const output = execCli('setup --help', { cwd });
expect(execCli('setup -h', { cwd })).toBe(output);
expect(normalizeHelpOutput(output)).toMatchSnapshot();
});
test('rejects unknown setup options', ({ execCli, expect }) => {
expect(() => execCli('setup --unknown', { cwd })).toThrow();
});
test('reports missing and repeated hooks directory options', ({ expect }) => {
const missing = runSetup(['--hooks-dir']);
expect(missing.status).toBe(1);
expect(missing.stderr).toContain('--hooks-dir');
const repeated = runSetup(['--hooks-dir', 'first', '--hooks-dir', 'second']);
expect(repeated.status).toBe(1);
expect(repeated.stderr).toContain(
'The --hooks-dir option cannot be specified more than once.',
);
});
test('rejects invalid hooks directory options', ({ expect }) => {
const empty = runSetup(['--hooks-dir', '']);
expect(empty.status).toBe(1);
expect(empty.stderr).toContain('Git hooks directory must not be empty.');
const absolute = runSetup(['--hooks-dir', path.join(cwd, 'hooks')]);
expect(absolute.status).toBe(1);
expect(absolute.stderr).toContain(
'Git hooks directory must be relative to the Git repository root.',
);
const parent = runSetup(['--hooks-dir', '../hooks']);
expect(parent.status).toBe(1);
expect(parent.stderr).toContain('Git hooks directory must not contain "..".');
});
test('installs hooks silently without loading Rstack config', ({
execCli,
expect,
}) => {
initRepository();
writeFileSync(
path.join(cwd, 'rstack.config.ts'),
'throw new Error("must not load");\n',
);
expect(execCli('setup', { cwd, env })).toBe('');
expect(git(['config', '--local', '--get', 'core.hooksPath'])).toBe(hooksPath);
expect(existsSync(path.join(cwd, hooksPath, 'runner'))).toBe(true);
expect(existsSync(path.join(cwd, '.rstack', 'hooks', 'pre-commit'))).toBe(
false,
);
expect(execCli('setup', { cwd, env })).toBe('');
});
test('installs root-relative hooks and reports owner conflicts', ({
execCli,
expect,
}) => {
initRepository();
const frontend = path.join(cwd, 'frontend');
const docs = path.join(cwd, 'docs');
mkdirSync(frontend);
mkdirSync(docs);
expect(
execCli('setup --hooks-dir "custom hooks"', { cwd: frontend, env }),
).toBe('');
expect(git(['config', '--local', '--get', 'core.hooksPath'])).toBe(
'custom hooks/_',
);
expect(existsSync(path.join(cwd, 'custom hooks', '_', 'runner'))).toBe(true);
const conflict = runSetup(['--hooks-dir', 'custom hooks'], docs);
expect(conflict.status).toBe(0);
expect(`${conflict.stdout}${conflict.stderr}`).toContain(
'Git hooks are already managed by Rstack project "frontend"',
);
});
test('skips non-Git directories without creating files', ({
execCli,
expect,
}) => {
expect(execCli('setup', { cwd, env })).toContain(
'info Git hooks setup skipped: not a Git repository.',
);
expect(existsSync(path.join(cwd, '.rstack'))).toBe(false);
});
test('skips setup when hooks are disabled', ({ execCli, expect }) => {
const output = execCli('setup', { cwd, env: { ...env, RSTACK_HOOKS: '0' } });
expect(output).toContain(
'info Git hooks setup skipped: disabled by RSTACK_HOOKS.',
);
expect(existsSync(path.join(cwd, '.rstack'))).toBe(false);
});
test('exits with an error when Git is unavailable', ({ expect }) => {
const result = spawnSync(process.execPath, [RSTACK_BIN_PATH, 'setup'], {
cwd,
encoding: 'utf8',
env: { ...env, PATH: '', Path: '' },
});
expect(result.status).toBe(1);
expect(result.stderr).toContain('Git command not found.');
});