ucn 3.0.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.
Potentially problematic release.
This version of ucn might be problematic. Click here for more details.
- package/.claude/skills/ucn/SKILL.md +77 -0
- package/LICENSE +21 -0
- package/README.md +135 -0
- package/cli/index.js +2437 -0
- package/core/discovery.js +513 -0
- package/core/imports.js +558 -0
- package/core/output.js +1274 -0
- package/core/parser.js +279 -0
- package/core/project.js +3261 -0
- package/index.js +52 -0
- package/languages/go.js +653 -0
- package/languages/index.js +267 -0
- package/languages/java.js +826 -0
- package/languages/javascript.js +1346 -0
- package/languages/python.js +667 -0
- package/languages/rust.js +950 -0
- package/languages/utils.js +457 -0
- package/package.json +42 -0
- package/test/fixtures/go/go.mod +3 -0
- package/test/fixtures/go/main.go +257 -0
- package/test/fixtures/go/service.go +187 -0
- package/test/fixtures/java/DataService.java +279 -0
- package/test/fixtures/java/Main.java +287 -0
- package/test/fixtures/java/Utils.java +199 -0
- package/test/fixtures/java/pom.xml +6 -0
- package/test/fixtures/javascript/main.js +109 -0
- package/test/fixtures/javascript/package.json +1 -0
- package/test/fixtures/javascript/service.js +88 -0
- package/test/fixtures/javascript/utils.js +67 -0
- package/test/fixtures/python/main.py +198 -0
- package/test/fixtures/python/pyproject.toml +3 -0
- package/test/fixtures/python/service.py +166 -0
- package/test/fixtures/python/utils.py +118 -0
- package/test/fixtures/rust/Cargo.toml +3 -0
- package/test/fixtures/rust/main.rs +253 -0
- package/test/fixtures/rust/service.rs +210 -0
- package/test/fixtures/rust/utils.rs +154 -0
- package/test/fixtures/typescript/main.ts +154 -0
- package/test/fixtures/typescript/package.json +1 -0
- package/test/fixtures/typescript/repository.ts +149 -0
- package/test/fixtures/typescript/types.ts +114 -0
- package/test/parser.test.js +3661 -0
- package/test/public-repos-test.js +477 -0
- package/test/systematic-test.js +619 -0
- package/ucn.js +8 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Public Repository Test Script for UCN
|
|
5
|
+
*
|
|
6
|
+
* Tests UCN against real public repositories to find edge cases and bugs.
|
|
7
|
+
* Run with: node test/public-repos-test.js [--verbose] [--keep]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { execSync, spawnSync } = require('child_process');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
|
|
15
|
+
// Configuration
|
|
16
|
+
const UCN_PATH = path.join(__dirname, '..', 'ucn.js');
|
|
17
|
+
const TEMP_DIR = path.join(os.tmpdir(), 'ucn-test-repos');
|
|
18
|
+
const TIMEOUT = 60000; // 60 seconds
|
|
19
|
+
|
|
20
|
+
// Parse args
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
23
|
+
const keepRepos = args.includes('--keep');
|
|
24
|
+
|
|
25
|
+
// Colors
|
|
26
|
+
const c = {
|
|
27
|
+
reset: '\x1b[0m',
|
|
28
|
+
red: '\x1b[31m',
|
|
29
|
+
green: '\x1b[32m',
|
|
30
|
+
yellow: '\x1b[33m',
|
|
31
|
+
blue: '\x1b[34m',
|
|
32
|
+
cyan: '\x1b[36m',
|
|
33
|
+
dim: '\x1b[2m',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Results tracking
|
|
37
|
+
const results = {
|
|
38
|
+
passed: 0,
|
|
39
|
+
failed: 0,
|
|
40
|
+
bugs: [],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Public repositories to test - small, well-known repos
|
|
44
|
+
const repos = {
|
|
45
|
+
javascript: [
|
|
46
|
+
{
|
|
47
|
+
name: 'preact-signals',
|
|
48
|
+
url: 'https://github.com/preactjs/signals',
|
|
49
|
+
dir: 'packages/core/src',
|
|
50
|
+
symbols: ['signal', 'computed', 'effect', 'batch'],
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
typescript: [
|
|
54
|
+
{
|
|
55
|
+
name: 'zod',
|
|
56
|
+
url: 'https://github.com/colinhacks/zod',
|
|
57
|
+
dir: 'src',
|
|
58
|
+
symbols: ['ZodType', 'string', 'parse', 'safeParse'],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
python: [
|
|
62
|
+
{
|
|
63
|
+
name: 'httpx',
|
|
64
|
+
url: 'https://github.com/encode/httpx',
|
|
65
|
+
dir: 'httpx',
|
|
66
|
+
symbols: ['Client', 'get', 'post', 'request'],
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
go: [
|
|
70
|
+
{
|
|
71
|
+
name: 'cobra',
|
|
72
|
+
url: 'https://github.com/spf13/cobra',
|
|
73
|
+
dir: '.',
|
|
74
|
+
symbols: ['Command', 'Execute', 'AddCommand', 'Flags'],
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
rust: [
|
|
78
|
+
{
|
|
79
|
+
name: 'ripgrep',
|
|
80
|
+
url: 'https://github.com/BurntSushi/ripgrep',
|
|
81
|
+
dir: 'crates/core',
|
|
82
|
+
symbols: ['Searcher', 'search', 'new', 'build'],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
java: [
|
|
86
|
+
{
|
|
87
|
+
name: 'gson',
|
|
88
|
+
url: 'https://github.com/google/gson',
|
|
89
|
+
dir: 'gson/src/main/java/com/google/gson',
|
|
90
|
+
symbols: ['Gson', 'toJson', 'fromJson', 'JsonElement'],
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Commands to test
|
|
96
|
+
const commands = [
|
|
97
|
+
{ name: 'toc', args: [] },
|
|
98
|
+
{ name: 'stats', args: [] },
|
|
99
|
+
{ name: 'find', args: ['$SYM'] },
|
|
100
|
+
{ name: 'usages', args: ['$SYM'] },
|
|
101
|
+
{ name: 'context', args: ['$SYM'] },
|
|
102
|
+
{ name: 'about', args: ['$SYM'] },
|
|
103
|
+
{ name: 'smart', args: ['$SYM'] },
|
|
104
|
+
{ name: 'impact', args: ['$SYM'] },
|
|
105
|
+
{ name: 'trace', args: ['$SYM', '--depth=2'] },
|
|
106
|
+
{ name: 'api', args: [] },
|
|
107
|
+
{ name: 'deadcode', args: [] },
|
|
108
|
+
{ name: 'find', args: ['$SYM', '--json'], id: 'find-json' },
|
|
109
|
+
{ name: 'toc', args: ['--json'], id: 'toc-json' },
|
|
110
|
+
{ name: 'search', args: ['TODO'] },
|
|
111
|
+
{ name: 'fn', args: ['$SYM'] },
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Clone a repository
|
|
116
|
+
*/
|
|
117
|
+
function cloneRepo(url, name) {
|
|
118
|
+
const repoPath = path.join(TEMP_DIR, name);
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(repoPath)) {
|
|
121
|
+
if (verbose) console.log(`${c.dim} Using cached: ${name}${c.reset}`);
|
|
122
|
+
return repoPath;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log(`${c.dim} Cloning: ${name}...${c.reset}`);
|
|
126
|
+
try {
|
|
127
|
+
execSync(`git clone --depth 1 ${url} ${repoPath}`, {
|
|
128
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
129
|
+
timeout: 120000,
|
|
130
|
+
});
|
|
131
|
+
return repoPath;
|
|
132
|
+
} catch (e) {
|
|
133
|
+
console.log(`${c.yellow} ⚠ Failed to clone ${name}: ${e.message}${c.reset}`);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Run UCN command
|
|
140
|
+
*/
|
|
141
|
+
function runUcn(targetPath, command, args = []) {
|
|
142
|
+
const fullArgs = ['node', UCN_PATH, targetPath, command, ...args, '--no-cache'];
|
|
143
|
+
const cmdStr = fullArgs.join(' ');
|
|
144
|
+
|
|
145
|
+
if (verbose) {
|
|
146
|
+
console.log(`${c.dim} Running: ${cmdStr}${c.reset}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const result = spawnSync('node', [UCN_PATH, targetPath, command, ...args, '--no-cache'], {
|
|
151
|
+
timeout: TIMEOUT,
|
|
152
|
+
encoding: 'utf8',
|
|
153
|
+
maxBuffer: 50 * 1024 * 1024, // 50MB buffer
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (result.error) {
|
|
157
|
+
return { success: false, error: result.error.message, command: cmdStr };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (result.status !== 0) {
|
|
161
|
+
const stderr = result.stderr || '';
|
|
162
|
+
// Check for expected "no results" type errors
|
|
163
|
+
const isExpectedNoResult =
|
|
164
|
+
stderr.includes('No matches found') ||
|
|
165
|
+
stderr.includes('not found') ||
|
|
166
|
+
stderr.includes('No usages found') ||
|
|
167
|
+
stderr.includes('No tests found') ||
|
|
168
|
+
stderr.includes('No deadcode') ||
|
|
169
|
+
stderr.includes('No imports');
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
success: isExpectedNoResult,
|
|
173
|
+
output: result.stdout,
|
|
174
|
+
error: stderr,
|
|
175
|
+
command: cmdStr,
|
|
176
|
+
exitCode: result.status,
|
|
177
|
+
isExpectedNoResult,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return { success: true, output: result.stdout, command: cmdStr };
|
|
182
|
+
} catch (e) {
|
|
183
|
+
return { success: false, error: e.message, command: cmdStr };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Check for error patterns in output
|
|
189
|
+
*/
|
|
190
|
+
function checkForErrors(output) {
|
|
191
|
+
if (!output) return null;
|
|
192
|
+
|
|
193
|
+
const patterns = [
|
|
194
|
+
{ pattern: /TypeError: .+/g, name: 'TypeError' },
|
|
195
|
+
{ pattern: /ReferenceError: .+/g, name: 'ReferenceError' },
|
|
196
|
+
{ pattern: /SyntaxError: .+/g, name: 'SyntaxError' },
|
|
197
|
+
{ pattern: /Cannot read propert(y|ies) .+ of (undefined|null)/gi, name: 'Property access on null/undefined' },
|
|
198
|
+
{ pattern: /is not a function/gi, name: 'Not a function' },
|
|
199
|
+
{ pattern: /undefined is not/gi, name: 'Undefined error' },
|
|
200
|
+
{ pattern: /FATAL ERROR/gi, name: 'Fatal error' },
|
|
201
|
+
{ pattern: /Maximum call stack/gi, name: 'Stack overflow' },
|
|
202
|
+
{ pattern: /heap out of memory/gi, name: 'Memory error' },
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
for (const { pattern, name } of patterns) {
|
|
206
|
+
const match = output.match(pattern);
|
|
207
|
+
if (match) {
|
|
208
|
+
return { type: name, match: match[0] };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Test a repository
|
|
217
|
+
*/
|
|
218
|
+
function testRepo(lang, repo) {
|
|
219
|
+
console.log(`\n${c.blue}Testing ${lang}/${repo.name}${c.reset}`);
|
|
220
|
+
|
|
221
|
+
const repoPath = cloneRepo(repo.url, repo.name);
|
|
222
|
+
if (!repoPath) {
|
|
223
|
+
results.failed++;
|
|
224
|
+
results.bugs.push({
|
|
225
|
+
language: lang,
|
|
226
|
+
repo: repo.name,
|
|
227
|
+
error: 'Failed to clone repository',
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const targetPath = path.join(repoPath, repo.dir);
|
|
233
|
+
if (!fs.existsSync(targetPath)) {
|
|
234
|
+
console.log(`${c.yellow} ⚠ Directory not found: ${repo.dir}${c.reset}`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Test each command
|
|
239
|
+
for (const cmd of commands) {
|
|
240
|
+
const cmdArgs = cmd.args.map(a => (a === '$SYM' ? repo.symbols[0] : a));
|
|
241
|
+
const testId = cmd.id || `${cmd.name}(${cmdArgs.join(',')})`;
|
|
242
|
+
|
|
243
|
+
const result = runUcn(targetPath, cmd.name, cmdArgs);
|
|
244
|
+
|
|
245
|
+
// Check for crashes/errors
|
|
246
|
+
const errorInOutput = checkForErrors(result.output);
|
|
247
|
+
const errorInStderr = checkForErrors(result.error);
|
|
248
|
+
const foundError = errorInOutput || errorInStderr;
|
|
249
|
+
|
|
250
|
+
if (foundError) {
|
|
251
|
+
results.failed++;
|
|
252
|
+
results.bugs.push({
|
|
253
|
+
language: lang,
|
|
254
|
+
repo: repo.name,
|
|
255
|
+
command: cmd.name,
|
|
256
|
+
args: cmdArgs,
|
|
257
|
+
error: `${foundError.type}: ${foundError.match}`,
|
|
258
|
+
fullCommand: result.command,
|
|
259
|
+
});
|
|
260
|
+
console.log(` ${c.red}✗${c.reset} ${testId}: ${foundError.type}`);
|
|
261
|
+
if (verbose) {
|
|
262
|
+
console.log(` ${c.red}${foundError.match}${c.reset}`);
|
|
263
|
+
}
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check for non-zero exit without expected "no results"
|
|
268
|
+
if (!result.success && !result.isExpectedNoResult) {
|
|
269
|
+
results.failed++;
|
|
270
|
+
results.bugs.push({
|
|
271
|
+
language: lang,
|
|
272
|
+
repo: repo.name,
|
|
273
|
+
command: cmd.name,
|
|
274
|
+
args: cmdArgs,
|
|
275
|
+
error: result.error || 'Command failed',
|
|
276
|
+
fullCommand: result.command,
|
|
277
|
+
exitCode: result.exitCode,
|
|
278
|
+
});
|
|
279
|
+
console.log(` ${c.red}✗${c.reset} ${testId}`);
|
|
280
|
+
if (verbose) {
|
|
281
|
+
console.log(` ${c.red}${result.error || 'Failed'}${c.reset}`);
|
|
282
|
+
}
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Check JSON validity for JSON commands
|
|
287
|
+
if (cmd.args.includes('--json') && result.output) {
|
|
288
|
+
try {
|
|
289
|
+
JSON.parse(result.output);
|
|
290
|
+
} catch (e) {
|
|
291
|
+
results.failed++;
|
|
292
|
+
results.bugs.push({
|
|
293
|
+
language: lang,
|
|
294
|
+
repo: repo.name,
|
|
295
|
+
command: cmd.name,
|
|
296
|
+
args: cmdArgs,
|
|
297
|
+
error: `Invalid JSON: ${e.message}`,
|
|
298
|
+
fullCommand: result.command,
|
|
299
|
+
output: result.output.substring(0, 200),
|
|
300
|
+
});
|
|
301
|
+
console.log(` ${c.red}✗${c.reset} ${testId}: Invalid JSON`);
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
results.passed++;
|
|
307
|
+
if (verbose) {
|
|
308
|
+
console.log(` ${c.green}✓${c.reset} ${testId}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Additional stress tests
|
|
315
|
+
*/
|
|
316
|
+
function runStressTests() {
|
|
317
|
+
console.log(`\n${c.blue}Running STRESS TESTS${c.reset}`);
|
|
318
|
+
|
|
319
|
+
const jsFixture = path.join(__dirname, 'fixtures', 'javascript');
|
|
320
|
+
|
|
321
|
+
const tests = [
|
|
322
|
+
// Large depth values
|
|
323
|
+
{
|
|
324
|
+
name: 'Very large depth',
|
|
325
|
+
run: () => runUcn(jsFixture, 'trace', ['processData', '--depth=100']),
|
|
326
|
+
check: r => !checkForErrors(r.output) && !checkForErrors(r.error),
|
|
327
|
+
},
|
|
328
|
+
// Many concurrent operations simulation (sequential but rapid)
|
|
329
|
+
{
|
|
330
|
+
name: 'Rapid sequential commands',
|
|
331
|
+
run: () => {
|
|
332
|
+
for (let i = 0; i < 10; i++) {
|
|
333
|
+
const r = runUcn(jsFixture, 'toc', []);
|
|
334
|
+
if (!r.success) return r;
|
|
335
|
+
}
|
|
336
|
+
return { success: true };
|
|
337
|
+
},
|
|
338
|
+
check: r => r.success,
|
|
339
|
+
},
|
|
340
|
+
// Large symbol names in search
|
|
341
|
+
{
|
|
342
|
+
name: 'Pattern-like symbol',
|
|
343
|
+
run: () => runUcn(jsFixture, 'find', ['.*']),
|
|
344
|
+
check: r => !checkForErrors(r.output),
|
|
345
|
+
},
|
|
346
|
+
// Empty string searches
|
|
347
|
+
{
|
|
348
|
+
name: 'Empty search',
|
|
349
|
+
run: () => runUcn(jsFixture, 'search', ['']),
|
|
350
|
+
check: r => !checkForErrors(r.output),
|
|
351
|
+
},
|
|
352
|
+
// Newlines in search
|
|
353
|
+
{
|
|
354
|
+
name: 'Newline in search',
|
|
355
|
+
run: () => runUcn(jsFixture, 'search', ['test\ntest']),
|
|
356
|
+
check: r => !checkForErrors(r.output),
|
|
357
|
+
},
|
|
358
|
+
// Null bytes
|
|
359
|
+
{
|
|
360
|
+
name: 'Null byte in search',
|
|
361
|
+
run: () => runUcn(jsFixture, 'search', ['test\x00test']),
|
|
362
|
+
check: r => !checkForErrors(r.output),
|
|
363
|
+
},
|
|
364
|
+
];
|
|
365
|
+
|
|
366
|
+
for (const test of tests) {
|
|
367
|
+
const result = test.run();
|
|
368
|
+
const passed = test.check(result);
|
|
369
|
+
|
|
370
|
+
if (passed) {
|
|
371
|
+
results.passed++;
|
|
372
|
+
if (verbose) {
|
|
373
|
+
console.log(` ${c.green}✓${c.reset} ${test.name}`);
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
results.failed++;
|
|
377
|
+
results.bugs.push({
|
|
378
|
+
language: 'stress',
|
|
379
|
+
command: test.name,
|
|
380
|
+
error: result.error || 'Test failed',
|
|
381
|
+
fullCommand: result.command,
|
|
382
|
+
});
|
|
383
|
+
console.log(` ${c.red}✗${c.reset} ${test.name}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Print summary
|
|
390
|
+
*/
|
|
391
|
+
function printSummary() {
|
|
392
|
+
console.log(`\n${c.cyan}${'='.repeat(60)}${c.reset}`);
|
|
393
|
+
console.log(`${c.cyan}PUBLIC REPO TEST SUMMARY${c.reset}`);
|
|
394
|
+
console.log(`${c.cyan}${'='.repeat(60)}${c.reset}`);
|
|
395
|
+
|
|
396
|
+
console.log(`\n ${c.green}Passed:${c.reset} ${results.passed}`);
|
|
397
|
+
console.log(` ${c.red}Failed:${c.reset} ${results.failed}`);
|
|
398
|
+
console.log(` Total: ${results.passed + results.failed}`);
|
|
399
|
+
|
|
400
|
+
if (results.bugs.length > 0) {
|
|
401
|
+
console.log(`\n${c.red}BUGS FOUND (${results.bugs.length}):${c.reset}`);
|
|
402
|
+
console.log(`${'-'.repeat(60)}`);
|
|
403
|
+
|
|
404
|
+
// Group by language
|
|
405
|
+
const byLang = {};
|
|
406
|
+
for (const bug of results.bugs) {
|
|
407
|
+
const key = bug.repo ? `${bug.language}/${bug.repo}` : bug.language;
|
|
408
|
+
if (!byLang[key]) byLang[key] = [];
|
|
409
|
+
byLang[key].push(bug);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
for (const [key, bugs] of Object.entries(byLang)) {
|
|
413
|
+
console.log(`\n${c.yellow}${key}:${c.reset}`);
|
|
414
|
+
for (const bug of bugs) {
|
|
415
|
+
console.log(` • ${bug.command}: ${bug.error}`);
|
|
416
|
+
if (bug.fullCommand) {
|
|
417
|
+
console.log(` ${c.dim}${bug.fullCommand}${c.reset}`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Save to file
|
|
423
|
+
const bugsFile = path.join(__dirname, 'public-repos-bugs.json');
|
|
424
|
+
fs.writeFileSync(bugsFile, JSON.stringify(results.bugs, null, 2));
|
|
425
|
+
console.log(`\n${c.dim}Bug report saved to: ${bugsFile}${c.reset}`);
|
|
426
|
+
} else {
|
|
427
|
+
console.log(`\n${c.green}No bugs found!${c.reset}`);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
console.log(`${c.cyan}${'='.repeat(60)}${c.reset}`);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Cleanup
|
|
435
|
+
*/
|
|
436
|
+
function cleanup() {
|
|
437
|
+
if (!keepRepos && fs.existsSync(TEMP_DIR)) {
|
|
438
|
+
console.log(`\n${c.dim}Cleaning up temp directory...${c.reset}`);
|
|
439
|
+
fs.rmSync(TEMP_DIR, { recursive: true, force: true });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Main
|
|
445
|
+
*/
|
|
446
|
+
async function main() {
|
|
447
|
+
console.log(`${c.cyan}UCN Public Repository Test Suite${c.reset}`);
|
|
448
|
+
console.log(`${c.dim}Testing against real-world repositories${c.reset}`);
|
|
449
|
+
|
|
450
|
+
// Create temp directory
|
|
451
|
+
if (!fs.existsSync(TEMP_DIR)) {
|
|
452
|
+
fs.mkdirSync(TEMP_DIR, { recursive: true });
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Test each language
|
|
456
|
+
for (const [lang, repoList] of Object.entries(repos)) {
|
|
457
|
+
for (const repo of repoList) {
|
|
458
|
+
testRepo(lang, repo);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Run stress tests
|
|
463
|
+
runStressTests();
|
|
464
|
+
|
|
465
|
+
// Print summary
|
|
466
|
+
printSummary();
|
|
467
|
+
|
|
468
|
+
// Cleanup
|
|
469
|
+
cleanup();
|
|
470
|
+
|
|
471
|
+
process.exit(results.failed > 0 ? 1 : 0);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
main().catch(e => {
|
|
475
|
+
console.error(`${c.red}Fatal error: ${e.message}${c.reset}`);
|
|
476
|
+
process.exit(1);
|
|
477
|
+
});
|