Add nudge_channels to MessagingConfig schema (gt-3shmx)
- Add NudgeChannels field to MessagingConfig struct in types.go - Initialize NudgeChannels map in NewMessagingConfig() - Add validation in validateMessagingConfig(): channel names must be non-empty and each channel must have at least one recipient - Add tests for valid nudge channels and empty recipient validation 🤖 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
d28ba6e2c6
commit
d94fb4669b
@@ -629,6 +629,9 @@ func validateMessagingConfig(c *MessagingConfig) error {
|
|||||||
if c.Announces == nil {
|
if c.Announces == nil {
|
||||||
c.Announces = make(map[string]AnnounceConfig)
|
c.Announces = make(map[string]AnnounceConfig)
|
||||||
}
|
}
|
||||||
|
if c.NudgeChannels == nil {
|
||||||
|
c.NudgeChannels = make(map[string][]string)
|
||||||
|
}
|
||||||
|
|
||||||
// Validate lists have at least one recipient
|
// Validate lists have at least one recipient
|
||||||
for name, recipients := range c.Lists {
|
for name, recipients := range c.Lists {
|
||||||
@@ -657,6 +660,16 @@ func validateMessagingConfig(c *MessagingConfig) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate nudge channels have non-empty names and at least one recipient
|
||||||
|
for name, recipients := range c.NudgeChannels {
|
||||||
|
if name == "" {
|
||||||
|
return fmt.Errorf("%w: nudge channel name cannot be empty", ErrMissingField)
|
||||||
|
}
|
||||||
|
if len(recipients) == 0 {
|
||||||
|
return fmt.Errorf("%w: nudge channel '%s' has no recipients", ErrMissingField, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -566,6 +566,8 @@ func TestMessagingConfigRoundTrip(t *testing.T) {
|
|||||||
Readers: []string{"@town"},
|
Readers: []string{"@town"},
|
||||||
RetainCount: 100,
|
RetainCount: 100,
|
||||||
}
|
}
|
||||||
|
original.NudgeChannels["workers"] = []string{"gastown/polecats/*", "gastown/crew/*"}
|
||||||
|
original.NudgeChannels["witnesses"] = []string{"*/witness"}
|
||||||
|
|
||||||
if err := SaveMessagingConfig(path, original); err != nil {
|
if err := SaveMessagingConfig(path, original); err != nil {
|
||||||
t.Fatalf("SaveMessagingConfig: %v", err)
|
t.Fatalf("SaveMessagingConfig: %v", err)
|
||||||
@@ -606,6 +608,17 @@ func TestMessagingConfigRoundTrip(t *testing.T) {
|
|||||||
if a, ok := loaded.Announces["alerts"]; !ok || a.RetainCount != 100 {
|
if a, ok := loaded.Announces["alerts"]; !ok || a.RetainCount != 100 {
|
||||||
t.Error("announce not preserved")
|
t.Error("announce not preserved")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check nudge channels
|
||||||
|
if len(loaded.NudgeChannels) != 2 {
|
||||||
|
t.Errorf("NudgeChannels count = %d, want 2", len(loaded.NudgeChannels))
|
||||||
|
}
|
||||||
|
if workers, ok := loaded.NudgeChannels["workers"]; !ok || len(workers) != 2 {
|
||||||
|
t.Error("workers nudge channel not preserved")
|
||||||
|
}
|
||||||
|
if witnesses, ok := loaded.NudgeChannels["witnesses"]; !ok || len(witnesses) != 1 {
|
||||||
|
t.Error("witnesses nudge channel not preserved")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMessagingConfigValidation(t *testing.T) {
|
func TestMessagingConfigValidation(t *testing.T) {
|
||||||
@@ -696,6 +709,27 @@ func TestMessagingConfigValidation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "valid config with nudge channels",
|
||||||
|
config: &MessagingConfig{
|
||||||
|
Type: "messaging",
|
||||||
|
Version: 1,
|
||||||
|
NudgeChannels: map[string][]string{
|
||||||
|
"workers": {"gastown/polecats/*", "gastown/crew/*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nudge channel with no recipients",
|
||||||
|
config: &MessagingConfig{
|
||||||
|
Version: 1,
|
||||||
|
NudgeChannels: map[string][]string{
|
||||||
|
"empty": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
@@ -278,6 +278,11 @@ type MessagingConfig struct {
|
|||||||
// Used for broadcast announcements that don't need acknowledgment.
|
// Used for broadcast announcements that don't need acknowledgment.
|
||||||
// Example: {"alerts": {"readers": ["@town"]}}
|
// Example: {"alerts": {"readers": ["@town"]}}
|
||||||
Announces map[string]AnnounceConfig `json:"announces,omitempty"`
|
Announces map[string]AnnounceConfig `json:"announces,omitempty"`
|
||||||
|
|
||||||
|
// NudgeChannels are named groups for real-time nudge fan-out.
|
||||||
|
// Like mailing lists but for tmux send-keys instead of durable mail.
|
||||||
|
// Example: {"workers": ["gastown/polecats/*", "gastown/crew/*"], "witnesses": ["*/witness"]}
|
||||||
|
NudgeChannels map[string][]string `json:"nudge_channels,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueueConfig represents a work queue configuration.
|
// QueueConfig represents a work queue configuration.
|
||||||
@@ -311,5 +316,6 @@ func NewMessagingConfig() *MessagingConfig {
|
|||||||
Lists: make(map[string][]string),
|
Lists: make(map[string][]string),
|
||||||
Queues: make(map[string]QueueConfig),
|
Queues: make(map[string]QueueConfig),
|
||||||
Announces: make(map[string]AnnounceConfig),
|
Announces: make(map[string]AnnounceConfig),
|
||||||
|
NudgeChannels: make(map[string][]string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user