// Copyright 2020-2026 Project Capsule Authors // SPDX-License-Identifier: Apache-2.0 package quota_test import ( "testing" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/projectcapsule/capsule/pkg/runtime/jsonpath" "github.com/projectcapsule/capsule/pkg/runtime/quota" ) func contains(s, substr string) bool { return len(substr) == 0 || (len(s) >= len(substr) && stringContains(s, substr)) } func stringContains(s, substr string) bool { for i := 0; i+len(substr) <= len(s); i++ { if s[i:i+len(substr)] == substr { return true } } return false } func TestParseQuantities(t *testing.T) { t.Parallel() tests := []struct { name string input string want resource.Quantity wantErr bool errContains string }{ { name: "empty string returns error", input: "", wantErr: true, errContains: "no quantity values found", }, { name: "whitespace only returns error", input: " \n\t ", wantErr: true, errContains: "no quantity values found", }, { name: "single quantity", input: "100m", want: resource.MustParse("100m"), wantErr: false, }, { name: "multiple cpu quantities", input: "100m 200m 300m", want: resource.MustParse("600m"), wantErr: false, }, { name: "multiple memory quantities", input: "128Mi 256Mi 1Gi", want: resource.MustParse("1408Mi"), wantErr: false, }, { name: "mixed whitespace separators", input: "100m\t200m\n300m", want: resource.MustParse("600m"), wantErr: false, }, { name: "equivalent units", input: "1Gi 512Mi 512Mi", want: resource.MustParse("2Gi"), wantErr: false, }, { name: "invalid quantity returns error", input: "100m invalid 200m", wantErr: true, errContains: `invalid quantity "invalid"`, }, { name: "completely invalid input returns error", input: "nope", wantErr: true, errContains: `invalid quantity "nope"`, }, { name: "decimal SI quantities", input: "1 2 3", want: resource.MustParse("6"), wantErr: false, }, { name: "binary SI quantities", input: "1Ki 2Ki 3Ki", want: resource.MustParse("6Ki"), wantErr: false, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() got, err := quota.ParseQuantities(tt.input) if tt.wantErr { if err == nil { t.Fatalf("expected error, got nil") } if tt.errContains != "" && !contains(err.Error(), tt.errContains) { t.Fatalf("expected error to contain %q, got %q", tt.errContains, err.Error()) } return } if err != nil { t.Fatalf("expected no error, got %v", err) } if got.Cmp(tt.want) != 0 { t.Fatalf("expected quantity %q, got %q", tt.want.String(), got.String()) } }) } } func TestParseQuantityFromUnstructured_Success(t *testing.T) { t.Parallel() u := unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ "resources": map[string]interface{}{ "requests": map[string]interface{}{ "cpu": "250m", }, }, }, }, } jp, err := jsonpath.CompileJSONPath(".spec.resources.requests.cpu") if err != nil { t.Fatalf("expected no error, got %v", err) } got, err := quota.ParseQuantityFromUnstructured(u, jp) if err != nil { t.Fatalf("expected no error, got %v", err) } want := resource.MustParse("250m") if got.Cmp(want) != 0 { t.Fatalf("expected quantity %q, got %q", want.String(), got.String()) } } func TestParseQuantityFromUnstructured_MissingPathReturnsError(t *testing.T) { t.Parallel() u := unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{}, }, } jp, err := jsonpath.CompileJSONPath(".spec.resources.requests.cpu") if err != nil { t.Fatalf("expected no error compiling jsonpath, got %v", err) } _, err = quota.ParseQuantityFromUnstructured(u, jp) if err == nil { t.Fatal("expected error, got nil") } if !contains(err.Error(), "did not resolve to any value") && !contains(err.Error(), "no quantity values found") { t.Fatalf("expected missing quantity error, got %q", err.Error()) } } func TestParseQuantityFromUnstructured_WhitespaceOnlyReturnsError(t *testing.T) { t.Parallel() u := unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ "value": " \n\t ", }, }, } jp, err := jsonpath.CompileJSONPath(".spec.value") if err != nil { t.Fatalf("expected no error compiling jsonpath, got %v", err) } _, err = quota.ParseQuantityFromUnstructured(u, jp) if err == nil { t.Fatal("expected error, got nil") } if !contains(err.Error(), "did not resolve to any value") && !contains(err.Error(), "no quantity values found") { t.Fatalf("expected empty quantity error, got %q", err.Error()) } } func TestParseUsageFromUnstructured_Success(t *testing.T) { u := unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ "resources": map[string]interface{}{ "requests": map[string]interface{}{ "cpu": "250m", "memory": "512Mi", }, }, }, }, } jp, err := jsonpath.CompileJSONPath(".spec.resources.requests.cpu") if err != nil { t.Fatal("expected no error, got error", err) } got, err := quota.ParseUsageFromUnstructured(u, jp) if err != nil { t.Fatalf("expected no error, got %v", err) } if got != "250m" { t.Fatalf("expected %q, got %q", "250m", got) } } func TestParseUsageFromUnstructured_TrimsWhitespace(t *testing.T) { u := unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ "value": " hello ", }, }, } jp, err := jsonpath.CompileJSONPath(" .spec.value") if err != nil { t.Fatal("expected no error, got error", err) } got, err := quota.ParseUsageFromUnstructured(u, jp) if err != nil { t.Fatalf("expected no error, got %v", err) } if got != "hello" { t.Fatalf("expected %q, got %q", "hello", got) } } func TestParseUsageFromUnstructured_MissingPath(t *testing.T) { t.Parallel() u := unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{}, }, } jp, err := jsonpath.CompileJSONPath(".spec.resources.requests.cpu") if err != nil { t.Fatalf("expected no error compiling jsonpath, got %v", err) } _, err = quota.ParseUsageFromUnstructured(u, jp) if err != nil { t.Fatal("expected no error, got err") } } func TestParseUsageFromUnstructured_InvalidJSONPath(t *testing.T) { t.Parallel() _, err := jsonpath.CompileJSONPath(".spec[") if err == nil { t.Fatal("expected compile error, got nil") } } func TestParseUsageFromUnstructured_EmptySourcePath(t *testing.T) { t.Parallel() _, err := jsonpath.CompileJSONPath("") if err == nil { t.Fatal("expected compile error, got nil") } } func TestParseUsageFromUnstructured_SourcePathMustStartWithDot(t *testing.T) { t.Parallel() _, err := jsonpath.CompileJSONPath("spec.resources.requests.cpu") if err == nil { t.Fatal("expected compile error, got nil") } } func TestParseUsageFromUnstructured_RejectsControlWhitespace(t *testing.T) { t.Parallel() _, err := jsonpath.CompileJSONPath(".spec.\nrequests.cpu") if err == nil { t.Fatal("expected compile error, got nil") } }