forked from nautilus/gateway
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cache_test.go
200 lines (168 loc) · 5.09 KB
/
cache_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package gateway
import (
"errors"
"testing"
"time"
"github.com/nautilus/graphql"
"github.com/stretchr/testify/assert"
)
// MockPlanner always returns the provided list of plans. Useful in testing.
type testPlannerCounter struct {
Count int
Plans QueryPlanList
}
func (p *testPlannerCounter) Plan(*PlanningContext) (QueryPlanList, error) {
// increment the count
p.Count++
// return the plans
return p.Plans, nil
}
func TestCacheOptions(t *testing.T) {
t.Parallel()
// turn the combo into a remote schema
schema, _ := graphql.LoadSchema(`
type Query {
value: String!
}
`)
// create a gateway that doesn't wrap any schemas and has no query plan cache
gw, err := New([]*graphql.RemoteSchema{
{
URL: "asdf",
Schema: schema,
},
}, WithNoQueryPlanCache())
if !assert.Nil(t, err) {
return
}
// make sure that the query plan cache is one that doesn't cache
_, ok := gw.queryPlanCache.(*NoQueryPlanCache)
assert.True(t, ok)
}
func TestNoQueryPlanCache(t *testing.T) {
t.Parallel()
cacheKey := "asdf"
// the plan we are expecting back
plans := QueryPlanList{}
// instantiate a planner that can count how many times it was invoked
planner := &testPlannerCounter{
Plans: plans,
}
// an instance of the NoCache cache
cache := &NoQueryPlanCache{}
// ask the cache to retrieve the same hash twice
plan1, err := cache.Retrieve(nil, &cacheKey, planner)
if !assert.Nil(t, err) {
return
}
plan2, err := cache.Retrieve(nil, &cacheKey, planner)
if !assert.Nil(t, err) {
return
}
// make sure that we computed two plans
assert.Equal(t, 2, planner.Count)
// and we got the same plan back both times
assert.Equal(t, plan1, plans)
assert.Equal(t, plan2, plans)
}
func TestAutomaticQueryPlanCache(t *testing.T) {
t.Parallel()
cacheKey := "asdf"
// the plan we are expecting back
plans := QueryPlanList{}
// instantiate a planner that can count how many times it was invoked
planner := &testPlannerCounter{
Plans: plans,
}
// an instance of the NoCache cache
cache := NewAutomaticQueryPlanCache()
// passing no query and an unknown hash should return an error with the magic string
plan1, err := cache.Retrieve(&PlanningContext{}, &cacheKey, planner)
if !assert.NotNil(t, err, "error was nil") {
return
}
assert.Equal(t, err.Error(), MessageMissingCachedQuery)
assert.Nil(t, plan1)
// passing a non-empty query along with a hash associates the resulting plan with the hash
plan2, err := cache.Retrieve(&PlanningContext{Query: "hello"}, &cacheKey, planner)
if !assert.Nil(t, err) {
return
}
assert.Equal(t, plan2, plans)
// do the same thing we did in step 1 (ask without a query body)
plan3, err := cache.Retrieve(&PlanningContext{}, &cacheKey, planner)
assert.Equal(t, plan3, plans)
if !assert.Nil(t, err) {
return
}
// we should have only computed the plan once
assert.Equal(t, 1, planner.Count)
}
func TestAutomaticQueryPlanCache_passPlannerErrors(t *testing.T) {
t.Parallel()
cacheKey := "asdf"
// instantiate a planner that can count how many times it was invoked
planner := &MockErrPlanner{errors.New("Error")}
// an instance of the NoCache cache
cache := NewAutomaticQueryPlanCache()
// passing no query and an unknown hash should return an error with the magic string
_, err := cache.Retrieve(&PlanningContext{Query: "Asdf"}, &cacheKey, planner)
if !assert.NotNil(t, err, "error was nil") {
return
}
}
func TestAutomaticQueryPlanCache_setCacheKey(t *testing.T) {
t.Parallel()
// instantiate a planner that can count how many times it was invoked
planner := &testPlannerCounter{
Plans: QueryPlanList{},
}
// an instance of the NoCache cache
cache := NewAutomaticQueryPlanCache()
// the key of the cache
cacheKey := ""
// plan a query
_, err := cache.Retrieve(&PlanningContext{Query: "hello"}, &cacheKey, planner)
assert.NoError(t, err)
// make sure that the key was changed
if cacheKey == "" {
t.Error("Cache key was not updated")
return
}
}
func TestAutomaticQueryPlanCache_garbageCollection(t *testing.T) {
t.Parallel()
cacheKey := "asdf"
// the plan we are expecting back
plans := QueryPlanList{}
// instantiate a planner that can count how many times it was invoked
planner := &testPlannerCounter{
Plans: plans,
}
// an instance of the NoCache cache
cache := NewAutomaticQueryPlanCache().WithCacheTTL(100 * time.Millisecond)
// retrieving the plan back to back should hit the cached version
_, err := cache.Retrieve(&PlanningContext{Query: "hello"}, &cacheKey, planner)
if !assert.Nil(t, err) {
return
}
_, err = cache.Retrieve(&PlanningContext{Query: "hello"}, &cacheKey, planner)
if !assert.Nil(t, err) {
return
}
// the plan should have only been computed once
assert.Equal(t, 1, planner.Count)
// wait longer than the cache ttl
time.Sleep(150 * time.Millisecond)
// ask for it twice more
_, err = cache.Retrieve(&PlanningContext{Query: "hello"}, &cacheKey, planner)
if !assert.Nil(t, err) {
return
}
_, err = cache.Retrieve(&PlanningContext{}, &cacheKey, planner)
if !assert.Nil(t, err) {
return
}
// we should have only generated the plan twice now (once more than before)
assert.Equal(t, 2, planner.Count)
}