| 1 | package bot |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | // TestClampMemory pins the read-side clamp (the scratchpad and the sidecar |
| 9 | // share the slot unsigned, so both sides re-clamp whatever they read — keep |
| 10 | // this in step with web/static/apps/memory.js sanitizeMemory). |
| 11 | func TestClampMemory(t *testing.T) { |
| 12 | long := strings.Repeat("x", maxMemFactChars+50) |
| 13 | m := memoryState{Facts: []memFact{ |
| 14 | {ID: "m1", Text: " keeps replies short "}, |
| 15 | {Text: "no id — repaired"}, |
| 16 | {ID: "m3", Text: " "}, // blank — dropped |
| 17 | {ID: "m4", Text: long}, |
| 18 | }} |
| 19 | clampMemory(&m) |
| 20 | if m.V != 1 || len(m.Facts) != 3 { |
| 21 | t.Fatalf("clamped shape: v=%d facts=%d", m.V, len(m.Facts)) |
| 22 | } |
| 23 | if m.Facts[0].Text != "keeps replies short" { |
| 24 | t.Errorf("trim: %q", m.Facts[0].Text) |
| 25 | } |
| 26 | if m.Facts[1].ID == "" { |
| 27 | t.Error("missing id not repaired") |
| 28 | } |
| 29 | if len(m.Facts[2].Text) != maxMemFactChars { |
| 30 | t.Errorf("overlong fact not clamped: %d", len(m.Facts[2].Text)) |
| 31 | } |
| 32 | |
| 33 | over := memoryState{} |
| 34 | for i := 0; i < maxMemFacts+10; i++ { |
| 35 | over.Facts = append(over.Facts, memFact{ID: newMemID(), Text: "fact"}) |
| 36 | } |
| 37 | clampMemory(&over) |
| 38 | if len(over.Facts) != maxMemFacts { |
| 39 | t.Fatalf("cap: %d facts survived", len(over.Facts)) |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | // TestMemoryToolsDMOnly pins the gate: remember/forget exist exactly where |
| 44 | // the memory lives — a DM — and never in groups. |
| 45 | func TestMemoryToolsDMOnly(t *testing.T) { |
| 46 | r := &Runtime{Session: &Session{Name: "Eleven"}} |
| 47 | has := func(tools []anthTool, name string) bool { |
| 48 | for _, tl := range tools { |
| 49 | if tl.Name == name { |
| 50 | return true |
| 51 | } |
| 52 | } |
| 53 | return false |
| 54 | } |
| 55 | dm := r.replyTools(&ThreadCtx{Kind: "dm"}) |
| 56 | if !has(dm, "remember") || !has(dm, "forget") { |
| 57 | t.Error("DM palette missing the memory tools") |
| 58 | } |
| 59 | group := r.replyTools(&ThreadCtx{Kind: "group"}) |
| 60 | if has(group, "remember") || has(group, "forget") { |
| 61 | t.Error("group palette must not carry the memory tools") |
| 62 | } |
| 63 | } |
| 64 | |