The previous tutorial had our two actors introducing themselves. In that scenario, we've given one actor the role of initiator and another of recipient.
In this tutorial, we'll model a situation where a random number of people have a conversation. They first all need to introduce themselves after which each one can talk.
Create file meeting.yaml in scenarios.
Create subdirectory meeting in features for the test files.
Wildcard actors
In this scenario, we don't exactly know how many participants the process will have. However, we do that each participant has essentially the same role. We'll number the actors when we instantiate the process.
Test case
In the initial state, everyone has the opportunity to introduce themselves. With an introduction, we stay in the initial state. When someone starts to talk we transition to the talking state.
meeting/golden.feature
Feature: Three persons introduce themselves and have a conversationBackground:Given the process is created from the "meeting" scenarioAnd "Bob" is the "person_1" actorAnd "Eve" is the "person_2" actorAnd "Jack" is the "person_3" actorScenario:When "Bob" does "introduce" with: | name | Bob | | organization | Jasny |Then the process is in "initial"And the last event is not skippedAnd actor "person_1" has "name" is "Bob"And actor "person_1" has "organization" is "Jasny"When "Eve" does "introduce" with: | name | Eve | | organization | Acme Inc |Then the process is in "initial"And the last event is not skippedAnd actor "person_2" has "name" is "Eve"And actor "person_2" has "organization" is "Acme Inc"When "Jack" does "introduce" with: | name | Jack | | organization | LTO Network |Then the process is in "initial"And the last event is not skippedAnd actor "person_3" has "name" is "Jack"And actor "person_3" has "organization" is "LTO Network"When "Bob" does "talk" with "What do you think about the weather?"Then the process is in "talking"And the last event is not skippedWhen "Eve" does "talk" with "It's pretty cold"Then the process is in "talking"And the last event is not skippedWhen "Bob" does "talk" with "True, but at least it's not raining"Then the process is in "talking"And the last event is not skippedWhen "Jack" does "talk" with "I like blockchain!"Then the process is in "talking"And the last event is not skippedThen the result is:""" - Bob - What do you think about the weather? - Eve - It's pretty cold - Bob - True, but at least it's not raining - Jack - I like blockchain! """
The scenario
Instead of defining the actors individually, we use a wildcard. The actor schema is applied to every actor with the key person_{number}.
For goto we can use null (which is ~ in YAML) to indicate that we don't want to transition to another state.
It's bad practice to have a process without an end state. We'll add a way to end the conversation later in this tutorial.
Everyone introduced?
With the scenario of the introduction tutorial, the state machine made sure that both parties did an introduction before being able to start the conversation. However, with this scenario that's not the case. Any actor can start talking and skip the introductions. Additionally, we don't need actors to introduce themselves twice.
Test case
meeting/conditions.feature
Feature: People are not committing to the social normsBackground:Given the process is created from the "meeting" scenarioAnd "Bob" is the "person_1" actorAnd "Eve" is the "person_2" actorAnd "Jack" is the "person_3" actorScenario: Person introduces himself and starts to talkWhen "Bob" does "introduce" with: | name | Bob | | organization | Jasny |And "Bob" does "talk" with "What do you think about the weather?"Then the last event is skippedScenario: Person starts talking without introducing himselfWhen "Bob" does "introduce" with: | name | Bob | | organization | Jasny |When "Eve" does "introduce" with: | name | Eve | | organization | Acme Inc |When "Jack" does "talk" with "I like blockchain!"Then the last event is skippedScenario: Person does an introduction twiceWhen "Bob" does "introduce" with: | name | Bob | | organization | Jasny |When "Bob" does "introduce" with: | name | Bob Builder | | organization | Jasny |Then the last event is skippedScenario: Person tries to talk twiceWhen "Bob" does "introduce" with: | name | Bob | | organization | Jasny |When "Eve" does "introduce" with: | name | Eve | | organization | Acme Inc |When "Jack" does "introduce" with: | name | Jack | | organization | LTO Network |When "Bob" does "talk"Then the process is in "wait_on_recipient"When "Bob" does "talk"Then the process is in "wait_on_recipient"And the last event is skipped
The scenario
We can use if conditions to disallow an action or a transition. The field needs to contain a boolean value. We typically use <ref> data function for conditions. This function performs a JMESPath query against the process.
For the "introduce" action we specify a condition to prevent an actor from performing it if we already know its name.
We'll add a condition statement for state transition from "initial" to "talking", which validates that the name of every actor is known.
The last condition is on the "talk" action. We compare if the current actor is not the actor of the previous event to ensure a person doesn't talk twice.
current.actor is a reference to the object of actors. That object doesn't contain the actor key. Instead, we need to get the value from the events.
The last event is the event of the current action, with JMESPath that's events[-1].
The previous event would be events[-2]. However, we want to exclude timeout and skipped events, so we do (events[?(!skipped && actor)] | @[-2]).
No introductions
In case the persons already know each other, there is no need for introductions. We can initialize actors using the start instructions of the process.
meeting/no-introductions.feature
Feature: Two persons have a conversation without an introductionBackground:Given the process is created from the "meeting" scenarioAnd "Bob" is the "person_1" actor with: | name | Bob | | organization | Jasny |And "Eve" is the "person_2" actor with: | name | Eve | | organization | Acme Inc |Scenario:When "Bob" does "talk" with "What do you think about the weather?"Then the process is in "talking"And the last event is not skippedWhen "Eve" does "talk" with "It's pretty cold"Then the process is in "talking"And the last event is not skippedThen the result is:""" - Bob - What do you think about the weather? - Eve - It's pretty cold """
Leaving the conversation
Instead of ending the interaction, any person can choose to leave. The remaining persons can continue the conversation. When there are less than two persons left, the process should end.
Test case
meeting/leave.feature
Feature: People leave the conversationBackground:Given the process is created from the "meeting" scenarioAnd "Bob" is the "person_1" actor with: | name | Bob | | organization | Jasny |And "Eve" is the "person_2" actor with: | name | Eve | | organization | Acme Inc |And "Jack" is the "person_3" actor with: | name | Jack | | organization | LTO Network |Then actor "person_1" has "is_present" is trueAnd actor "person_2" has "is_present" is trueAnd actor "person_3" has "is_present" is trueScenario: Person leaves and later the second leavesWhen "Bob" does "talk" with "What do you think about the weather?"When "Eve" does "talk" with "It's pretty cold"When "Bob" does "talk" with "True, but at least it's not raining"When "Jack" does "talk" with "I like blockchain!"When "Bob" does "leave"Then actor "person_1" has "is_present" is falseAnd the process is in "talking"When "Eve" does "talk" with "What is blockchain?"And "Jack" does "talk" with "Do you know Bitcoin?"And "Eve" does "talk" with "*sigh*"When "Eve" does "leave"Then actor "person_2" has "is_present" is falseAnd the process ended with:""" - Bob - What do you think about the weather? - Eve - It's pretty cold - Bob - True, but at least it's not raining - Jack - I like blockchain! - Eve - What is blockchain? - Jack - Do you know Bitcoin? - Eve - *sigh* """Scenario: Person tries to say something after already leftWhen "Bob" does "talk" with "What do you think about the weather?"And "Eve" does "talk" with "It's pretty cold"When "Bob" does "leave"And "Bob" does "talk" with "True, but at least it's not raining"Then the last event is skipped
The scenario
We add an is_present property to the actor, which defaults to true. The "leave" action will set is_present to false for the current actor.
As long as there are two or more actors present, the "leave" action will not cause a state transition. If that condition is not true, the process will end. Transitions are validated in order. Therefore we only need to have a condition on the first transition of "leave" action in the "talking" state.