mockaton 12.7.1 → 13.0.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.
- package/README.md +53 -17
- package/index.d.ts +2 -13
- package/package.json +4 -9
- package/src/client/ApiCommander.js +7 -11
- package/src/client/ApiConstants.js +1 -4
- package/src/client/Filename.js +22 -22
- package/src/client/app-header.js +6 -6
- package/src/client/app-payload-viewer.js +52 -25
- package/src/client/app-store.js +11 -64
- package/src/client/app-store.test.js +15 -1
- package/src/client/app.css +54 -44
- package/src/client/app.js +28 -100
- package/src/client/{app-icons.js → graphics.js} +1 -1
- package/src/client/watcherDev.js +7 -7
- package/src/server/Api.js +4 -38
- package/src/server/MockBroker.js +16 -13
- package/src/server/MockDispatcher.js +18 -4
- package/src/server/Mockaton.js +1 -8
- package/src/server/Mockaton.test.config.js +1 -3
- package/src/server/Mockaton.test.js +98 -194
- package/src/server/ProxyRelay.js +26 -15
- package/src/server/Watcher.js +1 -33
- package/src/server/cli.js +9 -13
- package/src/server/cli.test.js +4 -5
- package/src/server/config.js +0 -7
- package/src/server/mockBrokersCollection.js +11 -13
- package/src/server/utils/mime.js +0 -1
- package/src/server/StaticDispatcher.js +0 -36
- package/src/server/staticCollection.js +0 -56
package/src/server/MockBroker.js
CHANGED
|
@@ -11,14 +11,15 @@ export class MockBroker {
|
|
|
11
11
|
this.file = '' // selected mock filename
|
|
12
12
|
this.mocks = [] // filenames
|
|
13
13
|
this.status = -1
|
|
14
|
+
this.isStatic = false // doesn’t follow filename convention
|
|
14
15
|
this.delayed = false
|
|
15
16
|
this.proxied = false
|
|
16
|
-
this.
|
|
17
|
+
this.autoStatus = 0
|
|
17
18
|
this.urlMaskMatches = new UrlMatcher(file).urlMaskMatches
|
|
18
19
|
this.register(file)
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
#
|
|
22
|
+
#isStatus = (file, status) => parseFilename(file).status === status
|
|
22
23
|
|
|
23
24
|
#sortMocks() {
|
|
24
25
|
this.mocks.sort()
|
|
@@ -27,7 +28,7 @@ export class MockBroker {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
register(file) {
|
|
30
|
-
if (this.
|
|
31
|
+
if (this.autoStatus && this.#isStatus(file, this.autoStatus))
|
|
31
32
|
this.selectFile(file)
|
|
32
33
|
this.mocks.push(file)
|
|
33
34
|
this.#sortMocks()
|
|
@@ -44,27 +45,29 @@ export class MockBroker {
|
|
|
44
45
|
hasMock = file => this.mocks.includes(file)
|
|
45
46
|
|
|
46
47
|
selectFile(filename) {
|
|
48
|
+
const { status, isStatic } = parseFilename(filename)
|
|
47
49
|
this.file = filename
|
|
50
|
+
this.status = status
|
|
51
|
+
this.isStatic = isStatic
|
|
48
52
|
this.proxied = false
|
|
49
|
-
this.
|
|
50
|
-
this.status = parseFilename(filename).status
|
|
53
|
+
this.autoStatus = 0
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
selectDefaultFile() {
|
|
54
57
|
this.selectFile(this.mocks[0])
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
const shouldUnset = this.
|
|
60
|
+
toggleStatus(status) {
|
|
61
|
+
const shouldUnset = this.autoStatus === status || (!this.autoStatus && this.status === status)
|
|
59
62
|
if (shouldUnset)
|
|
60
63
|
this.selectDefaultFile()
|
|
61
64
|
else {
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
this.selectFile(
|
|
65
|
+
const fStatus = this.mocks.find(f => parseFilename(f).status === status)
|
|
66
|
+
if (fStatus)
|
|
67
|
+
this.selectFile(fStatus)
|
|
65
68
|
else {
|
|
66
|
-
this.
|
|
67
|
-
this.status =
|
|
69
|
+
this.autoStatus = status
|
|
70
|
+
this.status = status
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
73
|
this.proxied = false
|
|
@@ -75,7 +78,7 @@ export class MockBroker {
|
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
setProxied(proxied) {
|
|
78
|
-
this.
|
|
81
|
+
this.autoStatus = 0
|
|
79
82
|
this.proxied = proxied
|
|
80
83
|
}
|
|
81
84
|
|
|
@@ -4,6 +4,7 @@ import { logger } from './utils/logger.js'
|
|
|
4
4
|
|
|
5
5
|
import { proxy } from './ProxyRelay.js'
|
|
6
6
|
import { cookie } from './cookie.js'
|
|
7
|
+
import { parseFilename } from '../client/Filename.js'
|
|
7
8
|
import { echoFilePlugin } from './MockDispatcherPlugins.js'
|
|
8
9
|
import { brokerByRoute } from './mockBrokersCollection.js'
|
|
9
10
|
import { config, calcDelay } from './config.js'
|
|
@@ -29,12 +30,25 @@ export async function dispatchMock(req, response) {
|
|
|
29
30
|
if (cookie.getCurrent())
|
|
30
31
|
response.setHeader('Set-Cookie', cookie.getCurrent())
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
const { isStatic } = parseFilename(broker.file)
|
|
34
|
+
|
|
35
|
+
if (isStatic && req.headers.range && !broker.autoStatus) {
|
|
36
|
+
setTimeout(async () => {
|
|
37
|
+
await response.partialContent(req.headers.range, join(config.mocksDir, broker.file))
|
|
38
|
+
}, Number(broker.delayed && calcDelay()))
|
|
39
|
+
logger.accessMock(req.url, broker.file)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
response.statusCode = broker.autoStatus
|
|
44
|
+
? broker.autoStatus
|
|
34
45
|
: broker.status
|
|
35
|
-
|
|
46
|
+
|
|
47
|
+
const { mime, body } = broker.autoStatus
|
|
36
48
|
? { mime: '', body: '' }
|
|
37
|
-
:
|
|
49
|
+
: isStatic
|
|
50
|
+
? echoFilePlugin(join(config.mocksDir, broker.file))
|
|
51
|
+
: await applyPlugins(join(config.mocksDir, broker.file), req, response)
|
|
38
52
|
|
|
39
53
|
response.setHeader('Content-Type', mime)
|
|
40
54
|
response.setHeader('Content-Length', length(body))
|
package/src/server/Mockaton.js
CHANGED
|
@@ -15,25 +15,21 @@ import { config, setup } from './config.js'
|
|
|
15
15
|
import { apiPatchReqs, apiGetReqs } from './Api.js'
|
|
16
16
|
|
|
17
17
|
import { dispatchMock } from './MockDispatcher.js'
|
|
18
|
-
import { dispatchStatic } from './StaticDispatcher.js'
|
|
19
18
|
|
|
20
|
-
import * as staticCollection from './staticCollection.js'
|
|
21
19
|
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
22
20
|
|
|
23
21
|
import { watchDevSPA } from './WatcherDevClient.js'
|
|
24
|
-
import { watchMocksDir
|
|
22
|
+
import { watchMocksDir } from './Watcher.js'
|
|
25
23
|
|
|
26
24
|
|
|
27
25
|
export function Mockaton(options) {
|
|
28
26
|
return new Promise((resolve, reject) => {
|
|
29
27
|
setup(options)
|
|
30
28
|
mockBrokerCollection.init()
|
|
31
|
-
staticCollection.init()
|
|
32
29
|
|
|
33
30
|
if (config.watcherEnabled) {
|
|
34
31
|
register('./cacheBustResolver.js', import.meta.url)
|
|
35
32
|
watchMocksDir()
|
|
36
|
-
watchStaticDir()
|
|
37
33
|
}
|
|
38
34
|
if (config.hotReload)
|
|
39
35
|
watchDevSPA()
|
|
@@ -84,9 +80,6 @@ async function onRequest(req, response) {
|
|
|
84
80
|
else if (method === 'GET' && apiGetReqs.has(pathname))
|
|
85
81
|
apiGetReqs.get(pathname)(req, response)
|
|
86
82
|
|
|
87
|
-
else if (method === 'GET' && staticCollection.brokerByRoute(pathname))
|
|
88
|
-
await dispatchStatic(req, response)
|
|
89
|
-
|
|
90
83
|
else
|
|
91
84
|
await dispatchMock(req, response)
|
|
92
85
|
}
|
|
@@ -6,9 +6,7 @@ export default {
|
|
|
6
6
|
userB: jwtCookie('CookieB', { email: 'john@example.test' }),
|
|
7
7
|
},
|
|
8
8
|
extraHeaders: ['custom_header_name', 'custom_header_val'],
|
|
9
|
-
extraMimes: {
|
|
10
|
-
['custom_extension']: 'custom_mime'
|
|
11
|
-
},
|
|
9
|
+
extraMimes: { ['custom_extension']: 'custom_mime' },
|
|
12
10
|
logLevel: 'verbose',
|
|
13
11
|
corsOrigins: ['https://example.test'],
|
|
14
12
|
corsExposedHeaders: ['Content-Encoding'],
|
|
@@ -7,7 +7,7 @@ import { mkdtempSync } from 'node:fs'
|
|
|
7
7
|
import { randomUUID } from 'node:crypto'
|
|
8
8
|
import { equal, deepEqual, match } from 'node:assert/strict'
|
|
9
9
|
import { describe, test, before, beforeEach, after } from 'node:test'
|
|
10
|
-
import { writeFile, unlink, mkdir, readFile, rename } from 'node:fs/promises'
|
|
10
|
+
import { writeFile, unlink, mkdir, readFile, rename, readdir } from 'node:fs/promises'
|
|
11
11
|
|
|
12
12
|
import { mimeFor } from './utils/mime.js'
|
|
13
13
|
import { parseFilename } from '../client/Filename.js'
|
|
@@ -17,14 +17,12 @@ import CONFIG from './Mockaton.test.config.js'
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
const mocksDir = mkdtempSync(join(tmpdir(), 'mocks'))
|
|
20
|
-
const staticDir = mkdtempSync(join(tmpdir(), 'static'))
|
|
21
20
|
|
|
22
21
|
const stdout = []
|
|
23
22
|
const stderr = []
|
|
24
23
|
const proc = spawn(join(import.meta.dirname, 'cli.js'), [
|
|
24
|
+
mocksDir,
|
|
25
25
|
'--config', join(import.meta.dirname, 'Mockaton.test.config.js'),
|
|
26
|
-
'--mocks-dir', mocksDir,
|
|
27
|
-
'--static-dir', staticDir,
|
|
28
26
|
'--no-open'
|
|
29
27
|
])
|
|
30
28
|
|
|
@@ -42,13 +40,13 @@ const serverAddr = await new Promise((resolve, reject) => {
|
|
|
42
40
|
after(() => proc.kill('SIGUSR2'))
|
|
43
41
|
|
|
44
42
|
|
|
43
|
+
const rmFromMocksDir = f => unlink(join(mocksDir, f))
|
|
44
|
+
const listFromMocksDir = d => readdir(join(mocksDir, d))
|
|
45
45
|
const readFromMocksDir = f => readFile(join(mocksDir, f), 'utf8')
|
|
46
46
|
|
|
47
47
|
const makeDirInMocks = dir => mkdir(join(mocksDir, dir), { recursive: true })
|
|
48
|
-
const makeDirInStaticMocks = dir => mkdir(join(staticDir, dir), { recursive: true })
|
|
49
48
|
|
|
50
49
|
const renameInMocksDir = (src, target) => rename(join(mocksDir, src), join(mocksDir, target))
|
|
51
|
-
const renameInStaticMocksDir = (src, target) => rename(join(staticDir, src), join(staticDir, target))
|
|
52
50
|
|
|
53
51
|
|
|
54
52
|
const api = new Commander(serverAddr)
|
|
@@ -126,7 +124,7 @@ class Fixture extends BaseFixture {
|
|
|
126
124
|
class FixtureStatic extends BaseFixture {
|
|
127
125
|
constructor(file, body = '') {
|
|
128
126
|
super(file, body)
|
|
129
|
-
this.dir =
|
|
127
|
+
this.dir = mocksDir
|
|
130
128
|
this.urlMask = '/' + file
|
|
131
129
|
this.method = 'GET'
|
|
132
130
|
}
|
|
@@ -147,26 +145,24 @@ describe('Windows', () => {
|
|
|
147
145
|
|
|
148
146
|
describe('Rejects malicious URLs', () => {
|
|
149
147
|
[
|
|
150
|
-
['double-encoded', `/${encodeURIComponent(encodeURIComponent('/'))}user
|
|
151
|
-
['encoded null byte', '/user%00/admin'
|
|
152
|
-
['invalid percent-encoding', '/user%ZZ'
|
|
153
|
-
['encoded CRLF sequence', '/user%0d%0aSet-Cookie:%20x=1'
|
|
154
|
-
['overlong/illegal UTF-8 sequence', '/user%C0%AF'
|
|
155
|
-
['double-double-encoding trick', '/%25252Fuser'
|
|
156
|
-
['zero-width/invisible char', '/user%E2%80%8Binfo'
|
|
157
|
-
['encoded path traversal', '/user/..%2Fadmin'
|
|
158
|
-
['raw path traversal', '/../user'
|
|
159
|
-
|
|
160
|
-
['very long path', '/'.repeat(2048 + 1), 414]
|
|
148
|
+
['double-encoded', 400, `/${encodeURIComponent(encodeURIComponent('/'))}user`],
|
|
149
|
+
['encoded null byte', 400, '/user%00/admin'],
|
|
150
|
+
['invalid percent-encoding', 400, '/user%ZZ'],
|
|
151
|
+
['encoded CRLF sequence', 400, '/user%0d%0aSet-Cookie:%20x=1'],
|
|
152
|
+
['overlong/illegal UTF-8 sequence', 400, '/user%C0%AF'],
|
|
153
|
+
['double-double-encoding trick', 400, '/%25252Fuser'],
|
|
154
|
+
['zero-width/invisible char', 404, '/user%E2%80%8Binfo'],
|
|
155
|
+
['encoded path traversal', 404, '/user/..%2Fadmin'],
|
|
156
|
+
['raw path traversal', 404, '/../user'],
|
|
157
|
+
['very long path', 414, '/'.repeat(2048 + 1)]
|
|
161
158
|
]
|
|
162
|
-
.
|
|
163
|
-
|
|
164
|
-
equal((await request(url)).status, status)))
|
|
159
|
+
.forEach(([title, status, url]) => test(title, async () =>
|
|
160
|
+
equal((await request(url)).status, status)))
|
|
165
161
|
})
|
|
166
162
|
|
|
167
163
|
|
|
168
|
-
describe('
|
|
169
|
-
test('
|
|
164
|
+
describe('Filename Convention', () => {
|
|
165
|
+
test('registers invalid filenames as GET 200', async () => {
|
|
170
166
|
const fx0 = new Fixture('bar.GET._INVALID_STATUS_.json')
|
|
171
167
|
const fx1 = new Fixture('foo._INVALID_METHOD_.202.json')
|
|
172
168
|
const fx2 = new Fixture('missing-method-and-status.json')
|
|
@@ -175,10 +171,10 @@ describe('Warnings', () => {
|
|
|
175
171
|
await fx2.write()
|
|
176
172
|
await api.reset()
|
|
177
173
|
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
174
|
+
const s = await fetchState()
|
|
175
|
+
equal(s.brokersByMethod.GET['/bar.GET._INVALID_STATUS_.json'].file, 'bar.GET._INVALID_STATUS_.json')
|
|
176
|
+
equal(s.brokersByMethod.GET['/foo._INVALID_METHOD_.202.json'].file, 'foo._INVALID_METHOD_.202.json')
|
|
177
|
+
equal(s.brokersByMethod.GET['/missing-method-and-status.json'].file, 'missing-method-and-status.json')
|
|
182
178
|
|
|
183
179
|
await fx0.unlink()
|
|
184
180
|
await fx1.unlink()
|
|
@@ -369,15 +365,17 @@ describe('Proxy Fallback', () => {
|
|
|
369
365
|
describe('Fallback', () => {
|
|
370
366
|
let fallbackServer
|
|
371
367
|
const CUSTOM_COOKIES = ['cookieX=x', 'cookieY=y']
|
|
372
|
-
const BODY_PAYLOAD = '
|
|
368
|
+
const BODY_PAYLOAD = { a: 'b' }
|
|
369
|
+
const expectedBody = JSON.stringify(BODY_PAYLOAD, null, ' ') // config.formatCollectedJSON=true
|
|
370
|
+
|
|
373
371
|
before(async () => {
|
|
374
372
|
fallbackServer = createServer(async (req, response) => {
|
|
375
373
|
response.writeHead(423, {
|
|
376
374
|
'custom_header': 'my_custom_header',
|
|
377
|
-
'content-type': mimeFor('.
|
|
375
|
+
'content-type': mimeFor('.json'),
|
|
378
376
|
'set-cookie': CUSTOM_COOKIES
|
|
379
377
|
})
|
|
380
|
-
response.end(BODY_PAYLOAD)
|
|
378
|
+
response.end(JSON.stringify(BODY_PAYLOAD))
|
|
381
379
|
})
|
|
382
380
|
await promisify(fallbackServer.listen).bind(fallbackServer, 0, '127.0.0.1')()
|
|
383
381
|
await api.setProxyFallback(`http://localhost:${fallbackServer.address().port}`)
|
|
@@ -386,15 +384,31 @@ describe('Proxy Fallback', () => {
|
|
|
386
384
|
|
|
387
385
|
after(() => fallbackServer.close())
|
|
388
386
|
|
|
389
|
-
test('Relays to fallback server and saves the mock', async () => {
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
equal(
|
|
394
|
-
equal(
|
|
387
|
+
test('Relays to fallback server and saves the mock (we req twice, so the second one gets a unique comment)', async () => {
|
|
388
|
+
const r1 = await request(`/non-existing-mock/${randomUUID()}`, { method: 'POST' })
|
|
389
|
+
const r2 = await request(`/non-existing-mock/${randomUUID()}`, { method: 'POST' })
|
|
390
|
+
|
|
391
|
+
equal(r1.status, 423)
|
|
392
|
+
equal(r2.status, 423)
|
|
393
|
+
|
|
394
|
+
equal(r1.headers.get('custom_header'), 'my_custom_header')
|
|
395
|
+
equal(r2.headers.get('custom_header'), 'my_custom_header')
|
|
396
|
+
|
|
397
|
+
equal(r1.headers.get('set-cookie'), CUSTOM_COOKIES.join(', '))
|
|
398
|
+
equal(r2.headers.get('set-cookie'), CUSTOM_COOKIES.join(', '))
|
|
399
|
+
|
|
400
|
+
deepEqual(await r2.json(), BODY_PAYLOAD)
|
|
401
|
+
deepEqual(await r1.json(), BODY_PAYLOAD)
|
|
395
402
|
|
|
396
|
-
const
|
|
397
|
-
equal(
|
|
403
|
+
const savedMocks = await listFromMocksDir('non-existing-mock')
|
|
404
|
+
equal(savedMocks.length, 2)
|
|
405
|
+
|
|
406
|
+
equal(await readFromMocksDir('non-existing-mock/[id].POST.423.json'), expectedBody)
|
|
407
|
+
for (const m of savedMocks) {
|
|
408
|
+
const f = join('non-existing-mock', m)
|
|
409
|
+
equal(await readFromMocksDir(f), expectedBody)
|
|
410
|
+
await rmFromMocksDir(f)
|
|
411
|
+
}
|
|
398
412
|
})
|
|
399
413
|
})
|
|
400
414
|
|
|
@@ -479,20 +493,20 @@ describe('Proxy Fallback', () => {
|
|
|
479
493
|
equal((await r.json()).proxied, false)
|
|
480
494
|
})
|
|
481
495
|
|
|
482
|
-
test('unsets
|
|
496
|
+
test('unsets autoStatus', async () => {
|
|
483
497
|
const fx = new Fixture('unset-500-on-proxy.GET.200.txt')
|
|
484
498
|
await fx.sync()
|
|
485
499
|
await api.setProxyFallback('https://example.test')
|
|
486
500
|
|
|
487
|
-
const r0 = await api.
|
|
501
|
+
const r0 = await api.toggleStatus(500, fx.method, fx.urlMask)
|
|
488
502
|
const b0 = await r0.json()
|
|
489
503
|
equal(b0.proxied, false)
|
|
490
|
-
equal(b0.
|
|
504
|
+
equal(b0.autoStatus, 500)
|
|
491
505
|
|
|
492
506
|
const r1 = await api.setRouteIsProxied(fx.method, fx.urlMask, true)
|
|
493
507
|
const b1 = await r1.json()
|
|
494
508
|
equal(b1.proxied, true)
|
|
495
|
-
equal(b1.
|
|
509
|
+
equal(b1.autoStatus, 0)
|
|
496
510
|
|
|
497
511
|
await fx.unlink()
|
|
498
512
|
await api.setProxyFallback('')
|
|
@@ -535,14 +549,6 @@ describe('404', () => {
|
|
|
535
549
|
await fx.unlink()
|
|
536
550
|
})
|
|
537
551
|
|
|
538
|
-
test('404s ignored static files', async () => {
|
|
539
|
-
const fx = new FixtureStatic('static-ignored.js~')
|
|
540
|
-
await fx.write()
|
|
541
|
-
await api.reset()
|
|
542
|
-
const r = await fx.request()
|
|
543
|
-
equal(r.status, 404)
|
|
544
|
-
await fx.unlink()
|
|
545
|
-
})
|
|
546
552
|
})
|
|
547
553
|
|
|
548
554
|
|
|
@@ -642,7 +648,7 @@ describe('Static Files', () => {
|
|
|
642
648
|
await fxsIndex.write()
|
|
643
649
|
await fxsAsset.write()
|
|
644
650
|
await api.reset()
|
|
645
|
-
})
|
|
651
|
+
})
|
|
646
652
|
|
|
647
653
|
describe('Static File Serving', () => {
|
|
648
654
|
test('Defaults to index.html', async () => {
|
|
@@ -660,64 +666,10 @@ describe('Static Files', () => {
|
|
|
660
666
|
})
|
|
661
667
|
})
|
|
662
668
|
|
|
663
|
-
test('
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
fxsIndex.urlMask
|
|
668
|
-
])
|
|
669
|
-
})
|
|
670
|
-
|
|
671
|
-
describe('Set Static Route is Delayed', () => {
|
|
672
|
-
test('422 for non-existing route', async () => {
|
|
673
|
-
const r = await api.setStaticRouteIsDelayed('/non-existing', true)
|
|
674
|
-
equal(r.status, 422)
|
|
675
|
-
equal(await r.text(), `Static route does not exist: /non-existing`)
|
|
676
|
-
})
|
|
677
|
-
|
|
678
|
-
test('422 for invalid delayed value', async () => {
|
|
679
|
-
const r = await api.setStaticRouteIsDelayed(fxsIndex.urlMask, 'not-a-boolean')
|
|
680
|
-
equal(await r.text(), 'Expected boolean for "delayed"')
|
|
681
|
-
})
|
|
682
|
-
|
|
683
|
-
test('200', async () => {
|
|
684
|
-
await api.setStaticRouteIsDelayed(fxsIndex.urlMask, true)
|
|
685
|
-
const { staticBrokers } = await fetchState()
|
|
686
|
-
equal(staticBrokers[fxsIndex.urlMask].delayed, true)
|
|
687
|
-
})
|
|
688
|
-
})
|
|
689
|
-
|
|
690
|
-
describe('Set Static Route Status Code', () => {
|
|
691
|
-
test('422 for non-existing route', async () => {
|
|
692
|
-
const r = await api.setStaticRouteStatus('/non-existing', 200)
|
|
693
|
-
equal(r.status, 422)
|
|
694
|
-
equal(await r.text(), `Static route does not exist: /non-existing`)
|
|
695
|
-
})
|
|
696
|
-
|
|
697
|
-
test('422 for invalid delayed value', async () => {
|
|
698
|
-
const r = await api.setStaticRouteStatus(fxsIndex.urlMask, 'not-200-or-404')
|
|
699
|
-
equal(r.status, 422)
|
|
700
|
-
equal(await r.text(), 'Expected 200 or 404 status code')
|
|
701
|
-
})
|
|
702
|
-
|
|
703
|
-
test('sets 404 and 200', async () => {
|
|
704
|
-
await api.setStaticRouteStatus(fxsIndex.urlMask, 404)
|
|
705
|
-
const r0 = await fxsIndex.request()
|
|
706
|
-
equal(r0.status, 404)
|
|
707
|
-
|
|
708
|
-
await api.setStaticRouteStatus(fxsIndex.urlMask, 200)
|
|
709
|
-
const r1 = await fxsIndex.request()
|
|
710
|
-
equal(r1.status, 200)
|
|
711
|
-
})
|
|
712
|
-
|
|
713
|
-
test('404s on a registered route but its file has been deleted', async () => {
|
|
714
|
-
// Possible: (1) due to watcher delay. (2) or, when not-watching and deleting.
|
|
715
|
-
const fx = new FixtureStatic('to-be-deleted.js')
|
|
716
|
-
await fx.sync()
|
|
717
|
-
await fx.unlink()
|
|
718
|
-
const r = await fx.request()
|
|
719
|
-
equal(r.status, 404)
|
|
720
|
-
})
|
|
669
|
+
test('are part of the normal mocks list', async () => {
|
|
670
|
+
const s = await fetchState()
|
|
671
|
+
equal(s.brokersByMethod.GET[fxsAsset.urlMask].file, fxsAsset.file)
|
|
672
|
+
equal(s.brokersByMethod.GET[fxsIndex.urlMask].file, fxsIndex.file)
|
|
721
673
|
})
|
|
722
674
|
|
|
723
675
|
describe('Static Partial Content', () => {
|
|
@@ -741,27 +693,27 @@ describe('Static Files', () => {
|
|
|
741
693
|
await fxsIndex.unlink()
|
|
742
694
|
await fxsAsset.unlink()
|
|
743
695
|
await api.reset()
|
|
744
|
-
const
|
|
745
|
-
equal(
|
|
746
|
-
equal(
|
|
696
|
+
const s = await fetchState()
|
|
697
|
+
equal(s.brokersByMethod.GET?.[fxsIndex.urlMask], undefined)
|
|
698
|
+
equal(s.brokersByMethod.GET?.[fxsAsset.urlMask], undefined)
|
|
747
699
|
})
|
|
748
700
|
})
|
|
749
701
|
|
|
750
702
|
|
|
751
|
-
describe('
|
|
703
|
+
describe('Auto Status', () => {
|
|
752
704
|
test('toggling ON 500 on a route without 500 auto-generates one', async () => {
|
|
753
705
|
const fx = new Fixture('toggling-500-without-500.GET.200.json')
|
|
754
706
|
await fx.sync()
|
|
755
707
|
equal((await fx.request()).status, fx.status)
|
|
756
708
|
|
|
757
|
-
const bp0 = await api.
|
|
709
|
+
const bp0 = await api.toggleStatus(500, fx.method, fx.urlMask)
|
|
758
710
|
const b0 = await bp0.json()
|
|
759
|
-
equal(b0.
|
|
711
|
+
equal(b0.autoStatus, 500)
|
|
760
712
|
equal(b0.status, 500)
|
|
761
713
|
equal((await fx.request()).status, 500)
|
|
762
714
|
|
|
763
|
-
const r1 = await api.
|
|
764
|
-
equal((await r1.json()).
|
|
715
|
+
const r1 = await api.toggleStatus(500, fx.method, fx.urlMask)
|
|
716
|
+
equal((await r1.json()).autoStatus, 0)
|
|
765
717
|
equal((await fx.request()).status, fx.status)
|
|
766
718
|
})
|
|
767
719
|
|
|
@@ -772,15 +724,15 @@ describe('500', () => {
|
|
|
772
724
|
await fx500.write()
|
|
773
725
|
await api.reset()
|
|
774
726
|
|
|
775
|
-
const bp0 = await api.
|
|
727
|
+
const bp0 = await api.toggleStatus(500, fx200.method, fx200.urlMask)
|
|
776
728
|
const b0 = await bp0.json()
|
|
777
|
-
equal(b0.
|
|
729
|
+
equal(b0.autoStatus, 0)
|
|
778
730
|
equal(b0.status, 500)
|
|
779
731
|
equal(await (await fx200.request()).text(), fx500.body)
|
|
780
732
|
|
|
781
|
-
const bp1 = await api.
|
|
733
|
+
const bp1 = await api.toggleStatus(500, fx200.method, fx200.urlMask)
|
|
782
734
|
const b1 = await bp1.json()
|
|
783
|
-
equal(b0.
|
|
735
|
+
equal(b0.autoStatus, 0)
|
|
784
736
|
equal(b1.status, 200)
|
|
785
737
|
equal(await (await fx200.request()).text(), fx200.body)
|
|
786
738
|
|
|
@@ -793,11 +745,29 @@ describe('500', () => {
|
|
|
793
745
|
await fx.sync()
|
|
794
746
|
await api.setProxyFallback('https://example.test')
|
|
795
747
|
await api.setRouteIsProxied(fx.method, fx.urlMask, true)
|
|
796
|
-
await api.
|
|
748
|
+
await api.toggleStatus(500, fx.method, fx.urlMask)
|
|
797
749
|
equal((await fx.fetchBroker()).proxied, false)
|
|
798
750
|
await fx.unlink()
|
|
799
751
|
await api.setProxyFallback('')
|
|
800
752
|
})
|
|
753
|
+
|
|
754
|
+
test('toggling ON 404 for static routes', async () => {
|
|
755
|
+
const fx = new FixtureStatic('static-404.txt')
|
|
756
|
+
await fx.sync()
|
|
757
|
+
equal((await fx.request()).status, 200)
|
|
758
|
+
|
|
759
|
+
const bp0 = await api.toggleStatus(404, fx.method, fx.urlMask)
|
|
760
|
+
const b0 = await bp0.json()
|
|
761
|
+
equal(b0.autoStatus, 404)
|
|
762
|
+
equal(b0.status, 404)
|
|
763
|
+
equal((await fx.request()).status, 404)
|
|
764
|
+
|
|
765
|
+
const r1 = await api.toggleStatus(404, fx.method, fx.urlMask)
|
|
766
|
+
equal((await r1.json()).autoStatus, 0)
|
|
767
|
+
equal((await fx.request()).status, 200)
|
|
768
|
+
|
|
769
|
+
await fx.unlink()
|
|
770
|
+
})
|
|
801
771
|
})
|
|
802
772
|
|
|
803
773
|
|
|
@@ -1090,16 +1060,16 @@ describe('Registering Mocks', () => {
|
|
|
1090
1060
|
equal(b, undefined)
|
|
1091
1061
|
})
|
|
1092
1062
|
|
|
1093
|
-
test('registering a 500 unsets
|
|
1063
|
+
test('registering a 500 unsets autoStatus', async () => {
|
|
1094
1064
|
const fx200 = new Fixture('reg-error.GET.200.txt')
|
|
1095
1065
|
const fx500 = new Fixture('reg-error.GET.500.txt')
|
|
1096
1066
|
await fx200.register()
|
|
1097
|
-
await api.
|
|
1067
|
+
await api.toggleStatus(500, fx200.method, fx200.urlMask)
|
|
1098
1068
|
const b0 = await fx200.fetchBroker()
|
|
1099
|
-
equal(b0.
|
|
1069
|
+
equal(b0.autoStatus, 500)
|
|
1100
1070
|
await fx500.register()
|
|
1101
1071
|
const b1 = await fx200.fetchBroker()
|
|
1102
|
-
equal(b1.
|
|
1072
|
+
equal(b1.autoStatus, 0)
|
|
1103
1073
|
deepEqual(b1.mocks, [
|
|
1104
1074
|
fx200.file,
|
|
1105
1075
|
fx500.file
|
|
@@ -1142,76 +1112,10 @@ describe('Registering Mocks', () => {
|
|
|
1142
1112
|
})
|
|
1143
1113
|
|
|
1144
1114
|
|
|
1145
|
-
describe('Registering Static Mocks', () => {
|
|
1146
|
-
test('when watcher is off, newly added mocks do not get registered', async () => {
|
|
1147
|
-
await api.setWatchMocks(false)
|
|
1148
|
-
const fx = new FixtureStatic('non-auto-registered-file.txt')
|
|
1149
|
-
await fx.write()
|
|
1150
|
-
await sleep()
|
|
1151
|
-
const { staticBrokers } = await fetchState()
|
|
1152
|
-
equal(staticBrokers['/' + fx.file], undefined)
|
|
1153
|
-
await fx.unlink()
|
|
1154
|
-
})
|
|
1155
|
-
|
|
1156
|
-
const fx = new FixtureStatic('static-register.txt', 'static-body')
|
|
1157
|
-
test('registers static', async () => {
|
|
1158
|
-
await api.setWatchMocks(true)
|
|
1159
|
-
await fx.register()
|
|
1160
|
-
const { staticBrokers } = await fetchState()
|
|
1161
|
-
deepEqual(staticBrokers, {
|
|
1162
|
-
['/' + fx.file]: {
|
|
1163
|
-
route: '/' + fx.file,
|
|
1164
|
-
status: 200,
|
|
1165
|
-
delayed: false
|
|
1166
|
-
}
|
|
1167
|
-
})
|
|
1168
|
-
const response = await fx.request()
|
|
1169
|
-
equal(response.status, 200)
|
|
1170
|
-
equal(await response.text(), fx.body)
|
|
1171
|
-
})
|
|
1172
|
-
|
|
1173
|
-
test('unregisters static', async () => {
|
|
1174
|
-
await fx.unregister()
|
|
1175
|
-
const { staticBrokers } = await fetchState()
|
|
1176
|
-
deepEqual(staticBrokers, {})
|
|
1177
|
-
})
|
|
1178
|
-
|
|
1179
|
-
describe('getSyncVersion', () => {
|
|
1180
|
-
const fx0 = new FixtureStatic('reg0/static0.txt')
|
|
1181
|
-
let version
|
|
1182
|
-
before(async () => {
|
|
1183
|
-
await makeDirInStaticMocks('reg0')
|
|
1184
|
-
await fx0.sync()
|
|
1185
|
-
version = await resolveOnNextSyncVersion(-1)
|
|
1186
|
-
})
|
|
1187
|
-
|
|
1188
|
-
const fx = new FixtureStatic('static1.txt')
|
|
1189
|
-
test('responds when a file is added', async () => {
|
|
1190
|
-
const prom = resolveOnNextSyncVersion(version)
|
|
1191
|
-
await fx.write()
|
|
1192
|
-
equal(await prom, version + 1)
|
|
1193
|
-
})
|
|
1194
|
-
|
|
1195
|
-
test('responds when a file is deleted', async () => {
|
|
1196
|
-
const prom = resolveOnNextSyncVersion(version + 1)
|
|
1197
|
-
await fx.unlink()
|
|
1198
|
-
equal(await prom, version + 2)
|
|
1199
|
-
})
|
|
1200
|
-
|
|
1201
|
-
test('responds when dir is renamed', async () => {
|
|
1202
|
-
const p0 = resolveOnNextSyncVersion(version + 2)
|
|
1203
|
-
await renameInStaticMocksDir('reg0', 'reg1')
|
|
1204
|
-
equal(await p0, version + 3)
|
|
1205
|
-
|
|
1206
|
-
const s = await fetchState()
|
|
1207
|
-
equal(s.staticBrokers['/reg1/static0.txt'].route, '/reg1/static0.txt')
|
|
1208
|
-
})
|
|
1209
|
-
})
|
|
1210
|
-
})
|
|
1211
1115
|
|
|
1212
1116
|
|
|
1213
|
-
|
|
1214
|
-
|
|
1117
|
+
function sleep(ms = 100) {
|
|
1118
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
1215
1119
|
}
|
|
1216
1120
|
|
|
1217
1121
|
|