Skip to content

Commit 3da6139

Browse files
Initial stateful observers prior to evaluation (#1163)
1 parent 55657d8 commit 3da6139

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

cel/cel_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package cel
1717
import (
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+
17691859
func TestPartialVars(t *testing.T) {
17701860
env := testEnv(t,
17711861
Variable("x", StringType),

interpreter/interpretable.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ func (oi *ObservableInterpretable) ObserveEval(vars Activation, observer func(an
138138
if err != nil {
139139
return types.WrapErr(err)
140140
}
141+
// Provide an initial reference to the state to ensure state is available
142+
// even in cases of interrupting errors generated during evaluation.
143+
observer(obs.GetState(vars))
141144
}
142145
result := oi.Interpretable.Eval(vars)
143146
// Get the state which needs to be reported back as having been observed.

0 commit comments

Comments
 (0)