@dependabit/github-client 0.1.1
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 +7 -0
- package/LICENSE +21 -0
- package/README.md +266 -0
- package/dist/auth/basic.d.ts +46 -0
- package/dist/auth/basic.d.ts.map +1 -0
- package/dist/auth/basic.js +88 -0
- package/dist/auth/basic.js.map +1 -0
- package/dist/auth/oauth.d.ts +48 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +139 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/token.d.ts +40 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +67 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/auth.d.ts +47 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +78 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +53 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +74 -0
- package/dist/client.js.map +1 -0
- package/dist/commits.d.ts +57 -0
- package/dist/commits.d.ts.map +1 -0
- package/dist/commits.js +113 -0
- package/dist/commits.js.map +1 -0
- package/dist/feedback.d.ts +69 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +111 -0
- package/dist/feedback.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/issues.d.ts +55 -0
- package/dist/issues.d.ts.map +1 -0
- package/dist/issues.js +123 -0
- package/dist/issues.js.map +1 -0
- package/dist/rate-limit.d.ts +71 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +145 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/releases.d.ts +50 -0
- package/dist/releases.d.ts.map +1 -0
- package/dist/releases.js +113 -0
- package/dist/releases.js.map +1 -0
- package/package.json +39 -0
- package/src/auth/basic.ts +102 -0
- package/src/auth/oauth.ts +183 -0
- package/src/auth/token.ts +81 -0
- package/src/auth.ts +100 -0
- package/src/client.test.ts +115 -0
- package/src/client.ts +109 -0
- package/src/commits.ts +184 -0
- package/src/feedback.ts +166 -0
- package/src/index.ts +15 -0
- package/src/issues.ts +185 -0
- package/src/rate-limit.ts +210 -0
- package/src/releases.ts +149 -0
- package/test/auth/basic.test.ts +122 -0
- package/test/auth/oauth.test.ts +196 -0
- package/test/auth/token.test.ts +97 -0
- package/test/commits.test.ts +169 -0
- package/test/feedback.test.ts +203 -0
- package/test/issues.test.ts +197 -0
- package/test/rate-limit.test.ts +154 -0
- package/test/releases.test.ts +187 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,197 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { RateLimitHandler } from '../src/rate-limit.js';
|
|
3
|
+
|
|
4
|
+
// Mock the octokit module
|
|
5
|
+
vi.mock('octokit', () => {
|
|
6
|
+
const mockRateLimitGet = vi.fn().mockResolvedValue({
|
|
7
|
+
data: {
|
|
8
|
+
rate: {
|
|
9
|
+
limit: 5000,
|
|
10
|
+
remaining: 4000,
|
|
11
|
+
reset: Math.floor(Date.now() / 1000) + 3600,
|
|
12
|
+
used: 1000
|
|
13
|
+
},
|
|
14
|
+
resources: {
|
|
15
|
+
core: {
|
|
16
|
+
limit: 5000,
|
|
17
|
+
remaining: 4000,
|
|
18
|
+
reset: Math.floor(Date.now() / 1000) + 3600,
|
|
19
|
+
used: 1000
|
|
20
|
+
},
|
|
21
|
+
search: {
|
|
22
|
+
limit: 30,
|
|
23
|
+
remaining: 25,
|
|
24
|
+
reset: Math.floor(Date.now() / 1000) + 3600,
|
|
25
|
+
used: 5
|
|
26
|
+
},
|
|
27
|
+
graphql: {
|
|
28
|
+
limit: 5000,
|
|
29
|
+
remaining: 4500,
|
|
30
|
+
reset: Math.floor(Date.now() / 1000) + 3600,
|
|
31
|
+
used: 500
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
class MockOctokit {
|
|
38
|
+
rest = {
|
|
39
|
+
rateLimit: {
|
|
40
|
+
get: mockRateLimitGet
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
Octokit: MockOctokit
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('RateLimitHandler', () => {
|
|
51
|
+
let handler: RateLimitHandler;
|
|
52
|
+
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
handler = new RateLimitHandler();
|
|
55
|
+
vi.clearAllMocks();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('checkRateLimit', () => {
|
|
59
|
+
it('should return rate limit status', async () => {
|
|
60
|
+
const result = await handler.checkRateLimit();
|
|
61
|
+
|
|
62
|
+
expect(result).toBeDefined();
|
|
63
|
+
expect(result.limit).toBeGreaterThan(0);
|
|
64
|
+
expect(result.remaining).toBeDefined();
|
|
65
|
+
expect(result.reset).toBeInstanceOf(Date);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should warn when approaching rate limit', async () => {
|
|
69
|
+
const result = await handler.checkRateLimit();
|
|
70
|
+
|
|
71
|
+
if (result.remaining < result.limit * 0.1) {
|
|
72
|
+
expect(result.warning).toBeDefined();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('waitIfNeeded', () => {
|
|
78
|
+
it('should not wait when rate limit is sufficient', async () => {
|
|
79
|
+
const startTime = Date.now();
|
|
80
|
+
await handler.waitIfNeeded();
|
|
81
|
+
const endTime = Date.now();
|
|
82
|
+
|
|
83
|
+
expect(endTime - startTime).toBeLessThan(1000);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should calculate wait time when rate limited', async () => {
|
|
87
|
+
const rateLimitInfo = {
|
|
88
|
+
limit: 5000,
|
|
89
|
+
remaining: 0,
|
|
90
|
+
reset: new Date(Date.now() + 60000) // 1 minute from now
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const waitTime = handler.calculateWaitTime(rateLimitInfo);
|
|
94
|
+
|
|
95
|
+
expect(waitTime).toBeGreaterThan(0);
|
|
96
|
+
expect(waitTime).toBeLessThanOrEqual(60000);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should return 0 wait time when not rate limited', () => {
|
|
100
|
+
const rateLimitInfo = {
|
|
101
|
+
limit: 5000,
|
|
102
|
+
remaining: 4000,
|
|
103
|
+
reset: new Date(Date.now() + 3600000)
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const waitTime = handler.calculateWaitTime(rateLimitInfo);
|
|
107
|
+
|
|
108
|
+
expect(waitTime).toBe(0);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('reserveBudget', () => {
|
|
113
|
+
it('should reserve API call budget', async () => {
|
|
114
|
+
const result = await handler.reserveBudget(10);
|
|
115
|
+
|
|
116
|
+
expect(result).toBeDefined();
|
|
117
|
+
expect(result.reserved).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should reject when insufficient budget', async () => {
|
|
121
|
+
const result = await handler.reserveBudget(10000);
|
|
122
|
+
|
|
123
|
+
expect(result.reserved).toBe(false);
|
|
124
|
+
expect(result.reason).toBeDefined();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should provide wait time when budget unavailable', async () => {
|
|
128
|
+
const result = await handler.reserveBudget(10000);
|
|
129
|
+
|
|
130
|
+
if (!result.reserved) {
|
|
131
|
+
expect(result.waitTime).toBeDefined();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('getRateLimitStatus', () => {
|
|
137
|
+
it('should return current rate limit status', async () => {
|
|
138
|
+
const status = await handler.getRateLimitStatus();
|
|
139
|
+
|
|
140
|
+
expect(status).toBeDefined();
|
|
141
|
+
expect(status.core).toBeDefined();
|
|
142
|
+
expect(status.search).toBeDefined();
|
|
143
|
+
expect(status.graphql).toBeDefined();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should include percentage remaining', async () => {
|
|
147
|
+
const status = await handler.getRateLimitStatus();
|
|
148
|
+
|
|
149
|
+
expect(status.core.percentageRemaining).toBeDefined();
|
|
150
|
+
expect(status.core.percentageRemaining).toBeGreaterThanOrEqual(0);
|
|
151
|
+
expect(status.core.percentageRemaining).toBeLessThanOrEqual(100);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { ReleaseManager } from '../src/releases.js';
|
|
3
|
+
|
|
4
|
+
// Mock the octokit module
|
|
5
|
+
vi.mock('octokit', () => {
|
|
6
|
+
const mockGetLatestRelease = vi.fn().mockImplementation((params: any) => {
|
|
7
|
+
// Return 404 for specific repos
|
|
8
|
+
if (params.repo === 'no-releases') {
|
|
9
|
+
const error: any = new Error('Not Found');
|
|
10
|
+
error.status = 404;
|
|
11
|
+
return Promise.reject(error);
|
|
12
|
+
}
|
|
13
|
+
return Promise.resolve({
|
|
14
|
+
data: {
|
|
15
|
+
tag_name: 'v1.0.0',
|
|
16
|
+
name: 'Release v1.0.0',
|
|
17
|
+
published_at: '2024-01-01T00:00:00Z',
|
|
18
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
19
|
+
body: 'Release notes',
|
|
20
|
+
html_url: 'https://github.com/test-owner/test-repo/releases/v1.0.0',
|
|
21
|
+
prerelease: false,
|
|
22
|
+
draft: false
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const mockListReleases = vi.fn().mockImplementation((params: any) => {
|
|
28
|
+
// Return empty for specific repos
|
|
29
|
+
if (params.repo === 'no-releases') {
|
|
30
|
+
const error: any = new Error('Not Found');
|
|
31
|
+
error.status = 404;
|
|
32
|
+
return Promise.reject(error);
|
|
33
|
+
}
|
|
34
|
+
return Promise.resolve({
|
|
35
|
+
data: [
|
|
36
|
+
{
|
|
37
|
+
tag_name: 'v1.0.0',
|
|
38
|
+
name: 'Release v1.0.0',
|
|
39
|
+
published_at: '2024-01-01T00:00:00Z',
|
|
40
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
41
|
+
body: 'Release notes',
|
|
42
|
+
html_url: 'https://github.com/test-owner/test-repo/releases/v1.0.0',
|
|
43
|
+
prerelease: false,
|
|
44
|
+
draft: false
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const mockGetReleaseByTag = vi.fn().mockResolvedValue({
|
|
51
|
+
data: {
|
|
52
|
+
tag_name: 'v1.0.0',
|
|
53
|
+
name: 'Release v1.0.0',
|
|
54
|
+
published_at: '2024-01-01T00:00:00Z',
|
|
55
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
56
|
+
body: 'Release notes',
|
|
57
|
+
html_url: 'https://github.com/test-owner/test-repo/releases/v1.0.0',
|
|
58
|
+
prerelease: false,
|
|
59
|
+
draft: false
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
class MockOctokit {
|
|
64
|
+
rest = {
|
|
65
|
+
repos: {
|
|
66
|
+
getLatestRelease: mockGetLatestRelease,
|
|
67
|
+
listReleases: mockListReleases,
|
|
68
|
+
getReleaseByTag: mockGetReleaseByTag
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
Octokit: MockOctokit
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('ReleaseManager', () => {
|
|
79
|
+
let releaseManager: ReleaseManager;
|
|
80
|
+
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
releaseManager = new ReleaseManager();
|
|
83
|
+
vi.clearAllMocks();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('getLatestRelease', () => {
|
|
87
|
+
it('should fetch latest release', async () => {
|
|
88
|
+
const params = {
|
|
89
|
+
owner: 'test-owner',
|
|
90
|
+
repo: 'test-repo'
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const result = await releaseManager.getLatestRelease(params);
|
|
94
|
+
|
|
95
|
+
expect(result).toBeDefined();
|
|
96
|
+
expect(result.tagName).toBeDefined();
|
|
97
|
+
expect(result.publishedAt).toBeInstanceOf(Date);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should return null when no releases exist', async () => {
|
|
101
|
+
const params = {
|
|
102
|
+
owner: 'test-owner',
|
|
103
|
+
repo: 'no-releases'
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const result = await releaseManager.getLatestRelease(params);
|
|
107
|
+
|
|
108
|
+
expect(result).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should include release notes', async () => {
|
|
112
|
+
const params = {
|
|
113
|
+
owner: 'test-owner',
|
|
114
|
+
repo: 'test-repo'
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const result = await releaseManager.getLatestRelease(params);
|
|
118
|
+
|
|
119
|
+
expect(result?.body).toBeDefined();
|
|
120
|
+
expect(result?.htmlUrl).toBeDefined();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('getAllReleases', () => {
|
|
125
|
+
it('should fetch all releases', async () => {
|
|
126
|
+
const params = {
|
|
127
|
+
owner: 'test-owner',
|
|
128
|
+
repo: 'test-repo'
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const result = await releaseManager.getAllReleases(params);
|
|
132
|
+
|
|
133
|
+
expect(Array.isArray(result)).toBe(true);
|
|
134
|
+
expect(result.length).toBeGreaterThan(0);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should support pagination', async () => {
|
|
138
|
+
const params = {
|
|
139
|
+
owner: 'test-owner',
|
|
140
|
+
repo: 'test-repo',
|
|
141
|
+
page: 2,
|
|
142
|
+
perPage: 10
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const result = await releaseManager.getAllReleases(params);
|
|
146
|
+
|
|
147
|
+
expect(Array.isArray(result)).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should return empty array when no releases', async () => {
|
|
151
|
+
const params = {
|
|
152
|
+
owner: 'test-owner',
|
|
153
|
+
repo: 'no-releases'
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const result = await releaseManager.getAllReleases(params);
|
|
157
|
+
|
|
158
|
+
expect(result).toEqual([]);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('compareReleases', () => {
|
|
163
|
+
it('should detect new releases', () => {
|
|
164
|
+
const oldReleases = [{ tagName: 'v1.0.0', publishedAt: new Date('2024-01-01') }];
|
|
165
|
+
|
|
166
|
+
const newReleases = [
|
|
167
|
+
{ tagName: 'v1.1.0', publishedAt: new Date('2024-01-02') },
|
|
168
|
+
{ tagName: 'v1.0.0', publishedAt: new Date('2024-01-01') }
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
const result = releaseManager.compareReleases(oldReleases, newReleases);
|
|
172
|
+
|
|
173
|
+
expect(result.newReleases).toHaveLength(1);
|
|
174
|
+
expect(result.newReleases[0].tagName).toBe('v1.1.0');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should return empty when no new releases', () => {
|
|
178
|
+
const oldReleases = [{ tagName: 'v1.0.0', publishedAt: new Date('2024-01-01') }];
|
|
179
|
+
|
|
180
|
+
const newReleases = [{ tagName: 'v1.0.0', publishedAt: new Date('2024-01-01') }];
|
|
181
|
+
|
|
182
|
+
const result = releaseManager.compareReleases(oldReleases, newReleases);
|
|
183
|
+
|
|
184
|
+
expect(result.newReleases).toHaveLength(0);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|