(t *testing.T)
| 781 | } |
| 782 | |
| 783 | func TestConcurrentEvalCancelDuringDebounce(t *testing.T) { |
| 784 | // Tests context cancellation while awaiting a debounce timeout in the completion drain loop. |
| 785 | prg := mustProgram(t, `delayed_rpc("first", 10) + delayed_rpc("second", 1000)`, |
| 786 | cel.Function("delayed_rpc", |
| 787 | cel.Overload("delayed_rpc_string_int", []*cel.Type{cel.StringType, cel.IntType}, cel.StringType, |
| 788 | cel.AsyncBinding(func(ctx context.Context, args ...ref.Val) ref.Val { |
| 789 | msg := string(args[0].(types.String)) |
| 790 | delayMs := time.Duration(int64(args[1].(types.Int))) * time.Millisecond |
| 791 | time.Sleep(delayMs) |
| 792 | return types.String(msg) |
| 793 | }), |
| 794 | ), |
| 795 | ), |
| 796 | cel.ConcurrentDrainStrategy(async.DrainReady(10*time.Second)), |
| 797 | ) |
| 798 | |
| 799 | ctx, cancel := context.WithCancel(context.Background()) |
| 800 | resCh := prg.ConcurrentEval(ctx, cel.NoVars()) |
| 801 | |
| 802 | // Wait for the first call (10ms) to complete and enter the 10-second debounce wait. |
| 803 | time.Sleep(30 * time.Millisecond) |
| 804 | cancel() |
| 805 | |
| 806 | select { |
| 807 | case res := <-resCh: |
| 808 | if res.Err == nil || !errors.Is(res.Err, context.Canceled) { |
| 809 | t.Fatalf("ConcurrentEval() error = %v, want context.Canceled", res.Err) |
| 810 | } |
| 811 | case <-time.After(5 * time.Second): |
| 812 | t.Fatal("ConcurrentEval() timed out waiting for cancellation during debounce") |
| 813 | } |
| 814 | } |
| 815 | |
| 816 | func TestConcurrentEvalRecover(t *testing.T) { |
| 817 | env, err := cel.NewEnv( |
nothing calls this directly
no test coverage detected