M2: ai Coach interface and Service over a pluggable Backend
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type fakeBackend struct {
|
||||
out string
|
||||
err error
|
||||
gotPrompt string
|
||||
}
|
||||
|
||||
func (f *fakeBackend) Run(ctx context.Context, prompt string) (string, error) {
|
||||
f.gotPrompt = prompt
|
||||
return f.out, f.err
|
||||
}
|
||||
func (f *fakeBackend) Name() string { return "fake" }
|
||||
|
||||
func TestServiceCoachSuccess(t *testing.T) {
|
||||
fb := &fakeBackend{out: `here you go {"next_action":"Write tests","success_condition":"green","timebox_minutes":30}`}
|
||||
svc := NewService(fb)
|
||||
p, err := svc.Coach(context.Background(), "write the tests")
|
||||
if err != nil {
|
||||
t.Fatalf("coach: %v", err)
|
||||
}
|
||||
if p.NextAction != "Write tests" || p.TimeboxSecs != 1800 {
|
||||
t.Fatalf("bad proposal: %+v", p)
|
||||
}
|
||||
if !strings.Contains(fb.gotPrompt, "write the tests") {
|
||||
t.Fatalf("prompt should embed the intent, got: %s", fb.gotPrompt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceCoachBackendError(t *testing.T) {
|
||||
fb := &fakeBackend{err: errors.New("boom")}
|
||||
if _, err := NewService(fb).Coach(context.Background(), "x"); err == nil {
|
||||
t.Fatal("want backend error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceCoachUnparseable(t *testing.T) {
|
||||
fb := &fakeBackend{out: "I cannot help with that."}
|
||||
if _, err := NewService(fb).Coach(context.Background(), "x"); !errors.Is(err, ErrNoJSON) {
|
||||
t.Fatalf("want ErrNoJSON, got %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user