Files
gastown/internal/mail/router_test.go
Steve Yegge 9c718b0d7d test: Add unit tests for mail router and mailbox (gt-1pelm)
Add comprehensive tests for:
- router_test.go: 7 tests covering detectTownRoot, isTownLevelAddress,
  addressToSessionID, isSelfMail, shouldBeWisp, resolveBeadsDir
- mailbox_test.go: 17 tests for legacy mailbox operations
- types_test.go: Enhanced with additional priority and message tests

Coverage: 57.8% for mail package (legacy mailbox fully tested,
beads integration requires runtime infrastructure)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 16:16:24 -08:00

222 lines
4.9 KiB
Go

package mail
import (
"os"
"path/filepath"
"testing"
)
func TestDetectTownRoot(t *testing.T) {
// Create temp directory structure
tmpDir := t.TempDir()
townRoot := filepath.Join(tmpDir, "town")
mayorDir := filepath.Join(townRoot, "mayor")
rigDir := filepath.Join(townRoot, "gastown", "polecats", "test")
// Create mayor/town.json marker
if err := os.MkdirAll(mayorDir, 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(mayorDir, "town.json"), []byte("{}"), 0644); err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(rigDir, 0755); err != nil {
t.Fatal(err)
}
tests := []struct {
name string
startDir string
want string
}{
{
name: "from town root",
startDir: townRoot,
want: townRoot,
},
{
name: "from rig subdirectory",
startDir: rigDir,
want: townRoot,
},
{
name: "from mayor directory",
startDir: mayorDir,
want: townRoot,
},
{
name: "from non-town directory",
startDir: tmpDir,
want: "", // No town.json marker above tmpDir
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := detectTownRoot(tt.startDir)
if got != tt.want {
t.Errorf("detectTownRoot(%q) = %q, want %q", tt.startDir, got, tt.want)
}
})
}
}
func TestIsTownLevelAddress(t *testing.T) {
tests := []struct {
address string
want bool
}{
{"mayor", true},
{"mayor/", true},
{"deacon", true},
{"deacon/", true},
{"gastown/refinery", false},
{"gastown/polecats/Toast", false},
{"gastown/", false},
{"", false},
}
for _, tt := range tests {
t.Run(tt.address, func(t *testing.T) {
got := isTownLevelAddress(tt.address)
if got != tt.want {
t.Errorf("isTownLevelAddress(%q) = %v, want %v", tt.address, got, tt.want)
}
})
}
}
func TestAddressToSessionID(t *testing.T) {
tests := []struct {
address string
want string
}{
{"mayor", "gt-mayor"},
{"mayor/", "gt-mayor"},
{"gastown/refinery", "gt-gastown-refinery"},
{"gastown/Toast", "gt-gastown-Toast"},
{"beads/witness", "gt-beads-witness"},
{"gastown/", ""}, // Empty target
{"gastown", ""}, // No slash
{"", ""}, // Empty address
}
for _, tt := range tests {
t.Run(tt.address, func(t *testing.T) {
got := addressToSessionID(tt.address)
if got != tt.want {
t.Errorf("addressToSessionID(%q) = %q, want %q", tt.address, got, tt.want)
}
})
}
}
func TestIsSelfMail(t *testing.T) {
tests := []struct {
from string
to string
want bool
}{
{"mayor/", "mayor/", true},
{"mayor", "mayor/", true},
{"mayor/", "mayor", true},
{"gastown/Toast", "gastown/Toast", true},
{"gastown/Toast/", "gastown/Toast", true},
{"mayor/", "deacon/", false},
{"gastown/Toast", "gastown/Nux", false},
{"", "", true},
}
for _, tt := range tests {
t.Run(tt.from+"->"+tt.to, func(t *testing.T) {
got := isSelfMail(tt.from, tt.to)
if got != tt.want {
t.Errorf("isSelfMail(%q, %q) = %v, want %v", tt.from, tt.to, got, tt.want)
}
})
}
}
func TestShouldBeWisp(t *testing.T) {
r := &Router{}
tests := []struct {
name string
msg *Message
want bool
}{
{
name: "explicit wisp flag",
msg: &Message{Subject: "Regular message", Wisp: true},
want: true,
},
{
name: "POLECAT_STARTED subject",
msg: &Message{Subject: "POLECAT_STARTED: Toast"},
want: true,
},
{
name: "polecat_done subject (lowercase)",
msg: &Message{Subject: "polecat_done: work complete"},
want: true,
},
{
name: "NUDGE subject",
msg: &Message{Subject: "NUDGE: check your hook"},
want: true,
},
{
name: "START_WORK subject",
msg: &Message{Subject: "START_WORK: gt-123"},
want: true,
},
{
name: "regular message",
msg: &Message{Subject: "Please review this PR"},
want: false,
},
{
name: "handoff message (not auto-wisp)",
msg: &Message{Subject: "HANDOFF: context notes"},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := r.shouldBeWisp(tt.msg)
if got != tt.want {
t.Errorf("shouldBeWisp(%v) = %v, want %v", tt.msg.Subject, got, tt.want)
}
})
}
}
func TestResolveBeadsDir(t *testing.T) {
// With town root set
r := NewRouterWithTownRoot("/work/dir", "/home/user/gt")
got := r.resolveBeadsDir("gastown/Toast")
want := "/home/user/gt/.beads"
if got != want {
t.Errorf("resolveBeadsDir with townRoot = %q, want %q", got, want)
}
// Without town root (fallback to workDir)
r2 := &Router{workDir: "/work/dir", townRoot: ""}
got2 := r2.resolveBeadsDir("mayor/")
want2 := "/work/dir/.beads"
if got2 != want2 {
t.Errorf("resolveBeadsDir without townRoot = %q, want %q", got2, want2)
}
}
func TestNewRouterWithTownRoot(t *testing.T) {
r := NewRouterWithTownRoot("/work/rig", "/home/gt")
if r.workDir != "/work/rig" {
t.Errorf("workDir = %q, want '/work/rig'", r.workDir)
}
if r.townRoot != "/home/gt" {
t.Errorf("townRoot = %q, want '/home/gt'", r.townRoot)
}
}