|
18 | 18 | package v2 |
19 | 19 |
|
20 | 20 | import ( |
| 21 | + "os" |
21 | 22 | "testing" |
22 | 23 |
|
| 24 | + "github.com/google/cel-go/cel" |
23 | 25 | "github.com/stretchr/testify/assert" |
24 | 26 | "github.com/stretchr/testify/require" |
| 27 | + "sigs.k8s.io/yaml" |
25 | 28 | ) |
26 | 29 |
|
27 | 30 | func strPtr(s string) *string { return &s } |
28 | 31 |
|
| 32 | +// celSubjectRule is the CEL expression used in the +kubebuilder:validation:XValidation |
| 33 | +// marker on ApisixRouteHTTPMatchExprSubject. |
| 34 | +const celSubjectRule = `self.scope == 'Path' || self.name != ''` |
| 35 | + |
| 36 | +func evalCELSubjectRule(t *testing.T, scope, name string) bool { |
| 37 | + t.Helper() |
| 38 | + env, err := cel.NewEnv( |
| 39 | + cel.Variable("self", cel.MapType(cel.StringType, cel.StringType)), |
| 40 | + ) |
| 41 | + require.NoError(t, err) |
| 42 | + ast, issues := env.Compile(celSubjectRule) |
| 43 | + require.NoError(t, issues.Err()) |
| 44 | + prg, err := env.Program(ast) |
| 45 | + require.NoError(t, err) |
| 46 | + out, _, err := prg.Eval(map[string]any{ |
| 47 | + "self": map[string]any{"scope": scope, "name": name}, |
| 48 | + }) |
| 49 | + require.NoError(t, err) |
| 50 | + return out.Value().(bool) |
| 51 | +} |
| 52 | + |
| 53 | +// TestCEL_SubjectRule_Logic verifies the CEL expression used in the XValidation marker. |
| 54 | +func TestCEL_SubjectRule_Logic(t *testing.T) { |
| 55 | + // Non-Path scopes with a non-empty name must pass. |
| 56 | + for _, scope := range []string{ScopeHeader, ScopeQuery, ScopeCookie, ScopeVariable, ScopeBody} { |
| 57 | + assert.True(t, evalCELSubjectRule(t, scope, "field"), "scope=%s with name should pass", scope) |
| 58 | + } |
| 59 | + // Path scope with empty name must pass (name is ignored for Path). |
| 60 | + assert.True(t, evalCELSubjectRule(t, ScopePath, ""), "Path with empty name should pass") |
| 61 | + // Non-Path scopes with empty name must fail. |
| 62 | + for _, scope := range []string{ScopeHeader, ScopeQuery, ScopeCookie, ScopeVariable, ScopeBody} { |
| 63 | + assert.False(t, evalCELSubjectRule(t, scope, ""), "scope=%s with empty name should fail", scope) |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +// TestCEL_SubjectRule_InCRD verifies the generated CRD YAML contains the XValidation rule |
| 68 | +// with correct (ASCII) quote characters and not typographic quotes. |
| 69 | +func TestCEL_SubjectRule_InCRD(t *testing.T) { |
| 70 | + const crdPath = "../../config/crd/bases/apisix.apache.org_apisixroutes.yaml" |
| 71 | + data, err := os.ReadFile(crdPath) |
| 72 | + require.NoError(t, err, "CRD file should exist; run 'make manifests' if missing") |
| 73 | + |
| 74 | + var crd map[string]any |
| 75 | + require.NoError(t, yaml.Unmarshal(data, &crd)) |
| 76 | + |
| 77 | + raw := string(data) |
| 78 | + // The CEL rule must appear with ASCII single-quotes only. |
| 79 | + assert.Contains(t, raw, `self.scope == 'Path' || self.name != ''`, |
| 80 | + "CRD should contain the XValidation rule with ASCII quotes") |
| 81 | + // Ensure no typographic/smart quotes crept in. |
| 82 | + assert.NotContains(t, raw, "\u2018", "CRD must not contain left single quotation mark \u2018") |
| 83 | + assert.NotContains(t, raw, "\u2019", "CRD must not contain right single quotation mark \u2019") |
| 84 | + assert.NotContains(t, raw, "\u201c", "CRD must not contain left double quotation mark \u201c") |
| 85 | + assert.NotContains(t, raw, "\u201d", "CRD must not contain right double quotation mark \u201d") |
| 86 | +} |
| 87 | + |
29 | 88 | func TestToVars_ScopeBody_SimpleField(t *testing.T) { |
30 | 89 | exprs := ApisixRouteHTTPMatchExprs{ |
31 | 90 | { |
|
0 commit comments