From 92a7ff35d06cfc5175b6f025e5a3cded6aa5678d Mon Sep 17 00:00:00 2001 From: Smokey <150986568+jacisjake@users.noreply.github.com> Date: Wed, 28 Oct 2026 05:33:52 -0600 Subject: [PATCH] =?UTF-8?q?test(conspiracy):=20red=20for=20#24=20=E2=80=94?= =?UTF-8?q?=20distinct=20lens=20invites,=20capped=20to=20MurderSize?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conspiracy_test.go | 93 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 conspiracy_test.go diff --git a/conspiracy_test.go b/conspiracy_test.go new file mode 100644 index 0000000..20f1609 --- /dev/null +++ b/conspiracy_test.go @@ -0,0 +1,93 @@ +package main + +import ( + "context" + "net/http" + "net/http/httptest" + "strings" + "sync" + "testing" +) + +// recordBrain stores every (system,prompt) it is asked; replies as a claim. +type recordBrain struct { + mu sync.Mutex + pairs [][2]string +} + +func (b *recordBrain) Name() string { return "recorder" } +func (b *recordBrain) Ask(ctx context.Context, system, prompt string) (string, int, error) { + b.mu.Lock() + b.pairs = append(b.pairs, [2]string{system, prompt}) + b.mu.Unlock() + return "KIND: claim\nOn it.", 10, nil +} + +func newRecordServer(t *testing.T) (*httptest.Server, *Server, *recordBrain) { + t.Helper() + ts, s := newTestNestServer(t) + rb := &recordBrain{} + s.p.Flock.brain = rb + return ts, s, rb +} + +func TestConspiracyInvitesDistinctLenses(t *testing.T) { + ts, _, rb := newRecordServer(t) + id := newNest(t, ts, "Lab", `"Hugin","Munin","Soot"`) + code, req := post(t, ts.URL+"/api/nests/"+id+"/messages", + `{"author":"owner","kind":"request","mode":"conspiracy","text":"is this launch risky"}`) + if code != http.StatusCreated || req["mode"] != "conspiracy" { + t.Fatalf("post: %d %v", code, req) + } + awaitMessages(t, ts, id, 4) // request + 3 lens claims + + rb.mu.Lock() + defer rb.mu.Unlock() + if len(rb.pairs) != 3 { + t.Fatalf("want exactly 3 invited prompts, got %d", len(rb.pairs)) + } + seen := map[string]bool{} + for _, pr := range rb.pairs { + if !strings.Contains(pr[1], conspiracyLine) { + t.Fatalf("every invited prompt must carry the lens line: %q", pr[1]) + } + hits := 0 + for _, name := range []string{"Hugin", "Munin", "Soot"} { + if strings.Contains(pr[1], "("+name+")") { + hits++ + } + } + if hits != 1 { + t.Fatalf("prompt should name exactly one crow's lens, got %d in %q", hits, pr[1]) + } + if seen[pr[1]] { + t.Fatal("two invited prompts were identical") + } + seen[pr[1]] = true + } +} + +func TestConspiracyInviteCapped(t *testing.T) { + ts, _, rb := newRecordServer(t) + id := newNest(t, ts, "Wide", strings.Join(quoteAll(roster), ",")) + code, out := post(t, ts.URL+"/api/nests/"+id+"/messages", + `{"author":"owner","kind":"request","mode":"conspiracy","invited":4,"text":"risk"}`) + if code != http.StatusCreated { + t.Fatalf("post: %d %v", code, out) + } + awaitMessages(t, ts, id, 4) + rb.mu.Lock() + n := len(rb.pairs) + rb.mu.Unlock() + if n != 3 { // min(invited 4, MurderSize 3) + t.Fatalf("want invite capped to MurderSize=3, got %d", n) + } +} + +func quoteAll(xs []string) []string { + out := make([]string, len(xs)) + for i, x := range xs { + out[i] = `"` + x + `"` + } + return out +}