Skip to content

Commit 81e2ff6

Browse files
authored
ci: Move flaky npm tests into specific scheduled API Control script (#1154)
* Moving failing tests into their own directory * ci: Add API Control daily checks * chore: cleanup
1 parent 65ed595 commit 81e2ff6

File tree

6 files changed

+133
-53
lines changed

6 files changed

+133
-53
lines changed

.github/workflows/api-control.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: API CONTROL
2+
on:
3+
schedule:
4+
# Everyday, at 10am
5+
- cron: '0 10 * * *'
6+
7+
jobs:
8+
api-control:
9+
name: 🛂 API Control
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: 📁 Checkout code
13+
uses: actions/checkout@v4
14+
15+
- name: ⚙️ Setup node
16+
uses: actions/setup-node@v3
17+
with:
18+
node-version: '18.18'
19+
cache: 'yarn'
20+
21+
- name: 📦 Install dependencies
22+
run: yarn install --frozen-lockfile
23+
24+
- name: 🛂 API Control
25+
run: GITHUB_RUN_ID="${{ github.run_id }}" yarn test:api-control

jest.config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// eslint-disable-next-line import/no-commonjs
22
module.exports = {
3-
name: 'npm',
4-
53
transform: {
64
'^.+\\.[jt]sx?$': 'ts-jest',
75
},
86
testMatch: ['<rootDir>/src/**/*.test.[jt]s'],
7+
// By default, ignore the slow and flaky tests testing external APIs. Those
8+
// will be run specifically with `yarn run test:api-control`
9+
testPathIgnorePatterns: ['api-control'],
910
globals: {
1011
'ts-jest': {
1112
diagnostics: false,

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"start": "UV_THREADPOOL_SIZE=64 node --max-old-space-size=1500 dist/index.js",
1717
"start_new": "indexName=npm-search-new bootstrapIndexName=npm-search-new.tmp UV_THREADPOOL_SIZE=64 node --max-old-space-size=1500 dist/index.js",
1818
"test:watch": "jest --watchAll --no-watchman",
19+
"test:api-control": "./scripts/test-api-control",
1920
"test": "jest --forceExit",
2021
"publish:check": "node ./scripts/publish-check.js",
2122
"publish:github": "./scripts/publish-github",

scripts/test-api-control

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/sh
2+
# API Control tests assert that the external third party APIs we are using
3+
# return data in an expected format. They query the relevant API with real (not
4+
# mocked) HTTP calls.
5+
#
6+
# As those tests are slow, and have a higher probability of flakiness because of
7+
# network issues or timeouts, we don't want to run them on each pre-commit hook
8+
# or CI commit. They can instead be run manually, or periodically from the CI.
9+
#
10+
# When it fails on the CI, it will generate a GitHub issue with the failure details
11+
# as well as a link to the run.
12+
13+
# Running locally, with colors and live output
14+
if [ "$GITHUB_RUN_ID" = "" ]; then
15+
jest \
16+
./src/__tests__/api-control \
17+
--forceExit \
18+
--testPathIgnorePatterns=''
19+
exit $?
20+
fi
21+
22+
# Running on CI, creating an issue on failure
23+
echo "Wait while we run the tests"
24+
output=$(jest \
25+
./src/__tests__/api-control \
26+
--forceExit \
27+
--testPathIgnorePatterns='' 2>&1)
28+
exitCode=$?
29+
echo "$output"
30+
31+
# Stop on success
32+
if [ "$exitCode" = "0" ]; then
33+
exit 0
34+
fi
35+
36+
# Create the issue on failure
37+
gh issue create \
38+
--title "API Control failed" \
39+
--body "\
40+
One of the external APIs we depend on failed to return coherent data in our periodic test.
41+
Maybe it's a temporary issue, maybe they changed their format.
42+
43+
https://github.com/algolia/npm-search/actions/runs/$GITHUB_RUN_ID
44+
45+
\`\`\`
46+
$output
47+
\`\`\`"
48+
49+
# Still mark the job as failed
50+
exit 1

src/__tests__/api-control/npm.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import * as api from '../../npm/index';
2+
3+
jest.setTimeout(15000);
4+
5+
describe('findAll()', () => {
6+
it('contains the correct keys', async () => {
7+
const all = await api.findAll({ limit: 2, startkey: '0' });
8+
9+
expect(all).toEqual(
10+
expect.objectContaining({
11+
offset: expect.any(Number),
12+
total_rows: expect.any(Number),
13+
})
14+
);
15+
16+
expect(all.rows).toHaveLength(2);
17+
18+
expect(all.rows[0]).toEqual(
19+
expect.objectContaining({
20+
id: '0',
21+
key: '0',
22+
value: { rev: '11-61bb2c49ce3202a3e0ab9a65646b4b4d' },
23+
})
24+
);
25+
});
26+
});
27+
28+
describe('getDoc()', () => {
29+
it('retrieves a single doc', async () => {
30+
const doc = await api.getDoc(
31+
'jsdelivr',
32+
'8-734f30eea3baad0a62452a3bff1dd116'
33+
);
34+
35+
expect(doc.name).toBe('jsdelivr');
36+
expect(Object.keys(doc.versions)).toHaveLength(2);
37+
});
38+
});
39+
40+
describe('getInfo()', () => {
41+
let registryInfo;
42+
beforeAll(async () => {
43+
registryInfo = await api.getInfo();
44+
});
45+
46+
it('contains the correct keys', () => {
47+
expect(registryInfo).toEqual(
48+
expect.objectContaining({
49+
nbDocs: expect.any(Number),
50+
seq: expect.any(Number),
51+
})
52+
);
53+
});
54+
});

src/npm/__tests__/index.test.ts

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,6 @@ import { computeDownload } from '../index';
55

66
jest.setTimeout(15000);
77

8-
describe('findAll()', () => {
9-
it('contains the correct keys', async () => {
10-
const all = await api.findAll({ limit: 2, startkey: '0' });
11-
12-
expect(all).toEqual(
13-
expect.objectContaining({
14-
offset: expect.any(Number),
15-
total_rows: expect.any(Number),
16-
})
17-
);
18-
19-
expect(all.rows).toHaveLength(2);
20-
21-
expect(all.rows[0]).toEqual(
22-
expect.objectContaining({
23-
id: '0',
24-
key: '0',
25-
value: { rev: '11-61bb2c49ce3202a3e0ab9a65646b4b4d' },
26-
})
27-
);
28-
});
29-
});
30-
31-
describe('getDoc()', () => {
32-
it('retrieves a single doc', async () => {
33-
const doc = await api.getDoc(
34-
'jsdelivr',
35-
'8-734f30eea3baad0a62452a3bff1dd116'
36-
);
37-
38-
expect(doc.name).toBe('jsdelivr');
39-
expect(Object.keys(doc.versions)).toHaveLength(2);
40-
});
41-
});
42-
438
describe('getDocFromRegistry()', () => {
449
it('retrieves a single doc', async () => {
4510
const doc = await api.getDocFromRegistry('jsdelivr');
@@ -61,22 +26,6 @@ describe('getDocFromRegistry()', () => {
6126
});
6227
});
6328

64-
describe('getInfo()', () => {
65-
let registryInfo;
66-
beforeAll(async () => {
67-
registryInfo = await api.getInfo();
68-
});
69-
70-
it('contains the correct keys', () => {
71-
expect(registryInfo).toEqual(
72-
expect.objectContaining({
73-
nbDocs: expect.any(Number),
74-
seq: expect.any(Number),
75-
})
76-
);
77-
});
78-
});
79-
8029
describe('getDependents()', () => {
8130
let dependents;
8231
beforeAll(async () => {

0 commit comments

Comments
 (0)