Files
capsule/pkg/api/runtime/expression_test.go
T
77d1810bb9 feat: add metadata enforcement (#1990)
* fix(controller): decode old object for delete requests

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix: preserve ca-bundles injected from external providers

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat: add metadata enforcement

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat: add metadata enforcement

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: add resourcepoolclaim validation

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-02 13:42:08 +02:00

918 lines
19 KiB
Go

// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package runtime
import (
"errors"
"fmt"
"regexp"
"testing"
)
type fakeExpressionRegexMatcher struct {
t *testing.T
calls int
err error
matches map[string]bool
seen []ExpressionRegex
}
func (m *fakeExpressionRegexMatcher) MatchRegex(expr ExpressionRegex, value string) (bool, error) {
m.t.Helper()
m.calls++
m.seen = append(m.seen, expr)
if m.err != nil {
return false, m.err
}
key := fmt.Sprintf("%s|%t|%s", expr.Expression, expr.Negate, value)
if matched, ok := m.matches[key]; ok {
return matched, nil
}
re, err := regexp.Compile(expr.Expression)
if err != nil {
return false, err
}
matched := re.MatchString(value)
if expr.Negate {
return !matched, nil
}
return matched, nil
}
func TestExpressionMatch_Matches(t *testing.T) {
t.Parallel()
tests := []struct {
name string
match ExpressionMatch
value string
wantMatch bool
wantErr bool
}{
{
name: "exact matches single value",
match: ExpressionMatch{
Exact: []string{"default-scheduler"},
},
value: "default-scheduler",
wantMatch: true,
},
{
name: "exact does not match different value",
match: ExpressionMatch{
Exact: []string{"default-scheduler"},
},
value: "custom-scheduler",
wantMatch: false,
},
{
name: "exact matches one of multiple values",
match: ExpressionMatch{
Exact: []string{"default-scheduler", "custom-scheduler", "team-scheduler"},
},
value: "custom-scheduler",
wantMatch: true,
},
{
name: "exact does not match any of multiple values",
match: ExpressionMatch{
Exact: []string{"default-scheduler", "custom-scheduler"},
},
value: "other-scheduler",
wantMatch: false,
},
{
name: "exact is case sensitive",
match: ExpressionMatch{
Exact: []string{"Default-Scheduler"},
},
value: "default-scheduler",
wantMatch: false,
},
{
name: "exact uses literal string not pattern",
match: ExpressionMatch{
Exact: []string{"team-.*"},
},
value: "team-a",
wantMatch: false,
},
{
name: "exact matches literal pattern string",
match: ExpressionMatch{
Exact: []string{"team-.*"},
},
value: "team-.*",
wantMatch: true,
},
{
name: "exact with empty value can match empty string when present",
match: ExpressionMatch{
Exact: []string{""},
},
value: "",
wantMatch: true,
},
{
name: "regex matches value",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^team-[a-z0-9-]+$",
},
},
value: "team-alpha-1",
wantMatch: true,
},
{
name: "regex does not match value",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^team-[a-z0-9-]+$",
},
},
value: "kube-scheduler",
wantMatch: false,
},
{
name: "regex is not implicitly anchored",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "team",
},
},
value: "my-team-scheduler",
wantMatch: true,
},
{
name: "invalid regex returns error",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "[",
},
},
value: "team-alpha",
wantErr: true,
},
{
name: "combined exact and regex matches by exact",
match: ExpressionMatch{
Exact: []string{"default-scheduler"},
ExpressionRegex: ExpressionRegex{
Expression: "^team-[a-z0-9-]+$",
},
},
value: "default-scheduler",
wantMatch: true,
},
{
name: "combined exact and regex matches by regex",
match: ExpressionMatch{
Exact: []string{"default-scheduler"},
ExpressionRegex: ExpressionRegex{
Expression: "^team-[a-z0-9-]+$",
},
},
value: "team-alpha",
wantMatch: true,
},
{
name: "combined exact and regex does not match either",
match: ExpressionMatch{
Exact: []string{"default-scheduler"},
ExpressionRegex: ExpressionRegex{
Expression: "^team-[a-z0-9-]+$",
},
},
value: "other-scheduler",
wantMatch: false,
},
{
name: "combined exact match skips invalid regex",
match: ExpressionMatch{
Exact: []string{"default-scheduler"},
ExpressionRegex: ExpressionRegex{
Expression: "[",
},
},
value: "default-scheduler",
wantMatch: true,
},
{
name: "combined exact miss evaluates invalid regex and returns error",
match: ExpressionMatch{
Exact: []string{"default-scheduler"},
ExpressionRegex: ExpressionRegex{
Expression: "[",
},
},
value: "team-alpha",
wantErr: true,
},
{
name: "negated exact matching value returns false",
match: ExpressionMatch{
Exact: []string{"default-scheduler"},
ExpressionRegex: ExpressionRegex{
Negate: true,
},
},
value: "default-scheduler",
wantMatch: false,
},
{
name: "negated exact non matching value returns true",
match: ExpressionMatch{
Exact: []string{"default-scheduler"},
ExpressionRegex: ExpressionRegex{
Negate: true,
},
},
value: "custom-scheduler",
wantMatch: true,
},
{
name: "negated exact with multiple values matching one returns false",
match: ExpressionMatch{
Exact: []string{"default-scheduler", "custom-scheduler"},
ExpressionRegex: ExpressionRegex{
Negate: true,
},
},
value: "custom-scheduler",
wantMatch: false,
},
{
name: "negated regex matching value returns false",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^trusted/.*",
Negate: true,
},
},
value: "trusted/platform/app:1",
wantMatch: false,
},
{
name: "negated regex non matching value returns true",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^trusted/.*",
Negate: true,
},
},
value: "docker.io/library/nginx:latest",
wantMatch: true,
},
{
name: "negated combined exact match returns false",
match: ExpressionMatch{
Exact: []string{"trusted/platform/app:1"},
ExpressionRegex: ExpressionRegex{
Expression: "^trusted/.+",
Negate: true,
},
},
value: "trusted/platform/app:1",
wantMatch: false,
},
{
name: "negated combined regex match returns false",
match: ExpressionMatch{
Exact: []string{"trusted/platform/app:1"},
ExpressionRegex: ExpressionRegex{
Expression: "^trusted/.+",
Negate: true,
},
},
value: "trusted/other/app:1",
wantMatch: false,
},
{
name: "negated combined no match returns true",
match: ExpressionMatch{
Exact: []string{"trusted/platform/app:1"},
ExpressionRegex: ExpressionRegex{
Expression: "^trusted/.+",
Negate: true,
},
},
value: "harbor/platform/app:1",
wantMatch: true,
},
{
name: "empty matcher returns error",
match: ExpressionMatch{},
value: "anything",
wantErr: true,
wantMatch: false,
},
{
name: "empty exact slice with empty regex returns error",
match: ExpressionMatch{
Exact: []string{},
},
value: "anything",
wantErr: true,
},
{
name: "nil exact with whitespace regex is treated as regex and does not match",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: " ",
},
},
value: "anything",
wantMatch: false,
},
{
name: "duplicate exact values still match",
match: ExpressionMatch{
Exact: []string{"a", "a", "b"},
},
value: "a",
wantMatch: true,
},
{
name: "exact values are not trimmed",
match: ExpressionMatch{
Exact: []string{" value "},
},
value: "value",
wantMatch: false,
},
{
name: "exact values match with spaces when value has spaces",
match: ExpressionMatch{
Exact: []string{" value "},
},
value: " value ",
wantMatch: true,
},
{
name: "regex can match empty value",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^$",
},
},
value: "",
wantMatch: true,
},
{
name: "negated regex can reject empty value",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^$",
Negate: true,
},
},
value: "",
wantMatch: false,
},
{
name: "negated regex can match non empty value against empty regex",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^$",
Negate: true,
},
},
value: "non-empty",
wantMatch: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := tt.match.Matches(tt.value)
if tt.wantErr {
if err == nil {
t.Fatalf("Matches() expected error, got nil")
}
return
}
if err != nil {
t.Fatalf("Matches() unexpected error: %v", err)
}
if got != tt.wantMatch {
t.Fatalf("Matches() = %t, want %t", got, tt.wantMatch)
}
})
}
}
func TestExpressionMatch_MatchesWithExpressionMatcher_NilMatcherFallback(t *testing.T) {
t.Parallel()
tests := []struct {
name string
match ExpressionMatch
value string
wantMatch bool
wantErr bool
}{
{
name: "nil matcher exact match",
match: ExpressionMatch{
Exact: []string{"default-scheduler"},
},
value: "default-scheduler",
wantMatch: true,
},
{
name: "nil matcher regex match",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^team-.*",
},
},
value: "team-a",
wantMatch: true,
},
{
name: "nil matcher negated regex non match returns true",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^trusted/.*",
Negate: true,
},
},
value: "docker.io/library/nginx:latest",
wantMatch: true,
},
{
name: "nil matcher invalid regex returns error",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "[",
},
},
value: "team-a",
wantErr: true,
},
{
name: "nil matcher empty expression match returns error",
match: ExpressionMatch{},
value: "team-a",
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := tt.match.MatchesWithExpressionMatcher(nil, tt.value)
if tt.wantErr {
if err == nil {
t.Fatalf("MatchesWithExpressionMatcher(nil) expected error, got nil")
}
return
}
if err != nil {
t.Fatalf("MatchesWithExpressionMatcher(nil) unexpected error: %v", err)
}
if got != tt.wantMatch {
t.Fatalf("MatchesWithExpressionMatcher(nil) = %t, want %t", got, tt.wantMatch)
}
})
}
}
func TestExpressionMatch_MatchesWithExpressionMatcher_UsesMatcherForRegex(t *testing.T) {
t.Parallel()
matcher := &fakeExpressionRegexMatcher{
t: t,
matches: map[string]bool{
"^team-.*|false|team-a": true,
},
}
match := ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^team-.*",
},
}
got, err := match.MatchesWithExpressionMatcher(matcher, "team-a")
if err != nil {
t.Fatalf("MatchesWithExpressionMatcher() unexpected error: %v", err)
}
if !got {
t.Fatalf("MatchesWithExpressionMatcher() = false, want true")
}
if matcher.calls != 1 {
t.Fatalf("MatchRegex() calls = %d, want 1", matcher.calls)
}
if len(matcher.seen) != 1 {
t.Fatalf("seen expressions = %d, want 1", len(matcher.seen))
}
if matcher.seen[0].Expression != "^team-.*" {
t.Fatalf("seen expression = %q, want %q", matcher.seen[0].Expression, "^team-.*")
}
if matcher.seen[0].Negate {
t.Fatalf("seen negate = true, want false")
}
}
func TestExpressionMatch_MatchesWithExpressionMatcher_PassesNegateToMatcher(t *testing.T) {
t.Parallel()
matcher := &fakeExpressionRegexMatcher{
t: t,
matches: map[string]bool{
"^trusted/.*|true|docker.io/library/nginx:latest": true,
},
}
match := ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^trusted/.*",
Negate: true,
},
}
got, err := match.MatchesWithExpressionMatcher(matcher, "docker.io/library/nginx:latest")
if err != nil {
t.Fatalf("MatchesWithExpressionMatcher() unexpected error: %v", err)
}
if !got {
t.Fatalf("MatchesWithExpressionMatcher() = false, want true")
}
if matcher.calls != 1 {
t.Fatalf("MatchRegex() calls = %d, want 1", matcher.calls)
}
if len(matcher.seen) != 1 {
t.Fatalf("seen expressions = %d, want 1", len(matcher.seen))
}
if !matcher.seen[0].Negate {
t.Fatalf("seen negate = false, want true")
}
}
func TestExpressionMatch_MatchesWithExpressionMatcher_DoesNotUseMatcherWhenExactMatches(t *testing.T) {
t.Parallel()
matcher := &fakeExpressionRegexMatcher{
t: t,
err: errors.New("matcher should not be called"),
}
match := ExpressionMatch{
Exact: []string{"default-scheduler"},
ExpressionRegex: ExpressionRegex{
Expression: "[",
},
}
got, err := match.MatchesWithExpressionMatcher(matcher, "default-scheduler")
if err != nil {
t.Fatalf("MatchesWithExpressionMatcher() unexpected error: %v", err)
}
if !got {
t.Fatalf("MatchesWithExpressionMatcher() = false, want true")
}
if matcher.calls != 0 {
t.Fatalf("MatchRegex() calls = %d, want 0", matcher.calls)
}
}
func TestExpressionMatch_MatchesWithExpressionMatcher_UsesMatcherWhenExactDoesNotMatch(t *testing.T) {
t.Parallel()
matcher := &fakeExpressionRegexMatcher{
t: t,
matches: map[string]bool{
"^team-.*|false|team-a": true,
},
}
match := ExpressionMatch{
Exact: []string{"default-scheduler"},
ExpressionRegex: ExpressionRegex{
Expression: "^team-.*",
},
}
got, err := match.MatchesWithExpressionMatcher(matcher, "team-a")
if err != nil {
t.Fatalf("MatchesWithExpressionMatcher() unexpected error: %v", err)
}
if !got {
t.Fatalf("MatchesWithExpressionMatcher() = false, want true")
}
if matcher.calls != 1 {
t.Fatalf("MatchRegex() calls = %d, want 1", matcher.calls)
}
}
func TestExpressionMatch_MatchesWithExpressionMatcher_ReturnsMatcherError(t *testing.T) {
t.Parallel()
wantErr := errors.New("compile failed")
matcher := &fakeExpressionRegexMatcher{
t: t,
err: wantErr,
}
match := ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^team-.*",
},
}
got, err := match.MatchesWithExpressionMatcher(matcher, "team-a")
if err == nil {
t.Fatalf("MatchesWithExpressionMatcher() expected error, got nil")
}
if !errors.Is(err, wantErr) {
t.Fatalf("MatchesWithExpressionMatcher() error = %v, want %v", err, wantErr)
}
if got {
t.Fatalf("MatchesWithExpressionMatcher() = true, want false on error")
}
if matcher.calls != 1 {
t.Fatalf("MatchRegex() calls = %d, want 1", matcher.calls)
}
}
func TestExpressionMatch_MatchesAndMatchesWithExpressionMatcher_AgreeForNilMatcher(t *testing.T) {
t.Parallel()
tests := []struct {
name string
match ExpressionMatch
value string
}{
{
name: "exact only",
match: ExpressionMatch{
Exact: []string{"a", "b"},
},
value: "a",
},
{
name: "regex only",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^a+$",
},
},
value: "aaa",
},
{
name: "combined exact and regex exact wins",
match: ExpressionMatch{
Exact: []string{"a"},
ExpressionRegex: ExpressionRegex{
Expression: "^b+$",
},
},
value: "a",
},
{
name: "combined exact and regex regex wins",
match: ExpressionMatch{
Exact: []string{"a"},
ExpressionRegex: ExpressionRegex{
Expression: "^b+$",
},
},
value: "bbb",
},
{
name: "negated exact",
match: ExpressionMatch{
Exact: []string{"a"},
ExpressionRegex: ExpressionRegex{
Negate: true,
},
},
value: "b",
},
{
name: "negated regex",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "^a+$",
Negate: true,
},
},
value: "bbb",
},
{
name: "invalid regex",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Expression: "[",
},
},
value: "a",
},
{
name: "empty matcher",
match: ExpressionMatch{},
value: "a",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
gotMatches, errMatches := tt.match.Matches(tt.value)
gotWithMatcher, errWithMatcher := tt.match.MatchesWithExpressionMatcher(nil, tt.value)
if (errMatches != nil) != (errWithMatcher != nil) {
t.Fatalf(
"error mismatch: Matches() err=%v, MatchesWithExpressionMatcher(nil) err=%v",
errMatches,
errWithMatcher,
)
}
if gotMatches != gotWithMatcher {
t.Fatalf(
"result mismatch: Matches()=%t, MatchesWithExpressionMatcher(nil)=%t",
gotMatches,
gotWithMatcher,
)
}
})
}
}
func TestContainsExact(t *testing.T) {
t.Parallel()
tests := []struct {
name string
values []string
value string
want bool
}{
{
name: "nil values",
values: nil,
value: "a",
want: false,
},
{
name: "empty values",
values: []string{},
value: "a",
want: false,
},
{
name: "contains value",
values: []string{"a", "b", "c"},
value: "b",
want: true,
},
{
name: "does not contain value",
values: []string{"a", "b", "c"},
value: "d",
want: false,
},
{
name: "case sensitive",
values: []string{"A"},
value: "a",
want: false,
},
{
name: "empty string",
values: []string{""},
value: "",
want: true,
},
{
name: "whitespace is significant",
values: []string{" a "},
value: "a",
want: false,
},
{
name: "whitespace matches exactly",
values: []string{" a "},
value: " a ",
want: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := containsExact(tt.values, tt.value)
if got != tt.want {
t.Fatalf("containsExact(%v, %q) = %t, want %t", tt.values, tt.value, got, tt.want)
}
})
}
}
func TestExpressionMatch_applyNegate(t *testing.T) {
t.Parallel()
tests := []struct {
name string
match ExpressionMatch
matched bool
want bool
}{
{
name: "non negated true",
match: ExpressionMatch{},
matched: true,
want: true,
},
{
name: "non negated false",
match: ExpressionMatch{},
matched: false,
want: false,
},
{
name: "negated true",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Negate: true,
},
},
matched: true,
want: false,
},
{
name: "negated false",
match: ExpressionMatch{
ExpressionRegex: ExpressionRegex{
Negate: true,
},
},
matched: false,
want: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := tt.match.applyNegate(tt.matched)
if got != tt.want {
t.Fatalf("applyNegate(%t) = %t, want %t", tt.matched, got, tt.want)
}
})
}
}