| 1 | package bot |
| 2 | |
| 3 | import ( |
| 4 | "regexp" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | // TestTriggeredCharterHome pins the home-thread gate: in a member bot's own |
| 9 | // thread the OWNER triggers without a mention; the bot's persona-named |
| 10 | // editions (no uid) and anyone else never do. |
| 11 | func TestTriggeredCharterHome(t *testing.T) { |
| 12 | r := &Runtime{ |
| 13 | Session: &Session{Name: "Eleven"}, |
| 14 | ai: &anthropicClient{}, |
| 15 | Charters: true, |
| 16 | mention: regexp.MustCompile(`(?i)(^|[^a-z0-9])@Eleven\b`), |
| 17 | charterHomes: map[string]charterHome{"th-home": {BotID: "bot-1", Owner: "u-owner"}}, |
| 18 | } |
| 19 | cases := []struct { |
| 20 | name string |
| 21 | thread string |
| 22 | text string |
| 23 | uid string |
| 24 | want bool |
| 25 | }{ |
| 26 | {"owner, no mention, home thread", "th-home", "Hello?", "u-owner", true}, |
| 27 | {"owner, other thread", "th-else", "Hello?", "u-owner", false}, |
| 28 | {"persona edition (no uid), home thread", "th-home", "Tonight's news…", "", false}, |
| 29 | {"someone else's uid, home thread", "th-home", "Hello?", "u-other", false}, |
| 30 | {"mention still works anywhere", "th-else", "@Eleven hi", "u-owner", true}, |
| 31 | } |
| 32 | for _, c := range cases { |
| 33 | tc := &ThreadCtx{ID: c.thread, Kind: "group"} |
| 34 | if got := r.triggered(tc, c.text, c.uid); got != c.want { |
| 35 | t.Errorf("%s: triggered = %v, want %v", c.name, got, c.want) |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | // Without a model there is no conversational brain to answer — the home |
| 40 | // thread must not trigger the deterministic floor's degrade hint. |
| 41 | r.ai = nil |
| 42 | if r.triggered(&ThreadCtx{ID: "th-home", Kind: "group"}, "Hello?", "u-owner") { |
| 43 | t.Error("home thread must not trigger without an AI brain") |
| 44 | } |
| 45 | } |
| 46 | |