package tide import ( "context" "fmt" "strings" ) // ReplayFlow executes each recorded step against target and diffs responses. func ReplayFlow(ctx context.Context, flow Flow, cfg ReplayConfig) (Result, error) { if err := validateFlow(flow); err != nil { return Result{}, err } if strings.TrimSpace(cfg.Target) == "" { return Result{}, fmt.Errorf("tide: replay target is required") } client := cfg.Client if client == nil { client = defaultClient() } limit := maxBody(cfg.MaxBody) store := cfg.Store if store == nil { store = mustMemoryStore() } result := Result{OK: true, Steps: make([]StepResult, 0, len(flow.Steps))} skipRest := false for _, step := range flow.Steps { if skipRest { result.Steps = append(result.Steps, StepResult{ID: step.ID, Skipped: true}) continue } sr := StepResult{ID: step.ID, OK: true} want := step if err := materializeSidecar(cfg.BaseDir, &want.Response); err != nil { return result, fmt.Errorf("tide: replay step %s: %w", step.ID, err) } req, err := expandRequest(step.Request, store) if err != nil { sr.OK = false result.OK = false sr.Diffs = append(sr.Diffs, Diff{Path: "request", Expected: "resolved placeholders", Actual: err.Error()}) result.Steps = append(result.Steps, sr) skipRest = true continue } got, err := doStep(ctx, client, cfg.Target, req, limit) if err != nil { return result, fmt.Errorf("tide: replay step %s: %w", step.ID, err) } live := step live.Request = req live.Response = got if err := CaptureStep(store, &live); err != nil { sr.OK = false result.OK = false sr.Diffs = append(sr.Diffs, Diff{Path: "capture", Expected: "captured value", Actual: err.Error()}) result.Steps = append(result.Steps, sr) skipRest = true continue } _ = ScrubStep(store, &live) sr.Diffs = append(sr.Diffs, compareStep(want, live.Response)...) if len(sr.Diffs) > 0 { sr.OK = false result.OK = false } result.Steps = append(result.Steps, sr) } if !result.OK { return result, &MismatchError{Result: result} } return result, nil } func mustMemoryStore() *Store { s, _ := OpenStore("") return s } func compareStep(want Step, got Response) []Diff { var diffs []Diff if want.Response.Status != 0 && got.Status != want.Response.Status { diffs = append(diffs, Diff{ Path: "status", Expected: fmt.Sprintf("%d", want.Response.Status), Actual: fmt.Sprintf("%d", got.Status), }) } diffs = append(diffs, compareHeaders(want.Response.Headers, got.Headers, want.Headers)...) diffs = append(diffs, compareBodies(want.Response, got, want)...) return diffs }