1 package db
2
3 import (
4 "context"
5 "errors"
6 "strings"
7 "testing"
8 "testing/synctest"
9 "time"
10 )
11
12 func TestWithFetchTimeout_BelowFloor(t *testing.T) {
13 orig := MinFetchTimeout
14 t.Cleanup(func() { MinFetchTimeout = orig })
15
16 synctest.Test(t, func(t *testing.T) {
17 MinFetchTimeout = 50 * time.Millisecond
18 ctx, cancel := WithFetchTimeout(context.Background(), "fetch x", 5*time.Millisecond)
19 defer cancel()
20 deadline, ok := ctx.Deadline()
21 if !ok {
22 t.Fatal("expected deadline to be set")
23 }
24 if got := time.Until(deadline); got < 40*time.Millisecond || got > 60*time.Millisecond {
25 t.Fatalf("deadline remaining %v, want ~%v (the floor)", got, MinFetchTimeout)
26 }
27 })
28 }
29
30 func TestWithFetchTimeout_AboveFloor(t *testing.T) {
31 orig := MinFetchTimeout
32 t.Cleanup(func() { MinFetchTimeout = orig })
33
34 synctest.Test(t, func(t *testing.T) {
35 MinFetchTimeout = 10 * time.Millisecond
36 ctx, cancel := WithFetchTimeout(context.Background(), "fetch x", 250*time.Millisecond)
37 defer cancel()
38 deadline, ok := ctx.Deadline()
39 if !ok {
40 t.Fatal("expected deadline to be set")
41 }
42 if got := time.Until(deadline); got < 240*time.Millisecond || got > 260*time.Millisecond {
43 t.Fatalf("deadline remaining %v, want ~250ms (the interval)", got)
44 }
45 })
46 }
47
48 func TestWithFetchTimeout_DeadlineFires(t *testing.T) {
49 orig := MinFetchTimeout
50 t.Cleanup(func() { MinFetchTimeout = orig })
51
52 synctest.Test(t, func(t *testing.T) {
53 MinFetchTimeout = 25 * time.Millisecond
54 ctx, cancel := WithFetchTimeout(context.Background(), "fetch db_stats", time.Millisecond)
55 defer cancel()
56 time.Sleep(50 * time.Millisecond)
57 if !errors.Is(ctx.Err(), context.DeadlineExceeded) {
58 t.Fatalf("expected context.DeadlineExceeded, got %v", ctx.Err())
59 }
60 cause := context.Cause(ctx)
61 if !strings.Contains(cause.Error(), "fetch db_stats") {
62 t.Fatalf("cause %q does not embed operation name", cause)
63 }
64 })
65 }
66
67 func TestWithOpTimeout_DeadlineEqualsD(t *testing.T) {
68 synctest.Test(t, func(t *testing.T) {
69 ctx, cancel := WithOpTimeout(context.Background(), "ping", 75*time.Millisecond)
70 defer cancel()
71 deadline, ok := ctx.Deadline()
72 if !ok {
73 t.Fatal("expected deadline")
74 }
75 if got := time.Until(deadline); got < 65*time.Millisecond || got > 85*time.Millisecond {
76 t.Fatalf("deadline remaining %v, want ~75ms", got)
77 }
78 })
79 }
80
81 func TestWithOpTimeout_DeadlineFires(t *testing.T) {
82 synctest.Test(t, func(t *testing.T) {
83 ctx, cancel := WithOpTimeout(context.Background(), "resolve source-x", 25*time.Millisecond)
84 defer cancel()
85 time.Sleep(50 * time.Millisecond)
86 if !errors.Is(ctx.Err(), context.DeadlineExceeded) {
87 t.Fatalf("expected context.DeadlineExceeded, got %v", ctx.Err())
88 }
89 cause := context.Cause(ctx)
90 if !strings.Contains(cause.Error(), "resolve source-x") {
91 t.Fatalf("cause %q does not embed operation name", cause)
92 }
93 })
94 }
95
96 func TestWithOpTimeout_ParentCancel(t *testing.T) {
97 synctest.Test(t, func(t *testing.T) {
98 parent, parentCancel := context.WithCancel(context.Background())
99 ctx, cancel := WithOpTimeout(parent, "ping", time.Hour)
100 defer cancel()
101 parentCancel()
102 time.Sleep(time.Millisecond)
103 if !errors.Is(ctx.Err(), context.Canceled) {
104 t.Fatalf("expected context.Canceled, got %v", ctx.Err())
105 }
106 cause := context.Cause(ctx)
107 if errors.Is(cause, context.DeadlineExceeded) {
108 t.Fatalf("cause should not be DeadlineExceeded for parent cancel; got %v", cause)
109 }
110 })
111 }
112