package run_test import ( "context" "errors" "testing" "gitea.stevedudenhoeffer.com/steve/majordomo/llm" "gitea.stevedudenhoeffer.com/steve/majordomo/provider/fake" "gitea.stevedudenhoeffer.com/steve/executus/deliver" "gitea.stevedudenhoeffer.com/steve/executus/run" "gitea.stevedudenhoeffer.com/steve/executus/tool" ) type recordingDelivery struct { target deliver.Target output string errored error delivers int } func (d *recordingDelivery) Deliver(_ context.Context, t deliver.Target, output string, _ []deliver.Artifact) (string, error) { d.target, d.output, d.delivers = t, output, d.delivers+1 return "msg-1", nil } func (d *recordingDelivery) DeliverError(_ context.Context, t deliver.Target, e error) error { d.target, d.errored = t, e return nil } func TestDeliveryWired(t *testing.T) { d := &recordingDelivery{} fp := fake.New("fake") fp.Enqueue("m", fake.Reply("the output")) m, _ := fp.Model("m") ex := run.New(run.Config{ Registry: tool.NewRegistry(), Models: func(ctx context.Context, _ string) (context.Context, llm.Model, error) { return ctx, m, nil }, Ports: run.Ports{Delivery: d}, }) // With a delivery target, the executor posts the output. ex.Run(context.Background(), run.RunnableAgent{Name: "x", ModelTier: "m"}, tool.Invocation{RunID: "r", DeliveryKind: "channel", DeliveryID: "chan-9"}, "go") if d.delivers != 1 || d.output != "the output" || d.target.ID != "chan-9" || d.target.Kind != "channel" { t.Fatalf("delivery wrong: %+v out=%q", d.target, d.output) } } func TestNoDeliveryWithoutTarget(t *testing.T) { d := &recordingDelivery{} fp := fake.New("fake") fp.Enqueue("m", fake.Reply("x")) m, _ := fp.Model("m") ex := run.New(run.Config{ Registry: tool.NewRegistry(), Models: func(ctx context.Context, _ string) (context.Context, llm.Model, error) { return ctx, m, nil }, Ports: run.Ports{Delivery: d}, }) // No DeliveryID → executor delivers nothing (caller reads Result.Output). ex.Run(context.Background(), run.RunnableAgent{Name: "x", ModelTier: "m"}, tool.Invocation{RunID: "r"}, "go") if d.delivers != 0 { t.Errorf("no target should mean no delivery, got %d", d.delivers) } } // TestNoDeliveryOnEarlyResolveError: an error BEFORE the run starts (model // resolve) returns before delivery is reached — neither Deliver nor DeliverError // fires. (Delivery covers run OUTCOMES, not pre-run setup failures.) func TestNoDeliveryOnEarlyResolveError(t *testing.T) { d := &recordingDelivery{} ex := run.New(run.Config{ Registry: tool.NewRegistry(), Models: func(ctx context.Context, _ string) (context.Context, llm.Model, error) { return ctx, nil, errors.New("resolve boom") }, Ports: run.Ports{Delivery: d}, }) ex.Run(context.Background(), run.RunnableAgent{Name: "x", ModelTier: "m"}, tool.Invocation{RunID: "r", DeliveryKind: "channel", DeliveryID: "chan-9"}, "go") if d.delivers != 0 || d.errored != nil { t.Errorf("early resolve failure should neither Deliver nor DeliverError: delivers=%d errored=%v", d.delivers, d.errored) } } // TestDeliverErrorOnRunFailure: an in-loop run failure (the model errors) routes // through DeliverError with the run error. func TestDeliverErrorOnRunFailure(t *testing.T) { d := &recordingDelivery{} fp := fake.New("fake") fp.Enqueue("m", fake.Step{Err: errors.New("model boom")}) // model errors mid-run m, _ := fp.Model("m") ex := run.New(run.Config{ Registry: tool.NewRegistry(), Models: func(ctx context.Context, _ string) (context.Context, llm.Model, error) { return ctx, m, nil }, Ports: run.Ports{Delivery: d}, }) res := ex.Run(context.Background(), run.RunnableAgent{Name: "x", ModelTier: "m"}, tool.Invocation{RunID: "r", DeliveryKind: "channel", DeliveryID: "chan-9"}, "go") if res.Err == nil { t.Fatal("expected a run error") } if d.delivers != 0 { t.Errorf("a failed run should not Deliver (success path), got %d", d.delivers) } if d.errored == nil || d.target.ID != "chan-9" { t.Errorf("a failed run with a target should DeliverError to chan-9, got errored=%v target=%+v", d.errored, d.target) } }