/* Copyright 2025 The KubeVela Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package ast import ( "fmt" "regexp" "strings" "testing" "cuelang.org/go/cue/ast" "cuelang.org/go/cue/parser" "cuelang.org/go/cue/token" "github.com/stretchr/testify/require" ) func TestGetFieldByPath(t *testing.T) { tests := []struct { name string path string found bool label string }{ { name: "Field does exist", path: "component.attributes.status.health", found: true, label: "health", }, { name: "Field does not exist", path: "component.attributes.status.missingField", found: false, label: "", }, { name: "Top level field can be found", path: "component", found: true, label: "component", }, { name: "Empty path returns false", path: "", found: false, }, } src := ` component: { attributes: { status: { health: "ok" } } }` file, err := parser.ParseFile("-", src) require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { field, ok := GetFieldByPath(file, tt.path) require.Equal(t, tt.found, ok) if ok { require.Equal(t, tt.label, GetFieldLabel(field.Label)) } }) } } func TestGetNodeByPath(t *testing.T) { tests := []struct { name string path string found bool fieldLabel string nodeType string }{ { name: "Node and field exist", path: "component.attributes.status.health", found: true, fieldLabel: "health", nodeType: "*ast.BasicLit", }, { name: "Field does not exist", path: "component.attributes.status.missingField", found: false, }, { name: "Top level field can be found", path: "component", found: true, fieldLabel: "component", nodeType: "*ast.StructLit", }, { name: "Empty path", path: "", found: false, }, { name: "Valid path that ends on struct", path: "component.attributes.status", found: true, fieldLabel: "status", nodeType: "*ast.StructLit", }, } src := ` component: { attributes: { status: { health: "ok" } } }` file, err := parser.ParseFile("-", src) require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { node, field, ok := GetNodeByPath(file, tt.path) require.Equal(t, tt.found, ok) if ok { require.NotNil(t, field) require.Equal(t, tt.fieldLabel, GetFieldLabel(field.Label)) if tt.nodeType != "" { require.Equal(t, tt.nodeType, fmt.Sprintf("%T", node)) } } }) } } func TestUpdateNodeByPath(t *testing.T) { tests := []struct { name string path string newValue string shouldSet bool expected string }{ { name: "Update nested field", path: "component.attributes.status.health", newValue: `"updated"`, shouldSet: true, expected: `"updated"`, }, { name: "Update of missing field", path: "component.attributes.status.missingField", newValue: `"missing!"`, shouldSet: false, }, { name: "Update top level field", path: "component", newValue: `"top"`, shouldSet: true, expected: `"top"`, }, { name: "Empty path", path: "", newValue: `"shouldFail"`, shouldSet: false, }, } src := ` component: { attributes: { status: { health: "ok" } } }` for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { file, err := parser.ParseFile("-", src) require.NoError(t, err) newExpr := &ast.BasicLit{Kind: token.STRING, Value: tt.newValue} ok := UpdateNodeByPath(file, tt.path, newExpr) require.Equal(t, tt.shouldSet, ok) if ok { field, found := GetFieldByPath(file, tt.path) require.True(t, found) basicLit, isLit := field.Value.(*ast.BasicLit) require.True(t, isLit) require.Equal(t, tt.expected, basicLit.Value) } }) } } func TestStringifyStructLitAsCueString(t *testing.T) { tests := []struct { name string input string contains []string shouldFail bool }{ { name: "Simple string and number fields", input: ` { name: "example" count: 42 }`, contains: []string{ `name: "example"`, `count: 42`, }, }, { name: "Boolean and nested struct", input: ` { enabled: true metadata: { version: "v1" } }`, contains: []string{ `enabled: true`, `metadata: {`, `version: "v1"`, }, }, { name: "Empty struct", input: ` { }`, contains: []string{ `{}`, }, }, { name: "Struct with list", input: ` { items: [1, 2, 3] }`, contains: []string{ `items: [1, 2, 3]`, }, }, { name: "Invalid CUE input", input: ` { name: "missing comma" 123bad }`, shouldFail: true, }, { name: "Conditional fields with if statements", input: ` { if context.status.healthy { message: "Healthy! (\(context.status.details.replicaReadyRatio * 100)% pods running)" } if !context.status.healthy { message: "Unhealthy! (\(context.status.details.replicaReadyRatio * 100)% pods running)" } }`, contains: []string{ `if context.status.healthy {`, `message: "Healthy! (\(context.status.details.replicaReadyRatio*100)% pods running)"`, `if !context.status.healthy {`, `message: "Unhealthy! (\(context.status.details.replicaReadyRatio*100)% pods running)"`, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { expr, err := parser.ParseExpr("-", tt.input) if tt.shouldFail { require.Error(t, err) return } require.NoError(t, err) structLit, ok := expr.(*ast.StructLit) require.True(t, ok, "expected struct literal") strLit, err := StringifyStructLitAsCueString(structLit) require.NoError(t, err) for _, substr := range tt.contains { require.Contains(t, normalizeString(strLit.Value), normalizeString(substr)) } }) } } func TestGetFieldLabel(t *testing.T) { tests := []struct { name string label ast.Label expected string }{ { name: "Ident label", label: &ast.Ident{Name: "foo"}, expected: "foo", }, { name: "BasicLit string label", label: &ast.BasicLit{Value: `"bar"`}, expected: "bar", }, { name: "BasicLit empty string", label: &ast.BasicLit{Value: `""`}, expected: "", }, { name: "BasicLit with quotes and spaces", label: &ast.BasicLit{Value: `" spaced "`}, expected: " spaced ", }, { name: "Nil label", label: nil, expected: "", }, { name: "Unsupported label type", label: &ast.ListLit{}, // not a valid label expected: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := GetFieldLabel(tt.label) require.Equal(t, tt.expected, result) }) } } func TestTrimCueRawString(t *testing.T) { tests := []struct { name string input string expected string }{ { name: "Triple quoted raw string with hash", input: `#"""foo bar"""#`, expected: `foo bar`, }, { name: "Triple quoted raw string without hash", input: `"""foo bar"""`, expected: `foo bar`, }, { name: "Double quoted standard string", input: `"foo bar"`, expected: `foo bar`, }, { name: "Unquoted fallback string", input: `foo bar`, expected: `foo bar`, }, { name: "Triple quoted with leading/trailing spaces", input: ` #"""foo bar"""# `, expected: `foo bar`, }, { name: "Escaped quoted string", input: `"line 1\nline 2"`, expected: "line 1\nline 2", }, { name: "Invalid quoted string (fallback)", input: `"unterminated`, expected: `"unterminated`, }, { name: "raw string with tab character", input: `#"""hello\tworld"""#`, expected: `hello world`, }, { name: "quoted string with escaped tab", input: `"hello\tworld"`, expected: `hello world`, }, { name: "raw string with multiple tabs", input: `#"""line1\t\tvalue line2\t\tvalue"""#`, expected: `line1 value line2 value`, }, { name: "triple quoted string with tabs", input: `"""hello\tworld"""`, expected: `hello world`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { out := TrimCueRawString(tt.input) require.Equal(t, tt.expected, out) }) } } func TestValidateCueStringLiteral(t *testing.T) { tests := []struct { name string lit *ast.BasicLit validator func(*ast.StructLit) error expectErr bool errContain string }{ { name: "valid struct literal", lit: &ast.BasicLit{ Kind: token.STRING, Value: `#""" name: "test" age: 30 """#`, }, validator: validStructValidator, expectErr: false, }, { name: "empty struct literal fails validator", lit: &ast.BasicLit{ Kind: token.STRING, Value: `#""" """#`, }, validator: validStructValidator, expectErr: true, errContain: "struct is empty", }, { name: "empty string passes", lit: &ast.BasicLit{ Kind: token.STRING, Value: `""`, }, validator: validStructValidator, expectErr: false, }, { name: "invalid cue content fails parse", lit: &ast.BasicLit{ Kind: token.STRING, Value: `#""" invalid: @@@ """#`, }, validator: validStructValidator, expectErr: true, errContain: "invalid cue content in string literal", }, { name: "non-string literal fails", lit: &ast.BasicLit{ Kind: token.INT, Value: `123`, }, validator: validStructValidator, expectErr: true, errContain: "not a string literal", }, { name: "parsed type mismatch returns error", lit: &ast.BasicLit{ Kind: token.STRING, Value: `#""" """#`, // empty struct literal }, validator: func(s *ast.StructLit) error { return fmt.Errorf("forced error") }, expectErr: true, errContain: "forced error", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateCueStringLiteral[*ast.StructLit](tt.lit, tt.validator) if tt.expectErr { require.Error(t, err) if tt.errContain != "" { require.Contains(t, err.Error(), tt.errContain) } } else { require.NoError(t, err) } }) } } func normalizeString(s string) string { s = strings.ReplaceAll(s, "\t", " ") re := regexp.MustCompile(`\s+`) return re.ReplaceAllString(s, " ") } func validStructValidator(slit *ast.StructLit) error { if len(slit.Elts) == 0 { return fmt.Errorf("struct is empty") } return nil } func TestWrapCueStruct(t *testing.T) { tests := []struct { name string input string expected string }{ { name: "simple multiline string", input: "field1: \"value1\"\nfield2: 42", expected: "{\nfield1: \"value1\"\nfield2: 42\n}", }, { name: "empty string", input: "", expected: "{\n\n}", }, { name: "single line", input: "foo: bar", expected: "{\nfoo: bar\n}", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := WrapCueStruct(tt.input) require.Equal(t, tt.expected, got) }) } } func TestFindAndValidateField(t *testing.T) { tests := []struct { name string input string fieldName string expectedFound bool expectedErrMessage string validator fieldValidator }{ { name: "find field at top level without validator", input: `{ field1: "value1" field2: "value2" }`, fieldName: "field1", expectedFound: true, }, { name: "field not found at any level", input: `{ field1: "value1" field2: "value2" }`, fieldName: "field3", expectedFound: false, }, { name: "find field at top level with validator that passes", input: `{ message: "hello world" other: "value" }`, fieldName: "message", expectedFound: true, validator: func(expr ast.Expr) error { if basicLit, ok := expr.(*ast.BasicLit); ok { if basicLit.Value != `"hello world"` { return fmt.Errorf("expected hello world, got %s", basicLit.Value) } } return nil }, }, { name: "find field at top level with validator that fails", input: `{ message: "hello world" other: "value" }`, fieldName: "message", expectedFound: true, expectedErrMessage: "expected goodbye, got", validator: func(expr ast.Expr) error { if basicLit, ok := expr.(*ast.BasicLit); ok { if basicLit.Value != `"goodbye"` { return fmt.Errorf("expected goodbye, got %s", basicLit.Value) } } return nil }, }, { name: "find field in simple if statement", input: `{ otherField: "value" if condition { message: "found in if" } }`, fieldName: "message", expectedFound: true, }, { name: "find field in nested if statements", input: `{ otherField: "value" if condition1 { if condition2 { message: "deeply nested" } } }`, fieldName: "message", expectedFound: true, }, { name: "find field in if-else chain", input: `{ status: "running" if status == "running" { message: "service is running" } if status == "stopped" { message: "service is stopped" } }`, fieldName: "message", expectedFound: true, }, { name: "find field in complex nested conditionals", input: `{ phase: context.output.status.phase replicas: context.output.status.replicas readyReplicas: *0 | int if context.output.status.readyReplicas != _|_ { readyReplicas: context.output.status.readyReplicas } if phase == "Running" { if readyReplicas == replicas { message: "All replicas are ready" } if readyReplicas < replicas { message: "Some replicas are not ready" } } if phase != "Running" { message: "Deployment is not running" } }`, fieldName: "message", expectedFound: true, }, { name: "find field in if with validator", input: `{ condition: true if condition { isHealth: true } }`, fieldName: "isHealth", expectedFound: true, validator: func(expr ast.Expr) error { if basicLit, ok := expr.(*ast.BasicLit); ok { if basicLit.Value != "true" { return fmt.Errorf("expected true, got %s", basicLit.Value) } } return nil }, }, { name: "find field in nested if with failing validator", input: `{ condition: true if condition { if nestedCondition { isHealth: false } } }`, fieldName: "isHealth", expectedFound: true, expectedErrMessage: "expected true, got", validator: func(expr ast.Expr) error { if basicLit, ok := expr.(*ast.BasicLit); ok { if basicLit.Value != "true" { return fmt.Errorf("expected true, got %s", basicLit.Value) } } return nil }, }, { name: "field not found in comprehension without if clause", input: `{ items: [for x in list { value: x }] }`, fieldName: "message", expectedFound: false, }, { name: "find field with complex expressions in if", input: `{ replicas: context.output.status.replicas readyReplicas: context.output.status.readyReplicas if (replicas | *0) != (readyReplicas | *0) { message: "not ready" } }`, fieldName: "message", expectedFound: true, }, { name: "find field with quoted label in if statement", input: `{ condition: true if condition { "field-with-dash": "value" } }`, fieldName: "field-with-dash", expectedFound: true, }, { name: "find field in multiple nested levels", input: `{ level1: "value" if condition1 { level2: "value" if condition2 { level3: "value" if condition3 { message: "deeply nested field" } } } }`, fieldName: "message", expectedFound: true, }, { name: "empty struct with if statements", input: `{ if condition { } }`, fieldName: "message", expectedFound: false, }, { name: "field exists in both top level and if statement (finds top level first)", input: `{ message: "top level" if condition { message: "in if" } }`, fieldName: "message", expectedFound: true, validator: func(expr ast.Expr) error { if basicLit, ok := expr.(*ast.BasicLit); ok { if basicLit.Value == `"top level"` { return nil } return fmt.Errorf("expected top level message, got %s", basicLit.Value) } return fmt.Errorf("expected string literal") }, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { expr, err := parser.ParseExpr("-", tt.input) require.NoError(t, err) structLit, ok := expr.(*ast.StructLit) require.True(t, ok, "input should parse as struct literal") found, err := FindAndValidateField(structLit, tt.fieldName, tt.validator) require.Equal(t, tt.expectedFound, found, "found result should match expected") if tt.expectedErrMessage != "" { require.Error(t, err) require.Contains(t, err.Error(), tt.expectedErrMessage) } else { require.NoError(t, err) } }) } }