// Copyright 2020-2026 Project Capsule Authors // SPDX-License-Identifier: Apache-2.0 package cel import ( "context" "strings" "testing" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apiserver/pkg/cel/environment" ) func TestCompiledExpressionEvaluateBoolean(t *testing.T) { t.Parallel() compiler, err := NewCompiler() if err != nil { t.Fatalf("NewCompiler() error = %v", err) } compiled, err := compiler.CompileBoolean( `object.spec.containers.exists(c, c.image == "nginx:1.27.0")`, environment.StoredExpressions, ) if err != nil { t.Fatalf("CompileBoolean() error = %v", err) } object := unstructured.Unstructured{Object: map[string]any{ "spec": map[string]any{ "containers": []any{ map[string]any{"name": "main", "image": "nginx:1.27.0"}, }, }, }} matched, err := compiled.EvaluateBoolean(context.Background(), object) if err != nil { t.Fatalf("EvaluateBoolean() error = %v", err) } if !matched { t.Fatal("EvaluateBoolean() = false, want true") } } func TestCompileBooleanRejectsNonBooleanResult(t *testing.T) { t.Parallel() compiler, err := NewCompiler() if err != nil { t.Fatalf("NewCompiler() error = %v", err) } _, err = compiler.CompileBoolean(`object.metadata.name`, environment.StoredExpressions) if err == nil || !strings.Contains(err.Error(), "must evaluate to bool") { t.Fatalf("CompileBoolean() error = %v, want boolean result type error", err) } } func TestCompiledExpressionEvaluateSingleQuantity(t *testing.T) { t.Parallel() compiler, err := NewCompiler() if err != nil { t.Fatalf("NewCompiler() error = %v", err) } compiled, err := compiler.CompileQuantity( `quantity(object.spec.resources.requests["storage"])`, environment.StoredExpressions, ) if err != nil { t.Fatalf("CompileQuantity() error = %v", err) } object := unstructured.Unstructured{Object: map[string]any{ "spec": map[string]any{ "resources": map[string]any{ "requests": map[string]any{"storage": "2Gi"}, }, }, }} got, err := compiled.EvaluateQuantity(context.Background(), object) if err != nil { t.Fatalf("EvaluateQuantity() error = %v", err) } if got.Cmp(resource.MustParse("2Gi")) != 0 { t.Fatalf("EvaluateQuantity() = %s, want 2Gi", got.String()) } } func TestCompiledExpressionSumsQuantityList(t *testing.T) { t.Parallel() compiler, err := NewCompiler() if err != nil { t.Fatalf("NewCompiler() error = %v", err) } compiled, err := compiler.CompileQuantity( `object.spec.containers`+ `.filter(c, has(c.resources) && has(c.resources.requests) && "cpu" in c.resources.requests)`+ `.map(c, quantity(c.resources.requests["cpu"]))`, environment.StoredExpressions, ) if err != nil { t.Fatalf("CompileQuantity() error = %v", err) } object := unstructured.Unstructured{Object: map[string]any{ "spec": map[string]any{ "containers": []any{ map[string]any{ "resources": map[string]any{ "requests": map[string]any{"cpu": "250m"}, }, }, map[string]any{ "resources": map[string]any{ "requests": map[string]any{"cpu": "500m"}, }, }, map[string]any{"name": "no-request"}, }, }, }} got, err := compiled.EvaluateQuantity(context.Background(), object) if err != nil { t.Fatalf("EvaluateQuantity() error = %v", err) } if got.Cmp(resource.MustParse("750m")) != 0 { t.Fatalf("EvaluateQuantity() = %s, want 750m", got.String()) } } func TestCompiledExpressionRejectsEmptyQuantityList(t *testing.T) { t.Parallel() compiler, err := NewCompiler() if err != nil { t.Fatalf("NewCompiler() error = %v", err) } compiled, err := compiler.CompileQuantity( `object.spec.values.map(v, quantity(v))`, environment.StoredExpressions, ) if err != nil { t.Fatalf("CompileQuantity() error = %v", err) } object := unstructured.Unstructured{Object: map[string]any{ "spec": map[string]any{"values": []any{}}, }} _, err = compiled.EvaluateQuantity(context.Background(), object) if err == nil || !strings.Contains(err.Error(), "empty quantity list") { t.Fatalf("EvaluateQuantity() error = %v, want empty list error", err) } } func TestCompileQuantityRejectsStringResult(t *testing.T) { t.Parallel() compiler, err := NewCompiler() if err != nil { t.Fatalf("NewCompiler() error = %v", err) } _, err = compiler.CompileQuantity( `string(object.spec.value)`, environment.StoredExpressions, ) if err == nil || !strings.Contains(err.Error(), "kubernetes.Quantity") { t.Fatalf("CompileQuantity() error = %v, want quantity result type error", err) } }