package main import ( "context" "strings" "testing" "time" ) func TestVoicedPromptHasThreeLayers(t *testing.T) { p, _ := personaOf("Hugin") s := voicedBanterSystem(p, voices["Hugin"]) for _, want := range []string{ markerBanter, "You are Hugin, Thought", "MEMORY:", "LINE:", // layer 1 "Favor:", "Rhythm:", "Never: Hedging", "Never: System words", // layer 2 "word for word", "Hand the turn back: end with a dare", // layer 3 } { if !strings.Contains(s, want) { t.Errorf("voiced prompt missing %q", want) } } if !strings.HasPrefix(s, markerBanter) { t.Error("voiced prompt must start with the banter marker") } } func TestParseVoicedDropsMemoryAndKeepsLine(t *testing.T) { got, ok := parseVoiced("MEMORY: I flew that road once.\nLINE: \"Slow as ever, Munin. Race me?\"", "Hugin") if !ok || got != "Slow as ever, Munin. Race me?" { t.Fatalf("got %q %v", got, ok) } if got, ok := parseVoiced("MEMORY: x\nLINE:\nRace me, Munin.", "Hugin"); !ok || got != "Race me, Munin." { t.Fatalf("line on next row: got %q %v", got, ok) } // A memory with no line must never reach the chat. if got, ok := parseVoiced("MEMORY: I flew that road once.", "Hugin"); ok { t.Fatalf("leaked memory: %q", got) } // A reply that ignored the format (or came from an unrelated model) is dropped. for _, bad := range []string{"Race me, Munin.", "User Safety: safe"} { if got, ok := parseVoiced(bad, "Hugin"); ok { t.Fatalf("accepted unlabeled reply %q", got) } } // The line is still cleaned and validated. if _, ok := parseVoiced("MEMORY: x\nLINE: see https://example.com", "Hugin"); ok { t.Fatal("accepted a link") } } func TestVoicedCrowSpeaksThroughMemoryAndLine(t *testing.T) { b := &promptBrain{reply: "MEMORY: Odin feared for him less. Still stings.\nLINE: Back by breakfast, Munin. Try to keep up."} em := &recordEmitter{} c := newChatter(b, em, &flockMemory{}) c.moment(context.Background(), "L-1", "Munin", "Hugin", "The human approved Munin's answer.") c.wait(5 * time.Second) if len(em.events) != 1 { t.Fatalf("got %d lines", len(em.events)) } if got := em.events[0]["text"]; got != "Back by breakfast, Munin. Try to keep up." { t.Fatalf("memory leaked or line wrong: %q", got) } if !strings.Contains(b.prompts[0], "MEMORY:") || !strings.Contains(b.prompts[0], "Odin's two ravens") { t.Fatalf("voiced prompt or retrieved lore missing:\n%s", b.prompts[0]) } } func TestUnvoicedCrowKeepsPlainBanter(t *testing.T) { b := &promptBrain{reply: "Nice try."} em := &recordEmitter{} c := newChatter(b, em, &flockMemory{}) c.moment(context.Background(), "L-1", "Hugin", "Soot", "The human rejected Hugin's answer.") c.wait(5 * time.Second) if len(em.events) != 1 || em.events[0]["text"] != "Nice try." { t.Fatalf("got %v", em.events) } if strings.Contains(b.prompts[0], "MEMORY:") { t.Error("an unvoiced crow must not get the two-step prompt") } } func TestEveryVoiceBelongsToAPersonaAndIsComplete(t *testing.T) { for name, v := range voices { if _, ok := personaOf(name); !ok { t.Errorf("voice %q has no persona", name) } if len(v.Diction) == 0 || len(v.Rhythm) == 0 || len(v.Never) == 0 || v.Hook == "" { t.Errorf("voice %q is incomplete", name) } } }