@dependabit/detector 0.1.16 → 0.1.18
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.
- package/CHANGELOG.md +20 -0
- package/dist/detector.d.ts +179 -12
- package/dist/detector.d.ts.map +1 -1
- package/dist/detector.js +121 -12
- package/dist/detector.js.map +1 -1
- package/dist/diff-parser.js.map +1 -1
- package/dist/llm/client.d.ts +112 -3
- package/dist/llm/client.d.ts.map +1 -1
- package/dist/llm/client.js +34 -2
- package/dist/llm/client.js.map +1 -1
- package/dist/llm/copilot.d.ts.map +1 -1
- package/dist/llm/copilot.js +1 -7
- package/dist/llm/copilot.js.map +1 -1
- package/dist/llm/prompts.d.ts +42 -2
- package/dist/llm/prompts.d.ts.map +1 -1
- package/dist/llm/prompts.js +42 -2
- package/dist/llm/prompts.js.map +1 -1
- package/dist/parsers/code-comments.js.map +1 -1
- package/dist/parsers/package-files.js.map +1 -1
- package/dist/parsers/readme.d.ts +1 -1
- package/dist/parsers/readme.d.ts.map +1 -1
- package/dist/parsers/readme.js +1 -1
- package/dist/parsers/readme.js.map +1 -1
- package/package.json +28 -11
- package/src/detector.ts +0 -1043
- package/src/diff-parser.ts +0 -257
- package/src/index.ts +0 -43
- package/src/llm/client.ts +0 -85
- package/src/llm/copilot.ts +0 -150
- package/src/llm/prompts.ts +0 -111
- package/src/parsers/code-comments.ts +0 -178
- package/src/parsers/package-files.ts +0 -156
- package/src/parsers/readme.ts +0 -191
- package/test/detector.test.ts +0 -169
- package/test/diff-parser.test.ts +0 -187
- package/test/llm/client.test.ts +0 -31
- package/test/llm/copilot.test.ts +0 -334
- package/test/parsers/code-comments.test.ts +0 -98
- package/test/parsers/package-files.test.ts +0 -52
- package/test/parsers/readme.test.ts +0 -52
- package/tsconfig.json +0 -10
- package/tsconfig.tsbuildinfo +0 -1
package/test/diff-parser.test.ts
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
parseDiff,
|
|
4
|
-
extractAddedContent,
|
|
5
|
-
extractRemovedContent,
|
|
6
|
-
getChangedFiles
|
|
7
|
-
} from '../src/diff-parser.js';
|
|
8
|
-
|
|
9
|
-
describe('Diff Parser Tests', () => {
|
|
10
|
-
describe('parseDiff', () => {
|
|
11
|
-
it('should parse unified diff format', () => {
|
|
12
|
-
const diff = `@@ -1,3 +1,4 @@
|
|
13
|
-
Line 1
|
|
14
|
-
-Old line
|
|
15
|
-
+New line
|
|
16
|
-
+Another new line
|
|
17
|
-
Line 3`;
|
|
18
|
-
|
|
19
|
-
const result = parseDiff(diff);
|
|
20
|
-
|
|
21
|
-
expect(result.additions).toContain('New line');
|
|
22
|
-
expect(result.additions).toContain('Another new line');
|
|
23
|
-
expect(result.deletions).toContain('Old line');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should handle diff with only additions', () => {
|
|
27
|
-
const diff = `@@ -0,0 +1,3 @@
|
|
28
|
-
+First line
|
|
29
|
-
+Second line
|
|
30
|
-
+Third line`;
|
|
31
|
-
|
|
32
|
-
const result = parseDiff(diff);
|
|
33
|
-
|
|
34
|
-
expect(result.additions).toHaveLength(3);
|
|
35
|
-
expect(result.deletions).toHaveLength(0);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should handle diff with only deletions', () => {
|
|
39
|
-
const diff = `@@ -1,3 +0,0 @@
|
|
40
|
-
-First line
|
|
41
|
-
-Second line
|
|
42
|
-
-Third line`;
|
|
43
|
-
|
|
44
|
-
const result = parseDiff(diff);
|
|
45
|
-
|
|
46
|
-
expect(result.additions).toHaveLength(0);
|
|
47
|
-
expect(result.deletions).toHaveLength(3);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should ignore context lines (no +/-)', () => {
|
|
51
|
-
const diff = `@@ -1,5 +1,5 @@
|
|
52
|
-
Context line 1
|
|
53
|
-
-Old line
|
|
54
|
-
+New line
|
|
55
|
-
Context line 2
|
|
56
|
-
Context line 3`;
|
|
57
|
-
|
|
58
|
-
const result = parseDiff(diff);
|
|
59
|
-
|
|
60
|
-
expect(result.additions).toHaveLength(1);
|
|
61
|
-
expect(result.deletions).toHaveLength(1);
|
|
62
|
-
expect(result.additions[0]).toBe('New line');
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('extractAddedContent', () => {
|
|
67
|
-
it('should extract URLs from added lines', () => {
|
|
68
|
-
const additions = [
|
|
69
|
-
'Check out https://example.com/api for details',
|
|
70
|
-
'See documentation at https://docs.example.com',
|
|
71
|
-
'Regular line without URL'
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
const result = extractAddedContent(additions);
|
|
75
|
-
|
|
76
|
-
expect(result.urls).toContain('https://example.com/api');
|
|
77
|
-
expect(result.urls).toContain('https://docs.example.com');
|
|
78
|
-
expect(result.urls).toHaveLength(2);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should extract dependencies from package.json additions', () => {
|
|
82
|
-
const additions = [
|
|
83
|
-
' "dependencies": {',
|
|
84
|
-
' "axios": "^1.0.0",',
|
|
85
|
-
' "lodash": "^4.17.21"',
|
|
86
|
-
' }'
|
|
87
|
-
];
|
|
88
|
-
|
|
89
|
-
const result = extractAddedContent(additions, 'package.json');
|
|
90
|
-
|
|
91
|
-
expect(result.packageDeps).toContain('axios');
|
|
92
|
-
expect(result.packageDeps).toContain('lodash');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should extract references from code comments', () => {
|
|
96
|
-
const additions = [
|
|
97
|
-
'// Based on https://github.com/example/repo',
|
|
98
|
-
'/* Reference: https://arxiv.org/abs/2301.12345 */',
|
|
99
|
-
'const x = 5; // Regular comment'
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
const result = extractAddedContent(additions);
|
|
103
|
-
|
|
104
|
-
expect(result.urls).toContain('https://github.com/example/repo');
|
|
105
|
-
expect(result.urls).toContain('https://arxiv.org/abs/2301.12345');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should handle empty additions', () => {
|
|
109
|
-
const result = extractAddedContent([]);
|
|
110
|
-
|
|
111
|
-
expect(result.urls).toHaveLength(0);
|
|
112
|
-
expect(result.packageDeps).toHaveLength(0);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe('extractRemovedContent', () => {
|
|
117
|
-
it('should extract URLs from removed lines', () => {
|
|
118
|
-
const deletions = [
|
|
119
|
-
'Old link: https://old-example.com',
|
|
120
|
-
'Deprecated: https://deprecated.example.com/api'
|
|
121
|
-
];
|
|
122
|
-
|
|
123
|
-
const result = extractRemovedContent(deletions);
|
|
124
|
-
|
|
125
|
-
expect(result.urls).toContain('https://old-example.com');
|
|
126
|
-
expect(result.urls).toContain('https://deprecated.example.com/api');
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('should extract dependencies from package.json deletions', () => {
|
|
130
|
-
const deletions = [' "old-package": "^1.0.0",', ' "deprecated-lib": "^2.0.0"'];
|
|
131
|
-
|
|
132
|
-
const result = extractRemovedContent(deletions, 'package.json');
|
|
133
|
-
|
|
134
|
-
expect(result.packageDeps).toContain('old-package');
|
|
135
|
-
expect(result.packageDeps).toContain('deprecated-lib');
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
describe('getChangedFiles', () => {
|
|
140
|
-
it('should identify files relevant for dependency analysis', () => {
|
|
141
|
-
const files = [
|
|
142
|
-
{ filename: 'README.md', status: 'modified' as const },
|
|
143
|
-
{ filename: 'src/index.ts', status: 'modified' as const },
|
|
144
|
-
{ filename: 'package.json', status: 'modified' as const },
|
|
145
|
-
{ filename: 'docs/guide.md', status: 'added' as const },
|
|
146
|
-
{ filename: 'test/unit.test.ts', status: 'modified' as const }
|
|
147
|
-
];
|
|
148
|
-
|
|
149
|
-
const result = getChangedFiles(files);
|
|
150
|
-
|
|
151
|
-
expect(result.relevantFiles).toContain('README.md');
|
|
152
|
-
expect(result.relevantFiles).toContain('package.json');
|
|
153
|
-
expect(result.relevantFiles).toContain('docs/guide.md');
|
|
154
|
-
expect(result.packageFiles).toContain('package.json');
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('should exclude irrelevant file types', () => {
|
|
158
|
-
const files = [
|
|
159
|
-
{ filename: 'image.png', status: 'added' as const },
|
|
160
|
-
{ filename: 'video.mp4', status: 'added' as const },
|
|
161
|
-
{ filename: 'README.md', status: 'modified' as const }
|
|
162
|
-
];
|
|
163
|
-
|
|
164
|
-
const result = getChangedFiles(files);
|
|
165
|
-
|
|
166
|
-
expect(result.relevantFiles).not.toContain('image.png');
|
|
167
|
-
expect(result.relevantFiles).not.toContain('video.mp4');
|
|
168
|
-
expect(result.relevantFiles).toContain('README.md');
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('should identify package manifest files', () => {
|
|
172
|
-
const files = [
|
|
173
|
-
{ filename: 'package.json', status: 'modified' as const },
|
|
174
|
-
{ filename: 'requirements.txt', status: 'modified' as const },
|
|
175
|
-
{ filename: 'Cargo.toml', status: 'modified' as const },
|
|
176
|
-
{ filename: 'go.mod', status: 'modified' as const }
|
|
177
|
-
];
|
|
178
|
-
|
|
179
|
-
const result = getChangedFiles(files);
|
|
180
|
-
|
|
181
|
-
expect(result.packageFiles).toContain('package.json');
|
|
182
|
-
expect(result.packageFiles).toContain('requirements.txt');
|
|
183
|
-
expect(result.packageFiles).toContain('Cargo.toml');
|
|
184
|
-
expect(result.packageFiles).toContain('go.mod');
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
});
|
package/test/llm/client.test.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
|
|
3
|
-
describe('LLMProvider interface', () => {
|
|
4
|
-
it('should define analyze method signature', () => {
|
|
5
|
-
// This is a type test - we'll verify the interface exists
|
|
6
|
-
// The interface will be implemented in src/llm/client.ts
|
|
7
|
-
expect(true).toBe(true);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it('should define getSupportedModels method', () => {
|
|
11
|
-
expect(true).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('should define getRateLimit method', () => {
|
|
15
|
-
expect(true).toBe(true);
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
describe('LLMResponse', () => {
|
|
20
|
-
it('should include detected dependencies', () => {
|
|
21
|
-
expect(true).toBe(true);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('should include confidence scores', () => {
|
|
25
|
-
expect(true).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should include usage metadata (tokens, latency)', () => {
|
|
29
|
-
expect(true).toBe(true);
|
|
30
|
-
});
|
|
31
|
-
});
|
package/test/llm/copilot.test.ts
DELETED
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { promisify } from 'node:util';
|
|
3
|
-
import { GitHubCopilotProvider } from '../../src/llm/copilot.js';
|
|
4
|
-
import type { DetectedDependency } from '../../src/llm/client.js';
|
|
5
|
-
|
|
6
|
-
// Mock child_process with proper execFile behavior
|
|
7
|
-
vi.mock('node:child_process', () => {
|
|
8
|
-
const mockCallback = vi.fn();
|
|
9
|
-
|
|
10
|
-
// The custom promisify implementation that mimics Node.js behavior
|
|
11
|
-
const customPromisify = vi.fn((...execArgs) => {
|
|
12
|
-
return new Promise((resolve, reject) => {
|
|
13
|
-
// Call the mock with a callback that converts to { stdout, stderr }
|
|
14
|
-
mockCallback(...execArgs, (err: Error | null, stdout: string, stderr: string) => {
|
|
15
|
-
if (err) {
|
|
16
|
-
const error = err as NodeJS.ErrnoException & { stdout?: string; stderr?: string };
|
|
17
|
-
error.stdout = stdout;
|
|
18
|
-
error.stderr = stderr;
|
|
19
|
-
reject(error);
|
|
20
|
-
} else {
|
|
21
|
-
resolve({ stdout, stderr });
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
mockCallback[promisify.custom] = customPromisify;
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
execFile: mockCallback
|
|
31
|
-
};
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
import { execFile } from 'node:child_process';
|
|
35
|
-
const mockExecFileCallback = vi.mocked(execFile);
|
|
36
|
-
|
|
37
|
-
describe('GitHubCopilotProvider', () => {
|
|
38
|
-
let provider: GitHubCopilotProvider;
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
vi.clearAllMocks();
|
|
42
|
-
provider = new GitHubCopilotProvider();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
afterEach(() => {
|
|
46
|
-
vi.restoreAllMocks();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe('CLI invocation', () => {
|
|
50
|
-
it('should use execFile with correct command and flags', async () => {
|
|
51
|
-
const mockResponse = JSON.stringify({ dependencies: [] });
|
|
52
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
53
|
-
if (typeof callback === 'function') {
|
|
54
|
-
// Use correct callback signature: callback(error, stdout, stderr)
|
|
55
|
-
callback(null, mockResponse, '');
|
|
56
|
-
}
|
|
57
|
-
return {} as any;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
await provider.analyze('test content', 'test prompt');
|
|
61
|
-
|
|
62
|
-
expect(mockExecFileCallback).toHaveBeenCalledWith(
|
|
63
|
-
'gh',
|
|
64
|
-
expect.arrayContaining([
|
|
65
|
-
'copilot',
|
|
66
|
-
'-p',
|
|
67
|
-
expect.any(String),
|
|
68
|
-
'--silent',
|
|
69
|
-
'--allow-all-tools'
|
|
70
|
-
]),
|
|
71
|
-
expect.objectContaining({
|
|
72
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
73
|
-
timeout: 60000
|
|
74
|
-
}),
|
|
75
|
-
expect.any(Function)
|
|
76
|
-
);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should pass prompt directly without shell escaping', async () => {
|
|
80
|
-
const mockResponse = JSON.stringify({ dependencies: [] });
|
|
81
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
82
|
-
if (typeof callback === 'function') {
|
|
83
|
-
callback(null, mockResponse, '');
|
|
84
|
-
}
|
|
85
|
-
return {} as any;
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const promptWithSpecialChars = 'test "quotes" $dollar `backtick`';
|
|
89
|
-
await provider.analyze('content', promptWithSpecialChars);
|
|
90
|
-
|
|
91
|
-
const callArgs = mockExecFileCallback.mock.calls[0];
|
|
92
|
-
const args = callArgs?.[1] as string[];
|
|
93
|
-
const fullPrompt = args?.[2];
|
|
94
|
-
|
|
95
|
-
// Verify prompt contains special characters unescaped
|
|
96
|
-
expect(fullPrompt).toContain('test "quotes" $dollar `backtick`');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should handle prompts with newlines', async () => {
|
|
100
|
-
const mockResponse = JSON.stringify({ dependencies: [] });
|
|
101
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
102
|
-
if (typeof callback === 'function') {
|
|
103
|
-
callback(null, mockResponse, '');
|
|
104
|
-
}
|
|
105
|
-
return {} as any;
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
const promptWithNewlines = 'line1\nline2\nline3';
|
|
109
|
-
await provider.analyze('content', promptWithNewlines);
|
|
110
|
-
|
|
111
|
-
const callArgs = mockExecFileCallback.mock.calls[0];
|
|
112
|
-
const args = callArgs?.[1] as string[];
|
|
113
|
-
const fullPrompt = args?.[2];
|
|
114
|
-
|
|
115
|
-
expect(fullPrompt).toContain('line1\nline2\nline3');
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('should handle prompts ending with backslash', async () => {
|
|
119
|
-
const mockResponse = JSON.stringify({ dependencies: [] });
|
|
120
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
121
|
-
if (typeof callback === 'function') {
|
|
122
|
-
callback(null, mockResponse, '');
|
|
123
|
-
}
|
|
124
|
-
return {} as any;
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const promptWithBackslash = 'path\\to\\file\\';
|
|
128
|
-
await provider.analyze('content', promptWithBackslash);
|
|
129
|
-
|
|
130
|
-
const callArgs = mockExecFileCallback.mock.calls[0];
|
|
131
|
-
const args = callArgs?.[1] as string[];
|
|
132
|
-
const fullPrompt = args?.[2];
|
|
133
|
-
|
|
134
|
-
expect(fullPrompt).toContain('path\\to\\file\\');
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
describe('response parsing', () => {
|
|
139
|
-
it('should parse JSON response correctly', async () => {
|
|
140
|
-
const mockDeps: DetectedDependency[] = [
|
|
141
|
-
{
|
|
142
|
-
name: 'test-package',
|
|
143
|
-
version: '1.0.0',
|
|
144
|
-
type: 'npm',
|
|
145
|
-
confidence: 0.9,
|
|
146
|
-
context: 'test context'
|
|
147
|
-
}
|
|
148
|
-
];
|
|
149
|
-
const mockResponse = JSON.stringify({ dependencies: mockDeps });
|
|
150
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
151
|
-
if (typeof callback === 'function') {
|
|
152
|
-
callback(null, mockResponse, '');
|
|
153
|
-
}
|
|
154
|
-
return {} as any;
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const result = await provider.analyze('content', 'prompt');
|
|
158
|
-
|
|
159
|
-
expect(result.dependencies).toEqual(mockDeps);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should parse JSON wrapped in markdown code blocks', async () => {
|
|
163
|
-
const mockDeps: DetectedDependency[] = [
|
|
164
|
-
{
|
|
165
|
-
name: 'test-package',
|
|
166
|
-
version: '1.0.0',
|
|
167
|
-
type: 'npm',
|
|
168
|
-
confidence: 0.9,
|
|
169
|
-
context: 'test context'
|
|
170
|
-
}
|
|
171
|
-
];
|
|
172
|
-
const mockResponse = '```json\n' + JSON.stringify({ dependencies: mockDeps }) + '\n```';
|
|
173
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
174
|
-
if (typeof callback === 'function') {
|
|
175
|
-
callback(null, mockResponse, '');
|
|
176
|
-
}
|
|
177
|
-
return {} as any;
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const result = await provider.analyze('content', 'prompt');
|
|
181
|
-
|
|
182
|
-
expect(result.dependencies).toEqual(mockDeps);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('should handle malformed JSON gracefully', async () => {
|
|
186
|
-
const mockResponse = 'not valid json';
|
|
187
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
188
|
-
if (typeof callback === 'function') {
|
|
189
|
-
callback(null, mockResponse, '');
|
|
190
|
-
}
|
|
191
|
-
return {} as any;
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
const result = await provider.analyze('content', 'prompt');
|
|
195
|
-
|
|
196
|
-
expect(result.dependencies).toEqual([]);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should return empty dependencies on empty response', async () => {
|
|
200
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
201
|
-
if (typeof callback === 'function') {
|
|
202
|
-
callback(null, '', '');
|
|
203
|
-
}
|
|
204
|
-
return {} as any;
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
const result = await provider.analyze('content', 'prompt');
|
|
208
|
-
|
|
209
|
-
expect(result.dependencies).toEqual([]);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
describe('error handling', () => {
|
|
214
|
-
it('should handle CLI execution errors', async () => {
|
|
215
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
216
|
-
if (typeof callback === 'function') {
|
|
217
|
-
callback(new Error('CLI error'), '', 'Error message');
|
|
218
|
-
}
|
|
219
|
-
return {} as any;
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
const result = await provider.analyze('content', 'prompt');
|
|
223
|
-
|
|
224
|
-
expect(result.dependencies).toEqual([]);
|
|
225
|
-
expect(result.rawResponse).toContain('CLI error');
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('should handle stderr without stdout as error', async () => {
|
|
229
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
230
|
-
if (typeof callback === 'function') {
|
|
231
|
-
callback(null, '', 'Authentication failed');
|
|
232
|
-
}
|
|
233
|
-
return {} as any;
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
const result = await provider.analyze('content', 'prompt');
|
|
237
|
-
|
|
238
|
-
expect(result.dependencies).toEqual([]);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('should handle timeout errors', async () => {
|
|
242
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
243
|
-
if (typeof callback === 'function') {
|
|
244
|
-
const error = new Error('Command timeout') as NodeJS.ErrnoException;
|
|
245
|
-
error.code = 'ETIMEDOUT';
|
|
246
|
-
callback(error, '', '');
|
|
247
|
-
}
|
|
248
|
-
return {} as any;
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
const result = await provider.analyze('content', 'prompt');
|
|
252
|
-
|
|
253
|
-
expect(result.dependencies).toEqual([]);
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
it('should handle buffer overflow errors', async () => {
|
|
257
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
258
|
-
if (typeof callback === 'function') {
|
|
259
|
-
const error = new Error('maxBuffer exceeded') as NodeJS.ErrnoException;
|
|
260
|
-
error.code = 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER';
|
|
261
|
-
callback(error, '', '');
|
|
262
|
-
}
|
|
263
|
-
return {} as any;
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
const result = await provider.analyze('content', 'prompt');
|
|
267
|
-
|
|
268
|
-
expect(result.dependencies).toEqual([]);
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
describe('usage metadata', () => {
|
|
273
|
-
it('should include usage metadata in response', async () => {
|
|
274
|
-
const mockResponse = JSON.stringify({ dependencies: [] });
|
|
275
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
276
|
-
if (typeof callback === 'function') {
|
|
277
|
-
callback(null, mockResponse, '');
|
|
278
|
-
}
|
|
279
|
-
return {} as any;
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
const result = await provider.analyze('content', 'prompt');
|
|
283
|
-
|
|
284
|
-
expect(result.usage).toBeDefined();
|
|
285
|
-
expect(result.usage.promptTokens).toBeGreaterThan(0);
|
|
286
|
-
expect(result.usage.completionTokens).toBeGreaterThan(0);
|
|
287
|
-
expect(result.usage.totalTokens).toBeGreaterThan(0);
|
|
288
|
-
expect(result.usage.latencyMs).toBeGreaterThanOrEqual(0);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
it('should use configured model in usage metadata', async () => {
|
|
292
|
-
const mockResponse = JSON.stringify({ dependencies: [] });
|
|
293
|
-
mockExecFileCallback.mockImplementation((file, args, options, callback) => {
|
|
294
|
-
if (typeof callback === 'function') {
|
|
295
|
-
callback(null, mockResponse, '');
|
|
296
|
-
}
|
|
297
|
-
return {} as any;
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
const customProvider = new GitHubCopilotProvider({ model: 'gpt-4-turbo' });
|
|
301
|
-
const result = await customProvider.analyze('content', 'prompt');
|
|
302
|
-
|
|
303
|
-
expect(result.usage.model).toBe('gpt-4-turbo');
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
describe('getSupportedModels', () => {
|
|
308
|
-
it('should return list of supported models', () => {
|
|
309
|
-
const models = provider.getSupportedModels();
|
|
310
|
-
|
|
311
|
-
expect(models).toBeInstanceOf(Array);
|
|
312
|
-
expect(models.length).toBeGreaterThan(0);
|
|
313
|
-
expect(models).toContain('github-copilot');
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
describe('getRateLimit', () => {
|
|
318
|
-
it('should return rate limit info', async () => {
|
|
319
|
-
const rateLimit = await provider.getRateLimit();
|
|
320
|
-
|
|
321
|
-
expect(rateLimit).toBeDefined();
|
|
322
|
-
expect(rateLimit.remaining).toBe(-1);
|
|
323
|
-
expect(rateLimit.limit).toBe(-1);
|
|
324
|
-
});
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
describe('validateConfig', () => {
|
|
328
|
-
it('should validate configuration', () => {
|
|
329
|
-
const isValid = provider.validateConfig();
|
|
330
|
-
|
|
331
|
-
expect(typeof isValid).toBe('boolean');
|
|
332
|
-
});
|
|
333
|
-
});
|
|
334
|
-
});
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
|
|
3
|
-
describe('CodeCommentParser', () => {
|
|
4
|
-
describe('parse', () => {
|
|
5
|
-
it('should extract URLs from single-line comments', () => {
|
|
6
|
-
expect(true).toBe(true);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it('should extract URLs from multi-line comments', () => {
|
|
10
|
-
expect(true).toBe(true);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('should extract JSDoc @see tags', () => {
|
|
14
|
-
expect(true).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('should include file path and line number', () => {
|
|
18
|
-
// Expected: Include { file: 'src/foo.ts', line: 5 }
|
|
19
|
-
expect(true).toBe(true);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should handle TypeScript files', () => {
|
|
23
|
-
expect(true).toBe(true);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should handle JavaScript files', () => {
|
|
27
|
-
expect(true).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should handle Python files', () => {
|
|
31
|
-
expect(true).toBe(true);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should skip commented-out code', () => {
|
|
35
|
-
expect(true).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe('extractDocReferences', () => {
|
|
40
|
-
it('should identify specification references', () => {
|
|
41
|
-
expect(true).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should identify API endpoint references', () => {
|
|
45
|
-
const code = '// API endpoint: https://example.com/api';
|
|
46
|
-
expect(true).toBe(true);
|
|
47
|
-
expect(code).toContain('https://example.com/api');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should extract URLs from multi-line comments', () => {
|
|
51
|
-
const code = '/* Reference: https://example.com */';
|
|
52
|
-
expect(code).toContain('https://example.com');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should extract JSDoc @see tags', () => {
|
|
56
|
-
const code = '/** @see https://example.com/docs */';
|
|
57
|
-
expect(code).toContain('https://example.com/docs');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should include file path and line number', () => {
|
|
61
|
-
const code = '// Based on https://spec.example.com';
|
|
62
|
-
// Expected: Include { file: 'src/foo.ts', line: 5 }
|
|
63
|
-
expect(code).toContain('https://spec.example.com');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should handle TypeScript files', () => {
|
|
67
|
-
const code = '// Implementation from https://github.com/example/repo';
|
|
68
|
-
expect(code).toContain('https://github.com/example/repo');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('should handle JavaScript files', () => {
|
|
72
|
-
const code = '/* Spec: https://openapi.example.com/spec.yaml */';
|
|
73
|
-
expect(code).toContain('https://openapi.example.com/spec.yaml');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should handle Python files', () => {
|
|
77
|
-
const code = '# Reference: https://arxiv.org/abs/1234.5678';
|
|
78
|
-
expect(code).toContain('https://arxiv.org/abs/1234.5678');
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should skip commented-out code', () => {
|
|
82
|
-
const code = '// const url = "https://example.com"; // unused';
|
|
83
|
-
expect(code).toContain('https://example.com');
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe('extractDocReferences', () => {
|
|
88
|
-
it('should identify specification references', () => {
|
|
89
|
-
const code = '// Implements RFC 7231 specification';
|
|
90
|
-
expect(code).toContain('RFC 7231');
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should identify API endpoint references', () => {
|
|
94
|
-
const code = '// Calls POST /api/v1/users';
|
|
95
|
-
expect(code).toContain('/api/v1/users');
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
});
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
|
|
3
|
-
describe('PackageFileParser', () => {
|
|
4
|
-
describe('parsePackageJson', () => {
|
|
5
|
-
it('should extract repository URLs', () => {
|
|
6
|
-
expect(true).toBe(true);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it('should extract homepage URL', () => {
|
|
10
|
-
expect(true).toBe(true);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('should extract documentation URL', () => {
|
|
14
|
-
expect(true).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('should mark dependencies as tracked by dependabot', () => {
|
|
18
|
-
// Expected: Return empty (dependabot handles this)
|
|
19
|
-
expect(true).toBe(true);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should extract URLs from package description', () => {
|
|
23
|
-
expect(true).toBe(true);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('parseRequirementsTxt', () => {
|
|
28
|
-
it('should extract GitHub repository URLs from comments', () => {
|
|
29
|
-
expect(true).toBe(true);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should skip PyPI packages', () => {
|
|
33
|
-
// Expected: Return empty (dependabot handles this)
|
|
34
|
-
expect(true).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should extract documentation URLs from comments', () => {
|
|
38
|
-
expect(true).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('parseCargoToml', () => {
|
|
43
|
-
it('should extract repository URLs', () => {
|
|
44
|
-
expect(true).toBe(true);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should skip crates.io dependencies', () => {
|
|
48
|
-
// Expected: Return empty (dependabot handles this)
|
|
49
|
-
expect(true).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
});
|