Files
aether/pattern_test.go
Hugo Nijhuis adead7e980
All checks were successful
CI / build (pull_request) Successful in 18s
CI / build (push) Successful in 16s
Add wildcard namespace subscriptions
Support NATS-style wildcard patterns ("*" and ">") for subscribing
to events across multiple namespaces. This enables cross-cutting
concerns like logging, monitoring, and auditing without requiring
separate subscriptions for each namespace.

- Add pattern.go with MatchNamespacePattern and IsWildcardPattern
- Update EventBus to track wildcard subscribers separately
- Update NATSEventBus to use NATS native wildcard support
- Add comprehensive tests for pattern matching and EventBus wildcards
- Document security implications in all relevant code comments

Closes #20

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 19:24:26 +01:00

118 lines
3.8 KiB
Go

package aether
import "testing"
func TestMatchNamespacePattern(t *testing.T) {
tests := []struct {
name string
pattern string
namespace string
expected bool
}{
// Exact matches
{"exact match", "tenant-a", "tenant-a", true},
{"exact mismatch", "tenant-a", "tenant-b", false},
{"exact match with dots", "prod.tenant.a", "prod.tenant.a", true},
{"exact mismatch with dots", "prod.tenant.a", "prod.tenant.b", false},
// Empty cases
{"empty pattern", "", "tenant-a", false},
{"empty namespace exact", "tenant-a", "", false},
{"empty namespace catch-all", ">", "", false},
{"both empty", "", "", false},
// Single wildcard (*) - matches one token (NATS semantics: tokens are dot-separated)
{"star matches any single token", "*", "tenant-a", true},
{"star matches any single token 2", "*", "anything", true},
{"star does not match multi-token", "*", "prod.tenant", false},
{"prefix with star", "prod.*", "prod.tenant", true},
{"prefix with star 2", "prod.*", "prod.orders", true},
{"prefix with star no match extra tokens", "prod.*", "prod.tenant.orders", false},
{"prefix with star no match wrong prefix", "prod.*", "staging.tenant", false},
{"middle wildcard", "prod.*.orders", "prod.tenant.orders", true},
{"middle wildcard no match", "prod.*.orders", "prod.tenant.events", false},
{"multiple stars", "*.tenant.*", "prod.tenant.orders", true},
{"multiple stars 2", "*.*.orders", "prod.tenant.orders", true},
{"multiple stars no match", "*.*.orders", "prod.orders", false},
// Multi-token wildcard (>) - matches one or more tokens
{"greater matches one", ">", "tenant", true},
{"greater matches multi", ">", "prod.tenant.orders", true},
{"prefix greater", "prod.>", "prod.tenant", true},
{"prefix greater multi", "prod.>", "prod.tenant.orders.items", true},
{"prefix greater no match different prefix", "prod.>", "staging.tenant", false},
{"prefix greater requires at least one", "prod.>", "prod", false},
{"deep prefix greater", "prod.tenant.>", "prod.tenant.orders", true},
// Combined wildcards
{"star then greater", "*.>", "prod.tenant", true},
{"star then greater multi", "*.>", "prod.tenant.orders", true},
{"star then greater no match single", "*.>", "prod", false},
// Edge cases
{"trailing dot in pattern", "tenant.", "tenant.", true},
{"just dots", "..", "..", true},
{"star at end", "prod.tenant.*", "prod.tenant.a", true},
{"star at end no match", "prod.tenant.*", "prod.other.a", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := MatchNamespacePattern(tt.pattern, tt.namespace)
if result != tt.expected {
t.Errorf("MatchNamespacePattern(%q, %q) = %v, want %v",
tt.pattern, tt.namespace, result, tt.expected)
}
})
}
}
func TestIsWildcardPattern(t *testing.T) {
tests := []struct {
pattern string
expected bool
}{
{"tenant-a", false},
{"prod.tenant.orders", false},
{"*", true},
{"prod.*", true},
{"*.orders", true},
{">", true},
{"prod.>", true},
{"*.>", true},
{"prod.*.orders", true},
}
for _, tt := range tests {
t.Run(tt.pattern, func(t *testing.T) {
result := IsWildcardPattern(tt.pattern)
if result != tt.expected {
t.Errorf("IsWildcardPattern(%q) = %v, want %v",
tt.pattern, result, tt.expected)
}
})
}
}
func BenchmarkMatchNamespacePattern(b *testing.B) {
benchmarks := []struct {
name string
pattern string
namespace string
}{
{"exact", "tenant-a", "tenant-a"},
{"star", "*", "tenant-a"},
{"prefix_star", "prod.*", "prod.tenant"},
{"greater", ">", "prod.tenant.orders"},
{"complex", "prod.*.>", "prod.tenant.orders.items"},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
MatchNamespacePattern(bm.pattern, bm.namespace)
}
})
}
}