package oidc import ( "strings" "testing" ) func TestNewVerifierRequiresIssuer(t *testing.T) { _, err := NewVerifier(Config{}) if err == nil { t.Fatal("expected error for empty issuer") } } func TestNewVerifierRequiresHTTPSIssuer(t *testing.T) { tests := []struct { name string issuer string wantErr string }{ {"http rejected", "http://accounts.example.com", "https://"}, {"file rejected", "file:///etc/keys", "https://"}, {"empty scheme", "accounts.example.com", "https://"}, {"https accepted", "https://accounts.example.com", ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := NewVerifier(Config{Issuer: tt.issuer, Audience: "spire"}) if tt.wantErr == "" { // Should pass issuer validation (may still fail with "not yet implemented") if err != nil && strings.Contains(err.Error(), "https://") { t.Fatalf("issuer %q should be accepted, got: %v", tt.issuer, err) } } else { if err == nil || !strings.Contains(err.Error(), tt.wantErr) { t.Fatalf("issuer %q: expected error containing %q, got: %v", tt.issuer, tt.wantErr, err) } } }) } } func TestNewVerifierRejectsNonURLIssuer(t *testing.T) { // A string with no scheme gets scheme "" which isn't https _, err := NewVerifier(Config{Issuer: "not-a-url", Audience: "spire"}) if err == nil { t.Fatal("expected error for non-URL issuer") } if !strings.Contains(err.Error(), "https://") { t.Errorf("expected https scheme error, got: %v", err) } } func TestNewVerifierRequiresAudience(t *testing.T) { _, err := NewVerifier(Config{Issuer: "https://accounts.example.com"}) if err == nil { t.Fatal("expected error for empty audience") } if !strings.Contains(err.Error(), "audience is required") { t.Errorf("expected audience error, got: %v", err) } } func TestNewVerifierValidatesJWKSURL(t *testing.T) { _, err := NewVerifier(Config{ Issuer: "https://accounts.example.com", Audience: "spire", JWKSURL: "http://insecure.example.com/keys", }) if err == nil { t.Fatal("expected error for http JWKS URL") } if !strings.Contains(err.Error(), "https://") { t.Errorf("expected https scheme error, got: %v", err) } } func TestNewVerifierAcceptsHTTPSJWKSURL(t *testing.T) { _, err := NewVerifier(Config{ Issuer: "https://accounts.example.com", Audience: "spire", JWKSURL: "https://keys.example.com/.well-known/jwks.json", }) // Should pass URL validation (may fail with "not yet implemented") if err != nil && strings.Contains(err.Error(), "https://") { t.Fatalf("valid JWKS URL should be accepted, got: %v", err) } } func TestNewVerifierNotYetImplemented(t *testing.T) { _, err := NewVerifier(Config{ Issuer: "https://accounts.example.com", Audience: "spire", }) if err == nil { t.Fatal("expected not-yet-implemented error") } if !strings.Contains(err.Error(), "not yet implemented") { t.Errorf("expected not-yet-implemented, got: %v", err) } } func TestValidateAudienceMatch(t *testing.T) { err := ValidateAudience([]string{"api", "spire", "web"}, "spire") if err != nil { t.Fatalf("expected audience match, got: %v", err) } } func TestValidateAudienceMismatch(t *testing.T) { err := ValidateAudience([]string{"api", "web"}, "spire") if err == nil { t.Fatal("expected error for audience mismatch") } if !strings.Contains(err.Error(), "does not contain") { t.Errorf("expected audience mismatch error, got: %v", err) } } func TestValidateAudienceEmptyExpected(t *testing.T) { err := ValidateAudience([]string{"spire"}, "") if err == nil { t.Fatal("expected error for empty expected audience") } if !strings.Contains(err.Error(), "must not be empty") { t.Errorf("expected empty audience error, got: %v", err) } } func TestValidateAudienceEmptyList(t *testing.T) { err := ValidateAudience(nil, "spire") if err == nil { t.Fatal("expected error for empty audience list") } } func TestRequireHTTPSNoHost(t *testing.T) { err := requireHTTPS("https://", "test") if err == nil { t.Fatal("expected error for URL with no host") } if !strings.Contains(err.Error(), "no host") { t.Errorf("expected no-host error, got: %v", err) } }