Add sequential conversation coordinator
This commit is contained in:
109
internal/usecase/coordinate_test.go
Normal file
109
internal/usecase/coordinate_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"go.maximumdirect.net/promptkit/internal/domain"
|
||||
)
|
||||
|
||||
func TestCoordinateBudgetAndDelivery(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
budget int
|
||||
stop bool
|
||||
outcome CoordinationOutcome
|
||||
runs, callbacks int
|
||||
}{
|
||||
{"zero stop", 0, true, CoordinationStopped, 0, 1},
|
||||
{"zero exhausted", 0, false, CoordinationExhausted, 0, 1},
|
||||
{"stop after last", 2, true, CoordinationStopped, 2, 3},
|
||||
{"request after last", 2, false, CoordinationExhausted, 2, 3},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
start := domain.CaptureExchange(nil, "initial", nil, "start")
|
||||
runs, callbacks := 0, 0
|
||||
result, err := Coordinate(context.Background(), start, tc.budget, func(_ context.Context, turn ConversationTurn[string]) (*string, error) {
|
||||
callbacks++
|
||||
if turn.RemainingTurns != tc.budget-runs || (runs == 0 && turn.Result != nil) || (runs > 0 && (turn.Result == nil || *turn.Result != fmt.Sprint(runs))) {
|
||||
t.Fatalf("unexpected turn: %#v", turn)
|
||||
}
|
||||
if tc.stop && turn.RemainingTurns == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
value := "next"
|
||||
return &value, nil
|
||||
}, func(_ context.Context, current *domain.Exchange, _ *string) (*string, *domain.Exchange, error) {
|
||||
runs++
|
||||
value := fmt.Sprint(runs)
|
||||
return &value, domain.CaptureExchange(current, value, nil, value), nil
|
||||
})
|
||||
if err != nil || result.Outcome != tc.outcome || result.Undelivered != nil || runs != tc.runs || callbacks != tc.callbacks {
|
||||
t.Fatalf("result=%#v error=%v runs=%d callbacks=%d", result, err, runs, callbacks)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoordinateCancellationAndErrors(t *testing.T) {
|
||||
start := domain.CaptureExchange(nil, "initial", nil, "start")
|
||||
request := "continue"
|
||||
for _, mode := range []string{"before", "after run", "inside callback", "callback error", "run error"} {
|
||||
t.Run(mode, func(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
sentinel := errors.New("sentinel")
|
||||
calls := 0
|
||||
if mode == "before" {
|
||||
cancel()
|
||||
}
|
||||
result, err := Coordinate(ctx, start, 1, func(_ context.Context, turn ConversationTurn[string]) (*string, error) {
|
||||
calls++
|
||||
if mode == "inside callback" && turn.Result != nil {
|
||||
cancel()
|
||||
return nil, sentinel
|
||||
}
|
||||
if mode == "callback error" {
|
||||
return nil, fmt.Errorf("callback: %w", sentinel)
|
||||
}
|
||||
return &request, nil
|
||||
}, func(_ context.Context, current *domain.Exchange, _ *string) (*string, *domain.Exchange, error) {
|
||||
if mode == "run error" {
|
||||
return nil, nil, fmt.Errorf("run: %w", sentinel)
|
||||
}
|
||||
value := "response"
|
||||
if mode == "after run" {
|
||||
cancel()
|
||||
}
|
||||
return &value, domain.CaptureExchange(current, "next", nil, value), nil
|
||||
})
|
||||
if mode == "before" || mode == "after run" || mode == "inside callback" {
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("error = %v", err)
|
||||
}
|
||||
} else if !errors.Is(err, sentinel) {
|
||||
t.Fatalf("error = %v", err)
|
||||
}
|
||||
if result.Outcome != "" || (mode == "after run") != (result.Undelivered != nil) {
|
||||
t.Fatalf("result = %#v", result)
|
||||
}
|
||||
if mode == "before" && calls != 0 {
|
||||
t.Fatal("callback invoked after cancellation")
|
||||
}
|
||||
if mode == "inside callback" && calls != 2 {
|
||||
t.Fatalf("callbacks = %d", calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoordinatePropagatesPanic(t *testing.T) {
|
||||
defer func() {
|
||||
if recover() != "callback panic" {
|
||||
t.Error("panic did not propagate")
|
||||
}
|
||||
}()
|
||||
Coordinate[string, string](context.Background(), nil, 0, func(context.Context, ConversationTurn[string]) (*string, error) { panic("callback panic") }, nil)
|
||||
}
|
||||
Reference in New Issue
Block a user