@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,15 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
async function observation (input, none, context) {
|
|
4
|
+
let value = input.value
|
|
5
|
+
|
|
6
|
+
for (let i = 0; i < input.times; i++) {
|
|
7
|
+
const reply = await context.local.add({ input: { a: value, b: 1 } })
|
|
8
|
+
|
|
9
|
+
value = reply.output
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return value
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
exports.observation = observation
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
title: Should increment number
|
|
2
|
+
input:
|
|
3
|
+
value: 1
|
|
4
|
+
output: 2
|
|
5
|
+
local:
|
|
6
|
+
add:
|
|
7
|
+
input:
|
|
8
|
+
a: 1
|
|
9
|
+
b: 1
|
|
10
|
+
output: 2
|
|
11
|
+
---
|
|
12
|
+
title: Should increment with actual call
|
|
13
|
+
input:
|
|
14
|
+
value: 2
|
|
15
|
+
output: 3
|
|
16
|
+
---
|
|
17
|
+
title: Should increment twice (with second actual call)
|
|
18
|
+
input:
|
|
19
|
+
value: 0
|
|
20
|
+
times: 2
|
|
21
|
+
output: 2
|
|
22
|
+
local:
|
|
23
|
+
add:
|
|
24
|
+
- input:
|
|
25
|
+
a: 0
|
|
26
|
+
b: 1
|
|
27
|
+
output: 1
|
|
28
|
+
- input:
|
|
29
|
+
a: 1
|
|
30
|
+
b: 1
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: pots
|
|
2
|
+
namespace: tea
|
|
3
|
+
|
|
4
|
+
entity:
|
|
5
|
+
storage: mongodb
|
|
6
|
+
schema:
|
|
7
|
+
material*: [glass, ceramic, steel]
|
|
8
|
+
volume: number
|
|
9
|
+
booked: false
|
|
10
|
+
|
|
11
|
+
operations:
|
|
12
|
+
transit:
|
|
13
|
+
concurrency: none
|
|
14
|
+
input:
|
|
15
|
+
material: .
|
|
16
|
+
volume: .
|
|
17
|
+
booked: .
|
|
18
|
+
same:
|
|
19
|
+
input: .material
|
|
20
|
+
output: array
|
|
21
|
+
|
|
22
|
+
receivers:
|
|
23
|
+
store.orders.created:
|
|
24
|
+
transition: transit
|
|
25
|
+
binding: amqp # avoid discovery
|
|
26
|
+
|
|
27
|
+
configuration:
|
|
28
|
+
step: 0.5
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
async function observation (input, none, context) {
|
|
4
|
+
const criteria = `material==${input}`
|
|
5
|
+
const query = { criteria, limit }
|
|
6
|
+
|
|
7
|
+
// noinspection JSUnresolvedFunction
|
|
8
|
+
return context.local.enumerate({ query })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const limit = 10
|
|
12
|
+
|
|
13
|
+
exports.observation = observation
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
title: Should create
|
|
2
|
+
input:
|
|
3
|
+
material: glass
|
|
4
|
+
volume: 1.5
|
|
5
|
+
next:
|
|
6
|
+
material: glass
|
|
7
|
+
volume: 1.5
|
|
8
|
+
---
|
|
9
|
+
title: Should update
|
|
10
|
+
input:
|
|
11
|
+
material: steel
|
|
12
|
+
current:
|
|
13
|
+
material: glass
|
|
14
|
+
volume: 1.5
|
|
15
|
+
next:
|
|
16
|
+
material: steel
|
|
17
|
+
volume: 1.5
|
|
18
|
+
---
|
|
19
|
+
title: Should emit `created` event
|
|
20
|
+
input:
|
|
21
|
+
material: glass
|
|
22
|
+
volume: 1.5
|
|
23
|
+
next:
|
|
24
|
+
material: glass
|
|
25
|
+
volume: 1.5
|
|
26
|
+
events:
|
|
27
|
+
created:
|
|
28
|
+
material: glass
|
|
29
|
+
volume: 1.5
|
|
30
|
+
---
|
|
31
|
+
title: Should emit `updated` event
|
|
32
|
+
input:
|
|
33
|
+
material: steel
|
|
34
|
+
current:
|
|
35
|
+
material: glass
|
|
36
|
+
volume: 1.5
|
|
37
|
+
next:
|
|
38
|
+
material: steel
|
|
39
|
+
volume: 1.5
|
|
40
|
+
events:
|
|
41
|
+
updated:
|
|
42
|
+
material: steel
|
|
43
|
+
volume: 1.5
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { resolve } = require('node:path')
|
|
4
|
+
const { exceptions: { RequestContractException } } = require('@toa.io/core')
|
|
5
|
+
const stage = require('@toa.io/userland/stage')
|
|
6
|
+
|
|
7
|
+
const root = resolve(__dirname, '../components')
|
|
8
|
+
|
|
9
|
+
/** @type {toa.core.Component} */
|
|
10
|
+
let remote
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const path = resolve(root, 'math/calculations')
|
|
14
|
+
|
|
15
|
+
await stage.composition([path])
|
|
16
|
+
|
|
17
|
+
remote = await stage.remote('math.calculations')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
afterAll(async () => {
|
|
21
|
+
await stage.shutdown()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should call endpoint', async () => {
|
|
25
|
+
const a = Math.random()
|
|
26
|
+
const b = Math.random()
|
|
27
|
+
|
|
28
|
+
const reply = await remote.invoke('add', { input: { a, b } })
|
|
29
|
+
|
|
30
|
+
expect(reply.output).toStrictEqual(a + b)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should throw on invalid input', async () => {
|
|
34
|
+
const a = 'not a number'
|
|
35
|
+
const b = 'neither'
|
|
36
|
+
|
|
37
|
+
await expect(remote.invoke('add', { input: { a, b } }))
|
|
38
|
+
.rejects.toBeInstanceOf(RequestContractException)
|
|
39
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { resolve } = require('node:path')
|
|
4
|
+
const stage = require('@toa.io/userland/stage')
|
|
5
|
+
|
|
6
|
+
const binding = stage.binding.binding
|
|
7
|
+
const root = resolve(__dirname, '../components')
|
|
8
|
+
|
|
9
|
+
/** @type {toa.core.Component} */
|
|
10
|
+
let remote
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const path = resolve(root, 'tea/pots')
|
|
14
|
+
|
|
15
|
+
await stage.composition([path])
|
|
16
|
+
|
|
17
|
+
remote = await stage.remote('tea.pots')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
afterAll(async () => {
|
|
21
|
+
await stage.shutdown()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should receive event', async () => {
|
|
25
|
+
const created = await remote.invoke('transit', { input: { material: 'glass' } })
|
|
26
|
+
const id = created.output.id
|
|
27
|
+
const payload = { pot: id }
|
|
28
|
+
const message = { payload }
|
|
29
|
+
const request = { query: { id } }
|
|
30
|
+
|
|
31
|
+
await binding.emit('store.orders.created', message)
|
|
32
|
+
|
|
33
|
+
const reply = await remote.invoke('observe', request)
|
|
34
|
+
|
|
35
|
+
expect(reply.output.booked).toStrictEqual(true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should emit event', async () => {
|
|
39
|
+
expect.assertions(1)
|
|
40
|
+
|
|
41
|
+
const material = 'steel'
|
|
42
|
+
|
|
43
|
+
await binding.subscribe('tea.pots.created', (pot) => {
|
|
44
|
+
expect(pot.payload.material).toStrictEqual(material)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
await remote.invoke('transit', { input: { material } })
|
|
48
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { resolve } = require('node:path')
|
|
4
|
+
const stage = require('@toa.io/userland/stage')
|
|
5
|
+
|
|
6
|
+
const root = resolve(__dirname, '../components')
|
|
7
|
+
|
|
8
|
+
/** @type {toa.core.Component} */
|
|
9
|
+
let component
|
|
10
|
+
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
const path = resolve(root, 'math/calculations')
|
|
13
|
+
|
|
14
|
+
component = await stage.component(path)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
afterAll(async () => {
|
|
18
|
+
await stage.shutdown()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('should invoke', async () => {
|
|
22
|
+
const a = Math.random()
|
|
23
|
+
const b = Math.random()
|
|
24
|
+
|
|
25
|
+
const reply = await component.invoke('add', { input: { a, b } })
|
|
26
|
+
|
|
27
|
+
expect(reply.exception).toBeUndefined()
|
|
28
|
+
expect(reply.output).toStrictEqual(a + b)
|
|
29
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toa.io/userland",
|
|
3
|
+
"version": "0.2.1-dev.3",
|
|
4
|
+
"description": "Toa development kit",
|
|
5
|
+
"homepage": "https://toa.io",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Artem Gurtovoi",
|
|
8
|
+
"url": "https://github.com/temich"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/toa-io/toa.git"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/toa-io/toa/issues"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">= 12.0.0"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"main": "src/index.js",
|
|
25
|
+
"types": "types/index.d.ts",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@toa.io/boot": "*",
|
|
28
|
+
"@toa.io/core": "*",
|
|
29
|
+
"@toa.io/filesystem": "*",
|
|
30
|
+
"@toa.io/generic": "*",
|
|
31
|
+
"@toa.io/norm": "*",
|
|
32
|
+
"@toa.io/schemas": "*",
|
|
33
|
+
"@toa.io/yaml": "*",
|
|
34
|
+
"tap": "16.3.4"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Toa development kit
|
|
2
|
+
|
|
3
|
+
- [Stage](./stage): integration tests framework (Node.js)
|
|
4
|
+
- [Samples](./samples): YAML-based declarative tests
|
|
5
|
+
- Features[^1]: Gherkin steps definition ([cucumber.js](https://github.com/cucumber/cucumber-js))
|
|
6
|
+
|
|
7
|
+
See [example](./example).
|
|
8
|
+
|
|
9
|
+
[^1]: not yet implemented
|
|
Binary file
|
|
Binary file
|
package/samples/notes.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Developer notes
|
|
2
|
+
|
|
3
|
+
## Extending Replay
|
|
4
|
+
|
|
5
|
+
1. Update [`example`](../example). `toa replay` should either fail or throw exception.
|
|
6
|
+
2. Add [feature](./features). It should fail.
|
|
7
|
+
3. If the new decorator is being developed:
|
|
8
|
+
1. Add the [decorator](/extensions/sampling/docs/replay.md).
|
|
9
|
+
2. Add [boot extension](/runtime/boot/src/extensions).
|
|
10
|
+
4. Update `suite` [translation](./src/.suite/.component/translate.js).
|
|
11
|
+
5. Ensure feature is passing.
|
|
12
|
+
6. Ensure example is passing with `toa replay`.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Samples
|
|
2
|
+
|
|
3
|
+
## TL;DR
|
|
4
|
+
|
|
5
|
+
<a href="https://miro.com/app/board/uXjVOoy0ImU=/?moveToWidget=3458764532091744292&cot=14">
|
|
6
|
+
<picture>
|
|
7
|
+
<source media="(prefers-color-scheme: dark)" srcset="./docs/sampling-dark.jpg">
|
|
8
|
+
<img alt="4D" width="640" height="435" src="./docs/sampling-light.jpg">
|
|
9
|
+
</picture>
|
|
10
|
+
</a>
|
|
11
|
+
|
|
12
|
+
See [features](/features/replay).
|
|
13
|
+
|
|
14
|
+
## Operation Samples
|
|
15
|
+
|
|
16
|
+
Sample is an object containing values of operation inputs (i.e.: request, context outputs and
|
|
17
|
+
current state) to be substituted and outcomes (reply, context calls, next state and events emission)
|
|
18
|
+
to be verified. See its [schema](./src/.suite/.component/sample.cos.yaml).
|
|
19
|
+
|
|
20
|
+
> Although `input` and `output` are declared as arbitrary values, they must conform to the
|
|
21
|
+
> corresponding
|
|
22
|
+
> operation schemas.
|
|
23
|
+
|
|
24
|
+
### Declaration
|
|
25
|
+
|
|
26
|
+
> Samples must be declared
|
|
27
|
+
> as [multi-document YAML files](https://yaml.org/spec/1.2.2/#22-structures).
|
|
28
|
+
|
|
29
|
+
Operation samples must be located under the `samples` directory in the component or context root.
|
|
30
|
+
Sample file names must follow the convention: `namespace.name.operation.yaml`, that is, be an
|
|
31
|
+
endpoint of the operation samples to be applied to. For component-level sample files `namespace`
|
|
32
|
+
and `name` must match corresponding component, therefore are optional.
|
|
33
|
+
|
|
34
|
+
## Message Samples
|
|
35
|
+
|
|
36
|
+
Message Sample is an object containing receiver's input (`payload`) to be substituted and
|
|
37
|
+
outcomes (`input` and `query`) to be verified. Message sample may contain corresponding operation
|
|
38
|
+
sample. See its [schema](#).
|
|
39
|
+
|
|
40
|
+
> Message samples are always [autonomous](#autonomy).
|
|
41
|
+
|
|
42
|
+
### Declaration
|
|
43
|
+
|
|
44
|
+
Operation samples must be located under the `samples/messages` directory in the component or context
|
|
45
|
+
root. Samples file names must follow the convention `namesace.component.event.yaml`, that is, be a
|
|
46
|
+
label
|
|
47
|
+
of the event receiver is consuming.
|
|
48
|
+
|
|
49
|
+
## Autonomy
|
|
50
|
+
|
|
51
|
+
Component level samples are *autonomous*, namely, does not assume actual remote calls as
|
|
52
|
+
replaying of component-level samples will boot only that component. Remote call attempt not declared
|
|
53
|
+
within sample will cause an exception.
|
|
54
|
+
See [examples](../example/components/math/calculations/samples).
|
|
55
|
+
|
|
56
|
+
For context level samples (integration samples), remote calls with non-declared outputs will be
|
|
57
|
+
actually performed. Replaying these samples will boot the composition with all components of the
|
|
58
|
+
context (so as required extensions). See [examples](../example/samples).
|
|
59
|
+
|
|
60
|
+
> Integration samples are more flexible and less sensitive to implementation details.
|
|
61
|
+
|
|
62
|
+
## Replay
|
|
63
|
+
|
|
64
|
+
Samples may be *replayed* using [`toa replay`](/runtime/cli/readme.md#replay) command.
|
|
65
|
+
|
|
66
|
+
> Replaying samples requires local deployment environment.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const stage = require('@toa.io/userland/stage')
|
|
4
|
+
|
|
5
|
+
const { operation } = require('./operation')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param {string} id
|
|
10
|
+
* @param {toa.samples.operations.Set} set
|
|
11
|
+
* @param {boolean} autonomous
|
|
12
|
+
* @returns {Function}
|
|
13
|
+
*/
|
|
14
|
+
const component = (id, set, autonomous) =>
|
|
15
|
+
async (test) => {
|
|
16
|
+
const remote = await stage.remote(id)
|
|
17
|
+
|
|
18
|
+
for (const [endpoint, samples] of Object.entries(set)) {
|
|
19
|
+
await test.test(endpoint, operation(remote, endpoint, samples, autonomous))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
await remote.disconnect()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
exports.component = component
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { binding } = require('@toa.io/userland/stage').binding
|
|
4
|
+
|
|
5
|
+
const translate = require('./translate')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {toa.samples.messages.Set} messages
|
|
9
|
+
* @param {boolean} autonomous
|
|
10
|
+
* @return {Function}
|
|
11
|
+
*/
|
|
12
|
+
const messages = (messages, autonomous) =>
|
|
13
|
+
async (test) => {
|
|
14
|
+
for (const [label, samples] of Object.entries(messages)) {
|
|
15
|
+
await test.test(label, message(label, samples, autonomous))
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} label
|
|
21
|
+
* @param {toa.samples.Message[]} samples
|
|
22
|
+
* @param {boolean} autonomous
|
|
23
|
+
* @returns {function}
|
|
24
|
+
*/
|
|
25
|
+
const message = (label, samples, autonomous) =>
|
|
26
|
+
async (test) => {
|
|
27
|
+
let n = 0
|
|
28
|
+
|
|
29
|
+
for (const sample of samples) {
|
|
30
|
+
n++
|
|
31
|
+
|
|
32
|
+
const message = translate.message(sample, autonomous)
|
|
33
|
+
const name = sample.title ?? 'Sample #' + n
|
|
34
|
+
|
|
35
|
+
await test.test(name, async () => binding.emit(label, message))
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.messages = messages
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const translate = require('./translate')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {toa.core.Component} remote
|
|
7
|
+
* @param {string} endpoint
|
|
8
|
+
* @param {toa.samples.Operation[]} samples
|
|
9
|
+
* @param {boolean} autonomous
|
|
10
|
+
* @returns {Function}
|
|
11
|
+
*/
|
|
12
|
+
const operation = (remote, endpoint, samples, autonomous) =>
|
|
13
|
+
async (test) => {
|
|
14
|
+
let n = 0
|
|
15
|
+
|
|
16
|
+
for (const operation of samples) {
|
|
17
|
+
n++
|
|
18
|
+
|
|
19
|
+
const request = translate.operation(operation, autonomous)
|
|
20
|
+
const name = operation.title ?? 'Sample #' + n
|
|
21
|
+
|
|
22
|
+
await test.test(name, async () => remote.invoke(endpoint, request))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test.end()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
exports.operation = operation
|