@@ -20,6 +20,7 @@ import (
2020 "testing"
2121
2222 "github.com/google/cel-go/cel"
23+ "github.com/google/cel-go/checker"
2324)
2425
2526func TestEncoders (t * testing.T ) {
@@ -93,3 +94,128 @@ func TestEncodersVersion(t *testing.T) {
9394 t .Fatalf ("EncodersVersion(0) failed: %v" , err )
9495 }
9596}
97+
98+ func testEncodersCostsEnv (t * testing.T , version int , opts ... cel.EnvOption ) * cel.Env {
99+ t .Helper ()
100+ baseOpts := []cel.EnvOption {
101+ Encoders (EncodersVersion (uint32 (version ))),
102+ cel .EnableMacroCallTracking (),
103+ }
104+ env , err := cel .NewEnv (append (baseOpts , opts ... )... )
105+ if err != nil {
106+ t .Fatalf ("cel.NewEnv(Encoders()) failed: %v" , err )
107+ }
108+ return env
109+ }
110+
111+ func TestEncodersCosts (t * testing.T ) {
112+ tests := []struct {
113+ name string
114+ expr string
115+ vars []cel.EnvOption
116+ in map [string ]any
117+ hints map [string ]uint64
118+ estimatedCost checker.CostEstimate
119+ actualCost uint64
120+ version int
121+ }{
122+ {
123+ name : "encode_bytes_v0" ,
124+ expr : "base64.encode(x) == 'aGVsbG8='" ,
125+ vars : []cel.EnvOption {
126+ cel .Variable ("x" , cel .BytesType ),
127+ },
128+ in : map [string ]any {
129+ "x" : []byte ("hello" ),
130+ },
131+ hints : map [string ]uint64 {
132+ "x" : 100 ,
133+ },
134+ estimatedCost : checker .FixedCostEstimate (3 ), // x lookup (1) + encode (1) + == (1) = 3
135+ actualCost : 3 ,
136+ version : 0 ,
137+ },
138+ {
139+ name : "encode_bytes_v1" ,
140+ expr : "base64.encode(x) == 'aGVsbG8='" ,
141+ vars : []cel.EnvOption {
142+ cel .Variable ("x" , cel .BytesType ),
143+ },
144+ in : map [string ]any {
145+ "x" : []byte ("hello" ),
146+ },
147+ hints : map [string ]uint64 {
148+ "x" : 100 ,
149+ },
150+ estimatedCost : checker.CostEstimate {Min : 3 , Max : 13 }, // x lookup (1) + encode (100 * 0.1 + 1 = 11) + == (1) = 13
151+ actualCost : 4 , // x lookup (1) + encode (ceil(5 * 0.1) + 1 = 2) + == (1) = 4
152+ version : 1 ,
153+ },
154+ {
155+ name : "decode_string_v0" ,
156+ expr : "base64.decode(x) == b'hello'" ,
157+ vars : []cel.EnvOption {
158+ cel .Variable ("x" , cel .StringType ),
159+ },
160+ in : map [string ]any {
161+ "x" : "aGVsbG8=" ,
162+ },
163+ hints : map [string ]uint64 {
164+ "x" : 100 ,
165+ },
166+ estimatedCost : checker .FixedCostEstimate (3 ),
167+ actualCost : 3 ,
168+ version : 0 ,
169+ },
170+ {
171+ name : "decode_string_v1" ,
172+ expr : "base64.decode(x) == b'hello'" ,
173+ vars : []cel.EnvOption {
174+ cel .Variable ("x" , cel .StringType ),
175+ },
176+ in : map [string ]any {
177+ "x" : "aGVsbG8=" ,
178+ },
179+ hints : map [string ]uint64 {
180+ "x" : 100 ,
181+ },
182+ estimatedCost : checker.CostEstimate {Min : 3 , Max : 13 }, // x lookup (1) + decode (100 * 0.1 + 1 = 11) + == (1) = 13
183+ actualCost : 4 , // x lookup (1) + decode (ceil(8 * 0.1) + 1 = 2) + == (1) = 4
184+ version : 1 ,
185+ },
186+ {
187+ name : "encode_bytes_v1_literal" ,
188+ expr : "base64.encode(b'hello') == 'aGVsbG8='" ,
189+ estimatedCost : checker .FixedCostEstimate (3 ),
190+ actualCost : 3 ,
191+ version : 1 ,
192+ },
193+ {
194+ name : "decode_string_v1_literal" ,
195+ expr : "base64.decode('aGVsbG8=') == b'hello'" ,
196+ estimatedCost : checker .FixedCostEstimate (3 ),
197+ actualCost : 3 ,
198+ version : 1 ,
199+ },
200+ }
201+ for _ , tc := range tests {
202+ t .Run (tc .name , func (t * testing.T ) {
203+ env := testEncodersCostsEnv (t , tc .version , tc .vars ... )
204+ var asts []* cel.Ast
205+ pAst , iss := env .Parse (tc .expr )
206+ if iss .Err () != nil {
207+ t .Fatalf ("env.Parse(%v) failed: %v" , tc .expr , iss .Err ())
208+ }
209+ asts = append (asts , pAst )
210+ cAst , iss := env .Check (pAst )
211+ if iss .Err () != nil {
212+ t .Fatalf ("env.Check(%v) failed: %v" , tc .expr , iss .Err ())
213+ }
214+ testCheckCost (t , env , cAst , tc .hints , tc .estimatedCost )
215+ asts = append (asts , cAst )
216+ for _ , ast := range asts {
217+ testEvalWithCost (t , env , ast , tc .in , tc .actualCost )
218+ }
219+ })
220+ }
221+ }
0 commit comments