package ai import ( "context" "errors" "strings" "testing" ) func TestProposeParsesProposal(t *testing.T) { svc := NewService(&fakeBackend{out: `{"next_action":"call mum","rationale":"people goal"}`}) p, err := svc.Propose(context.Background(), "off-screen brief") if err != nil { t.Fatalf("Propose: %v", err) } if p.NextAction != "call mum" || p.Rationale != "people goal" { t.Fatalf("proposal = %+v", p) } } func TestProposeBackendError(t *testing.T) { fb := &fakeBackend{err: errors.New("boom")} if _, err := NewService(fb).Propose(context.Background(), "brief"); err == nil { t.Fatal("want backend error") } } func TestProposeUnparseable(t *testing.T) { fb := &fakeBackend{out: "I cannot help."} if _, err := NewService(fb).Propose(context.Background(), "brief"); !errors.Is(err, ErrNoJSON) { t.Fatalf("want ErrNoJSON, got %v", err) } } func TestProposePromptIncludesBrief(t *testing.T) { fb := &fakeBackend{out: `{"next_action":"a","rationale":"b"}`} if _, err := NewService(fb).Propose(context.Background(), "water the tomato plants"); err != nil { t.Fatalf("Propose: %v", err) } if !strings.Contains(fb.gotPrompt, "water the tomato plants") { t.Fatalf("prompt should embed the brief, got: %s", fb.gotPrompt) } } func TestProposePromptHasFollowUpRule(t *testing.T) { p := buildProposePrompt("brief") if !strings.Contains(p, "do not simply repeat") { t.Fatalf("prompt missing follow-up rule:\n%s", p) } }