package flow import ( "context" "encoding/json" "fmt" "testing" ) func TestRunRecordsOrderAndHooks(t *testing.T) { reg := NewRegistry() var trace []string reg.Action("a", func(_ context.Context, _ *State, _ json.RawMessage) error { trace = append(trace, "a") return nil }) reg.Action("b", func(_ context.Context, _ *State, _ json.RawMessage) error { trace = append(trace, "b") return nil }) reg.Hook("rec", func(_ context.Context, _ *State, info HookInfo, _ json.RawMessage) error { trace = append(trace, string(info.Phase)+":"+info.Step) return nil }) def := &Definition{Name: "t", Steps: []Step{ {Name: "s1", Action: "a", Hooks: Hooks{Before: []HookRef{{Type: "rec"}}, After: []HookRef{{Type: "rec"}}}}, {Name: "s2", Action: "b"}, }} res, err := NewEngine(reg, nil).Run(context.Background(), def, NewState(1, nil)) if err != nil { t.Fatal(err) } if res.Failed || len(res.Steps) != 2 { t.Fatalf("unexpected result %+v", res) } want := []string{"before:s1", "a", "after:s1", "b"} if fmt.Sprint(trace) != fmt.Sprint(want) { t.Fatalf("trace = %v, want %v", trace, want) } } func TestPredicateSkipsStep(t *testing.T) { reg := NewRegistry() ran := false reg.Action("a", func(_ context.Context, _ *State, _ json.RawMessage) error { ran = true; return nil }) reg.Predicate("never", func(_ context.Context, _ *State) (bool, error) { return false, nil }) def := &Definition{Name: "t", Steps: []Step{{Name: "s1", Action: "a", When: "never"}}} res, err := NewEngine(reg, nil).Run(context.Background(), def, NewState(1, nil)) if err != nil { t.Fatal(err) } if ran { t.Fatal("action ran despite predicate=false") } if !res.Steps[0].Skipped { t.Fatal("step not marked skipped") } } func TestContinueOnError(t *testing.T) { reg := NewRegistry() reg.Action("boom", func(_ context.Context, _ *State, _ json.RawMessage) error { return fmt.Errorf("x") }) reg.Action("ok", func(_ context.Context, _ *State, _ json.RawMessage) error { return nil }) def := &Definition{Name: "t", Steps: []Step{ {Name: "s1", Action: "boom", ContinueOnError: true}, {Name: "s2", Action: "ok"}, }} res, err := NewEngine(reg, nil).Run(context.Background(), def, NewState(1, nil)) if err != nil { t.Fatalf("continue-on-error should not abort: %v", err) } if res.Steps[0].Error == "" || res.Steps[1].Error != "" { t.Fatalf("unexpected step results %+v", res.Steps) } } func TestValidateCatchesUnknownRefs(t *testing.T) { reg := NewRegistry() reg.Action("a", func(_ context.Context, _ *State, _ json.RawMessage) error { return nil }) eng := NewEngine(reg, nil) if err := eng.Validate(&Definition{Name: "t", Steps: []Step{{Name: "s", Action: "missing"}}}); err == nil { t.Fatal("expected unknown-action error") } if err := eng.Validate(&Definition{Name: "t", Steps: []Step{{Name: "s", Action: "a"}}}); err != nil { t.Fatalf("valid def rejected: %v", err) } }