@toa.io/userland 0.2.1-dev.3
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/example/components/echo/beacon/manifest.toa.yaml +9 -0
- package/example/components/echo/beacon/operations/signal.js +7 -0
- package/example/components/echo/beacon/samples/signal.yaml +7 -0
- package/example/components/math/calculations/manifest.toa.yaml +16 -0
- package/example/components/math/calculations/operations/add.js +7 -0
- package/example/components/math/calculations/operations/increment.js +15 -0
- package/example/components/math/calculations/samples/add.yaml +11 -0
- package/example/components/math/calculations/samples/increment.yaml +30 -0
- package/example/components/math/proxy/manifest.toa.yaml +11 -0
- package/example/components/math/proxy/operations/add.js +10 -0
- package/example/components/math/proxy/samples/add.yaml +11 -0
- package/example/components/tea/pots/manifest.toa.yaml +28 -0
- package/example/components/tea/pots/operations/same.js +13 -0
- package/example/components/tea/pots/receivers/store.orders.created.js +8 -0
- package/example/components/tea/pots/samples/messages/store.orders.created.yaml +37 -0
- package/example/components/tea/pots/samples/same.yaml +8 -0
- package/example/components/tea/pots/samples/transit.yaml +43 -0
- package/example/context.toa.yaml +3 -0
- package/example/samples/math.proxy.add.yaml +5 -0
- package/example/samples/messages/store.orders.created.yaml +8 -0
- package/example/stage/call.test.js +39 -0
- package/example/stage/events.test.js +48 -0
- package/example/stage/invoke.test.js +29 -0
- package/package.json +36 -0
- package/readme.md +9 -0
- package/samples/docs/sampling-dark.jpg +0 -0
- package/samples/docs/sampling-light.jpg +0 -0
- package/samples/notes.md +12 -0
- package/samples/package.json +5 -0
- package/samples/readme.md +66 -0
- package/samples/src/.replay/.suite/component.js +25 -0
- package/samples/src/.replay/.suite/index.js +7 -0
- package/samples/src/.replay/.suite/messages.js +39 -0
- package/samples/src/.replay/.suite/operation.js +28 -0
- package/samples/src/.replay/.suite/operations.js +18 -0
- package/samples/src/.replay/.suite/translate/.message/index.js +5 -0
- package/samples/src/.replay/.suite/translate/.message/request.js +31 -0
- package/samples/src/.replay/.suite/translate/.operation/calls.js +49 -0
- package/samples/src/.replay/.suite/translate/.operation/cleanup.js +16 -0
- package/samples/src/.replay/.suite/translate/.operation/events.js +16 -0
- package/samples/src/.replay/.suite/translate/.operation/index.js +11 -0
- package/samples/src/.replay/.suite/translate/.operation/prepare.js +14 -0
- package/samples/src/.replay/.suite/translate/.operation/specials/configuration.js +26 -0
- package/samples/src/.replay/.suite/translate/.operation/specials/index.js +5 -0
- package/samples/src/.replay/.suite/translate/index.js +7 -0
- package/samples/src/.replay/.suite/translate/message.js +26 -0
- package/samples/src/.replay/.suite/translate/operation.js +47 -0
- package/samples/src/.replay/.suite/translate/schemas/index.js +7 -0
- package/samples/src/.replay/.suite/translate/schemas/message.cos.yaml +10 -0
- package/samples/src/.replay/.suite/translate/schemas/operation.cos.yaml +23 -0
- package/samples/src/.replay/index.js +5 -0
- package/samples/src/.replay/suite.js +22 -0
- package/samples/src/.replay/test.js +17 -0
- package/samples/src/components.js +13 -0
- package/samples/src/context.js +18 -0
- package/samples/src/index.js +9 -0
- package/samples/src/replay.js +17 -0
- package/samples/src/suite/.read/index.js +7 -0
- package/samples/src/suite/.read/messages.js +35 -0
- package/samples/src/suite/.read/operations.js +45 -0
- package/samples/src/suite/.read/parse.js +24 -0
- package/samples/src/suite/components.js +28 -0
- package/samples/src/suite/context.js +19 -0
- package/samples/src/suite/index.js +7 -0
- package/samples/test/components.test.js +39 -0
- package/samples/test/context/components/ok/manifest.toa.yaml +2 -0
- package/samples/test/context/components/ok/samples/do.yaml +11 -0
- package/samples/test/context/components/ok/samples/dummies.dummy.do.yaml +11 -0
- package/samples/test/context/components/ok/samples/dummies.dummy.undo.yaml +7 -0
- package/samples/test/context/components/ok/samples/messages/somewhere.something.happened.yaml +6 -0
- package/samples/test/context/context.toa.yaml +3 -0
- package/samples/test/context/samples/dummies.dummy.observe.yaml +6 -0
- package/samples/test/context/samples/dummies.dummy.transit.yaml +10 -0
- package/samples/test/context/samples/messages/somewhere.something.happened.yaml +6 -0
- package/samples/test/context.fixtures.js +8 -0
- package/samples/test/context.test.js +78 -0
- package/samples/test/replay.fixtures.js +72 -0
- package/samples/test/replay.mock.js +7 -0
- package/samples/test/replay.test.js +116 -0
- package/samples/test/replay.translate.message.fixtures.js +24 -0
- package/samples/test/replay.translate.message.test.js +123 -0
- package/samples/test/replay.translate.mock.js +17 -0
- package/samples/test/replay.translate.operation.fixtures.js +107 -0
- package/samples/test/replay.translate.operation.test.js +68 -0
- package/samples/test/stage.mock.js +34 -0
- package/samples/test/suite.components.test.js +85 -0
- package/samples/test/suite.context.test.js +79 -0
- package/samples/test/suite.mock.js +10 -0
- package/samples/types/index.d.ts +8 -0
- package/samples/types/message.d.ts +24 -0
- package/samples/types/operation.d.ts +36 -0
- package/samples/types/replay.d.ts +13 -0
- package/samples/types/suite.d.ts +19 -0
- package/stage/package.json +5 -0
- package/stage/readme.md +41 -0
- package/stage/src/binding/binding.js +49 -0
- package/stage/src/binding/consumer.js +30 -0
- package/stage/src/binding/emitter.js +26 -0
- package/stage/src/binding/factory.js +49 -0
- package/stage/src/binding/index.js +10 -0
- package/stage/src/binding/label.js +12 -0
- package/stage/src/binding/producer.js +43 -0
- package/stage/src/binding/receiver.js +35 -0
- package/stage/src/component.js +18 -0
- package/stage/src/composition.js +19 -0
- package/stage/src/index.js +18 -0
- package/stage/src/manifest.js +12 -0
- package/stage/src/remote.js +22 -0
- package/stage/src/shutdown.js +19 -0
- package/stage/src/state.js +17 -0
- package/stage/test/binding/binding.test.js +15 -0
- package/stage/test/binding/consumer.test.js +52 -0
- package/stage/test/binding/emitter.test.js +54 -0
- package/stage/test/binding/producer.fixtures.js +10 -0
- package/stage/test/binding/producer.test.js +49 -0
- package/stage/test/binding/receiver.fixtures.js +7 -0
- package/stage/test/binding/receiver.test.js +54 -0
- package/stage/test/binding.mock.js +8 -0
- package/stage/test/boot.mock.js +16 -0
- package/stage/test/component.test.js +25 -0
- package/stage/test/composition.test.js +38 -0
- package/stage/test/manifest.test.js +24 -0
- package/stage/test/remote.test.js +32 -0
- package/stage/test/shutdown.test.js +66 -0
- package/stage/test/state.mock.js +8 -0
- package/stage/test/state.test.js +27 -0
- package/stage/types/binding.d.ts +20 -0
- package/stage/types/index.d.ts +28 -0
- package/stage/types/state.d.ts +14 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { resolve } = require('node:path')
|
|
4
|
+
const { directory: { glob } } = require('@toa.io/filesystem')
|
|
5
|
+
|
|
6
|
+
const fixtures = require('./context.fixtures')
|
|
7
|
+
const { suite } = require('./suite.mock')
|
|
8
|
+
const { replay } = require('./replay.mock')
|
|
9
|
+
|
|
10
|
+
const mock = { suite, replay, ...fixtures.mock }
|
|
11
|
+
|
|
12
|
+
jest.mock('../src/suite', () => mock.suite)
|
|
13
|
+
jest.mock('../src/replay', () => mock.replay)
|
|
14
|
+
jest.mock('../src/components', () => mock.components)
|
|
15
|
+
|
|
16
|
+
const { context } = require('../src/context')
|
|
17
|
+
|
|
18
|
+
it('should be', async () => {
|
|
19
|
+
expect(context).toBeDefined()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const CONTEXT = resolve(__dirname, './context')
|
|
23
|
+
const COMPONENTS = resolve(CONTEXT, 'components/*')
|
|
24
|
+
|
|
25
|
+
/** @type {string[]} */
|
|
26
|
+
let paths
|
|
27
|
+
|
|
28
|
+
/** @type {boolean} */
|
|
29
|
+
let ok
|
|
30
|
+
|
|
31
|
+
beforeAll(async () => {
|
|
32
|
+
paths = await glob(COMPONENTS)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
beforeEach(async () => {
|
|
36
|
+
jest.clearAllMocks()
|
|
37
|
+
|
|
38
|
+
mock.components.components.mockImplementation(async () => true)
|
|
39
|
+
|
|
40
|
+
ok = await context(CONTEXT)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should replay context components sample sets', async () => {
|
|
44
|
+
expect(mock.components.components).toHaveBeenCalledWith(paths)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should load integration suite', async () => {
|
|
48
|
+
expect(mock.suite.context).toHaveBeenCalledWith(CONTEXT)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should replay integration suite', async () => {
|
|
52
|
+
const suite = await mock.suite.context.mock.results[0].value
|
|
53
|
+
|
|
54
|
+
expect(replay.replay).toHaveBeenCalledWith(suite, paths)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should return false if components replay failed', async () => {
|
|
58
|
+
jest.clearAllMocks()
|
|
59
|
+
|
|
60
|
+
mock.components.components.mockImplementation(async () => false)
|
|
61
|
+
|
|
62
|
+
ok = await context(CONTEXT)
|
|
63
|
+
|
|
64
|
+
expect(ok).toStrictEqual(false)
|
|
65
|
+
expect(replay.replay).not.toHaveBeenCalled()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should return replay result if components replay ok', async () => {
|
|
69
|
+
jest.clearAllMocks()
|
|
70
|
+
|
|
71
|
+
mock.components.components.mockImplementation(async () => true)
|
|
72
|
+
|
|
73
|
+
ok = await context(CONTEXT)
|
|
74
|
+
|
|
75
|
+
const mocked = await mock.replay.replay.mock.results[0].value
|
|
76
|
+
|
|
77
|
+
expect(ok).toStrictEqual(mocked)
|
|
78
|
+
})
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { generate } = require('randomstring')
|
|
4
|
+
const { random } = require('@toa.io/generic')
|
|
5
|
+
|
|
6
|
+
/** @type {toa.samples.Suite} */
|
|
7
|
+
const suite = { autonomous: true, operations: {} }
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @returns {toa.samples.Operation[]}
|
|
11
|
+
*/
|
|
12
|
+
const ops = () => {
|
|
13
|
+
const samples = []
|
|
14
|
+
|
|
15
|
+
for (let i = 0; i < random(3) + 1; i++) {
|
|
16
|
+
const sample = {
|
|
17
|
+
input: generate(),
|
|
18
|
+
output: generate(),
|
|
19
|
+
local: generate(),
|
|
20
|
+
current: generate(),
|
|
21
|
+
next: generate()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
samples.push(sample)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return samples
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @returns {toa.samples.Message[]}
|
|
32
|
+
*/
|
|
33
|
+
const msgs = () => {
|
|
34
|
+
/** @type {toa.samples.Message[]} */
|
|
35
|
+
const samples = []
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < random(3) + 1; i++) {
|
|
38
|
+
const sample = /** @type {toa.samples.Message} */ {
|
|
39
|
+
component: generate(),
|
|
40
|
+
payload: generate(),
|
|
41
|
+
input: generate()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
samples.push(sample)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return samples
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// components
|
|
51
|
+
for (let i = 0; i < random(3) + 1; i++) {
|
|
52
|
+
const id = generate() + '.' + generate()
|
|
53
|
+
const operations = {}
|
|
54
|
+
const messages = {}
|
|
55
|
+
|
|
56
|
+
for (let j = 0; j < random(3) + 1; j++) {
|
|
57
|
+
const endpoint = generate()
|
|
58
|
+
const label = generate()
|
|
59
|
+
|
|
60
|
+
operations[endpoint] = ops()
|
|
61
|
+
messages[label] = msgs()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
suite.operations[id] = operations
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** @type {string} */
|
|
68
|
+
const label = generate()
|
|
69
|
+
|
|
70
|
+
suite.messages = { [label]: msgs() }
|
|
71
|
+
|
|
72
|
+
exports.suite = suite
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { resolve } = require('node:path')
|
|
4
|
+
const clone = require('clone-deep')
|
|
5
|
+
|
|
6
|
+
const { stage } = require('./stage.mock')
|
|
7
|
+
const { translate } = require('./replay.translate.mock')
|
|
8
|
+
const mock = { stage, translate }
|
|
9
|
+
|
|
10
|
+
jest.mock('@toa.io/userland/stage', () => mock.stage)
|
|
11
|
+
jest.mock('../src/.replay/.suite/translate', () => mock.translate)
|
|
12
|
+
|
|
13
|
+
const fixtures = require('./replay.fixtures')
|
|
14
|
+
const { replay } = require('../src')
|
|
15
|
+
|
|
16
|
+
it('should be', () => {
|
|
17
|
+
expect(replay).toBeDefined()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
let ok
|
|
21
|
+
|
|
22
|
+
/** @type {toa.samples.Suite} */
|
|
23
|
+
let suite
|
|
24
|
+
|
|
25
|
+
const path = resolve(__dirname, '../../example/components/math/calculations')
|
|
26
|
+
const paths = [path]
|
|
27
|
+
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
jest.clearAllMocks()
|
|
30
|
+
|
|
31
|
+
suite = clone(fixtures.suite)
|
|
32
|
+
ok = await replay(suite, paths)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should boot composition', () => {
|
|
36
|
+
expect(stage.composition).toHaveBeenCalled()
|
|
37
|
+
expect(stage.composition).toHaveBeenCalledWith(paths)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('should invoke operations with translated samples', async () => {
|
|
41
|
+
const translation = (declaration) => {
|
|
42
|
+
const find = (call) => call[0].input === declaration.input
|
|
43
|
+
const index = mock.translate.operation.mock.calls.findIndex(find)
|
|
44
|
+
|
|
45
|
+
return mock.translate.operation.mock.results[index].value
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} component
|
|
50
|
+
* @return {Promise<jest.MockedObject<toa.core.Component>>}
|
|
51
|
+
*/
|
|
52
|
+
const find = async (component) => {
|
|
53
|
+
for (let n = 0; n < stage.remote.mock.calls.length; n++) {
|
|
54
|
+
const call = stage.remote.mock.calls[n]
|
|
55
|
+
|
|
56
|
+
if (call[0] === component) return stage.remote.mock.results[n].value
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
throw new Error(`Remote ${component} hasn't been connected`)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const [id, set] of Object.entries(fixtures.suite.operations)) {
|
|
63
|
+
const remote = await find(id)
|
|
64
|
+
|
|
65
|
+
for (const [endpoint, samples] of Object.entries(set)) {
|
|
66
|
+
for (const sample of samples) {
|
|
67
|
+
const request = translation(sample)
|
|
68
|
+
|
|
69
|
+
expect(remote.invoke).toHaveBeenCalledWith(endpoint, request)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should emit translated messages', async () => {
|
|
76
|
+
/**
|
|
77
|
+
* @param {toa.samples.Message} declaration
|
|
78
|
+
* @param {string} id
|
|
79
|
+
* @returns {{index: number, message: toa.sampling.Message}}
|
|
80
|
+
*/
|
|
81
|
+
const translation = (declaration) => {
|
|
82
|
+
const find = (call) => call[0].payload === declaration.payload
|
|
83
|
+
const index = mock.translate.message.mock.calls.findIndex(find)
|
|
84
|
+
const message = mock.translate.message.mock.results[index].value
|
|
85
|
+
const call = mock.translate.message.mock.calls[index]
|
|
86
|
+
|
|
87
|
+
expect(call[1]).toStrictEqual(fixtures.suite.autonomous)
|
|
88
|
+
|
|
89
|
+
return { index, message }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const [label, samples] of Object.entries(fixtures.suite.messages)) {
|
|
93
|
+
for (const sample of samples) {
|
|
94
|
+
const { index, message } = translation(sample)
|
|
95
|
+
|
|
96
|
+
expect(stage.binding.binding.emit)
|
|
97
|
+
.toHaveBeenNthCalledWith(index + 1, label, message)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should shutdown stage', () => {
|
|
103
|
+
expect(stage.shutdown).toHaveBeenCalled()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('should return results', () => {
|
|
107
|
+
expect(ok).toStrictEqual(true)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it.each(['messages', 'operations'])('should not throw if no %s defined', async (key) => {
|
|
111
|
+
delete suite[key]
|
|
112
|
+
|
|
113
|
+
ok = await replay(suite, paths)
|
|
114
|
+
|
|
115
|
+
expect(ok).toStrictEqual(true)
|
|
116
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { generate } = require('randomstring')
|
|
4
|
+
const { flip } = require('@toa.io/generic')
|
|
5
|
+
|
|
6
|
+
/** @type {toa.samples.Message} */
|
|
7
|
+
const message = {
|
|
8
|
+
title: generate(),
|
|
9
|
+
payload: { [generate()]: generate() },
|
|
10
|
+
input: generate(),
|
|
11
|
+
query: { id: generate() },
|
|
12
|
+
request: {
|
|
13
|
+
current: {
|
|
14
|
+
id: generate()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const autonomous = flip()
|
|
20
|
+
const component = generate()
|
|
21
|
+
|
|
22
|
+
exports.message = message
|
|
23
|
+
exports.autonomous = autonomous
|
|
24
|
+
exports.component = component
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const clone = require('clone-deep')
|
|
4
|
+
|
|
5
|
+
const fixtures = require('./replay.translate.message.fixtures')
|
|
6
|
+
const { message: translate } = require('../src/.replay/.suite/translate')
|
|
7
|
+
const { generate } = require('randomstring')
|
|
8
|
+
|
|
9
|
+
it('should be', async () => {
|
|
10
|
+
expect(translate).toBeDefined()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
/** @type {toa.samples.Message} */
|
|
14
|
+
let declaration
|
|
15
|
+
|
|
16
|
+
/** @type {toa.sampling.Message} */
|
|
17
|
+
let message
|
|
18
|
+
|
|
19
|
+
/** @type {toa.sampling.messages.Sample} */
|
|
20
|
+
let sample
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
jest.clearAllMocks()
|
|
24
|
+
|
|
25
|
+
declaration = clone(fixtures.message)
|
|
26
|
+
message = translate(declaration, fixtures.autonomous, fixtures.component)
|
|
27
|
+
sample = message.sample
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('should copy payload', async () => {
|
|
31
|
+
expect(message.payload).toStrictEqual(declaration.payload)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should copy title', async () => {
|
|
35
|
+
expect(sample.request.title).toStrictEqual(declaration.title)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should copy autonomous', async () => {
|
|
39
|
+
expect(sample.request.autonomous).toStrictEqual(fixtures.autonomous)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should copy component', async () => {
|
|
43
|
+
expect(sample.component).toStrictEqual(fixtures.component)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it.each(['input', 'query'])('should copy %s to request', async (key) => {
|
|
47
|
+
const value = declaration[key]
|
|
48
|
+
|
|
49
|
+
expect(sample.request.request[key]).toStrictEqual(value)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it.each(['input', 'query'])('should create request with %s', async (key) => {
|
|
53
|
+
const value = declaration[key]
|
|
54
|
+
|
|
55
|
+
delete declaration.request
|
|
56
|
+
|
|
57
|
+
const { sample } = translate(declaration, fixtures.autonomous, fixtures.component)
|
|
58
|
+
|
|
59
|
+
expect(sample.request.request[key]).toStrictEqual(value)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should keep request properties', async () => {
|
|
63
|
+
expect(sample.request.storage.current).toMatchObject(fixtures.message.request.current)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should add `terminate: true` if request is not defined (and sample is autonomous)',
|
|
67
|
+
async () => {
|
|
68
|
+
delete declaration.request
|
|
69
|
+
|
|
70
|
+
message = translate(declaration, fixtures.autonomous, fixtures.component)
|
|
71
|
+
|
|
72
|
+
expect(message.sample.request.terminate).toStrictEqual(true)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should not create undefined keys in request', async () => {
|
|
76
|
+
delete declaration.input
|
|
77
|
+
|
|
78
|
+
message = translate(declaration, fixtures.autonomous, fixtures.component)
|
|
79
|
+
|
|
80
|
+
expect('input' in message.sample.request.request).toStrictEqual(false)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should declare sample as authentic', async () => {
|
|
84
|
+
expect(sample.authentic).toStrictEqual(true)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
describe('validation', () => {
|
|
88
|
+
/** @type {toa.samples.Message} */
|
|
89
|
+
let declaration
|
|
90
|
+
|
|
91
|
+
const check = () => translate(declaration, fixtures.autonomous, fixtures.component)
|
|
92
|
+
|
|
93
|
+
beforeEach(() => {
|
|
94
|
+
declaration = clone(fixtures.message)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('should throw on schema mismatch', async () => {
|
|
98
|
+
// noinspection JSValidateTypes
|
|
99
|
+
declaration.title = { foo: 1 }
|
|
100
|
+
|
|
101
|
+
expect(check).toThrow('must be string')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('should throw on request schema mismatch', async () => {
|
|
105
|
+
// noinspection JSValidateTypes
|
|
106
|
+
declaration.request = { title: { foo: 1 } }
|
|
107
|
+
|
|
108
|
+
expect(check).toThrow('must be string')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it.each(['input', 'query'])('should throw if request.%s is defined', async (key) => {
|
|
112
|
+
expect.assertions(1)
|
|
113
|
+
|
|
114
|
+
declaration[key] = { id: generate() }
|
|
115
|
+
declaration.request = /** @type {toa.samples.Operation} */ { [key]: { id: generate() } }
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
check()
|
|
119
|
+
} catch (exception) {
|
|
120
|
+
expect(exception).toMatchObject({ path: '/request' })
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { generate } = require('randomstring')
|
|
4
|
+
|
|
5
|
+
// noinspection JSCheckFunctionSignatures
|
|
6
|
+
const operation = jest.fn((declaration) => {
|
|
7
|
+
const { input, ...sample } = declaration
|
|
8
|
+
|
|
9
|
+
return { input, sample }
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
// noinspection JSCheckFunctionSignatures
|
|
13
|
+
const message = jest.fn((declaration) => generate())
|
|
14
|
+
|
|
15
|
+
const translate = { operation, message }
|
|
16
|
+
|
|
17
|
+
exports.translate = translate
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { generate } = require('randomstring')
|
|
4
|
+
const { flip } = require('@toa.io/generic')
|
|
5
|
+
|
|
6
|
+
const title = generate()
|
|
7
|
+
const input = generate()
|
|
8
|
+
const output = generate()
|
|
9
|
+
const reply = { output }
|
|
10
|
+
|
|
11
|
+
const context = {}
|
|
12
|
+
|
|
13
|
+
context.declaration = {
|
|
14
|
+
local: {
|
|
15
|
+
do: {
|
|
16
|
+
input: generate(),
|
|
17
|
+
output: generate()
|
|
18
|
+
},
|
|
19
|
+
undo: [
|
|
20
|
+
{
|
|
21
|
+
input: generate()
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
output: generate()
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
context.sample = {
|
|
31
|
+
local: {
|
|
32
|
+
do: [
|
|
33
|
+
{
|
|
34
|
+
request: {
|
|
35
|
+
input: context.declaration.local.do.input
|
|
36
|
+
},
|
|
37
|
+
reply: {
|
|
38
|
+
output: context.declaration.local.do.output
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
undo: [
|
|
43
|
+
{
|
|
44
|
+
request: {
|
|
45
|
+
input: context.declaration.local.undo[0].input
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
reply: {
|
|
50
|
+
output: context.declaration.local.undo[1].output
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const storage = {}
|
|
58
|
+
|
|
59
|
+
storage.declaration = {
|
|
60
|
+
current: flip() ? { foo: generate() } : [{ foo: generate() }], // object or array
|
|
61
|
+
next: { foo: generate() }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
storage.sample = {
|
|
65
|
+
current: storage.declaration.current,
|
|
66
|
+
next: storage.declaration.next
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const events = { declaration: {}, sample: {} }
|
|
70
|
+
const label = generate()
|
|
71
|
+
|
|
72
|
+
events.declaration[label] = { [generate()]: generate() }
|
|
73
|
+
events.sample[label] = { payload: events.declaration[label] }
|
|
74
|
+
|
|
75
|
+
const extension = () => ([{ permanent: flip() }])
|
|
76
|
+
|
|
77
|
+
/** @type {toa.samples.Extensions} */
|
|
78
|
+
const extensions = { [generate()]: extension() }
|
|
79
|
+
|
|
80
|
+
/** @type {toa.samples.Operation} */
|
|
81
|
+
const declaration = {
|
|
82
|
+
title,
|
|
83
|
+
input,
|
|
84
|
+
output,
|
|
85
|
+
...context.declaration,
|
|
86
|
+
...storage.declaration,
|
|
87
|
+
events: events.declaration,
|
|
88
|
+
extensions
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** @type {toa.sampling.Request} */
|
|
92
|
+
const expected = {
|
|
93
|
+
input,
|
|
94
|
+
sample: {
|
|
95
|
+
authentic: true,
|
|
96
|
+
autonomous: true,
|
|
97
|
+
title,
|
|
98
|
+
reply,
|
|
99
|
+
context: context.sample,
|
|
100
|
+
storage: storage.sample,
|
|
101
|
+
events: events.sample,
|
|
102
|
+
extensions
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
exports.declaration = declaration
|
|
107
|
+
exports.expected = expected
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const clone = require('clone-deep')
|
|
4
|
+
const { generate } = require('randomstring')
|
|
5
|
+
|
|
6
|
+
const fixtures = require('./replay.translate.operation.fixtures')
|
|
7
|
+
const { operation: translate } = require('../src/.replay/.suite/translate')
|
|
8
|
+
|
|
9
|
+
it('should be', () => {
|
|
10
|
+
expect(translate).toBeDefined()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
/** @type {toa.samples.Operation} */
|
|
14
|
+
let declaration
|
|
15
|
+
|
|
16
|
+
/** @type {toa.sampling.Request} */
|
|
17
|
+
let request
|
|
18
|
+
|
|
19
|
+
/** @type {toa.sampling.request.Sample} */
|
|
20
|
+
let expected
|
|
21
|
+
|
|
22
|
+
const autonomous = true
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
declaration = clone(fixtures.declaration)
|
|
26
|
+
expected = clone(fixtures.expected)
|
|
27
|
+
request = translate(declaration, autonomous)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('should translate input', () => {
|
|
31
|
+
expect(request).toStrictEqual(expected)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('validation', () => {
|
|
35
|
+
it('should not allow additional properties', () => {
|
|
36
|
+
declaration.foo = generate()
|
|
37
|
+
|
|
38
|
+
expect(() => translate(declaration, true)).toThrow('must NOT have additional properties')
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('specials', () => {
|
|
43
|
+
it('should transform configuration object to permanent sample', async () => {
|
|
44
|
+
const configuration = { foo: generate() }
|
|
45
|
+
const declaration = /** @type {toa.samples.Operation} */ { configuration }
|
|
46
|
+
const request = translate(declaration, true)
|
|
47
|
+
|
|
48
|
+
expect(request.sample.extensions.configuration).toStrictEqual([{
|
|
49
|
+
result: configuration,
|
|
50
|
+
permanent: true
|
|
51
|
+
}])
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should throw if configuration is provided twice', async () => {
|
|
55
|
+
const configuration = { foo: generate() }
|
|
56
|
+
|
|
57
|
+
const declaration = {
|
|
58
|
+
configuration,
|
|
59
|
+
extensions: {
|
|
60
|
+
configuration: [{
|
|
61
|
+
result: configuration
|
|
62
|
+
}]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
expect(() => translate(declaration, true)).toThrow('ambiguous')
|
|
67
|
+
})
|
|
68
|
+
})
|