@toa.io/userland 1.2.0-dev.12 → 1.2.0-dev.15
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/samples/signal.yaml +0 -3
- package/example/components/math.calculations/samples/increment.yaml +3 -6
- package/package.json +10 -9
- package/samples/readme.md +1 -1
- package/samples/src/.replay/.suite/component.js +3 -8
- package/samples/src/.replay/.suite/operation.js +1 -1
- package/samples/src/.replay/.suite/operations.js +5 -2
- package/samples/src/.replay/index.js +2 -0
- package/samples/src/.replay/stage.js +60 -0
- package/samples/src/.replay/suite.js +3 -2
- package/samples/src/.replay/test.js +3 -2
- package/samples/src/context.js +1 -1
- package/samples/src/replay.js +4 -5
- package/samples/src/suite/components.js +3 -3
- package/samples/test/replay.test.js +104 -67
- package/samples/test/stage.mock.js +10 -14
- package/samples/test/suite.components.test.js +5 -31
- package/samples/types/suite.d.ts +17 -16
- package/stage/src/component.js +7 -2
- package/stage/test/component.test.js +1 -1
- package/stage/types/index.d.ts +2 -1
- package/example/components/tea.pots/samples/messages/store.orders.created.yaml +0 -37
- package/samples/test/replay.fixtures.js +0 -72
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
title: Should increment number
|
|
2
2
|
input:
|
|
3
3
|
value: 1
|
|
4
|
+
times: 1
|
|
4
5
|
output: 2
|
|
5
6
|
local:
|
|
6
7
|
add:
|
|
@@ -9,12 +10,7 @@ local:
|
|
|
9
10
|
b: 1
|
|
10
11
|
output: 2
|
|
11
12
|
---
|
|
12
|
-
title: Should increment
|
|
13
|
-
input:
|
|
14
|
-
value: 2
|
|
15
|
-
output: 3
|
|
16
|
-
---
|
|
17
|
-
title: Should increment twice (with second actual call)
|
|
13
|
+
title: Should increment twice
|
|
18
14
|
input:
|
|
19
15
|
value: 0
|
|
20
16
|
times: 2
|
|
@@ -28,3 +24,4 @@ local:
|
|
|
28
24
|
- input:
|
|
29
25
|
a: 1
|
|
30
26
|
b: 1
|
|
27
|
+
output: 2
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/userland",
|
|
3
|
-
"version": "1.2.0-dev.
|
|
3
|
+
"version": "1.2.0-dev.15",
|
|
4
4
|
"description": "Toa development kit",
|
|
5
5
|
"homepage": "https://toa.io",
|
|
6
6
|
"author": {
|
|
@@ -24,14 +24,15 @@
|
|
|
24
24
|
"main": "src/index.js",
|
|
25
25
|
"types": "types/index.d.ts",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@toa.io/boot": "1.0
|
|
28
|
-
"@toa.io/core": "1.1.0-dev.
|
|
29
|
-
"@toa.io/filesystem": "1.0
|
|
30
|
-
"@toa.io/generic": "0.11.0-dev.
|
|
31
|
-
"@toa.io/norm": "1.0.2-dev.
|
|
32
|
-
"@toa.io/schemas": "0.8.4-dev.
|
|
33
|
-
"@toa.io/
|
|
27
|
+
"@toa.io/boot": "1.1.0-dev.1",
|
|
28
|
+
"@toa.io/core": "1.1.0-dev.15",
|
|
29
|
+
"@toa.io/filesystem": "1.1.0-dev.1",
|
|
30
|
+
"@toa.io/generic": "0.11.0-dev.15",
|
|
31
|
+
"@toa.io/norm": "1.0.2-dev.15",
|
|
32
|
+
"@toa.io/schemas": "0.8.4-dev.15",
|
|
33
|
+
"@toa.io/storages.null": "0.6.0",
|
|
34
|
+
"@toa.io/yaml": "0.7.6-dev.15",
|
|
34
35
|
"tap": "16.3.4"
|
|
35
36
|
},
|
|
36
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "f479f6e79d5caaa207da4d0a3192425def1abf75"
|
|
37
38
|
}
|
package/samples/readme.md
CHANGED
|
@@ -36,7 +36,7 @@ Message Sample is an object containing receiver's input (`payload`) to be substi
|
|
|
36
36
|
outcomes (`input` and `query`) to be verified. Message sample may contain the corresponding operation
|
|
37
37
|
sample. See its [schema](./src/.replay/.suite/translate/schemas/message.cos.yaml).
|
|
38
38
|
|
|
39
|
-
> Message samples are
|
|
39
|
+
> Message samples are only supported at [context level](#autonomy).
|
|
40
40
|
|
|
41
41
|
### Declaration
|
|
42
42
|
|
|
@@ -5,21 +5,16 @@ const stage = require('@toa.io/userland/stage')
|
|
|
5
5
|
const { operation } = require('./operation')
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
* @param {string} id
|
|
8
|
+
* @param {toa.core.Component} component
|
|
10
9
|
* @param {toa.samples.operations.Set} set
|
|
11
10
|
* @param {boolean} autonomous
|
|
12
11
|
* @returns {Function}
|
|
13
12
|
*/
|
|
14
|
-
const component = (
|
|
13
|
+
const component = (component, set, autonomous) =>
|
|
15
14
|
async (test) => {
|
|
16
|
-
const remote = await stage.remote(id)
|
|
17
|
-
|
|
18
15
|
for (const [endpoint, samples] of Object.entries(set)) {
|
|
19
|
-
await test.test(endpoint, operation(
|
|
16
|
+
await test.test(endpoint, operation(component, endpoint, samples, autonomous))
|
|
20
17
|
}
|
|
21
|
-
|
|
22
|
-
await remote.disconnect()
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
exports.component = component
|
|
@@ -19,7 +19,7 @@ const operation = (remote, endpoint, samples, autonomous) =>
|
|
|
19
19
|
const request = translate.operation(operation, autonomous)
|
|
20
20
|
const name = operation.title ?? 'Sample #' + n
|
|
21
21
|
|
|
22
|
-
await test.test(name, async () => remote.invoke(endpoint, request))
|
|
22
|
+
await test.test(name, async () => await remote.invoke(endpoint, request))
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
test.end()
|
|
@@ -4,13 +4,16 @@ const { component } = require('./component')
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @param {toa.samples.suite.Operations} operations
|
|
7
|
+
* @param {Record<string, toa.core.Component>} components
|
|
7
8
|
* @param {boolean} autonomous
|
|
8
9
|
* @return {Function}
|
|
9
10
|
*/
|
|
10
|
-
const operations = (operations, autonomous) =>
|
|
11
|
+
const operations = (operations, components, autonomous) =>
|
|
11
12
|
async (test) => {
|
|
12
13
|
for (const [id, set] of Object.entries(operations)) {
|
|
13
|
-
|
|
14
|
+
const remote = components[id]
|
|
15
|
+
|
|
16
|
+
await test.test(id, component(remote, set, autonomous))
|
|
14
17
|
}
|
|
15
18
|
}
|
|
16
19
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const stage = require('@toa.io/userland/stage')
|
|
4
|
+
const storage = require.resolve('@toa.io/storages.null')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {string[]} paths
|
|
8
|
+
* @param {boolean} autonomous
|
|
9
|
+
* @return {Promise<Record<string, toa.core.Component>>}
|
|
10
|
+
*/
|
|
11
|
+
async function setup (paths, autonomous) {
|
|
12
|
+
if (!autonomous) return remotes(paths)
|
|
13
|
+
else return components(paths)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function remotes (paths) {
|
|
17
|
+
await stage.composition(paths)
|
|
18
|
+
|
|
19
|
+
const remotes = {}
|
|
20
|
+
|
|
21
|
+
for (const path of paths) {
|
|
22
|
+
const id = await getId(path)
|
|
23
|
+
|
|
24
|
+
remotes[id] = await stage.remote(id)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return remotes
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function components (paths) {
|
|
31
|
+
const components = {}
|
|
32
|
+
|
|
33
|
+
process.env.TOA_SAMPLING_AUTONOMOUS = '1'
|
|
34
|
+
|
|
35
|
+
for (const path of paths) {
|
|
36
|
+
const id = await getId(path)
|
|
37
|
+
|
|
38
|
+
components[id] = await stage.component(path, { storage })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
delete process.env.TOA_SAMPLING_AUTONOMOUS
|
|
42
|
+
|
|
43
|
+
return components
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function getId (path) {
|
|
47
|
+
const manifest = await stage.manifest(path)
|
|
48
|
+
|
|
49
|
+
return manifest.locator.id
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @return {Promise<void>}
|
|
54
|
+
*/
|
|
55
|
+
async function shutdown () {
|
|
56
|
+
await stage.shutdown()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
exports.setup = setup
|
|
60
|
+
exports.shutdown = shutdown
|
|
@@ -4,12 +4,13 @@ const replay = require('./.suite')
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @param {toa.samples.Suite} suite
|
|
7
|
+
* @param {Record<string, toa.core.Component>} components
|
|
7
8
|
* @returns {Function}
|
|
8
9
|
*/
|
|
9
|
-
const suite = (suite) =>
|
|
10
|
+
const suite = (suite, components) =>
|
|
10
11
|
async (test) => {
|
|
11
12
|
if ('operations' in suite) {
|
|
12
|
-
await test.test('Operations', replay.operations(suite.operations, suite.autonomous))
|
|
13
|
+
await test.test('Operations', replay.operations(suite.operations, components, suite.autonomous))
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
if ('messages' in suite) {
|
|
@@ -6,11 +6,12 @@ const replay = require('./suite')
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @param {toa.samples.Suite} suite
|
|
9
|
+
* @param {Record<string, toa.core.Component>} components
|
|
9
10
|
* @param {object} options
|
|
10
11
|
* @return {Promise<boolean>}
|
|
11
12
|
*/
|
|
12
|
-
const test = async (suite, options) => {
|
|
13
|
-
const result = await tap.test(suite.title, options, replay.suite(suite))
|
|
13
|
+
const test = async (suite, components, options) => {
|
|
14
|
+
const result = await tap.test(suite.title, options, replay.suite(suite, components))
|
|
14
15
|
|
|
15
16
|
return result === null ? false : result.ok
|
|
16
17
|
}
|
package/samples/src/context.js
CHANGED
|
@@ -9,8 +9,8 @@ const { replay } = require('./replay')
|
|
|
9
9
|
/** @type {toa.samples.replay.context} */
|
|
10
10
|
const context = async (path, options = {}) => {
|
|
11
11
|
const context = await norm.context(path)
|
|
12
|
-
const paths = context.components.map((component) => component.path)
|
|
13
12
|
const suite = await load(path, options)
|
|
13
|
+
const paths = context.components.map((component) => component.path)
|
|
14
14
|
|
|
15
15
|
let ok = true
|
|
16
16
|
|
package/samples/src/replay.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const stage = require('
|
|
4
|
-
const { test } = require('./.replay')
|
|
3
|
+
const { test, stage } = require('./.replay')
|
|
5
4
|
|
|
6
5
|
/** @type {toa.samples.replay.replay} */
|
|
7
|
-
const replay = async (suite, paths, options) => {
|
|
8
|
-
await stage.
|
|
6
|
+
const replay = async (suite, paths, options = undefined) => {
|
|
7
|
+
const components = await stage.setup(paths, suite.autonomous)
|
|
9
8
|
|
|
10
|
-
const ok = await test(suite, options)
|
|
9
|
+
const ok = await test(suite, components, options)
|
|
11
10
|
|
|
12
11
|
await stage.shutdown()
|
|
13
12
|
|
|
@@ -12,7 +12,7 @@ const read = require('./.read')
|
|
|
12
12
|
*/
|
|
13
13
|
const components = async (paths, options = {}) => {
|
|
14
14
|
/** @type {toa.samples.Suite} */
|
|
15
|
-
const suite = { title: 'Component samples', autonomous: true, operations: {}
|
|
15
|
+
const suite = { title: 'Component samples', autonomous: true, operations: {} }
|
|
16
16
|
|
|
17
17
|
for (const path of paths) {
|
|
18
18
|
const manifest = await norm.component(path)
|
|
@@ -20,9 +20,9 @@ const components = async (paths, options = {}) => {
|
|
|
20
20
|
options.id = manifest.locator.id
|
|
21
21
|
|
|
22
22
|
const operations = await read.operations(path, options)
|
|
23
|
-
const messages = await read.messages(path, options)
|
|
23
|
+
// const messages = await read.messages(path, options)
|
|
24
24
|
|
|
25
|
-
merge(suite, { operations
|
|
25
|
+
merge(suite, { operations })
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
return suite
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { resolve } = require('node:path')
|
|
4
|
-
const
|
|
4
|
+
const { directory } = require('@toa.io/filesystem')
|
|
5
5
|
|
|
6
6
|
const { stage } = require('./stage.mock')
|
|
7
7
|
const { translate } = require('./replay.translate.mock')
|
|
@@ -10,7 +10,8 @@ const mock = { stage, translate }
|
|
|
10
10
|
jest.mock('@toa.io/userland/stage', () => mock.stage)
|
|
11
11
|
jest.mock('../src/.replay/.suite/translate', () => mock.translate)
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const load = require('../src/suite')
|
|
14
|
+
|
|
14
15
|
const { replay } = require('../src')
|
|
15
16
|
|
|
16
17
|
it('should be', () => {
|
|
@@ -22,94 +23,130 @@ let ok
|
|
|
22
23
|
/** @type {toa.samples.Suite} */
|
|
23
24
|
let suite
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
const paths = [path]
|
|
27
|
-
|
|
28
|
-
beforeEach(async () => {
|
|
26
|
+
beforeEach(() => {
|
|
29
27
|
jest.clearAllMocks()
|
|
30
|
-
|
|
31
|
-
suite = clone(fixtures.suite)
|
|
32
|
-
ok = await replay(suite, paths)
|
|
33
28
|
})
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
describe('autonomous', () => {
|
|
31
|
+
let paths
|
|
32
|
+
|
|
33
|
+
beforeEach(async () => {
|
|
34
|
+
const path = resolve(__dirname, '../../example/components/tea.pots')
|
|
35
|
+
|
|
36
|
+
paths = [path]
|
|
37
|
+
suite = await load.components(paths)
|
|
38
|
+
ok = await replay(suite, paths)
|
|
39
|
+
})
|
|
39
40
|
|
|
40
|
-
it('should
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const index = mock.translate.operation.mock.calls.findIndex(find)
|
|
41
|
+
it('should not boot composition for autonomous suite', async () => {
|
|
42
|
+
expect(stage.composition).not.toHaveBeenCalled()
|
|
43
|
+
})
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
it('should invoke operations with translated samples', async () => {
|
|
46
|
+
const component = await stage.component.mock.results[0].value
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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]
|
|
48
|
+
for (const set of Object.values(suite.operations)) {
|
|
49
|
+
for (const [endpoint, samples] of Object.entries(set)) {
|
|
50
|
+
for (const sample of samples) {
|
|
51
|
+
const request = translation(sample)
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
expect(component.invoke).toHaveBeenCalledWith(endpoint, request)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
57
56
|
}
|
|
57
|
+
})
|
|
58
|
+
})
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
describe('integration', () => {
|
|
61
|
+
let paths
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
const
|
|
63
|
+
beforeEach(async () => {
|
|
64
|
+
const path = resolve(__dirname, '../../example')
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
paths = await directory.directories(resolve(path, 'components'))
|
|
67
|
+
suite = await load.context(path)
|
|
68
|
+
ok = await replay(suite, paths)
|
|
69
|
+
})
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
it('should boot composition', () => {
|
|
72
|
+
expect(stage.composition).toHaveBeenCalled()
|
|
73
|
+
expect(stage.composition).toHaveBeenCalledWith(paths)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('should invoke operations with translated samples', async () => {
|
|
77
|
+
/**
|
|
78
|
+
* @param {string} component
|
|
79
|
+
* @return {jest.MockedObject<toa.core.Component>}
|
|
80
|
+
*/
|
|
81
|
+
const getRemote = (component) => {
|
|
82
|
+
for (let n = 0; n < stage.remote.mock.calls.length; n++) {
|
|
83
|
+
const call = stage.remote.mock.calls[n]
|
|
84
|
+
|
|
85
|
+
if (call[0] === component) return stage.remote.mock.results[n].value
|
|
70
86
|
}
|
|
87
|
+
|
|
88
|
+
throw new Error(`Remote ${component} hasn't been connected`)
|
|
71
89
|
}
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
90
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
* @param {toa.samples.Message} declaration
|
|
78
|
-
* @returns {{index: number, message: toa.sampling.Message}}
|
|
79
|
-
*/
|
|
80
|
-
const translation = (declaration) => {
|
|
81
|
-
const find = (call) => call[0].payload === declaration.payload
|
|
82
|
-
const index = mock.translate.message.mock.calls.findIndex(find)
|
|
83
|
-
const message = mock.translate.message.mock.results[index].value
|
|
84
|
-
const call = mock.translate.message.mock.calls[index]
|
|
91
|
+
for (const [id, set] of Object.entries(suite.operations)) {
|
|
92
|
+
const remote = await getRemote(id)
|
|
85
93
|
|
|
86
|
-
|
|
94
|
+
for (const [endpoint, samples] of Object.entries(set)) {
|
|
95
|
+
for (const sample of samples) {
|
|
96
|
+
const request = translation(sample)
|
|
87
97
|
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
expect(remote.invoke).toHaveBeenCalledWith(endpoint, request)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('should emit translated messages', async () => {
|
|
105
|
+
/**
|
|
106
|
+
* @param {toa.samples.Message} declaration
|
|
107
|
+
* @returns {{index: number, message: toa.sampling.Message}}
|
|
108
|
+
*/
|
|
109
|
+
const translation = (declaration) => {
|
|
110
|
+
const find = (call) => call[0].payload === declaration.payload
|
|
111
|
+
const index = mock.translate.message.mock.calls.findIndex(find)
|
|
112
|
+
const message = mock.translate.message.mock.results[index].value
|
|
113
|
+
const call = mock.translate.message.mock.calls[index]
|
|
114
|
+
|
|
115
|
+
expect(call[1]).toStrictEqual(suite.autonomous)
|
|
116
|
+
|
|
117
|
+
return { index, message }
|
|
118
|
+
}
|
|
90
119
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
120
|
+
for (const [label, samples] of Object.entries(suite.messages)) {
|
|
121
|
+
for (const sample of samples) {
|
|
122
|
+
const { index, message } = translation(sample)
|
|
94
123
|
|
|
95
|
-
|
|
96
|
-
|
|
124
|
+
expect(stage.binding.binding.emit)
|
|
125
|
+
.toHaveBeenNthCalledWith(index + 1, label, message)
|
|
126
|
+
}
|
|
97
127
|
}
|
|
98
|
-
}
|
|
99
|
-
})
|
|
128
|
+
})
|
|
100
129
|
|
|
101
|
-
it('should shutdown stage', () => {
|
|
102
|
-
|
|
103
|
-
})
|
|
130
|
+
it('should shutdown stage', () => {
|
|
131
|
+
expect(stage.shutdown).toHaveBeenCalled()
|
|
132
|
+
})
|
|
104
133
|
|
|
105
|
-
it('should return results', () => {
|
|
106
|
-
|
|
107
|
-
})
|
|
134
|
+
it('should return results', () => {
|
|
135
|
+
expect(ok).toStrictEqual(true)
|
|
136
|
+
})
|
|
108
137
|
|
|
109
|
-
it.each(['messages', 'operations'])('should not throw if no %s defined', async (key) => {
|
|
110
|
-
|
|
138
|
+
it.each(['messages', 'operations'])('should not throw if no %s defined', async (key) => {
|
|
139
|
+
delete suite[key]
|
|
111
140
|
|
|
112
|
-
|
|
141
|
+
ok = await replay(suite, paths)
|
|
113
142
|
|
|
114
|
-
|
|
143
|
+
expect(ok).toStrictEqual(true)
|
|
144
|
+
})
|
|
115
145
|
})
|
|
146
|
+
|
|
147
|
+
const translation = (declaration) => {
|
|
148
|
+
const find = (call) => call[0].input === declaration.input
|
|
149
|
+
const index = mock.translate.operation.mock.calls.findIndex(find)
|
|
150
|
+
|
|
151
|
+
return mock.translate.operation.mock.results[index].value
|
|
152
|
+
}
|
|
@@ -2,33 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict'
|
|
4
4
|
|
|
5
|
-
const { generate } = require('randomstring')
|
|
6
5
|
const { Locator } = require('@toa.io/core')
|
|
6
|
+
const stage = jest.requireActual('@toa.io/userland/stage')
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
const name = generate()
|
|
10
|
-
const namespace = generate()
|
|
11
|
-
|
|
12
|
-
return new Locator(name, namespace)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// stage
|
|
16
|
-
const manifest = jest.fn(async (path) => ({ path, locator: locator() }))
|
|
8
|
+
const manifest = jest.fn(async (path) => stage.manifest(path))
|
|
17
9
|
const composition = jest.fn()
|
|
18
10
|
const shutdown = jest.fn()
|
|
19
11
|
|
|
20
12
|
const remote = jest.fn(async (id) => {
|
|
21
13
|
const [namespace, name] = id.split('.')
|
|
22
14
|
const locator = new Locator(name, namespace)
|
|
23
|
-
const invoke = jest.fn(async (
|
|
15
|
+
const invoke = jest.fn(async (endpoint, request) => request.reply)
|
|
24
16
|
const disconnect = jest.fn(async () => undefined)
|
|
25
17
|
|
|
26
18
|
return { locator, invoke, disconnect }
|
|
27
19
|
})
|
|
28
20
|
|
|
21
|
+
const component = jest.fn(async () => {
|
|
22
|
+
const invoke = jest.fn(async (endpoint, request) => request.reply)
|
|
23
|
+
|
|
24
|
+
return { invoke }
|
|
25
|
+
})
|
|
26
|
+
|
|
29
27
|
const emit = jest.fn()
|
|
30
28
|
const binding = { binding: { emit } }
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
exports.stage = stage
|
|
30
|
+
exports.stage = { manifest, composition, component, remote, shutdown, binding }
|
|
@@ -34,7 +34,7 @@ it('should define suite as autonomous', async () => {
|
|
|
34
34
|
expect(suite.autonomous).toStrictEqual(true)
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
it('should load
|
|
37
|
+
it('should load operation samples', async () => {
|
|
38
38
|
const expected = await operations()
|
|
39
39
|
|
|
40
40
|
expect(Object.keys(suite.operations)).toStrictEqual([component])
|
|
@@ -47,12 +47,6 @@ it('should load component samples', async () => {
|
|
|
47
47
|
expect(set.undo).toStrictEqual(expected.undo)
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
-
it('should load message samples', async () => {
|
|
51
|
-
const expected = await messages()
|
|
52
|
-
|
|
53
|
-
expect(suite.messages).toStrictEqual(expected)
|
|
54
|
-
})
|
|
55
|
-
|
|
56
50
|
describe('options', () => {
|
|
57
51
|
const paths = [dummy, pot]
|
|
58
52
|
|
|
@@ -63,11 +57,6 @@ describe('options', () => {
|
|
|
63
57
|
suite = await components(paths, options)
|
|
64
58
|
|
|
65
59
|
expect(suite.operations['dummies.pot']).toBeUndefined()
|
|
66
|
-
|
|
67
|
-
const messages = suite.messages['somewhere.something.happened']
|
|
68
|
-
|
|
69
|
-
expect(messages.length).toStrictEqual(1)
|
|
70
|
-
expect(messages[0].component).toStrictEqual('dummies.dummy')
|
|
71
60
|
})
|
|
72
61
|
|
|
73
62
|
it('should filter samples by operation name', async () => {
|
|
@@ -101,16 +90,14 @@ describe('options', () => {
|
|
|
101
90
|
expect(suite.operations['dummies.dummy'].undo[0].title).toStrictEqual('Should not undo')
|
|
102
91
|
})
|
|
103
92
|
|
|
104
|
-
it('should filter
|
|
93
|
+
it('should filter operation samples by title', async () => {
|
|
105
94
|
/** @type {toa.samples.suite.Options} */
|
|
106
|
-
const options = { title: '
|
|
95
|
+
const options = { title: 'Should not undo' }
|
|
107
96
|
|
|
108
97
|
suite = await components(paths, options)
|
|
109
98
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
expect(messages.length).toStrictEqual(1)
|
|
113
|
-
expect(messages[0].title).toStrictEqual(options.title)
|
|
99
|
+
expect(Object.keys(suite.operations['dummies.dummy']).length).toStrictEqual(1)
|
|
100
|
+
expect(suite.operations['dummies.dummy'].undo[0].title).toStrictEqual(options.title)
|
|
114
101
|
})
|
|
115
102
|
})
|
|
116
103
|
|
|
@@ -133,16 +120,3 @@ const operations = async () => {
|
|
|
133
120
|
do: [...do1, ...do2], undo
|
|
134
121
|
}
|
|
135
122
|
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
*
|
|
139
|
-
* @returns {Promise<toa.samples.messages.Set>}
|
|
140
|
-
*/
|
|
141
|
-
const messages = async () => {
|
|
142
|
-
const label = 'somewhere.something.happened'
|
|
143
|
-
const file = resolve(dummy, 'samples/messages', label + '.yaml')
|
|
144
|
-
const declarations = await yaml.load.all(file)
|
|
145
|
-
const messages = declarations.map((sample) => ({ component, ...sample }))
|
|
146
|
-
|
|
147
|
-
return { [label]: messages }
|
|
148
|
-
}
|
package/samples/types/suite.d.ts
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
|
+
import * as _core from '@toa.io/core/types'
|
|
1
2
|
import * as _operations from './operation'
|
|
2
3
|
import * as _messages from './message'
|
|
3
4
|
|
|
4
5
|
declare namespace toa.samples {
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
namespace suite {
|
|
8
|
+
type Operations = Record<string, _operations.Set>
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
10
|
+
type Options = {
|
|
11
|
+
id?: string
|
|
12
|
+
integration?: boolean
|
|
13
|
+
component?: string
|
|
14
|
+
operation?: string
|
|
15
|
+
title?: string
|
|
16
|
+
runner?: object
|
|
17
17
|
}
|
|
18
|
+
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
type Suite = {
|
|
21
|
+
title?: string
|
|
22
|
+
autonomous: boolean
|
|
23
|
+
operations?: suite.Operations
|
|
24
|
+
messages?: _messages.Set
|
|
25
|
+
}
|
|
25
26
|
|
|
26
27
|
}
|
|
27
28
|
|
package/stage/src/component.js
CHANGED
|
@@ -4,8 +4,10 @@ const boot = require('@toa.io/boot')
|
|
|
4
4
|
const { state } = require('./state')
|
|
5
5
|
|
|
6
6
|
/** @type {toa.stage.Component} */
|
|
7
|
-
const component = async (path) => {
|
|
8
|
-
|
|
7
|
+
const component = async (path, options) => {
|
|
8
|
+
options = Object.assign({}, DEFAULTS, options)
|
|
9
|
+
|
|
10
|
+
const manifest = await boot.manifest(path, options)
|
|
9
11
|
const component = await boot.component(manifest)
|
|
10
12
|
|
|
11
13
|
await component.connect()
|
|
@@ -15,4 +17,7 @@ const component = async (path) => {
|
|
|
15
17
|
return component
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
const binding = require.resolve('./binding')
|
|
21
|
+
const DEFAULTS = { bindings: [binding] }
|
|
22
|
+
|
|
18
23
|
exports.component = component
|
|
@@ -18,7 +18,7 @@ it('should boot component', async () => {
|
|
|
18
18
|
const path = generate()
|
|
19
19
|
const component = await stage.component(path)
|
|
20
20
|
|
|
21
|
-
expect(mock.boot.manifest).
|
|
21
|
+
expect(mock.boot.manifest.mock.calls[0][0]).toStrictEqual(path)
|
|
22
22
|
expect(mock.boot.component).toHaveBeenCalledWith(await mock.boot.manifest.mock.results[0].value)
|
|
23
23
|
expect(component).toStrictEqual(await mock.boot.component.mock.results[0].value)
|
|
24
24
|
expect(component.connect).toHaveBeenCalled()
|
package/stage/types/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as _core from '@toa.io/core/types'
|
|
2
2
|
import * as _norm from '@toa.io/norm/types'
|
|
3
|
+
import * as _composition from '@toa.io/boot/types/composition'
|
|
3
4
|
|
|
4
5
|
declare namespace toa.stage {
|
|
5
6
|
type Manifest = (path: string) => Promise<_norm.Component>
|
|
6
|
-
type Component = (path: string) => Promise<_core.Component>
|
|
7
|
+
type Component = (path: string, options?: _composition.Options) => Promise<_core.Component>
|
|
7
8
|
type Composition = (paths: string[]) => Promise<void>
|
|
8
9
|
type Remote = (id: string) => Promise<_core.Component>
|
|
9
10
|
type Shutdown = () => Promise<void>
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
title: Should book a pot
|
|
2
|
-
payload:
|
|
3
|
-
pot: 1b9d7983bc204c8f8928d843a666a642
|
|
4
|
-
input:
|
|
5
|
-
booked: true
|
|
6
|
-
query:
|
|
7
|
-
id: 1b9d7983bc204c8f8928d843a666a642
|
|
8
|
-
---
|
|
9
|
-
title: Should book a pot (with request sample)
|
|
10
|
-
payload:
|
|
11
|
-
pot: 1b9d7983bc204c8f8928d843a666a642
|
|
12
|
-
input:
|
|
13
|
-
booked: true
|
|
14
|
-
query:
|
|
15
|
-
id: 1b9d7983bc204c8f8928d843a666a642
|
|
16
|
-
request:
|
|
17
|
-
current:
|
|
18
|
-
id: 1b9d7983bc204c8f8928d843a666a642
|
|
19
|
-
material: glass
|
|
20
|
-
booked: false
|
|
21
|
-
next:
|
|
22
|
-
id: 1b9d7983bc204c8f8928d843a666a642
|
|
23
|
-
material: glass
|
|
24
|
-
booked: true
|
|
25
|
-
---
|
|
26
|
-
title: Should book a pot (without receiver output verification)
|
|
27
|
-
payload:
|
|
28
|
-
pot: 1b9d7983bc204c8f8928d843a666a642
|
|
29
|
-
request:
|
|
30
|
-
current:
|
|
31
|
-
id: 1b9d7983bc204c8f8928d843a666a642
|
|
32
|
-
material: glass
|
|
33
|
-
booked: false
|
|
34
|
-
next:
|
|
35
|
-
id: 1b9d7983bc204c8f8928d843a666a642
|
|
36
|
-
material: glass
|
|
37
|
-
booked: true
|
|
@@ -1,72 +0,0 @@
|
|
|
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
|