Add expandQueue() to resolve queue workers from messaging.json (gt-xkbze)
Add queue expansion to internal/mail/router.go following the expandList() pattern: - Add ErrUnknownQueue error for unknown queue names - Add expandQueue() method to look up QueueConfig from messaging.json - Add TestExpandQueue and TestExpandQueueNoTownRoot tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
fd1afc1340
commit
4a84f68d48
@@ -17,6 +17,9 @@ import (
|
||||
// ErrUnknownList indicates a mailing list name was not found in configuration.
|
||||
var ErrUnknownList = errors.New("unknown mailing list")
|
||||
|
||||
// ErrUnknownQueue indicates a queue name was not found in configuration.
|
||||
var ErrUnknownQueue = errors.New("unknown queue")
|
||||
|
||||
// Router handles message delivery via beads.
|
||||
// It routes messages to the correct beads database based on address:
|
||||
// - Town-level (mayor/, deacon/) -> {townRoot}/.beads
|
||||
@@ -96,6 +99,28 @@ func (r *Router) expandList(listName string) ([]string, error) {
|
||||
return recipients, nil
|
||||
}
|
||||
|
||||
// expandQueue returns the QueueConfig for a queue name.
|
||||
// Returns ErrUnknownQueue if the queue is not found.
|
||||
func (r *Router) expandQueue(queueName string) (*config.QueueConfig, error) {
|
||||
// Load messaging config from town root
|
||||
if r.townRoot == "" {
|
||||
return nil, fmt.Errorf("%w: %s (no town root)", ErrUnknownQueue, queueName)
|
||||
}
|
||||
|
||||
configPath := config.MessagingConfigPath(r.townRoot)
|
||||
cfg, err := config.LoadMessagingConfig(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading messaging config: %w", err)
|
||||
}
|
||||
|
||||
queueCfg, ok := cfg.Queues[queueName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", ErrUnknownQueue, queueName)
|
||||
}
|
||||
|
||||
return &queueCfg, nil
|
||||
}
|
||||
|
||||
// detectTownRoot finds the town root by looking for mayor/town.json.
|
||||
func detectTownRoot(startDir string) string {
|
||||
dir := startDir
|
||||
|
||||
@@ -400,6 +400,99 @@ func TestExpandListNoTownRoot(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandQueue(t *testing.T) {
|
||||
// Create temp directory with messaging config
|
||||
tmpDir := t.TempDir()
|
||||
configDir := filepath.Join(tmpDir, "config")
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Write messaging.json with test queues
|
||||
configContent := `{
|
||||
"type": "messaging",
|
||||
"version": 1,
|
||||
"queues": {
|
||||
"work/gastown": {"workers": ["gastown/polecats/*"], "max_claims": 3},
|
||||
"priority-high": {"workers": ["mayor/", "gastown/witness"]}
|
||||
}
|
||||
}`
|
||||
if err := os.WriteFile(filepath.Join(configDir, "messaging.json"), []byte(configContent), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := NewRouterWithTownRoot(tmpDir, tmpDir)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
queueName string
|
||||
wantWorkers []string
|
||||
wantMax int
|
||||
wantErr bool
|
||||
errString string
|
||||
}{
|
||||
{
|
||||
name: "work/gastown queue",
|
||||
queueName: "work/gastown",
|
||||
wantWorkers: []string{"gastown/polecats/*"},
|
||||
wantMax: 3,
|
||||
},
|
||||
{
|
||||
name: "priority-high queue",
|
||||
queueName: "priority-high",
|
||||
wantWorkers: []string{"mayor/", "gastown/witness"},
|
||||
wantMax: 0, // Not specified, defaults to 0
|
||||
},
|
||||
{
|
||||
name: "unknown queue",
|
||||
queueName: "nonexistent",
|
||||
wantErr: true,
|
||||
errString: "unknown queue",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := r.expandQueue(tt.queueName)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("expandQueue(%q) expected error, got nil", tt.queueName)
|
||||
} else if tt.errString != "" && !contains(err.Error(), tt.errString) {
|
||||
t.Errorf("expandQueue(%q) error = %v, want containing %q", tt.queueName, err, tt.errString)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("expandQueue(%q) unexpected error: %v", tt.queueName, err)
|
||||
return
|
||||
}
|
||||
if len(got.Workers) != len(tt.wantWorkers) {
|
||||
t.Errorf("expandQueue(%q).Workers = %v, want %v", tt.queueName, got.Workers, tt.wantWorkers)
|
||||
return
|
||||
}
|
||||
for i, worker := range got.Workers {
|
||||
if worker != tt.wantWorkers[i] {
|
||||
t.Errorf("expandQueue(%q).Workers[%d] = %q, want %q", tt.queueName, i, worker, tt.wantWorkers[i])
|
||||
}
|
||||
}
|
||||
if got.MaxClaims != tt.wantMax {
|
||||
t.Errorf("expandQueue(%q).MaxClaims = %d, want %d", tt.queueName, got.MaxClaims, tt.wantMax)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandQueueNoTownRoot(t *testing.T) {
|
||||
r := &Router{workDir: "/tmp", townRoot: ""}
|
||||
_, err := r.expandQueue("work")
|
||||
if err == nil {
|
||||
t.Error("expandQueue with no townRoot should error")
|
||||
}
|
||||
if !contains(err.Error(), "no town root") {
|
||||
t.Errorf("expandQueue error = %v, want containing 'no town root'", err)
|
||||
}
|
||||
}
|
||||
|
||||
// contains checks if s contains substr (helper for error checking)
|
||||
func contains(s, substr string) bool {
|
||||
return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsHelper(s, substr))
|
||||
|
||||
Reference in New Issue
Block a user