Skip to content

Commit baed0e5

Browse files
authored
Merge pull request #102 from sergiogbr/feat/statewithargs
Add StateMachine.StateWithArgs
2 parents 8279b62 + 250b10b commit baed0e5

2 files changed

Lines changed: 104 additions & 0 deletions

File tree

statemachine.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@ func (sm *StateMachine) State(ctx context.Context) (State, error) {
148148
return state, err
149149
}
150150

151+
// StateWithArgs returns the current state along with any arguments that were passed to the state mutator.
152+
// This is useful when using NewStateMachineWithExternalStorageAndArgs to retain additional state information.
153+
func (sm *StateMachine) StateWithArgs(ctx context.Context) (State, []any, error) {
154+
return sm.stateAccessor(ctx)
155+
}
156+
151157
// MustState returns the current state without the error.
152158
// It is safe to use this method when used together with NewStateMachine
153159
// or when using NewStateMachineWithExternalStorage / NewStateMachineWithExternalStorageAndArgs with a state accessor that

statemachine_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,104 @@ func TestStateMachine_NewStateMachineWithExternalStorageAndArgs(t *testing.T) {
106106
}
107107
}
108108

109+
func TestStateMachine_StateWithArgs(t *testing.T) {
110+
sm := NewStateMachine(stateA)
111+
sm.Configure(stateA).Permit(triggerX, stateB)
112+
113+
state, args, err := sm.StateWithArgs(context.Background())
114+
if err != nil {
115+
t.Errorf("StateWithArgs() error = %v, want nil", err)
116+
}
117+
if state != stateA {
118+
t.Errorf("StateWithArgs() state = %v, want %v", state, stateA)
119+
}
120+
if args != nil {
121+
t.Errorf("StateWithArgs() args = %v, want nil", args)
122+
}
123+
}
124+
125+
func TestStateMachine_StateWithArgs_ExternalStorage(t *testing.T) {
126+
var state State = stateB
127+
sm := NewStateMachineWithExternalStorage(func(_ context.Context) (State, error) {
128+
return state, nil
129+
}, func(_ context.Context, s State) error {
130+
state = s
131+
return nil
132+
}, FiringImmediate)
133+
134+
gotState, gotArgs, err := sm.StateWithArgs(context.Background())
135+
if err != nil {
136+
t.Errorf("StateWithArgs() error = %v, want nil", err)
137+
}
138+
if gotState != stateB {
139+
t.Errorf("StateWithArgs() state = %v, want %v", gotState, stateB)
140+
}
141+
if gotArgs != nil {
142+
t.Errorf("StateWithArgs() args = %v, want nil", gotArgs)
143+
}
144+
}
145+
146+
func TestStateMachine_StateWithArgs_ExternalStorageAndArgs(t *testing.T) {
147+
var state State = stateB
148+
var args = []any{"arg1", 42, errors.New("test error")}
149+
sm := NewStateMachineWithExternalStorageAndArgs(func(_ context.Context) (State, []any, error) {
150+
return state, args, nil
151+
}, func(_ context.Context, s State, a ...any) error {
152+
state = s
153+
args = a
154+
return nil
155+
}, FiringImmediate)
156+
sm.Configure(stateB).Permit(triggerX, stateC)
157+
158+
gotState, gotArgs, err := sm.StateWithArgs(context.Background())
159+
if err != nil {
160+
t.Errorf("StateWithArgs() error = %v, want nil", err)
161+
}
162+
if gotState != stateB {
163+
t.Errorf("StateWithArgs() state = %v, want %v", gotState, stateB)
164+
}
165+
if !reflect.DeepEqual(gotArgs, args) {
166+
t.Errorf("StateWithArgs() args = %v, want %v", gotArgs, args)
167+
}
168+
if got := gotArgs[0].(string); got != "arg1" {
169+
t.Errorf("expected arg 0 to be %v, got %v", "arg1", got)
170+
}
171+
if got := gotArgs[1].(int); got != 42 {
172+
t.Errorf("expected arg 1 to be %v, got %v", 42, got)
173+
}
174+
if got := gotArgs[2].(error).Error(); got != "test error" {
175+
t.Errorf("expected arg 2 to be %v, got %v", "test error", got)
176+
}
177+
178+
// Fire a transition with new arguments
179+
sm.Fire(triggerX, "arg2", 99)
180+
gotState, gotArgs, err = sm.StateWithArgs(context.Background())
181+
if err != nil {
182+
t.Errorf("StateWithArgs() error = %v, want nil", err)
183+
}
184+
if gotState != stateC {
185+
t.Errorf("StateWithArgs() state = %v, want %v", gotState, stateC)
186+
}
187+
if got := gotArgs[0].(string); got != "arg2" {
188+
t.Errorf("expected arg 0 to be %v, got %v", "arg2", got)
189+
}
190+
if got := gotArgs[1].(int); got != 99 {
191+
t.Errorf("expected arg 1 to be %v, got %v", 99, got)
192+
}
193+
}
194+
195+
func TestStateMachine_StateWithArgs_Error(t *testing.T) {
196+
sm := NewStateMachineWithExternalStorage(func(_ context.Context) (State, error) {
197+
return nil, errors.New("state accessor error")
198+
}, func(_ context.Context, s State) error { return nil }, FiringImmediate)
199+
200+
_, _, err := sm.StateWithArgs(context.Background())
201+
want := "state accessor error"
202+
if err == nil || err.Error() != want {
203+
t.Errorf("StateWithArgs() error = %v, want %v", err, want)
204+
}
205+
}
206+
109207
func TestStateMachine_Configure_SubstateIsIncludedInCurrentState(t *testing.T) {
110208
sm := NewStateMachine(stateB)
111209
sm.Configure(stateB).SubstateOf(stateC)

0 commit comments

Comments
 (0)