Behavioural typechecking for @actyx/machine-runner machines
npm install @actyx/machine-check(Closed)
open
Control
Opening
(Opening)
(Closed) --[open@Control]--> (Opening)
(Closed) --[open@Control]--> (Opening)
(Opening) --[update@Door]--> (Opening)
(Opening) --[open@Door]--> (Open)
(Open) --[close@Control]--> (Closing)
(Closing) --[update@Door]--> (Closing)
(Closing) --[close@Door]--> (Closed)
ts
import { Door, Control, HangarBay } from './example-proto.js'
import { SwarmProtocolType, checkProjection, checkSwarmProtocol } from '@actyx/machine-check'
const swarmProtocol: SwarmProtocolType = {
initial: 'Closed',
transitions: [
{
source: 'Closed',
target: 'Opening',
label: { cmd: 'open', role: 'Control', logType: ['opening'] },
},
{
source: 'Opening',
target: 'Opening',
label: { cmd: 'update', role: 'Door', logType: ['opening'] },
},
{
source: 'Opening',
target: 'Open',
label: { cmd: 'open', role: 'Door', logType: ['opened'] },
},
{
source: 'Open',
target: 'Closing',
label: { cmd: 'close', role: 'Control', logType: ['closing'] },
},
{
source: 'Closing',
target: 'Closing',
label: { cmd: 'update', role: 'Door', logType: ['closing'] },
},
{
source: 'Closing',
target: 'Closed',
label: { cmd: 'close', role: 'Door', logType: ['closed'] },
},
],
}
const subscriptions = {
Control: ['closing', 'closed', 'opening', 'opened'],
Door: ['closing', 'closed', 'opening', 'opened'],
}
`
The swarmProtocol describes the expected flow of events, written down as if we had full oversight or all machines that will later implement it.
And this is how we run the behaviour checker to first make sure that the swarm protocol is valid and then check that our machines implement it correctly:
`ts
describe('swarmProtocol', () => {
it('should be well-formed', () => {
expect(checkSwarmProtocol(swarmProtocol, subscriptions)).toEqual({ type: 'OK' })
})
it('should match Control', () => {
expect(
checkProjection(
swarmProtocol,
subscriptions,
'Control',
Control.Control.createJSONForAnalysis(Control.Closed),
),
).toEqual({ type: 'OK' })
})
it('should match Door', () => {
expect(
checkProjection(
swarmProtocol,
subscriptions,
'Door',
Door.Door.createJSONForAnalysis(Door.Closed),
),
).toEqual({ type: 'OK' })
})
})
`
You can of course use any testing framework you like.
In the example as given you’ll be notified that the overall protocol has a flaw:
`text
{
type: 'ERROR',
errors: [
'guard event type opening appears in transitions from multiple states',
'guard event type closing appears in transitions from multiple states'
]
}
`
This means that our clever reuse of the opening and closing event types for dual purposes (i.e. as transition to a moving door as well as progress update) may not be so clever after all — the update commands should yield more specific openingProgress and closingProgress event types instead.
Other than that, our machines are implemented correctly.
You can try to remove a command or reaction from the code to observe how this this pointed out by checkProjection()`.