package allowlist_test import ( "net/url" "testing" "sneak.berlin/go/pixa/internal/allowlist" ) const ( testExactHost = "cdn.example.com" testImageURL = "https://cdn.example.com/image.jpg" testSuffix = ".example.com" ) type isAllowedCase struct { name string patterns []string testURL string want bool } func runIsAllowedCases(t *testing.T, tests []isAllowedCase) { t.Helper() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() w := allowlist.New(tt.patterns) var u *url.URL if tt.testURL != "" { parsed, err := url.Parse(tt.testURL) if err != nil { t.Fatalf("failed to parse test URL: %v", err) } u = parsed } got := w.IsAllowed(u) if got != tt.want { t.Errorf("IsAllowed() = %v, want %v", got, tt.want) } }) } } func TestHostAllowList_IsAllowed_ExactMatch(t *testing.T) { t.Parallel() runIsAllowedCases(t, []isAllowedCase{ { name: "exact match", patterns: []string{testExactHost}, testURL: testImageURL, want: true, }, { name: "exact match case insensitive", patterns: []string{"CDN.Example.COM"}, testURL: testImageURL, want: true, }, { name: "exact match not found", patterns: []string{testExactHost}, testURL: "https://other.example.com/image.jpg", want: false, }, { name: "multiple patterns", patterns: []string{testExactHost, ".images.org", "static.test.net"}, testURL: "https://photos.images.org/image.jpg", want: true, }, { name: "empty allow list", patterns: []string{}, testURL: testImageURL, want: false, }, { name: "nil url", patterns: []string{testExactHost}, testURL: "", want: false, }, { name: "url with port", patterns: []string{testExactHost}, testURL: "https://cdn.example.com:443/image.jpg", want: true, }, { name: "whitespace in patterns", patterns: []string{" cdn.example.com ", " .other.com "}, testURL: testImageURL, want: true, }, }) } func TestHostAllowList_IsAllowed_SuffixMatch(t *testing.T) { t.Parallel() runIsAllowedCases(t, []isAllowedCase{ { name: "suffix match", patterns: []string{testSuffix}, testURL: testImageURL, want: true, }, { name: "suffix match deep subdomain", patterns: []string{testSuffix}, testURL: "https://cdn.images.example.com/image.jpg", want: true, }, { name: "suffix match apex domain", patterns: []string{testSuffix}, testURL: "https://example.com/image.jpg", want: true, }, { name: "suffix match not found", patterns: []string{testSuffix}, testURL: "https://notexample.com/image.jpg", want: false, }, { name: "suffix match partial not allowed", patterns: []string{testSuffix}, testURL: "https://fakeexample.com/image.jpg", want: false, }, }) } func TestHostAllowList_IsEmpty(t *testing.T) { t.Parallel() tests := []struct { name string patterns []string want bool }{ { name: "empty", patterns: []string{}, want: true, }, { name: "nil", patterns: nil, want: true, }, { name: "whitespace only", patterns: []string{" ", ""}, want: true, }, { name: "has entries", patterns: []string{"example.com"}, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() w := allowlist.New(tt.patterns) if got := w.IsEmpty(); got != tt.want { t.Errorf("IsEmpty() = %v, want %v", got, tt.want) } }) } } func TestHostAllowList_Count(t *testing.T) { t.Parallel() tests := []struct { name string patterns []string want int }{ { name: "empty", patterns: []string{}, want: 0, }, { name: "exact hosts only", patterns: []string{"a.com", "b.com", "c.com"}, want: 3, }, { name: "suffix hosts only", patterns: []string{".a.com", ".b.com"}, want: 2, }, { name: "mixed", patterns: []string{"exact.com", ".suffix.com"}, want: 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() w := allowlist.New(tt.patterns) if got := w.Count(); got != tt.want { t.Errorf("Count() = %v, want %v", got, tt.want) } }) } }