@@ -17,6 +17,7 @@ package cel
1717import (
1818 "bytes"
1919 "context"
20+ "errors"
2021 "fmt"
2122 "os"
2223 "reflect"
@@ -1766,6 +1767,95 @@ func TestEstimateCostAndRuntimeCost(t *testing.T) {
17661767 }
17671768}
17681769
1770+ func TestCostLimit (t * testing.T ) {
1771+ cases := []struct {
1772+ name string
1773+ expr string
1774+ decls []EnvOption
1775+ costLimit uint64
1776+ in any
1777+ err error
1778+ }{
1779+ {
1780+ name : "greater" ,
1781+ expr : `val1 > val2` ,
1782+ decls : []EnvOption {
1783+ Variable ("val1" , IntType ),
1784+ Variable ("val2" , IntType ),
1785+ },
1786+ in : map [string ]any {"val1" : 1 , "val2" : 2 },
1787+ costLimit : 10 ,
1788+ },
1789+ {
1790+ name : "greater - error" ,
1791+ expr : `val1 > val2` ,
1792+ decls : []EnvOption {
1793+ Variable ("val1" , IntType ),
1794+ Variable ("val2" , IntType ),
1795+ },
1796+ in : map [string ]any {"val1" : 1 , "val2" : 2 },
1797+ costLimit : 0 ,
1798+ err : errors .New ("actual cost limit exceeded" ),
1799+ },
1800+ }
1801+
1802+ for _ , tst := range cases {
1803+ tc := tst
1804+ t .Run (tc .name , func (t * testing.T ) {
1805+ t .Parallel ()
1806+ envOpts := []EnvOption {
1807+ CostEstimatorOptions (
1808+ checker .OverloadCostEstimate (overloads .TimestampToYear , estimateTimestampToYear ),
1809+ ),
1810+ }
1811+ envOpts = append (envOpts , tc .decls ... )
1812+ env := testEnv (t , envOpts ... )
1813+ ast , iss := env .Compile (tc .expr )
1814+ if iss .Err () != nil {
1815+ t .Fatalf ("env.Compile(%v) failed: %v" , tc .expr , iss .Err ())
1816+ }
1817+ est , err := env .EstimateCost (ast , testCostEstimator {hints : map [string ]uint64 {}})
1818+ if err != nil {
1819+ t .Fatalf ("Env.EstimateCost(ast *Ast, estimator checker.CostEstimator) failed to estimate cost: %s\n " , err )
1820+ }
1821+
1822+ checkedAst , iss := env .Check (ast )
1823+ if iss .Err () != nil {
1824+ t .Fatalf (`Env.Check(ast *Ast) failed to check expression: %v` , iss .Err ())
1825+ }
1826+ // Evaluate expression.
1827+ program , err := env .Program (checkedAst ,
1828+ CostTracking (testRuntimeCostEstimator {}),
1829+ CostTrackerOptions (
1830+ interpreter .OverloadCostTracker (overloads .TimestampToYear , trackTimestampToYear ),
1831+ ),
1832+ CostLimit (tc .costLimit ),
1833+ )
1834+ if err != nil {
1835+ t .Fatalf (`Env.Program(ast *Ast, opts ...ProgramOption) failed to construct program: %v` , err )
1836+ }
1837+ _ , details , err := program .Eval (tc .in )
1838+ if err != nil && tc .err == nil {
1839+ t .Fatalf (`Program.Eval(vars any) failed to evaluate expression: %v` , err )
1840+ }
1841+ actualCost := details .ActualCost ()
1842+ if actualCost == nil {
1843+ t .Errorf (`EvalDetails.ActualCost() got nil for "%s" cost, wanted %d` , tc .expr , actualCost )
1844+ }
1845+ if err == nil {
1846+ if est .Min > * actualCost || est .Max < * actualCost {
1847+ t .Errorf ("EvalDetails.ActualCost() failed to return a runtime cost %d is the range of estimate cost [%d, %d]" , * actualCost ,
1848+ est .Min , est .Max )
1849+ }
1850+ } else {
1851+ if ! strings .Contains (err .Error (), tc .err .Error ()) {
1852+ t .Fatalf ("program.Eval() got error %v, wanted error containing %v" , err , tc .err )
1853+ }
1854+ }
1855+ })
1856+ }
1857+ }
1858+
17691859func TestPartialVars (t * testing.T ) {
17701860 env := testEnv (t ,
17711861 Variable ("x" , StringType ),
0 commit comments