@opentermsarchive/engine 2.5.0 → 2.7.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.
@@ -0,0 +1,527 @@
1
+ import { createRequire } from 'module';
2
+
3
+ import { expect } from 'chai';
4
+ import nock from 'nock';
5
+
6
+ import GitLab from './index.js';
7
+
8
+ const require = createRequire(import.meta.url);
9
+
10
+ describe('GitLab', function () {
11
+ this.timeout(5000);
12
+
13
+ let MANAGED_LABELS;
14
+ let gitlab;
15
+ const PROJECT_ID = '4';
16
+
17
+ before(() => {
18
+ MANAGED_LABELS = require('./labels.json');
19
+ gitlab = new GitLab('owner/repo');
20
+ });
21
+
22
+ describe('#initialize', () => {
23
+ const scopes = [];
24
+
25
+ before(async () => {
26
+ const existingLabels = MANAGED_LABELS.slice(0, -2);
27
+
28
+ nock(gitlab.apiBaseURL)
29
+ .get(`/projects/${encodeURIComponent('owner/repo')}`)
30
+ .reply(200, { id: PROJECT_ID });
31
+
32
+ nock(gitlab.apiBaseURL)
33
+ .get(`/projects/${PROJECT_ID}/labels?with_counts=true`)
34
+ .reply(200, existingLabels);
35
+
36
+ const missingLabels = MANAGED_LABELS.slice(-2);
37
+
38
+ for (const label of missingLabels) {
39
+ scopes.push(nock(gitlab.apiBaseURL)
40
+ .post(`/projects/${PROJECT_ID}/labels`)
41
+ .reply(200, { name: label.name }));
42
+ }
43
+
44
+ await gitlab.initialize();
45
+ });
46
+
47
+ after(nock.cleanAll);
48
+
49
+ it('should create missing labels', () => {
50
+ scopes.forEach(scope => expect(scope.isDone()).to.be.true);
51
+ });
52
+ });
53
+
54
+ describe('#getRepositoryLabels', () => {
55
+ let scope;
56
+ let result;
57
+ const LABELS = [{ name: 'bug' }, { name: 'enhancement' }];
58
+
59
+ before(async () => {
60
+ scope = nock(gitlab.apiBaseURL)
61
+ .get(`/projects/${PROJECT_ID}/labels?with_counts=true`)
62
+ .reply(200, LABELS);
63
+
64
+ result = await gitlab.getRepositoryLabels();
65
+ });
66
+
67
+ after(nock.cleanAll);
68
+
69
+ it('fetches repository labels', () => {
70
+ expect(scope.isDone()).to.be.true;
71
+ });
72
+
73
+ it('returns the repository labels', () => {
74
+ expect(result).to.deep.equal(LABELS);
75
+ });
76
+ });
77
+
78
+ describe('#createLabel', () => {
79
+ let scope;
80
+ const LABEL = { name: 'new_label', color: 'ffffff' };
81
+
82
+ before(async () => {
83
+ scope = nock(gitlab.apiBaseURL)
84
+ .post(`/projects/${PROJECT_ID}/labels`, body => body.name === LABEL.name)
85
+ .reply(200, LABEL);
86
+
87
+ await gitlab.createLabel(LABEL);
88
+ });
89
+
90
+ after(nock.cleanAll);
91
+
92
+ it('creates the new label', () => {
93
+ expect(scope.isDone()).to.be.true;
94
+ });
95
+ });
96
+
97
+ describe('#createIssue', () => {
98
+ let scope;
99
+ let result;
100
+
101
+ const ISSUE = {
102
+ title: 'New Issue',
103
+ description: 'Description of the new issue',
104
+ labels: ['bug'],
105
+ };
106
+ const CREATED_ISSUE = {
107
+ title: 'New Issue',
108
+ description: 'Description of the new issue',
109
+ labels: ['bug'],
110
+ iid: 555,
111
+ web_url: 'https://example.com/test/test',
112
+ };
113
+
114
+ before(async () => {
115
+ scope = nock(gitlab.apiBaseURL)
116
+ .post(`/projects/${PROJECT_ID}/issues`)
117
+ .reply(200, CREATED_ISSUE);
118
+
119
+ result = await gitlab.createIssue(ISSUE);
120
+ });
121
+
122
+ after(nock.cleanAll);
123
+
124
+ it('creates the new issue', () => {
125
+ expect(scope.isDone()).to.be.true;
126
+ });
127
+
128
+ it('returns the created issue', () => {
129
+ expect(result).to.deep.equal(CREATED_ISSUE);
130
+ });
131
+ });
132
+
133
+ describe('#setIssueLabels', () => {
134
+ let scope;
135
+ const issue = {
136
+ iid: 123,
137
+ title: 'test issue',
138
+ };
139
+ const labels = [ 'bug', 'enhancement' ];
140
+
141
+ const response = {
142
+ iid: 123,
143
+ labels,
144
+ };
145
+
146
+ before(async () => {
147
+ scope = nock(gitlab.apiBaseURL)
148
+ .put(`/projects/${PROJECT_ID}/issues/${issue.iid}`, { labels })
149
+ .reply(200, response);
150
+
151
+ await gitlab.setIssueLabels({ issue, labels });
152
+ });
153
+
154
+ after(nock.cleanAll);
155
+
156
+ it('sets labels on the issue', () => {
157
+ expect(scope.isDone()).to.be.true;
158
+ });
159
+ });
160
+
161
+ describe('#openIssue', () => {
162
+ let scope;
163
+ const ISSUE = { iid: 123, title: 'issue reopened' };
164
+ const EXPECTED_REQUEST_BODY = { state_event: 'reopen' };
165
+ const response = { iid: 123 };
166
+
167
+ before(async () => {
168
+ scope = nock(gitlab.apiBaseURL)
169
+ .put(`/projects/${PROJECT_ID}/issues/${ISSUE.iid}`, EXPECTED_REQUEST_BODY)
170
+ .reply(200, response);
171
+
172
+ await gitlab.openIssue(ISSUE);
173
+ });
174
+
175
+ after(nock.cleanAll);
176
+
177
+ it('opens the issue', () => {
178
+ expect(scope.isDone()).to.be.true;
179
+ });
180
+ });
181
+
182
+ describe('#closeIssue', () => {
183
+ let scope;
184
+ const ISSUE = { iid: 123, title: 'close issue' };
185
+ const EXPECTED_REQUEST_BODY = { state_event: 'close' };
186
+ const response = { iid: 123 };
187
+
188
+ before(async () => {
189
+ scope = nock(gitlab.apiBaseURL)
190
+ .put(`/projects/${PROJECT_ID}/issues/${ISSUE.iid}`, EXPECTED_REQUEST_BODY)
191
+ .reply(200, response);
192
+
193
+ await gitlab.closeIssue(ISSUE);
194
+ });
195
+
196
+ after(nock.cleanAll);
197
+
198
+ it('closes the issue', () => {
199
+ expect(scope.isDone()).to.be.true;
200
+ });
201
+ });
202
+
203
+ describe('#getIssue', () => {
204
+ let scope;
205
+ let result;
206
+
207
+ const ISSUE = { number: 123, title: 'Test Issue' };
208
+ const ANOTHER_ISSUE = { number: 124, title: 'Test Issue 2' };
209
+
210
+ before(async () => {
211
+ scope = nock(gitlab.apiBaseURL)
212
+ .get(`/projects/${PROJECT_ID}/issues?search=${encodeURIComponent(ISSUE.title)}&per_page=100`)
213
+ .reply(200, [ ISSUE, ANOTHER_ISSUE ]);
214
+
215
+ result = await gitlab.getIssue({ title: ISSUE.title });
216
+ });
217
+
218
+ after(nock.cleanAll);
219
+
220
+ it('searches for the issue', () => {
221
+ expect(scope.isDone()).to.be.true;
222
+ });
223
+
224
+ it('returns the expected issue', () => {
225
+ expect(result).to.deep.equal(ISSUE);
226
+ });
227
+ });
228
+
229
+ describe('#addCommentToIssue', () => {
230
+ let scope;
231
+ const ISSUE = { iid: 123, title: 'Test Issue' };
232
+ const COMMENT = 'Test comment';
233
+ const response = { iid: 123, id: 23, body: 'Test comment' };
234
+
235
+ before(async () => {
236
+ scope = nock(gitlab.apiBaseURL)
237
+ .post(`/projects/${PROJECT_ID}/issues/${ISSUE.iid}/notes`, { body: COMMENT })
238
+ .reply(200, response);
239
+
240
+ await gitlab.addCommentToIssue({ issue: ISSUE, comment: COMMENT });
241
+ });
242
+
243
+ after(nock.cleanAll);
244
+
245
+ it('adds the comment to the issue', () => {
246
+ expect(scope.isDone()).to.be.true;
247
+ });
248
+ });
249
+
250
+ describe('#closeIssueWithCommentIfExists', () => {
251
+ after(nock.cleanAll);
252
+
253
+ context('when the issue exists and is open', () => {
254
+ const ISSUE = {
255
+ iid: 123,
256
+ title: 'Open Issue',
257
+ state: GitLab.ISSUE_STATE_OPEN,
258
+ };
259
+ let addCommentScope;
260
+ let closeIssueScope;
261
+ const COMMENT = 'Closing comment';
262
+ const responseAddcomment = { iid: 123, id: 23, body: COMMENT };
263
+ const closeissueBody = { state_event: 'close' };
264
+ const responseCloseissue = { iid: 123 };
265
+
266
+ before(async () => {
267
+ nock(gitlab.apiBaseURL)
268
+ .get(`/projects/${PROJECT_ID}/issues?search=${encodeURIComponent(ISSUE.title)}&per_page=100`)
269
+ .reply(200, [ISSUE]);
270
+
271
+ addCommentScope = nock(gitlab.apiBaseURL)
272
+ .post(`/projects/${PROJECT_ID}/issues/${ISSUE.iid}/notes`, { body: COMMENT })
273
+ .reply(200, responseAddcomment);
274
+
275
+ closeIssueScope = nock(gitlab.apiBaseURL)
276
+ .put(`/projects/${PROJECT_ID}/issues/${ISSUE.iid}`, closeissueBody)
277
+ .reply(200, responseCloseissue);
278
+
279
+ await gitlab.closeIssueWithCommentIfExists({ title: ISSUE.title, comment: COMMENT });
280
+ });
281
+
282
+ it('adds comment to the issue', () => {
283
+ expect(addCommentScope.isDone()).to.be.true;
284
+ });
285
+
286
+ it('closes the issue', () => {
287
+ expect(closeIssueScope.isDone()).to.be.true;
288
+ });
289
+ });
290
+
291
+ context('when the issue exists and is closed', () => {
292
+ const ISSUE = {
293
+ number: 123,
294
+ title: 'Closed Issue',
295
+ state: GitLab.ISSUE_STATE_CLOSED,
296
+ };
297
+ let addCommentScope;
298
+ let closeIssueScope;
299
+ const COMMENT = 'Closing comment';
300
+ const responseAddcomment = { iid: 123, id: 23, body: COMMENT };
301
+ const closeissueBody = { state_event: 'close' };
302
+ const responseCloseissue = { iid: 123 };
303
+
304
+ before(async () => {
305
+ nock(gitlab.apiBaseURL)
306
+ .get(`/projects/${PROJECT_ID}/issues?search=${encodeURIComponent(ISSUE.title)}&per_page=100`)
307
+ .reply(200, []);
308
+
309
+ addCommentScope = nock(gitlab.apiBaseURL)
310
+ .post(`/projects/${PROJECT_ID}/issues/${ISSUE.iid}/notes`, { body: COMMENT })
311
+ .reply(200, responseAddcomment);
312
+
313
+ closeIssueScope = nock(gitlab.apiBaseURL)
314
+ .put(`/projects/${PROJECT_ID}/issues/${ISSUE.iid}`, closeissueBody)
315
+ .reply(200, responseCloseissue);
316
+
317
+ await gitlab.closeIssueWithCommentIfExists({ title: ISSUE.title, comment: COMMENT });
318
+ });
319
+
320
+ it('does not add comment', () => {
321
+ expect(addCommentScope.isDone()).to.be.false;
322
+ });
323
+
324
+ it('does not attempt to close the issue', () => {
325
+ expect(closeIssueScope.isDone()).to.be.false;
326
+ });
327
+ });
328
+
329
+ context('when the issue does not exist', () => {
330
+ let addCommentScope;
331
+ let closeIssueScope;
332
+ const COMMENT = 'Closing comment';
333
+ const TITLE = 'Non-existent Issue';
334
+ const responseAddcomment = { iid: 123, id: 23, body: COMMENT };
335
+ const closeissueBody = { state_event: 'close' };
336
+ const responseCloseissue = { iid: 123 };
337
+
338
+ before(async () => {
339
+ nock(gitlab.apiBaseURL)
340
+ .get(`/projects/${PROJECT_ID}/issues?search=${encodeURIComponent(TITLE)}&per_page=100`)
341
+ .reply(200, []);
342
+
343
+ addCommentScope = nock(gitlab.apiBaseURL)
344
+ .post(/\/projects\/\d+\/issues\/\d+\/notes/, { body: COMMENT })
345
+ .reply(200, responseAddcomment);
346
+
347
+ closeIssueScope = nock(gitlab.apiBaseURL)
348
+ .put(/\/projects\/\d+\/issues\/\d+/, closeissueBody)
349
+ .reply(200, responseCloseissue);
350
+
351
+ await gitlab.closeIssueWithCommentIfExists({ title: TITLE, comment: COMMENT });
352
+ });
353
+
354
+ it('does not attempt to add comment', () => {
355
+ expect(addCommentScope.isDone()).to.be.false;
356
+ });
357
+
358
+ it('does not attempt to close the issue', () => {
359
+ expect(closeIssueScope.isDone()).to.be.false;
360
+ });
361
+ });
362
+ });
363
+
364
+ describe('#createOrUpdateIssue', () => {
365
+ before(async () => {
366
+ nock(gitlab.apiBaseURL)
367
+ .get(`/projects/${encodeURIComponent('owner/repo')}`)
368
+ .reply(200, { id: 4 });
369
+
370
+ nock(gitlab.apiBaseURL)
371
+ .get(`/projects/${PROJECT_ID}/labels?with_counts=true`)
372
+ .reply(200, MANAGED_LABELS);
373
+
374
+ await gitlab.initialize();
375
+ });
376
+
377
+ context('when the issue does not exist', () => {
378
+ let createIssueScope;
379
+ const ISSUE_TO_CREATE = {
380
+ title: 'New Issue',
381
+ description: 'Description of the new issue',
382
+ label: 'bug',
383
+ };
384
+
385
+ before(async () => {
386
+ nock(gitlab.apiBaseURL)
387
+ .get(`/projects/${PROJECT_ID}/issues?search=${encodeURIComponent(ISSUE_TO_CREATE.title)}&per_page=100`)
388
+ .reply(200, []); // Simulate that there is no issues on the repository
389
+
390
+ createIssueScope = nock(gitlab.apiBaseURL)
391
+ .post(
392
+ `/projects/${PROJECT_ID}/issues`,
393
+ {
394
+ title: ISSUE_TO_CREATE.title,
395
+ description: ISSUE_TO_CREATE.description,
396
+ labels: [ISSUE_TO_CREATE.label],
397
+ },
398
+ )
399
+ .reply(200, { iid: 123, web_url: 'https://example.com/test/test' });
400
+
401
+ await gitlab.createOrUpdateIssue(ISSUE_TO_CREATE);
402
+ });
403
+
404
+ it('creates the issue', () => {
405
+ expect(createIssueScope.isDone()).to.be.true;
406
+ });
407
+ });
408
+
409
+ context('when the issue already exists', () => {
410
+ const ISSUE = {
411
+ title: 'Existing Issue',
412
+ description: 'New comment',
413
+ label: 'location',
414
+ };
415
+
416
+ context('when issue is closed', () => {
417
+ let setIssueLabelsScope;
418
+ let addCommentScope;
419
+ let openIssueScope;
420
+
421
+ const GITLAB_RESPONSE_FOR_EXISTING_ISSUE = {
422
+ iid: 123,
423
+ title: ISSUE.title,
424
+ description: ISSUE.description,
425
+ labels: [{ name: 'selectors' }],
426
+ state: GitLab.ISSUE_STATE_CLOSED,
427
+ };
428
+
429
+ const EXPECTED_REQUEST_BODY = { state_event: 'reopen' };
430
+ const responseIssuereopened = { iid: 123 };
431
+ const responseSetLabels = {
432
+ iid: 123,
433
+ labels: ['location'],
434
+ };
435
+ const responseAddcomment = { iid: 123, id: 23, body: ISSUE.description };
436
+ const { iid } = GITLAB_RESPONSE_FOR_EXISTING_ISSUE;
437
+
438
+ before(async () => {
439
+ nock(gitlab.apiBaseURL)
440
+ .get(`/projects/${PROJECT_ID}/issues?search=${encodeURIComponent(ISSUE.title)}&per_page=100`)
441
+ .reply(200, [GITLAB_RESPONSE_FOR_EXISTING_ISSUE]);
442
+
443
+ openIssueScope = nock(gitlab.apiBaseURL)
444
+ .put(`/projects/${PROJECT_ID}/issues/${iid}`, EXPECTED_REQUEST_BODY)
445
+ .reply(200, responseIssuereopened);
446
+
447
+ setIssueLabelsScope = nock(gitlab.apiBaseURL)
448
+ .put(`/projects/${PROJECT_ID}/issues/${iid}`, { labels: ['location'] })
449
+ .reply(200, responseSetLabels);
450
+
451
+ addCommentScope = nock(gitlab.apiBaseURL)
452
+ .post(`/projects/${PROJECT_ID}/issues/${iid}/notes`, { body: ISSUE.description })
453
+ .reply(200, responseAddcomment);
454
+
455
+ await gitlab.createOrUpdateIssue(ISSUE);
456
+ });
457
+
458
+ it('reopens the issue', () => {
459
+ expect(openIssueScope.isDone()).to.be.true;
460
+ });
461
+
462
+ it("updates the issue's label", () => {
463
+ expect(setIssueLabelsScope.isDone()).to.be.true;
464
+ });
465
+
466
+ it('adds comment to the issue', () => {
467
+ expect(addCommentScope.isDone()).to.be.true;
468
+ });
469
+ });
470
+
471
+ context('when issue is already opened', () => {
472
+ let setIssueLabelsScope;
473
+ let addCommentScope;
474
+ let openIssueScope;
475
+
476
+ const GITLAB_RESPONSE_FOR_EXISTING_ISSUE = {
477
+ number: 123,
478
+ title: ISSUE.title,
479
+ description: ISSUE.description,
480
+ labels: [{ name: 'selectors' }],
481
+ state: GitLab.ISSUE_STATE_OPEN,
482
+ };
483
+
484
+ const EXPECTED_REQUEST_BODY = { state_event: 'reopen' };
485
+ const responseIssuereopened = { iid: 123 };
486
+ const responseSetLabels = {
487
+ iid: 123,
488
+ labels: ['location'],
489
+ };
490
+ const responseAddcomment = { iid: 123, id: 23, body: ISSUE.description };
491
+ const { iid } = GITLAB_RESPONSE_FOR_EXISTING_ISSUE;
492
+
493
+ before(async () => {
494
+ nock(gitlab.apiBaseURL)
495
+ .get(`/projects/${PROJECT_ID}/issues?search=${encodeURIComponent(ISSUE.title)}&per_page=100`)
496
+ .reply(200, [GITLAB_RESPONSE_FOR_EXISTING_ISSUE]);
497
+
498
+ openIssueScope = nock(gitlab.apiBaseURL)
499
+ .put(`/projects/${PROJECT_ID}/issues/${iid}`, EXPECTED_REQUEST_BODY)
500
+ .reply(200, responseIssuereopened);
501
+
502
+ setIssueLabelsScope = nock(gitlab.apiBaseURL)
503
+ .put(`/projects/${PROJECT_ID}/issues/${iid}`, { labels: ['location'] })
504
+ .reply(200, responseSetLabels);
505
+
506
+ addCommentScope = nock(gitlab.apiBaseURL)
507
+ .post(`/projects/${PROJECT_ID}/issues/${iid}/notes`, { body: ISSUE.description })
508
+ .reply(200, responseAddcomment);
509
+
510
+ await gitlab.createOrUpdateIssue(ISSUE);
511
+ });
512
+
513
+ it('does not change the issue state', () => {
514
+ expect(openIssueScope.isDone()).to.be.false;
515
+ });
516
+
517
+ it("updates the issue's label", () => {
518
+ expect(setIssueLabelsScope.isDone()).to.be.true;
519
+ });
520
+
521
+ it('adds comment to the issue', () => {
522
+ expect(addCommentScope.isDone()).to.be.true;
523
+ });
524
+ });
525
+ });
526
+ });
527
+ });
@@ -0,0 +1,77 @@
1
+ [
2
+ {
3
+ "name": "403",
4
+ "color": "#0b08a0",
5
+ "description": "Fetching fails with a 403 (forbidden) HTTP code"
6
+ },
7
+ {
8
+ "name": "429",
9
+ "color": "#0b08a0",
10
+ "description": "Fetching fails with a 429 (too many requests) HTTP code"
11
+ },
12
+ {
13
+ "name": "500",
14
+ "color": "#0b08a0",
15
+ "description": "Fetching fails with a 500 (internal server error) HTTP code"
16
+ },
17
+ {
18
+ "name": "502",
19
+ "color": "#0b08a0",
20
+ "description": "Fetching fails with a 502 (bad gateway) HTTP code"
21
+ },
22
+ {
23
+ "name": "503",
24
+ "color": "#0b08a0",
25
+ "description": "Fetching fails with a 503 (service unavailable) HTTP code"
26
+ },
27
+ {
28
+ "name": "certificate expired",
29
+ "color": "#0b08a0",
30
+ "description": "Fetching fails because the domain SSL certificate has expired"
31
+ },
32
+ {
33
+ "name": "EAI_AGAIN",
34
+ "color": "#0b08a0",
35
+ "description": "Fetching fails because the domain fails to resolve on DNS"
36
+ },
37
+ {
38
+ "name": "ENOTFOUND",
39
+ "color": "#0b08a0",
40
+ "description": "Fetching fails because the domain fails to resolve on DNS"
41
+ },
42
+ {
43
+ "name": "empty response",
44
+ "color": "#0b08a0",
45
+ "description": "Fetching fails with a “response is empty” error"
46
+ },
47
+ {
48
+ "name": "first certificate",
49
+ "color": "#0b08a0",
50
+ "description": "Fetching fails with an “unable to verify the first certificate” error"
51
+ },
52
+ {
53
+ "name": "redirects",
54
+ "color": "#0b08a0",
55
+ "description": "Fetching fails with a “too many redirects” error"
56
+ },
57
+ {
58
+ "name": "timeout",
59
+ "color": "#0b08a0",
60
+ "description": "Fetching fails with a timeout error"
61
+ },
62
+ {
63
+ "name": "to clarify",
64
+ "color": "#0496ff",
65
+ "description": "Default failure label"
66
+ },
67
+ {
68
+ "name": "selectors",
69
+ "color": "#FBCA04",
70
+ "description": "Extraction selectors are outdated"
71
+ },
72
+ {
73
+ "name": "location",
74
+ "color": "#FBCA04",
75
+ "description": "Fetch location is outdated"
76
+ }
77
+ ]
@@ -0,0 +1,30 @@
1
+ import { createRequire } from 'module';
2
+
3
+ import chai from 'chai';
4
+
5
+ import { MANAGED_BY_OTA_MARKER } from './index.js';
6
+
7
+ const require = createRequire(import.meta.url);
8
+
9
+ const { expect } = chai;
10
+ const labels = require('./labels.json');
11
+
12
+ const GITLAB_LABEL_DESCRIPTION_MAX_LENGTH = 255;
13
+
14
+ describe('Reporter GitLab labels', () => {
15
+ labels.forEach(label => {
16
+ describe(`"${label.name}"`, () => {
17
+ it('complies with the GitLab character limit for descriptions', () => {
18
+ const descriptionLength = label.description.length + MANAGED_BY_OTA_MARKER.length;
19
+
20
+ expect(descriptionLength).to.be.lessThan(GITLAB_LABEL_DESCRIPTION_MAX_LENGTH);
21
+ });
22
+
23
+ it('complies with the GitLab constraints for color', () => {
24
+ const validHexColorRegex = /^#[0-9a-fA-F]{6}$/;
25
+
26
+ expect(validHexColorRegex.test(label.color)).to.be.true;
27
+ });
28
+ });
29
+ });
30
+ });