mockaton 0.9.6 → 0.9.8
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/Api.js +1 -2
- package/MockBroker.js +9 -13
- package/MockDispatcher.js +29 -30
- package/README-dashboard-dropdown.png +0 -0
- package/README-dashboard.png +0 -0
- package/README-mocks-with-comments.png +0 -0
- package/README.md +16 -24
- package/Route.js +6 -8
- package/Tests.js +44 -36
- package/mockBrokersCollection.js +2 -3
- package/package.json +1 -1
- package/sample-mocks/api/user/.GET.500.txt +7 -0
- package/sample-mocks/api/user/.GET.501.txt +0 -7
- /package/sample-mocks/api/user/{edit-name.PATCH.501.txt → edit-name.PATCH.500.txt} +0 -0
- /package/sample-mocks/api/user/{friends.GET.501.txt → friends.GET.500.txt} +0 -0
- /package/sample-mocks/api/user/{logout.POST.501.txt → logout.POST.500.txt} +0 -0
- /package/sample-mocks/api/user/{videos.GET.501.txt → videos.GET.500.txt} +0 -0
- /package/sample-mocks/api/video/{[id].GET.501.txt → [id].GET.500.txt} +0 -0
- /package/sample-mocks/api/video/{list.GET.501.txt → list.GET.500.txt} +0 -0
- /package/sample-mocks/api/video/stat/[stat-id]/{[video-id].GET.501.txt → [video-id].GET.500.txt} +0 -0
- /package/sample-mocks/api/video/stat/[stat-id]/{all-videos?limit=[limit].GET.501.txt → all-videos?limit=[limit].GET.500.txt} +0 -0
- /package/sample-mocks/api/video/{upload.POST.501.txt → upload.POST.500.txt} +0 -0
package/Api.js
CHANGED
|
@@ -72,8 +72,7 @@ async function updateBroker(req, response) {
|
|
|
72
72
|
const broker = mockBrokersCollection.getBrokerByFilename(body[DF.file])
|
|
73
73
|
if (DF.delayed in body)
|
|
74
74
|
broker.updateDelay(body[DF.delayed])
|
|
75
|
-
|
|
76
|
-
broker.updateFile(body[DF.file])
|
|
75
|
+
broker.updateFile(body[DF.file])
|
|
77
76
|
sendOK(response)
|
|
78
77
|
}
|
|
79
78
|
catch (error) {
|
package/MockBroker.js
CHANGED
|
@@ -20,7 +20,6 @@ export class MockBroker {
|
|
|
20
20
|
this.mocks = [] // *.json,txt
|
|
21
21
|
this.currentMock = {
|
|
22
22
|
file: '',
|
|
23
|
-
status: 200,
|
|
24
23
|
delay: 0
|
|
25
24
|
}
|
|
26
25
|
|
|
@@ -36,10 +35,8 @@ export class MockBroker {
|
|
|
36
35
|
else if (file.endsWith('.mjs'))
|
|
37
36
|
this.transforms.push(file)
|
|
38
37
|
else {
|
|
39
|
-
if (!this.mocks.length)
|
|
38
|
+
if (!this.mocks.length)
|
|
40
39
|
this.currentMock.file = file // The first mock file option for a particular route becomes the default
|
|
41
|
-
this.currentMock.status = Route.parseFilename(file).status
|
|
42
|
-
}
|
|
43
40
|
this.mocks.push(file)
|
|
44
41
|
}
|
|
45
42
|
}
|
|
@@ -47,12 +44,11 @@ export class MockBroker {
|
|
|
47
44
|
urlMaskMatches(url) { return this.#route.urlMaskMatches(url) }
|
|
48
45
|
|
|
49
46
|
get file() { return this.currentMock.file }
|
|
50
|
-
get status() { return this.currentMock.status }
|
|
51
47
|
get delay() { return this.currentMock.delay }
|
|
48
|
+
get status() { return Route.parseFilename(this.currentMock.file).status }
|
|
52
49
|
|
|
53
50
|
updateFile(filename) {
|
|
54
51
|
this.currentMock.file = filename
|
|
55
|
-
this.currentMock.status = Route.parseFilename(filename).status
|
|
56
52
|
}
|
|
57
53
|
|
|
58
54
|
updateDelay(delayed) {
|
|
@@ -83,24 +79,24 @@ export class MockBroker {
|
|
|
83
79
|
return comments
|
|
84
80
|
}
|
|
85
81
|
|
|
86
|
-
|
|
87
|
-
if (!this.#
|
|
88
|
-
this.#
|
|
82
|
+
ensureItHas500() {
|
|
83
|
+
if (!this.#has500())
|
|
84
|
+
this.#write500()
|
|
89
85
|
}
|
|
90
86
|
|
|
91
|
-
#
|
|
87
|
+
#has500() {
|
|
92
88
|
return this.mocks.some(mock =>
|
|
93
|
-
Route.parseFilename(mock).status ===
|
|
89
|
+
Route.parseFilename(mock).status === 500)
|
|
94
90
|
}
|
|
95
91
|
|
|
96
|
-
#
|
|
92
|
+
#write500() {
|
|
97
93
|
// TODO handle route with transforms but without mocks
|
|
98
94
|
const { urlMask, method } = Route.parseFilename(this.mocks[0])
|
|
99
95
|
let mask = urlMask
|
|
100
96
|
const t = join(Config.mocksDir, urlMask)
|
|
101
97
|
if (existsSync(t) && lstatSync(t).isDirectory())
|
|
102
98
|
mask = urlMask + '/'
|
|
103
|
-
const file = `${mask}.${method}.
|
|
99
|
+
const file = `${mask}.${method}.500.txt`
|
|
104
100
|
writeFileSync(join(Config.mocksDir, file), '')
|
|
105
101
|
this.register(file)
|
|
106
102
|
}
|
package/MockDispatcher.js
CHANGED
|
@@ -10,46 +10,45 @@ import { parseJSON, JsonBodyParserError } from './utils/http-request.js'
|
|
|
10
10
|
import { sendInternalServerError, sendNotFound, sendFile, sendBadRequest } from './utils/http-response.js'
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
function serveDocumentation(req, response) {
|
|
14
|
-
sendFile(response, join(Config.mocksDir, decodeURIComponent(req.url)))
|
|
15
|
-
}
|
|
16
|
-
|
|
17
13
|
export async function dispatchMock(req, response) {
|
|
14
|
+
/* Serve Documentation */
|
|
18
15
|
if (req.method === 'GET' && req.url.endsWith('.md')) {
|
|
19
|
-
|
|
16
|
+
sendFile(response, join(Config.mocksDir, decodeURIComponent(req.url)))
|
|
20
17
|
return
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
const mockBroker = MockBrokerCollection.findMatchingBroker(req.method, req.url)
|
|
24
|
-
if (!mockBroker)
|
|
21
|
+
if (!mockBroker) {
|
|
25
22
|
sendNotFound(response)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const { file, status, delay, currentTransform } = mockBroker
|
|
28
|
+
console.log('\n', req.url, '→\n ', file)
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
response.statusCode = status
|
|
31
|
+
response.setHeader('content-type', mimeFor(file))
|
|
32
|
+
if (cookie.getCurrent())
|
|
33
|
+
response.setHeader('set-cookie', cookie.getCurrent())
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
setTimeout(() => response.end(mockAsText), delay)
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
console.error(error)
|
|
46
|
-
if (error instanceof JsonBodyParserError)
|
|
47
|
-
sendBadRequest(response)
|
|
48
|
-
else if (error.code === 'ENOENT')
|
|
49
|
-
sendNotFound(response) // file has been deleted
|
|
50
|
-
else
|
|
51
|
-
sendInternalServerError(response)
|
|
35
|
+
let mockAsText = readMock(file)
|
|
36
|
+
if (mockBroker.currentTransform) {
|
|
37
|
+
const body = await requestBodyForTransform(req, mockAsText)
|
|
38
|
+
const transformFunc = await importTransformFunc(currentTransform)
|
|
39
|
+
mockAsText = transformFunc(mockAsText, body, Config)
|
|
52
40
|
}
|
|
41
|
+
setTimeout(() => response.end(mockAsText), delay)
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error(error)
|
|
45
|
+
if (error instanceof JsonBodyParserError)
|
|
46
|
+
sendBadRequest(response)
|
|
47
|
+
else if (error.code === 'ENOENT')
|
|
48
|
+
sendNotFound(response) // file has been deleted
|
|
49
|
+
else
|
|
50
|
+
sendInternalServerError(response)
|
|
51
|
+
}
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
const nonSafeMethods = ['PATCH', 'POST', 'PUT', 'DELETE', 'CONNECT']
|
|
Binary file
|
package/README-dashboard.png
CHANGED
|
Binary file
|
|
Binary file
|
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Mockaton
|
|
2
2
|
_Mockaton_ is a mock server for developing and testing frontends.
|
|
3
3
|
|
|
4
|
-
It scans `Config.mocksDir` for files following a specific
|
|
5
|
-
|
|
4
|
+
It scans `Config.mocksDir` for files following a specific
|
|
5
|
+
file name convention, which is similar to the URL paths. For
|
|
6
|
+
example, the following file will be served for `/api/user/1234`
|
|
6
7
|
```
|
|
7
8
|
api/
|
|
8
9
|
api/user/
|
|
@@ -22,12 +23,12 @@ Each route can have many mocks, which could either be:
|
|
|
22
23
|
Those alternatives can be manually selected in the dashboard
|
|
23
24
|
UI, or programmatically, for instance, for setting up tests.
|
|
24
25
|
|
|
25
|
-
About the mock
|
|
26
|
+
About the default mock file, the first file in **alphabetical order** wins.
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
## Getting Started
|
|
29
30
|
The best way to learn _Mockaton_ is by checking out this repo and
|
|
30
|
-
exploring its [sample-mocks/](./sample-mocks) directory. Then run
|
|
31
|
+
exploring its [sample-mocks/](./sample-mocks) directory. Then, run
|
|
31
32
|
[`./_usage_example.js`](./_usage_example.js) and you’ll see this dashboard:
|
|
32
33
|
|
|
33
34
|

|
|
@@ -38,8 +39,8 @@ The **sample-mocks/** directory has three mock alternatives for serving
|
|
|
38
39
|
`/api/user/friends`:
|
|
39
40
|
- _200 - OK_
|
|
40
41
|
- _204 - No Content_ with an empty list of friends
|
|
41
|
-
-
|
|
42
|
-
- BTW,
|
|
42
|
+
- _500 - Internal Server Error_
|
|
43
|
+
- BTW, 500 mocks get autogenerated for routes that have no 500’s.
|
|
43
44
|
|
|
44
45
|

|
|
45
46
|
|
|
@@ -48,7 +49,7 @@ Comments are anything within parentheses, including them.
|
|
|
48
49
|

|
|
49
50
|
|
|
50
51
|
## Delay 🕓
|
|
51
|
-
The clock icon next to the mock selector
|
|
52
|
+
The clock icon next to the mock selector is a checkbox for delaying a
|
|
52
53
|
particular response. They are handy for testing spinners.
|
|
53
54
|
|
|
54
55
|
The milliseconds for the delay is globally configurable via `Config.delay = 1200`
|
|
@@ -136,13 +137,13 @@ api/foo.GET.200.json
|
|
|
136
137
|
```
|
|
137
138
|
api/video?limit=[limit].GET.200.json
|
|
138
139
|
```
|
|
139
|
-
The query string behaves like comments in the sense it’s
|
|
140
|
-
|
|
140
|
+
The query string behaves like comments in the sense it’s only used for documenting
|
|
141
|
+
the URL API contract. In other words, the query string is ignored when routing to it.
|
|
141
142
|
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
BTW, in Windows, filenames containing "?" are [not
|
|
144
|
+
permitted](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file),
|
|
145
|
+
but since that’s part of the query string, it’s ignored anyway.
|
|
144
146
|
|
|
145
|
-
https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
|
|
146
147
|
|
|
147
148
|
|
|
148
149
|
### Default (index-like) file
|
|
@@ -194,15 +195,6 @@ PATCH /mockaton/edit
|
|
|
194
195
|
---
|
|
195
196
|
|
|
196
197
|
### `/mockaton/bulk-select` Select all mocks that have a particular comment
|
|
197
|
-
Many mocks can be changed at once. We do that by searching the
|
|
198
|
-
comments on the filename. For example, `api/foo(demo-a).GET.200.json`
|
|
199
|
-
|
|
200
|
-
Non-matching mocks are ignored. For instance, if for a
|
|
201
|
-
particular API there is only `demo-a` and `demo-b`, changing to
|
|
202
|
-
`demo-c` will preserve the last one that was successfully set.
|
|
203
|
-
|
|
204
|
-
Similarly, if there’s no demo mock at all for
|
|
205
|
-
a route, the first dev mock (a-z) will be served.
|
|
206
198
|
|
|
207
199
|
```
|
|
208
200
|
PATCH /mockaton/bulk-select
|
|
@@ -220,7 +212,8 @@ PATCH /mockaton/reset
|
|
|
220
212
|
---
|
|
221
213
|
|
|
222
214
|
### `/mockaton/cookies` Select a cookie
|
|
223
|
-
In `Config.cookies`, each key is
|
|
215
|
+
In `Config.cookies`, each key is the label used
|
|
216
|
+
for changing it. Only one cookie can be set.
|
|
224
217
|
```
|
|
225
218
|
PATCH /mockaton/cookies
|
|
226
219
|
{
|
|
@@ -229,8 +222,7 @@ PATCH /mockaton/cookies
|
|
|
229
222
|
```
|
|
230
223
|
|
|
231
224
|
### `/mockaton/cookies` List Cookies
|
|
232
|
-
Sends a list of the
|
|
233
|
-
along with a flag indicated if it’s the selected.
|
|
225
|
+
Sends a list of the available cookies along with a flag indicated if it’s the selected.
|
|
234
226
|
```
|
|
235
227
|
GET /mockaton/cookies
|
|
236
228
|
```
|
package/Route.js
CHANGED
|
@@ -45,20 +45,18 @@ export class Route {
|
|
|
45
45
|
static parseFilename(file) {
|
|
46
46
|
const tokens = file.replace(Route.reComments, '').split('.')
|
|
47
47
|
|
|
48
|
-
let error = ''
|
|
49
48
|
if (tokens.length < 4)
|
|
50
|
-
error
|
|
51
|
-
|
|
52
|
-
const method = tokens.at(-3)
|
|
53
|
-
if (!httpMethods.includes(method))
|
|
54
|
-
error = `Unrecognized HTTP Method: "${method}"`
|
|
49
|
+
return { error: 'Invalid Filename Convention' }
|
|
55
50
|
|
|
56
51
|
const status = Number(tokens.at(-2))
|
|
57
52
|
if (!responseStatusIsValid(status))
|
|
58
|
-
error
|
|
53
|
+
return { error: `Invalid HTTP Response Status: "${status}"` }
|
|
54
|
+
|
|
55
|
+
const method = tokens.at(-3)
|
|
56
|
+
if (!httpMethods.includes(method))
|
|
57
|
+
return { error: `Unrecognized HTTP Method: "${method}"` }
|
|
59
58
|
|
|
60
59
|
return {
|
|
61
|
-
error,
|
|
62
60
|
urlMask: '/' + removeTrailingSlash(tokens.at(-4)),
|
|
63
61
|
method,
|
|
64
62
|
status
|
package/Tests.js
CHANGED
|
@@ -90,15 +90,10 @@ const fixtures = [
|
|
|
90
90
|
for (const [, file, body] of fixtures)
|
|
91
91
|
write(file, file.endsWith('.json') ? JSON.stringify(body) : body)
|
|
92
92
|
|
|
93
|
-
write('api/.GET.
|
|
93
|
+
write('api/.GET.500.txt', 'keeps non-autogenerated 500')
|
|
94
94
|
write('api/alternative(comment-2).GET.200.json', JSON.stringify({ comment: 2 }))
|
|
95
95
|
write('api/my-route(comment-2).GET.200.json', JSON.stringify({ comment: 2 }))
|
|
96
96
|
|
|
97
|
-
// These files ensure the server doesn’t crash. We don’t test their console.error
|
|
98
|
-
write('api/bad-filename.200.json', 'missing method')
|
|
99
|
-
write('api/bad-filename.GET.200', 'missing extension')
|
|
100
|
-
write('api/bad-filename.GET.json', 'missing response status')
|
|
101
|
-
|
|
102
97
|
writeStatic('index.html', '<h1>Static</h1>')
|
|
103
98
|
writeStatic('assets/app.js', 'const app = 1')
|
|
104
99
|
writeStatic('another-entry/index.html', '<h1>Another</h1>')
|
|
@@ -123,16 +118,17 @@ async function runTests() {
|
|
|
123
118
|
|
|
124
119
|
await testItUpdatesDelayAndFile(
|
|
125
120
|
'/api/alternative',
|
|
126
|
-
'api/alternative(comment-
|
|
121
|
+
'api/alternative(comment-2).GET.200.json',
|
|
122
|
+
JSON.stringify({ comment: 2 }))
|
|
127
123
|
|
|
128
|
-
await
|
|
124
|
+
await testAutogenerates500(
|
|
129
125
|
'/api/company-e/123?limit=9',
|
|
130
|
-
'api/company-e/[id]?limit=[limit].GET.
|
|
126
|
+
'api/company-e/[id]?limit=[limit].GET.500.txt')
|
|
131
127
|
|
|
132
|
-
await
|
|
128
|
+
await testPreservesExiting500(
|
|
133
129
|
'/api',
|
|
134
|
-
'api/.GET.
|
|
135
|
-
'keeps non-autogenerated
|
|
130
|
+
'api/.GET.500.txt',
|
|
131
|
+
'keeps non-autogenerated 500')
|
|
136
132
|
|
|
137
133
|
await reset()
|
|
138
134
|
await testItUpdatesTheCurrentSelectedMock(
|
|
@@ -148,12 +144,10 @@ async function runTests() {
|
|
|
148
144
|
'(this is the actual comment)',
|
|
149
145
|
'(another comment)'
|
|
150
146
|
])
|
|
151
|
-
await testItBulkSelectsByComment('(comment-2)',
|
|
152
|
-
[
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
]
|
|
156
|
-
)
|
|
147
|
+
await testItBulkSelectsByComment('(comment-2)', [
|
|
148
|
+
['/api/alternative', 'api/alternative(comment-2).GET.200.json', { comment: 2 }],
|
|
149
|
+
['/api/my-route', 'api/my-route(comment-2).GET.200.json', { comment: 2 }]
|
|
150
|
+
])
|
|
157
151
|
|
|
158
152
|
await reset()
|
|
159
153
|
for (const [url, file, body] of fixtures)
|
|
@@ -161,9 +155,8 @@ async function runTests() {
|
|
|
161
155
|
|
|
162
156
|
await testItUpdatesUserRole()
|
|
163
157
|
await testTransforms()
|
|
164
|
-
|
|
165
158
|
await testStaticFileServing()
|
|
166
|
-
|
|
159
|
+
await testInvalidFilenamesAreIgnored()
|
|
167
160
|
server.close()
|
|
168
161
|
}
|
|
169
162
|
|
|
@@ -208,7 +201,7 @@ async function testItUpdatesTheCurrentSelectedMock(url, file, expectedStatus, ex
|
|
|
208
201
|
})
|
|
209
202
|
}
|
|
210
203
|
|
|
211
|
-
async function testItUpdatesDelayAndFile(url, file) {
|
|
204
|
+
async function testItUpdatesDelayAndFile(url, file, expectedBody) {
|
|
212
205
|
await request(DP.edit, {
|
|
213
206
|
method: 'PATCH',
|
|
214
207
|
body: JSON.stringify({
|
|
@@ -217,35 +210,38 @@ async function testItUpdatesDelayAndFile(url, file) {
|
|
|
217
210
|
})
|
|
218
211
|
})
|
|
219
212
|
const now = new Date()
|
|
220
|
-
await request(url)
|
|
221
|
-
|
|
222
|
-
|
|
213
|
+
const res = await request(url)
|
|
214
|
+
const body = await res.text()
|
|
215
|
+
await describe('url: ' + url, () => {
|
|
216
|
+
it('body is: ' + expectedBody, () => equal(body, expectedBody))
|
|
217
|
+
it('delay is over 1 sec', () => equal((new Date()).getTime() - now.getTime() > 1000, true))
|
|
218
|
+
})
|
|
223
219
|
}
|
|
224
220
|
|
|
225
221
|
|
|
226
|
-
async function
|
|
222
|
+
async function testAutogenerates500(url, file) {
|
|
227
223
|
await request(DP.edit, {
|
|
228
224
|
method: 'PATCH',
|
|
229
225
|
body: JSON.stringify({ [DF.file]: file })
|
|
230
226
|
})
|
|
231
227
|
const res = await request(url)
|
|
232
228
|
const body = await res.text()
|
|
233
|
-
await describe('autogenerated
|
|
229
|
+
await describe('autogenerated 500', () => {
|
|
234
230
|
it('body is empty', () => equal(body, ''))
|
|
235
|
-
it('status is:
|
|
231
|
+
it('status is: 500', () => equal(res.status, 500))
|
|
236
232
|
})
|
|
237
233
|
}
|
|
238
234
|
|
|
239
|
-
async function
|
|
235
|
+
async function testPreservesExiting500(url, file, expectedBody) {
|
|
240
236
|
await request(DP.edit, {
|
|
241
237
|
method: 'PATCH',
|
|
242
238
|
body: JSON.stringify({ [DF.file]: file })
|
|
243
239
|
})
|
|
244
240
|
const res = await request(url)
|
|
245
241
|
const body = await res.text()
|
|
246
|
-
await describe('preserves existing
|
|
242
|
+
await describe('preserves existing 500', () => {
|
|
247
243
|
it('body is empty', () => equal(body, expectedBody))
|
|
248
|
-
it('status is:
|
|
244
|
+
it('status is: 500', () => equal(res.status, 500))
|
|
249
245
|
})
|
|
250
246
|
}
|
|
251
247
|
|
|
@@ -259,9 +255,7 @@ async function testExtractsAllComments(expected) {
|
|
|
259
255
|
async function testItBulkSelectsByComment(comment, tests) {
|
|
260
256
|
await request(DP.bulkSelect, {
|
|
261
257
|
method: 'PATCH',
|
|
262
|
-
body: JSON.stringify({
|
|
263
|
-
[DF.comment]: comment
|
|
264
|
-
})
|
|
258
|
+
body: JSON.stringify({ [DF.comment]: comment })
|
|
265
259
|
})
|
|
266
260
|
for (const [url, file, body] of tests)
|
|
267
261
|
await testMockDispatching(url, file, body)
|
|
@@ -305,9 +299,7 @@ export default function (mock, reqBody, config) {
|
|
|
305
299
|
await reset() // for registering the files
|
|
306
300
|
await request(DP.transform, {
|
|
307
301
|
method: 'PATCH',
|
|
308
|
-
body: JSON.stringify({
|
|
309
|
-
[DF.file]: 'api/transform.POST.200.mjs'
|
|
310
|
-
})
|
|
302
|
+
body: JSON.stringify({ [DF.file]: 'api/transform.POST.200.mjs' })
|
|
311
303
|
})
|
|
312
304
|
await testMockDispatching('/api/transform',
|
|
313
305
|
'api/transform.POST.200.json',
|
|
@@ -342,6 +334,22 @@ async function testStaticFileServing() {
|
|
|
342
334
|
})
|
|
343
335
|
}
|
|
344
336
|
|
|
337
|
+
async function testInvalidFilenamesAreIgnored() {
|
|
338
|
+
await it('Invalid filenames get skipped, so they don’t crash the server', async (t) => {
|
|
339
|
+
const consoleErrorSpy = t.mock.method(console, 'error')
|
|
340
|
+
consoleErrorSpy.mock.mockImplementation(() => {}) // so they don’t render in the test report
|
|
341
|
+
|
|
342
|
+
// An extension is needed for testing because of `Config.allowedExt`
|
|
343
|
+
write('api/_INVALID_FILENAME_CONVENTION_.json', '')
|
|
344
|
+
write('api/bad-filename.GET._INVALID_STATUS_.json', '')
|
|
345
|
+
write('api/bad-filename._INVALID_METHOD_.200.json', '')
|
|
346
|
+
await reset()
|
|
347
|
+
equal(consoleErrorSpy.mock.calls[0].arguments[0], 'Invalid Filename Convention')
|
|
348
|
+
equal(consoleErrorSpy.mock.calls[1].arguments[0], 'Invalid HTTP Response Status: "NaN"')
|
|
349
|
+
equal(consoleErrorSpy.mock.calls[2].arguments[0], 'Unrecognized HTTP Method: "_INVALID_METHOD_"')
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
|
|
345
353
|
|
|
346
354
|
// Utils
|
|
347
355
|
|
package/mockBrokersCollection.js
CHANGED
|
@@ -35,14 +35,13 @@ export function init() {
|
|
|
35
35
|
collection[method][urlMask].register(file)
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
forEachBroker(broker => broker.
|
|
38
|
+
forEachBroker(broker => broker.ensureItHas500())
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export const getAll = () => collection
|
|
42
|
-
export const getBroker = (method, urlMask) => collection[method][urlMask]
|
|
43
42
|
export const getBrokerByFilename = file => {
|
|
44
43
|
const { method, urlMask } = Route.parseFilename(file)
|
|
45
|
-
return
|
|
44
|
+
return collection[method][urlMask]
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
This is a plain text response for (/api/user).
|
|
2
|
+
|
|
3
|
+
In this case, it’s for mocking up a 500 - Internal Server Error.
|
|
4
|
+
|
|
5
|
+
This file could have been empty, or some JSON if it had a `.json` extension.
|
|
6
|
+
|
|
7
|
+
By the way, on initialization an 500 is auto-generated for routes that don’t have a 500.
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
This is a plain text response for (/api/user).
|
|
2
|
-
|
|
3
|
-
In this case, it’s for mocking up a 501 - Internal Server Error.
|
|
4
|
-
|
|
5
|
-
This file could have been empty, or some JSON if it had a `.json` extension.
|
|
6
|
-
|
|
7
|
-
By the way, on initialization an 501 is auto-generated for routes that don’t have a 501.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/sample-mocks/api/video/stat/[stat-id]/{[video-id].GET.501.txt → [video-id].GET.500.txt}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|