diff --git a/spec_registry.go b/spec_registry.go index 610806e..8ce659d 100644 --- a/spec_registry.go +++ b/spec_registry.go @@ -23,6 +23,9 @@ func RegisterSpecGroups(groups ...RouteGroup) { if group == nil { continue } + if specRegistryContains(group) { + continue + } specRegistry.groups = append(specRegistry.groups, group) } } @@ -37,3 +40,30 @@ func RegisteredSpecGroups() []RouteGroup { copy(out, specRegistry.groups) return out } + +// ResetSpecGroups clears the package-level spec registry. +// It is primarily intended for tests that need to isolate global state. +func ResetSpecGroups() { + specRegistry.mu.Lock() + defer specRegistry.mu.Unlock() + + specRegistry.groups = nil +} + +func specRegistryContains(group RouteGroup) bool { + key := specGroupKey(group) + for _, existing := range specRegistry.groups { + if specGroupKey(existing) == key { + return true + } + } + return false +} + +func specGroupKey(group RouteGroup) string { + if group == nil { + return "" + } + + return group.Name() + "\x00" + group.BasePath() +} diff --git a/spec_registry_test.go b/spec_registry_test.go new file mode 100644 index 0000000..dbd20f0 --- /dev/null +++ b/spec_registry_test.go @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: EUPL-1.2 + +package api_test + +import ( + "testing" + + "github.com/gin-gonic/gin" + + api "dappco.re/go/core/api" +) + +type specRegistryStubGroup struct { + name string + basePath string +} + +func (g *specRegistryStubGroup) Name() string { return g.name } +func (g *specRegistryStubGroup) BasePath() string { return g.basePath } +func (g *specRegistryStubGroup) RegisterRoutes(rg *gin.RouterGroup) {} + +func TestRegisterSpecGroups_Good_DeduplicatesByIdentity(t *testing.T) { + snapshot := api.RegisteredSpecGroups() + api.ResetSpecGroups() + t.Cleanup(func() { + api.ResetSpecGroups() + api.RegisterSpecGroups(snapshot...) + }) + + first := &specRegistryStubGroup{name: "alpha", basePath: "/alpha"} + second := &specRegistryStubGroup{name: "alpha", basePath: "/alpha"} + third := &specRegistryStubGroup{name: "beta", basePath: "/beta"} + + api.RegisterSpecGroups(nil, first, second, third, first) + + groups := api.RegisteredSpecGroups() + if len(groups) != 2 { + t.Fatalf("expected 2 unique groups, got %d", len(groups)) + } + + if groups[0].Name() != "alpha" || groups[0].BasePath() != "/alpha" { + t.Fatalf("expected first group to be alpha at /alpha, got %s at %s", groups[0].Name(), groups[0].BasePath()) + } + if groups[1].Name() != "beta" || groups[1].BasePath() != "/beta" { + t.Fatalf("expected second group to be beta at /beta, got %s at %s", groups[1].Name(), groups[1].BasePath()) + } +}