package main import ( "context" "encoding/json" "net/http" "net/http/httptest" "path/filepath" "testing" ) func fakeModeJev(t *testing.T, choice string, conf float64) *JevClient { t.Helper() return fakeJevFor(t, map[string]struct { choice string conf float64 }{"mode": {choice, conf}}) } // fakeJevFor answers only the named question keys; any other question in the // same request gets an empty answer (so callers fall back to headers). func fakeJevFor(t *testing.T, answers map[string]struct { choice string conf float64 }) *JevClient { t.Helper() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var body struct { Questions map[string]any `json:"questions"` } json.NewDecoder(r.Body).Decode(&body) out := map[string]any{} for q := range body.Questions { if a, ok := answers[q]; ok { out[q] = map[string]any{"type": "choice", "choice": a.choice, "confidence": a.conf, "probabilities": map[string]float64{a.choice: a.conf}} } } json.NewEncoder(w).Encode(map[string]any{"model": "jev-1.14.0", "answers": out, "usage": map[string]int{"input_tokens": 60, "output_tokens": 0}}) })) t.Cleanup(srv.Close) return &JevClient{url: srv.URL, key: "tk", model: "jev-latest", threshold: 0.7, client: srv.Client()} } func TestModeQuestionsExist(t *testing.T) { m := modeQuestions["mode"].(map[string]any) if m["type"] != "choice" { t.Fatalf("mode must be a choice question, got %v", m["type"]) } crit := m["criteria"].(map[string]string) for _, k := range []string{"solo", "conspiracy", "split", "other"} { if _, ok := crit[k]; !ok { t.Errorf("mode criteria missing %q escape/class: %v", k, crit) } } } func TestClassifyMode(t *testing.T) { dir := t.TempDir() flog, err := OpenFlightLog(filepath.Join(dir, "flight.jsonl")) if err != nil { t.Fatal(err) } defer flog.Close() f := NewFlock(MockBrain{}, DefaultGuardrails(), flog) f.SetClassifier(fakeModeJev(t, "conspiracy", 0.9)) got, ok := f.ClassifyMode(context.Background(), "Should we launch a CLI or a web app first?") if !ok || got != "conspiracy" { t.Fatalf("want conspiracy ok, got %q ok=%v", got, ok) } evs, err := ReadEvents(filepath.Join(dir, "flight.jsonl")) if err != nil { t.Fatal(err) } modes := 0 for _, e := range evs { if e.Kind == "mode" && e.Mode == "conspiracy" && e.Prob != nil { modes++ } } if modes != 1 { t.Fatalf("want one logged mode verdict with probs, events: %+v", evs) } } func TestClassifyModeUnsureNotOk(t *testing.T) { f := NewFlock(MockBrain{}, DefaultGuardrails(), nil) f.SetClassifier(fakeModeJev(t, "split", 0.4)) if got, ok := f.ClassifyMode(context.Background(), "hi"); ok || got != "" { t.Fatalf("below-band confidence must not be ok, got %q ok=%v", got, ok) } } func TestMessageModeFields(t *testing.T) { m := Message{NestID: "lab", Kind: "request", Mode: "conspiracy", Invited: 3} if m.Mode != "conspiracy" || m.Invited != 3 { t.Fatalf("Message must carry mode + invited, got %+v", m) } } func TestPostRequestJevMode(t *testing.T) { ts, s := newTestNestServer(t) s.p.Flock.SetClassifier(fakeModeJev(t, "conspiracy", 0.9)) id := newNest(t, ts, "Lab", `"Hugin"`) code, req := post(t, ts.URL+"/api/nests/"+id+"/messages", `{"author":"owner","kind":"request","text":"should we launch a CLI or web app"}`) if code != http.StatusCreated || req["mode"] != "conspiracy" || req["mode_tagged_by"] != "jev" { t.Fatalf("want conspiracy/jev, got %d %v", code, req) } stored := s.msgs.All(id)[0] if stored.Mode != "conspiracy" || stored.ModeTagged != "jev" { t.Fatalf("record not persisted with mode: %+v", stored) } } func TestPostRequestModeHeaderFallback(t *testing.T) { ts, _ := newTestNestServer(t) // no Jev set id := newNest(t, ts, "Lab", `"Hugin"`) code, req := post(t, ts.URL+"/api/nests/"+id+"/messages", `{"author":"owner","kind":"request","mode":"split","text":"do both parts"}`) if code != http.StatusCreated || req["mode"] != "split" || req["mode_tagged_by"] != "header" { t.Fatalf("want split/header, got %d %v", code, req) } } func TestSayHasNoMode(t *testing.T) { ts, s := newTestNestServer(t) s.p.Flock.SetClassifier(fakeModeJev(t, "conspiracy", 0.9)) id := newNest(t, ts, "Lab", `"Hugin"`) code, say := post(t, ts.URL+"/api/nests/"+id+"/messages", `{"author":"owner","kind":"say","text":"nice weather"}`) if code != http.StatusCreated { t.Fatalf("say: %d", code) } if _, ok := say["mode"]; ok { t.Fatalf("a say must not carry a mode: %v", say) } }