From dfaa8f3b82c33e2bce0821fc968f95251be47fca Mon Sep 17 00:00:00 2001 From: Radoslav Dimitrov Date: Mon, 4 May 2026 06:52:12 -0400 Subject: [PATCH] auth/oidc: test slice-typed extra claims comparison (#1245) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Follow-up to #1238 (closes #988). Adds direct unit-test coverage for the `claimMatches` helper, which was the actual change in #1238. `claimMatches` and `toAnySlice` are unexported, so the tests live in `oidc_internal_test.go` (`package auth`) — same pattern as `http_internal_test.go`. Cases covered: - scalar/scalar match and mismatch - scalar actual against array expected (both directions of overlap) - array actual against scalar expected - **array/array** — pre-#1238 this combination panicked with `comparing uncomparable type []interface {}` - `[]string` typed slice (exercises the typed-slice branch of `toAnySlice`, which `[]any` skips) - non-string scalars (int, bool) - empty actual array Test values use single letters (`x`, `y`, `p`, `q`) rather than role labels — only the relationships matter, and short tokens keep `goconst` quiet. ## Test plan - [x] `go test ./internal/api/handlers/v0/auth/...` — all 13 subtests pass - [x] `golangci-lint run ./internal/api/handlers/v0/auth/...` — no new issues vs main --- .../handlers/v0/auth/oidc_internal_test.go | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 internal/api/handlers/v0/auth/oidc_internal_test.go diff --git a/internal/api/handlers/v0/auth/oidc_internal_test.go b/internal/api/handlers/v0/auth/oidc_internal_test.go new file mode 100644 index 0000000..cc3e472 --- /dev/null +++ b/internal/api/handlers/v0/auth/oidc_internal_test.go @@ -0,0 +1,41 @@ +package auth + +import "testing" + +func TestClaimMatches(t *testing.T) { + tests := []struct { + name string + actual any + expected any + want bool + }{ + {"scalar/scalar match", "x", "x", true}, + {"scalar/scalar mismatch", "x", "y", false}, + + {"scalar actual in array expected", "x", []any{"x", "y"}, true}, + {"scalar actual not in array expected", "z", []any{"x", "y"}, false}, + + {"array actual contains scalar expected", []any{"x", "y"}, "x", true}, + {"array actual missing scalar expected", []any{"y", "z"}, "x", false}, + + // Pre-#1238 array/array comparison panicked with "comparing uncomparable type []interface {}". + {"array/array overlap", []any{"a", "b"}, []any{"b", "c"}, true}, + {"array/array disjoint", []any{"a", "b"}, []any{"c", "d"}, false}, + + // Exercises the []string branch of toAnySlice (concrete typed slice, not []any). + {"[]string actual matches scalar", []string{"p", "q"}, "p", true}, + {"scalar matches []string expected", "p", []string{"p", "q"}, true}, + + {"non-string scalar match", 42, 42, true}, + {"bool scalar match", true, true, true}, + {"empty actual array", []any{}, "x", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := claimMatches(tt.actual, tt.expected); got != tt.want { + t.Errorf("claimMatches(%v, %v) = %v, want %v", tt.actual, tt.expected, got, tt.want) + } + }) + } +}