Skip to content

Commit 542c520

Browse files
committed
chore: add tests
1 parent e70b66f commit 542c520

File tree

4 files changed

+155
-1
lines changed

4 files changed

+155
-1
lines changed

.eslintrc.js

+2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ module.exports = {
1313
files: 'test/**/*.+(t|j)s',
1414
rules: {
1515
'no-magic-numbers': 'off',
16+
'no-undef': 'off',
1617
'promise/prefer-await-to-callbacks': 'off',
1718
'unicorn/filename-case': 'off',
19+
'unicorn/consistent-function-scoping': 'off',
1820
},
1921
},
2022
],

test/helpers/mock_fetch.js

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const assert = require('assert')
2+
3+
module.exports = class MockFetch {
4+
constructor() {
5+
this.requests = []
6+
}
7+
8+
addExpectedRequest({ body, headers = {}, method, response, url }) {
9+
this.requests.push({ body, fulfilled: false, headers, method, response, url })
10+
11+
return this
12+
}
13+
14+
delete(options) {
15+
return this.addExpectedRequest({ ...options, method: 'delete' })
16+
}
17+
18+
get(options) {
19+
return this.addExpectedRequest({ ...options, method: 'get' })
20+
}
21+
22+
post(options) {
23+
return this.addExpectedRequest({ ...options, method: 'post' })
24+
}
25+
26+
put(options) {
27+
return this.addExpectedRequest({ ...options, method: 'put' })
28+
}
29+
30+
get fetcher() {
31+
// eslint-disable-next-line require-await
32+
return async (...args) => {
33+
const [url, options] = args
34+
const headers = options?.headers
35+
const urlString = url.toString()
36+
const match = this.requests.find(
37+
(request) =>
38+
request.method.toLowerCase() === options?.method.toLowerCase() &&
39+
request.url === urlString &&
40+
!request.fulfilled,
41+
)
42+
43+
if (!match) {
44+
throw new Error(`Unexpected fetch call: ${url}`)
45+
}
46+
47+
for (const key in match.headers) {
48+
assert.equal(headers[key], match.headers[key])
49+
}
50+
51+
if (typeof match.body === 'string') {
52+
assert.equal(options?.body, match.body)
53+
} else if (typeof match.body === 'function') {
54+
const bodyFn = match.body
55+
56+
bodyFn(options?.body)
57+
} else {
58+
assert.equal(options?.body, undefined)
59+
}
60+
61+
match.fulfilled = true
62+
63+
if (match.response instanceof Error) {
64+
throw match.response
65+
}
66+
67+
return match.response
68+
}
69+
}
70+
71+
get fulfilled() {
72+
return this.requests.every((request) => request.fulfilled)
73+
}
74+
}

test/types/Handler.test-d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Handler } from '../../src/main.js'
44

55
// Ensure void is NOT a valid return type in async handlers
66
expectError(() => {
7-
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unicorn/consistent-function-scoping
7+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
88
const handler: Handler = async () => {
99
// void
1010
}

test/unit/purge_cache.js

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
const process = require('process')
2+
3+
const test = require('ava')
4+
5+
const { purgeCache } = require('../../dist/lib/purge_cache')
6+
const { invokeLambda } = require('../helpers/main')
7+
const MockFetch = require('../helpers/mock_fetch')
8+
9+
const globalFetch = globalThis.fetch
10+
11+
test.beforeEach(() => {
12+
delete process.env.NETLIFY_PURGE_API_TOKEN
13+
delete process.env.SITE_ID
14+
})
15+
16+
test.afterEach(() => {
17+
globalThis.fetch = globalFetch
18+
})
19+
20+
test.serial('Calls the purge API endpoint and returns `undefined` if the operation was successful', async (t) => {
21+
const mockSiteID = '123456789'
22+
const mockToken = '1q2w3e4r5t6y7u8i9o0p'
23+
24+
process.env.NETLIFY_PURGE_API_TOKEN = mockToken
25+
process.env.SITE_ID = mockSiteID
26+
27+
const mockAPI = new MockFetch().post({
28+
body: (payload) => {
29+
const data = JSON.parse(payload)
30+
31+
t.is(data.site_id, mockSiteID)
32+
},
33+
headers: { Authorization: `Bearer ${mockToken}` },
34+
method: 'post',
35+
response: new Response(null, { status: 202 }),
36+
url: `https://api.netlify.com/api/v1/purge`,
37+
})
38+
const myFunction = async () => {
39+
await purgeCache()
40+
}
41+
42+
globalThis.fetch = mockAPI.fetcher
43+
44+
const response = await invokeLambda(myFunction)
45+
46+
t.is(response, undefined)
47+
t.true(mockAPI.fulfilled)
48+
})
49+
50+
test.serial('Throws if the API response does not have a successful status code', async (t) => {
51+
const mockSiteID = '123456789'
52+
const mockToken = '1q2w3e4r5t6y7u8i9o0p'
53+
54+
process.env.NETLIFY_PURGE_API_TOKEN = mockToken
55+
process.env.SITE_ID = mockSiteID
56+
57+
const mockAPI = new MockFetch().post({
58+
body: (payload) => {
59+
const data = JSON.parse(payload)
60+
61+
t.is(data.site_id, mockSiteID)
62+
},
63+
headers: { Authorization: `Bearer ${mockToken}` },
64+
method: 'post',
65+
response: new Response(null, { status: 500 }),
66+
url: `https://api.netlify.com/api/v1/purge`,
67+
})
68+
const myFunction = async () => {
69+
await purgeCache()
70+
}
71+
72+
globalThis.fetch = mockAPI.fetcher
73+
74+
await t.throwsAsync(
75+
async () => await invokeLambda(myFunction),
76+
'Cache purge API call returned an unexpected status code: 500',
77+
)
78+
})

0 commit comments

Comments
 (0)