diff --git a/api.go b/api.go index c9b17e8..dddae8e 100644 --- a/api.go +++ b/api.go @@ -72,7 +72,8 @@ func (e *Engine) Groups() []RouteGroup { // GroupsIter returns an iterator over all registered route groups. func (e *Engine) GroupsIter() iter.Seq[RouteGroup] { - return slices.Values(e.groups) + groups := slices.Clone(e.groups) + return slices.Values(groups) } // Register adds a route group to the engine. @@ -94,8 +95,9 @@ func (e *Engine) Channels() []string { // ChannelsIter returns an iterator over WebSocket channel names from registered StreamGroups. func (e *Engine) ChannelsIter() iter.Seq[string] { + groups := slices.Clone(e.groups) return func(yield func(string) bool) { - for _, g := range e.groups { + for _, g := range groups { if sg, ok := g.(StreamGroup); ok { for _, c := range sg.Channels() { if !yield(c) { diff --git a/modernization_test.go b/modernization_test.go index 21d08a2..1212a72 100644 --- a/modernization_test.go +++ b/modernization_test.go @@ -27,6 +27,28 @@ func TestEngine_GroupsIter(t *testing.T) { } } +func TestEngine_GroupsIter_Good_SnapshotsCurrentGroups(t *testing.T) { + e, _ := api.New() + g1 := &healthGroup{} + g2 := &stubGroup{} + e.Register(g1) + + iter := e.GroupsIter() + e.Register(g2) + + var groups []api.RouteGroup + for g := range iter { + groups = append(groups, g) + } + + if len(groups) != 1 { + t.Fatalf("expected iterator snapshot to contain 1 group, got %d", len(groups)) + } + if groups[0].Name() != "health-extra" { + t.Fatalf("expected snapshot to preserve original group, got %q", groups[0].Name()) + } +} + type streamGroupStub struct { healthGroup channels []string @@ -52,6 +74,26 @@ func TestEngine_ChannelsIter(t *testing.T) { } } +func TestEngine_ChannelsIter_Good_SnapshotsCurrentChannels(t *testing.T) { + e, _ := api.New() + g1 := &streamGroupStub{channels: []string{"ch1", "ch2"}} + g2 := &streamGroupStub{channels: []string{"ch3"}} + e.Register(g1) + + iter := e.ChannelsIter() + e.Register(g2) + + var channels []string + for ch := range iter { + channels = append(channels, ch) + } + + expected := []string{"ch1", "ch2"} + if !slices.Equal(channels, expected) { + t.Fatalf("expected snapshot channels %v, got %v", expected, channels) + } +} + func TestToolBridge_Iterators(t *testing.T) { b := api.NewToolBridge("/tools") desc := api.ToolDescriptor{Name: "test", Group: "g1"}