(t *testing.T)
| 656 | } |
| 657 | |
| 658 | func TestSyncEvalRejectsAsyncBeforeEvaluating(t *testing.T) { |
| 659 | // The async guard must fire at the entry point, before any evaluation: the async function |
| 660 | // must never be invoked (no goroutines launched, no work done) for Eval or ContextEval. |
| 661 | var called atomic.Int32 |
| 662 | prg := mustProgram(t, `rpc("a")`, |
| 663 | cel.Function("rpc", |
| 664 | cel.Overload("rpc_string", []*cel.Type{cel.StringType}, cel.StringType, |
| 665 | cel.AsyncBinding(func(ctx context.Context, args ...ref.Val) ref.Val { |
| 666 | called.Add(1) |
| 667 | return args[0] |
| 668 | }))), |
| 669 | ) |
| 670 | |
| 671 | if _, _, err := prg.Eval(cel.NoVars()); err == nil || !strings.Contains(err.Error(), "ConcurrentEval") { |
| 672 | t.Errorf("Eval() = %v, want ConcurrentEval error", err) |
| 673 | } |
| 674 | if _, _, err := prg.ContextEval(context.Background(), cel.NoVars()); err == nil || !strings.Contains(err.Error(), "ConcurrentEval") { |
| 675 | t.Errorf("ContextEval() = %v, want ConcurrentEval error", err) |
| 676 | } |
| 677 | if got := called.Load(); got != 0 { |
| 678 | t.Errorf("async function invoked %d times; the guard must reject before evaluating", got) |
| 679 | } |
| 680 | } |
| 681 | |
| 682 | func TestSyncEvalRejectedInAsyncEnv(t *testing.T) { |
| 683 | // Env-level rejection: an environment that declares any async function rejects the synchronous |
nothing calls this directly
no test coverage detected