@dependabit/github-client 0.1.13 → 0.1.15
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 +12 -0
- package/dist/auth/basic.js.map +1 -1
- package/dist/auth/oauth.js.map +1 -1
- package/dist/auth/token.js.map +1 -1
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +61 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +40 -1
- package/dist/client.js.map +1 -1
- package/dist/commits.js.map +1 -1
- package/dist/feedback.js.map +1 -1
- package/dist/issues.js.map +1 -1
- package/dist/rate-limit.d.ts +75 -2
- package/dist/rate-limit.d.ts.map +1 -1
- package/dist/rate-limit.js +50 -2
- package/dist/rate-limit.js.map +1 -1
- package/dist/releases.js.map +1 -1
- package/package.json +26 -9
- package/src/auth/basic.ts +0 -102
- package/src/auth/oauth.ts +0 -183
- package/src/auth/token.ts +0 -81
- package/src/auth.ts +0 -100
- package/src/client.test.ts +0 -115
- package/src/client.ts +0 -109
- package/src/commits.ts +0 -184
- package/src/feedback.ts +0 -166
- package/src/index.ts +0 -15
- package/src/issues.ts +0 -185
- package/src/rate-limit.ts +0 -210
- package/src/releases.ts +0 -149
- package/test/auth/basic.test.ts +0 -122
- package/test/auth/oauth.test.ts +0 -196
- package/test/auth/token.test.ts +0 -97
- package/test/commits.test.ts +0 -169
- package/test/feedback.test.ts +0 -203
- package/test/issues.test.ts +0 -197
- package/test/rate-limit.test.ts +0 -154
- package/test/releases.test.ts +0 -187
- package/tsconfig.json +0 -10
- package/tsconfig.tsbuildinfo +0 -1
package/test/auth/token.test.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { TokenAuthHandler } from '../../src/auth/token';
|
|
3
|
-
|
|
4
|
-
describe('TokenAuthHandler', () => {
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
vi.clearAllMocks();
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
describe('constructor', () => {
|
|
10
|
-
it('should create handler with GitHub PAT token', () => {
|
|
11
|
-
const token = 'ghp_testtoken123';
|
|
12
|
-
const handler = new TokenAuthHandler(token);
|
|
13
|
-
expect(handler).toBeInstanceOf(TokenAuthHandler);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('should create handler with API key', () => {
|
|
17
|
-
const token = 'api_key_12345';
|
|
18
|
-
const handler = new TokenAuthHandler(token);
|
|
19
|
-
expect(handler).toBeInstanceOf(TokenAuthHandler);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should throw error for empty token', () => {
|
|
23
|
-
expect(() => new TokenAuthHandler('')).toThrow('Token cannot be empty');
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('authenticate', () => {
|
|
28
|
-
it('should return auth object with token', async () => {
|
|
29
|
-
const token = 'ghp_testtoken123';
|
|
30
|
-
const handler = new TokenAuthHandler(token);
|
|
31
|
-
const auth = await handler.authenticate();
|
|
32
|
-
|
|
33
|
-
expect(auth).toEqual({
|
|
34
|
-
type: 'token',
|
|
35
|
-
token: token
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should validate token format for GitHub PAT', async () => {
|
|
40
|
-
const token = 'ghp_validtoken123456';
|
|
41
|
-
const handler = new TokenAuthHandler(token);
|
|
42
|
-
const auth = await handler.authenticate();
|
|
43
|
-
|
|
44
|
-
expect(auth.token).toBe(token);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should accept fine-grained tokens', async () => {
|
|
48
|
-
const token = 'github_pat_validtoken123456';
|
|
49
|
-
const handler = new TokenAuthHandler(token);
|
|
50
|
-
const auth = await handler.authenticate();
|
|
51
|
-
|
|
52
|
-
expect(auth.token).toBe(token);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('validate', () => {
|
|
57
|
-
it('should validate token format', () => {
|
|
58
|
-
const handler = new TokenAuthHandler('ghp_test123');
|
|
59
|
-
expect(handler.validate()).toBe(true);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should accept any non-empty token', () => {
|
|
63
|
-
// Token validation is lenient - allows API keys without GitHub prefix
|
|
64
|
-
const handler = new TokenAuthHandler('api_key_123');
|
|
65
|
-
expect(handler.validate()).toBe(true);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should accept various GitHub token prefixes', () => {
|
|
69
|
-
const validPrefixes = ['ghp_', 'gho_', 'ghu_', 'ghs_', 'ghr_', 'github_pat_'];
|
|
70
|
-
|
|
71
|
-
validPrefixes.forEach((prefix) => {
|
|
72
|
-
const handler = new TokenAuthHandler(`${prefix}test123`);
|
|
73
|
-
expect(handler.validate()).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe('getType', () => {
|
|
79
|
-
it('should return token type', () => {
|
|
80
|
-
const handler = new TokenAuthHandler('ghp_test');
|
|
81
|
-
expect(handler.getType()).toBe('token');
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe('token rotation', () => {
|
|
86
|
-
it('should allow token update', () => {
|
|
87
|
-
const handler = new TokenAuthHandler('ghp_old');
|
|
88
|
-
handler.updateToken('ghp_new');
|
|
89
|
-
expect(handler.getToken()).toBe('ghp_new');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('should throw error on empty token update', () => {
|
|
93
|
-
const handler = new TokenAuthHandler('ghp_test');
|
|
94
|
-
expect(() => handler.updateToken('')).toThrow('Token cannot be empty');
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
});
|
package/test/commits.test.ts
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { fetchCommits, getCommitDiff, parseCommitFiles } from '../src/commits.js';
|
|
3
|
-
|
|
4
|
-
// Mock the octokit module
|
|
5
|
-
const mockListCommits = vi.fn();
|
|
6
|
-
const mockGetCommit = vi.fn();
|
|
7
|
-
|
|
8
|
-
vi.mock('octokit', () => {
|
|
9
|
-
class MockOctokit {
|
|
10
|
-
rest = {
|
|
11
|
-
repos: {
|
|
12
|
-
listCommits: mockListCommits,
|
|
13
|
-
getCommit: mockGetCommit
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
Octokit: MockOctokit
|
|
20
|
-
};
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe('Commit Analysis Tests', () => {
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
vi.clearAllMocks();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe('fetchCommits', () => {
|
|
29
|
-
it('should fetch commits from GitHub API', async () => {
|
|
30
|
-
const mockCommits = [
|
|
31
|
-
{
|
|
32
|
-
sha: 'abc123',
|
|
33
|
-
commit: {
|
|
34
|
-
message: 'feat: add new dependency',
|
|
35
|
-
author: { name: 'Test User', date: '2024-01-01T00:00:00Z' }
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
sha: 'def456',
|
|
40
|
-
commit: {
|
|
41
|
-
message: 'fix: update README',
|
|
42
|
-
author: { name: 'Test User', date: '2024-01-01T01:00:00Z' }
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
mockListCommits.mockResolvedValue({
|
|
48
|
-
data: mockCommits
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const client = {
|
|
52
|
-
getOctokit: () => ({ rest: { repos: { listCommits: mockListCommits } } })
|
|
53
|
-
} as any;
|
|
54
|
-
const result = await fetchCommits(client, 'owner', 'repo', { since: '2024-01-01T00:00:00Z' });
|
|
55
|
-
|
|
56
|
-
expect(result).toHaveLength(2);
|
|
57
|
-
expect(result[0].sha).toBe('abc123');
|
|
58
|
-
expect(result[1].sha).toBe('def456');
|
|
59
|
-
expect(mockListCommits).toHaveBeenCalledWith({
|
|
60
|
-
owner: 'owner',
|
|
61
|
-
repo: 'repo',
|
|
62
|
-
since: '2024-01-01T00:00:00Z'
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should handle empty commit list', async () => {
|
|
67
|
-
mockListCommits.mockResolvedValue({ data: [] });
|
|
68
|
-
|
|
69
|
-
const client = {
|
|
70
|
-
getOctokit: () => ({ rest: { repos: { listCommits: mockListCommits } } })
|
|
71
|
-
} as any;
|
|
72
|
-
const result = await fetchCommits(client, 'owner', 'repo');
|
|
73
|
-
|
|
74
|
-
expect(result).toHaveLength(0);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe('getCommitDiff', () => {
|
|
79
|
-
it('should fetch commit diff with file changes', async () => {
|
|
80
|
-
const mockCommitData = {
|
|
81
|
-
sha: 'abc123',
|
|
82
|
-
files: [
|
|
83
|
-
{
|
|
84
|
-
filename: 'README.md',
|
|
85
|
-
status: 'modified',
|
|
86
|
-
additions: 5,
|
|
87
|
-
deletions: 2,
|
|
88
|
-
changes: 7,
|
|
89
|
-
patch: '@@ -1,3 +1,6 @@\n-Old line\n+New line\n+Another line'
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
filename: 'src/index.ts',
|
|
93
|
-
status: 'added',
|
|
94
|
-
additions: 10,
|
|
95
|
-
deletions: 0,
|
|
96
|
-
changes: 10,
|
|
97
|
-
patch: '@@ -0,0 +1,10 @@\n+export function test() {}'
|
|
98
|
-
}
|
|
99
|
-
]
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
mockGetCommit.mockResolvedValue({ data: mockCommitData });
|
|
103
|
-
|
|
104
|
-
const client = {
|
|
105
|
-
getOctokit: () => ({ rest: { repos: { getCommit: mockGetCommit } } })
|
|
106
|
-
} as any;
|
|
107
|
-
const result = await getCommitDiff(client, 'owner', 'repo', 'abc123');
|
|
108
|
-
|
|
109
|
-
expect(result.sha).toBe('abc123');
|
|
110
|
-
expect(result.files).toHaveLength(2);
|
|
111
|
-
expect(result.files[0].filename).toBe('README.md');
|
|
112
|
-
expect(result.files[0].status).toBe('modified');
|
|
113
|
-
expect(result.files[1].filename).toBe('src/index.ts');
|
|
114
|
-
expect(result.files[1].status).toBe('added');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('should handle commit without file changes', async () => {
|
|
118
|
-
mockGetCommit.mockResolvedValue({
|
|
119
|
-
data: { sha: 'abc123', files: [] }
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const client = {
|
|
123
|
-
getOctokit: () => ({ rest: { repos: { getCommit: mockGetCommit } } })
|
|
124
|
-
} as any;
|
|
125
|
-
const result = await getCommitDiff(client, 'owner', 'repo', 'abc123');
|
|
126
|
-
|
|
127
|
-
expect(result.files).toHaveLength(0);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe('parseCommitFiles', () => {
|
|
132
|
-
it('should extract changed files from commit', () => {
|
|
133
|
-
const files = [
|
|
134
|
-
{ filename: 'README.md', status: 'modified' as const, patch: '...' },
|
|
135
|
-
{ filename: 'src/index.ts', status: 'added' as const, patch: '...' },
|
|
136
|
-
{ filename: 'docs/guide.md', status: 'modified' as const, patch: '...' },
|
|
137
|
-
{ filename: 'test.ts', status: 'removed' as const }
|
|
138
|
-
];
|
|
139
|
-
|
|
140
|
-
const result = parseCommitFiles(files);
|
|
141
|
-
|
|
142
|
-
expect(result.modified).toContain('README.md');
|
|
143
|
-
expect(result.modified).toContain('docs/guide.md');
|
|
144
|
-
expect(result.added).toContain('src/index.ts');
|
|
145
|
-
expect(result.removed).toContain('test.ts');
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('should handle empty file list', () => {
|
|
149
|
-
const result = parseCommitFiles([]);
|
|
150
|
-
|
|
151
|
-
expect(result.added).toHaveLength(0);
|
|
152
|
-
expect(result.modified).toHaveLength(0);
|
|
153
|
-
expect(result.removed).toHaveLength(0);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it('should group files by type of change', () => {
|
|
157
|
-
const files = [
|
|
158
|
-
{ filename: 'file1.md', status: 'added' as const, patch: '...' },
|
|
159
|
-
{ filename: 'file2.md', status: 'added' as const, patch: '...' },
|
|
160
|
-
{ filename: 'file3.md', status: 'modified' as const, patch: '...' }
|
|
161
|
-
];
|
|
162
|
-
|
|
163
|
-
const result = parseCommitFiles(files);
|
|
164
|
-
|
|
165
|
-
expect(result.added).toHaveLength(2);
|
|
166
|
-
expect(result.modified).toHaveLength(1);
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
});
|
package/test/feedback.test.ts
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { FeedbackListener } from '../src/feedback.js';
|
|
3
|
-
import type { IssueManagerInterface } from '../src/feedback.js';
|
|
4
|
-
|
|
5
|
-
describe('FeedbackListener', () => {
|
|
6
|
-
let mockIssueManager: IssueManagerInterface;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
vi.clearAllMocks();
|
|
10
|
-
|
|
11
|
-
// Mock IssueManager
|
|
12
|
-
mockIssueManager = {
|
|
13
|
-
listIssues: vi.fn(),
|
|
14
|
-
getIssue: vi.fn()
|
|
15
|
-
} as IssueManagerInterface;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe('constructor', () => {
|
|
19
|
-
it('should create listener with issue manager', () => {
|
|
20
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
21
|
-
expect(listener).toBeInstanceOf(FeedbackListener);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('should accept custom labels', () => {
|
|
25
|
-
const config = {
|
|
26
|
-
truePositiveLabel: 'confirmed',
|
|
27
|
-
falsePositiveLabel: 'not-a-bug'
|
|
28
|
-
};
|
|
29
|
-
const listener = new FeedbackListener(mockIssueManager, config);
|
|
30
|
-
expect(listener).toBeInstanceOf(FeedbackListener);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe('collectFeedback', () => {
|
|
35
|
-
it('should collect issues with feedback labels', async () => {
|
|
36
|
-
const mockIssues = [
|
|
37
|
-
{ number: 1, labels: ['false-positive'], title: 'Test 1', created_at: '2026-01-01' },
|
|
38
|
-
{ number: 2, labels: ['true-positive'], title: 'Test 2', created_at: '2026-01-02' },
|
|
39
|
-
{ number: 3, labels: ['false-positive'], title: 'Test 3', created_at: '2026-01-03' }
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
vi.mocked(mockIssueManager.listIssues).mockResolvedValue(mockIssues as any);
|
|
43
|
-
|
|
44
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
45
|
-
const feedback = await listener.collectFeedback();
|
|
46
|
-
|
|
47
|
-
expect(feedback.truePositives).toHaveLength(1);
|
|
48
|
-
expect(feedback.falsePositives).toHaveLength(2);
|
|
49
|
-
expect(feedback.total).toBe(3);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should filter by date range', async () => {
|
|
53
|
-
const mockIssues = [
|
|
54
|
-
{
|
|
55
|
-
number: 1,
|
|
56
|
-
labels: ['false-positive'],
|
|
57
|
-
title: 'Old issue',
|
|
58
|
-
created_at: '2025-12-01T00:00:00Z'
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
number: 2,
|
|
62
|
-
labels: ['false-positive'],
|
|
63
|
-
title: 'Recent issue',
|
|
64
|
-
created_at: '2026-01-25T00:00:00Z'
|
|
65
|
-
}
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
vi.mocked(mockIssueManager.listIssues).mockResolvedValue(mockIssues as any);
|
|
69
|
-
|
|
70
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
71
|
-
const startDate = new Date('2026-01-01');
|
|
72
|
-
const feedback = await listener.collectFeedback({ startDate });
|
|
73
|
-
|
|
74
|
-
expect(feedback.falsePositives).toHaveLength(1);
|
|
75
|
-
expect(feedback.falsePositives[0].number).toBe(2);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should handle empty results', async () => {
|
|
79
|
-
vi.mocked(mockIssueManager.listIssues).mockResolvedValue([]);
|
|
80
|
-
|
|
81
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
82
|
-
const feedback = await listener.collectFeedback();
|
|
83
|
-
|
|
84
|
-
expect(feedback.truePositives).toHaveLength(0);
|
|
85
|
-
expect(feedback.falsePositives).toHaveLength(0);
|
|
86
|
-
expect(feedback.total).toBe(0);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should filter by repository', async () => {
|
|
90
|
-
const mockIssues = [
|
|
91
|
-
{ number: 1, labels: ['false-positive'], title: 'Test', repository: 'owner/repo1' },
|
|
92
|
-
{ number: 2, labels: ['false-positive'], title: 'Test', repository: 'owner/repo2' }
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
vi.mocked(mockIssueManager.listIssues).mockResolvedValue(mockIssues as any);
|
|
96
|
-
|
|
97
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
98
|
-
const feedback = await listener.collectFeedback({ repository: 'owner/repo1' });
|
|
99
|
-
|
|
100
|
-
expect(feedback.falsePositives).toHaveLength(1);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
describe('getFeedbackRate', () => {
|
|
105
|
-
it('should calculate false positive rate', async () => {
|
|
106
|
-
const mockIssues = [
|
|
107
|
-
{ number: 1, labels: ['false-positive'], title: 'FP1' },
|
|
108
|
-
{ number: 2, labels: ['true-positive'], title: 'TP1' },
|
|
109
|
-
{ number: 3, labels: ['true-positive'], title: 'TP2' },
|
|
110
|
-
{ number: 4, labels: ['false-positive'], title: 'FP2' }
|
|
111
|
-
];
|
|
112
|
-
|
|
113
|
-
vi.mocked(mockIssueManager.listIssues).mockResolvedValue(mockIssues as any);
|
|
114
|
-
|
|
115
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
116
|
-
const rate = await listener.getFeedbackRate();
|
|
117
|
-
|
|
118
|
-
expect(rate.falsePositiveRate).toBe(0.5); // 2 FP out of 4 total
|
|
119
|
-
expect(rate.truePositiveRate).toBe(0.5); // 2 TP out of 4 total
|
|
120
|
-
expect(rate.totalFeedback).toBe(4);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('should return 0 for empty feedback', async () => {
|
|
124
|
-
vi.mocked(mockIssueManager.listIssues).mockResolvedValue([]);
|
|
125
|
-
|
|
126
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
127
|
-
const rate = await listener.getFeedbackRate();
|
|
128
|
-
|
|
129
|
-
expect(rate.falsePositiveRate).toBe(0);
|
|
130
|
-
expect(rate.truePositiveRate).toBe(0);
|
|
131
|
-
expect(rate.totalFeedback).toBe(0);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('getRecentFeedback', () => {
|
|
136
|
-
it('should get feedback from last 30 days', async () => {
|
|
137
|
-
const now = new Date('2026-01-31');
|
|
138
|
-
const mockIssues = [
|
|
139
|
-
{
|
|
140
|
-
number: 1,
|
|
141
|
-
labels: ['false-positive'],
|
|
142
|
-
title: 'Recent',
|
|
143
|
-
created_at: '2026-01-20T00:00:00Z'
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
number: 2,
|
|
147
|
-
labels: ['false-positive'],
|
|
148
|
-
title: 'Old',
|
|
149
|
-
created_at: '2025-12-15T00:00:00Z'
|
|
150
|
-
}
|
|
151
|
-
];
|
|
152
|
-
|
|
153
|
-
vi.mocked(mockIssueManager.listIssues).mockResolvedValue(mockIssues as any);
|
|
154
|
-
|
|
155
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
156
|
-
const feedback = await listener.getRecentFeedback(30, now);
|
|
157
|
-
|
|
158
|
-
expect(feedback.falsePositives).toHaveLength(1);
|
|
159
|
-
expect(feedback.falsePositives[0].number).toBe(1);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should default to current date', async () => {
|
|
163
|
-
vi.mocked(mockIssueManager.listIssues).mockResolvedValue([]);
|
|
164
|
-
|
|
165
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
166
|
-
const feedback = await listener.getRecentFeedback(30);
|
|
167
|
-
|
|
168
|
-
expect(feedback).toBeDefined();
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe('monitorIssue', () => {
|
|
173
|
-
it('should check if issue has feedback label', async () => {
|
|
174
|
-
const issue = {
|
|
175
|
-
number: 1,
|
|
176
|
-
labels: ['false-positive'],
|
|
177
|
-
title: 'Test'
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
vi.mocked(mockIssueManager.getIssue).mockResolvedValue(issue as any);
|
|
181
|
-
|
|
182
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
183
|
-
const hasFeedback = await listener.monitorIssue(1);
|
|
184
|
-
|
|
185
|
-
expect(hasFeedback).toBe(true);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should return false for issues without feedback labels', async () => {
|
|
189
|
-
const issue = {
|
|
190
|
-
number: 1,
|
|
191
|
-
labels: ['bug', 'enhancement'],
|
|
192
|
-
title: 'Test'
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
vi.mocked(mockIssueManager.getIssue).mockResolvedValue(issue as any);
|
|
196
|
-
|
|
197
|
-
const listener = new FeedbackListener(mockIssueManager);
|
|
198
|
-
const hasFeedback = await listener.monitorIssue(1);
|
|
199
|
-
|
|
200
|
-
expect(hasFeedback).toBe(false);
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
});
|
package/test/issues.test.ts
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { IssueManager } from '../src/issues.js';
|
|
3
|
-
|
|
4
|
-
// Mock the octokit module
|
|
5
|
-
vi.mock('octokit', () => {
|
|
6
|
-
const mockIssuesCreate = vi.fn().mockResolvedValue({
|
|
7
|
-
data: {
|
|
8
|
-
number: 123,
|
|
9
|
-
html_url: 'https://github.com/test-owner/test-repo/issues/123',
|
|
10
|
-
labels: []
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const mockIssuesGet = vi.fn().mockResolvedValue({
|
|
15
|
-
data: {
|
|
16
|
-
number: 123,
|
|
17
|
-
body: 'Existing issue body',
|
|
18
|
-
labels: [{ name: 'existing-label' }]
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const mockIssuesUpdate = vi.fn().mockResolvedValue({
|
|
23
|
-
data: {
|
|
24
|
-
number: 123,
|
|
25
|
-
html_url: 'https://github.com/test-owner/test-repo/issues/123',
|
|
26
|
-
labels: []
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const mockSearchIssues = vi.fn().mockResolvedValue({
|
|
31
|
-
data: {
|
|
32
|
-
items: []
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
class MockOctokit {
|
|
37
|
-
rest = {
|
|
38
|
-
issues: {
|
|
39
|
-
create: mockIssuesCreate,
|
|
40
|
-
get: mockIssuesGet,
|
|
41
|
-
update: mockIssuesUpdate
|
|
42
|
-
},
|
|
43
|
-
search: {
|
|
44
|
-
issuesAndPullRequests: mockSearchIssues
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
Octokit: MockOctokit
|
|
51
|
-
};
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe('IssueManager', () => {
|
|
55
|
-
let issueManager: IssueManager;
|
|
56
|
-
|
|
57
|
-
beforeEach(() => {
|
|
58
|
-
issueManager = new IssueManager();
|
|
59
|
-
vi.clearAllMocks();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe('createIssue', () => {
|
|
63
|
-
it('should create issue with severity label', async () => {
|
|
64
|
-
const issueData = {
|
|
65
|
-
owner: 'test-owner',
|
|
66
|
-
repo: 'test-repo',
|
|
67
|
-
title: 'Dependency Update: test-package',
|
|
68
|
-
body: 'New version detected',
|
|
69
|
-
severity: 'major' as const,
|
|
70
|
-
dependency: {
|
|
71
|
-
id: 'test-id',
|
|
72
|
-
url: 'https://github.com/owner/repo'
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const result = await issueManager.createIssue(issueData);
|
|
77
|
-
|
|
78
|
-
expect(result).toBeDefined();
|
|
79
|
-
expect(result.number).toBeDefined();
|
|
80
|
-
expect(result.url).toBeDefined();
|
|
81
|
-
expect(result.labels).toContain('severity:major');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should include dependabit label', async () => {
|
|
85
|
-
const issueData = {
|
|
86
|
-
owner: 'test-owner',
|
|
87
|
-
repo: 'test-repo',
|
|
88
|
-
title: 'Dependency Update',
|
|
89
|
-
body: 'Changes detected',
|
|
90
|
-
severity: 'minor' as const,
|
|
91
|
-
dependency: {
|
|
92
|
-
id: 'test-id',
|
|
93
|
-
url: 'https://example.com'
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const result = await issueManager.createIssue(issueData);
|
|
98
|
-
|
|
99
|
-
expect(result.labels).toContain('dependabit');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should handle breaking changes with appropriate label', async () => {
|
|
103
|
-
const issueData = {
|
|
104
|
-
owner: 'test-owner',
|
|
105
|
-
repo: 'test-repo',
|
|
106
|
-
title: 'Breaking Change Detected',
|
|
107
|
-
body: 'Major version update with breaking changes',
|
|
108
|
-
severity: 'breaking' as const,
|
|
109
|
-
dependency: {
|
|
110
|
-
id: 'test-id',
|
|
111
|
-
url: 'https://github.com/owner/repo'
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const result = await issueManager.createIssue(issueData);
|
|
116
|
-
|
|
117
|
-
expect(result.labels).toContain('severity:breaking');
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should assign issue to AI agent when specified', async () => {
|
|
121
|
-
const issueData = {
|
|
122
|
-
owner: 'test-owner',
|
|
123
|
-
repo: 'test-repo',
|
|
124
|
-
title: 'Dependency Update',
|
|
125
|
-
body: 'Changes detected',
|
|
126
|
-
severity: 'major' as const,
|
|
127
|
-
dependency: {
|
|
128
|
-
id: 'test-id',
|
|
129
|
-
url: 'https://example.com'
|
|
130
|
-
},
|
|
131
|
-
assignee: 'copilot'
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const result = await issueManager.createIssue(issueData);
|
|
135
|
-
|
|
136
|
-
expect(result).toBeDefined();
|
|
137
|
-
expect(result.assignees).toContain('copilot');
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('findExistingIssue', () => {
|
|
142
|
-
it('should find existing issue for dependency', async () => {
|
|
143
|
-
const params = {
|
|
144
|
-
owner: 'test-owner',
|
|
145
|
-
repo: 'test-repo',
|
|
146
|
-
dependencyId: 'test-id'
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const result = await issueManager.findExistingIssue(params);
|
|
150
|
-
|
|
151
|
-
expect(result).toBeDefined();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('should return null when no existing issue found', async () => {
|
|
155
|
-
const params = {
|
|
156
|
-
owner: 'test-owner',
|
|
157
|
-
repo: 'test-repo',
|
|
158
|
-
dependencyId: 'non-existent-id'
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const result = await issueManager.findExistingIssue(params);
|
|
162
|
-
|
|
163
|
-
expect(result).toBeNull();
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
describe('updateIssue', () => {
|
|
168
|
-
it('should update existing issue with new information', async () => {
|
|
169
|
-
const updateData = {
|
|
170
|
-
owner: 'test-owner',
|
|
171
|
-
repo: 'test-repo',
|
|
172
|
-
issueNumber: 123,
|
|
173
|
-
body: 'Updated information',
|
|
174
|
-
severity: 'breaking' as const
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const result = await issueManager.updateIssue(updateData);
|
|
178
|
-
|
|
179
|
-
expect(result).toBeDefined();
|
|
180
|
-
expect(result.number).toBe(123);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('should append to existing issue body when specified', async () => {
|
|
184
|
-
const updateData = {
|
|
185
|
-
owner: 'test-owner',
|
|
186
|
-
repo: 'test-repo',
|
|
187
|
-
issueNumber: 123,
|
|
188
|
-
body: 'Additional update',
|
|
189
|
-
append: true
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const result = await issueManager.updateIssue(updateData);
|
|
193
|
-
|
|
194
|
-
expect(result).toBeDefined();
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
});
|