Skip to content

Commit d8e270f

Browse files
committed
Added tests for query cursors on various deployments.
[ci LONG=1] [ci TESTOPTIONS="-test.run ^TestCursor"]
1 parent a7c2449 commit d8e270f

File tree

2 files changed

+425
-0
lines changed

2 files changed

+425
-0
lines changed

tests/cursor_test.go

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
package tests
2+
3+
import (
4+
"context"
5+
"reflect"
6+
"testing"
7+
"time"
8+
9+
"github.com/dchest/uniuri"
10+
"github.com/stretchr/testify/assert"
11+
12+
driver "github.com/arangodb/go-driver"
13+
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
14+
"github.com/arangodb/kube-arangodb/pkg/client"
15+
)
16+
17+
// TestCursorSingle tests the creating of a single server deployment
18+
// with default settings and runs some cursor requests on it.
19+
func TestCursorSingle(t *testing.T) {
20+
longOrSkip(t)
21+
c := client.MustNewInCluster()
22+
kubecli := mustNewKubeClient(t)
23+
ns := getNamespace(t)
24+
25+
// Prepare deployment config
26+
depl := newDeployment("test-cur-sng-" + uniuri.NewLen(4))
27+
depl.Spec.Mode = api.NewMode(api.DeploymentModeSingle)
28+
29+
// Create deployment
30+
_, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl)
31+
if err != nil {
32+
t.Fatalf("Create deployment failed: %v", err)
33+
}
34+
defer deferedCleanupDeployment(c, depl.GetName(), ns)
35+
36+
// Wait for deployment to be ready
37+
apiObject, err := waitUntilDeployment(c, depl.GetName(), ns, deploymentIsReady())
38+
if err != nil {
39+
t.Fatalf("Deployment not running in time: %v", err)
40+
}
41+
42+
// Create a database client
43+
ctx := context.Background()
44+
client := mustNewArangodDatabaseClient(ctx, kubecli, apiObject, t)
45+
46+
// Wait for single server available
47+
if err := waitUntilVersionUp(client, nil); err != nil {
48+
t.Fatalf("Single server not running returning version in time: %v", err)
49+
}
50+
51+
// Check server role
52+
assert.NoError(t, client.SynchronizeEndpoints(ctx))
53+
role, err := client.ServerRole(ctx)
54+
assert.NoError(t, err)
55+
assert.Equal(t, driver.ServerRoleSingle, role)
56+
57+
// Run cursor tests
58+
runCursorTests(t, client)
59+
60+
// Cleanup
61+
removeDeployment(c, depl.GetName(), ns)
62+
}
63+
64+
// TestCursorResilientSingle tests the creating of a resilientsingle server deployment
65+
// with default settings.
66+
func TestCursorResilientSingle(t *testing.T) {
67+
longOrSkip(t)
68+
c := client.MustNewInCluster()
69+
kubecli := mustNewKubeClient(t)
70+
ns := getNamespace(t)
71+
72+
// Prepare deployment config
73+
depl := newDeployment("test-cur-rs-" + uniuri.NewLen(4))
74+
depl.Spec.Mode = api.NewMode(api.DeploymentModeResilientSingle)
75+
76+
// Create deployment
77+
_, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl)
78+
if err != nil {
79+
t.Fatalf("Create deployment failed: %v", err)
80+
}
81+
defer deferedCleanupDeployment(c, depl.GetName(), ns)
82+
83+
// Wait for deployment to be ready
84+
apiObject, err := waitUntilDeployment(c, depl.GetName(), ns, deploymentIsReady())
85+
if err != nil {
86+
t.Fatalf("Deployment not running in time: %v", err)
87+
}
88+
89+
// Create a database client
90+
ctx := context.Background()
91+
client := mustNewArangodDatabaseClient(ctx, kubecli, apiObject, t)
92+
93+
// Wait for single server available
94+
if err := waitUntilVersionUp(client, nil); err != nil {
95+
t.Fatalf("ResilientSingle servers not running returning version in time: %v", err)
96+
}
97+
98+
// Check server role
99+
assert.NoError(t, client.SynchronizeEndpoints(ctx))
100+
role, err := client.ServerRole(ctx)
101+
assert.NoError(t, err)
102+
assert.Equal(t, driver.ServerRoleSingleActive, role)
103+
104+
// Run cursor tests
105+
runCursorTests(t, client)
106+
107+
// Cleanup
108+
removeDeployment(c, depl.GetName(), ns)
109+
}
110+
111+
// TestCursorCluster tests the creating of a cluster deployment
112+
// with default settings.
113+
func TestCursorCluster(t *testing.T) {
114+
longOrSkip(t)
115+
c := client.MustNewInCluster()
116+
kubecli := mustNewKubeClient(t)
117+
ns := getNamespace(t)
118+
119+
// Prepare deployment config
120+
depl := newDeployment("test-cur-cls-" + uniuri.NewLen(4))
121+
depl.Spec.Mode = api.NewMode(api.DeploymentModeCluster)
122+
123+
// Create deployment
124+
_, err := c.DatabaseV1alpha().ArangoDeployments(ns).Create(depl)
125+
if err != nil {
126+
t.Fatalf("Create deployment failed: %v", err)
127+
}
128+
defer deferedCleanupDeployment(c, depl.GetName(), ns)
129+
130+
// Wait for deployment to be ready
131+
apiObject, err := waitUntilDeployment(c, depl.GetName(), ns, deploymentIsReady())
132+
if err != nil {
133+
t.Fatalf("Deployment not running in time: %v", err)
134+
}
135+
136+
// Create a database client
137+
ctx := context.Background()
138+
client := mustNewArangodDatabaseClient(ctx, kubecli, apiObject, t)
139+
140+
// Wait for single server available
141+
if err := waitUntilVersionUp(client, nil); err != nil {
142+
t.Fatalf("Cluster not running returning version in time: %v", err)
143+
}
144+
145+
// Check server role
146+
assert.NoError(t, client.SynchronizeEndpoints(ctx))
147+
role, err := client.ServerRole(ctx)
148+
assert.NoError(t, err)
149+
assert.Equal(t, driver.ServerRoleCoordinator, role)
150+
151+
// Run cursor tests
152+
runCursorTests(t, client)
153+
154+
// cleanup
155+
removeDeployment(c, depl.GetName(), ns)
156+
}
157+
158+
type Book struct {
159+
Title string
160+
}
161+
162+
type UserDoc struct {
163+
Name string `json:"name"`
164+
Age int `json:"age"`
165+
}
166+
167+
type queryTest struct {
168+
Query string
169+
BindVars map[string]interface{}
170+
ExpectSuccess bool
171+
ExpectedDocuments []interface{}
172+
DocumentType reflect.Type
173+
}
174+
175+
type queryTestContext struct {
176+
Context context.Context
177+
ExpectCount bool
178+
}
179+
180+
func runCursorTests(t *testing.T, client driver.Client) {
181+
// Create data set
182+
collectionData := map[string][]interface{}{
183+
"books": []interface{}{
184+
Book{Title: "Book 01"},
185+
Book{Title: "Book 02"},
186+
Book{Title: "Book 03"},
187+
Book{Title: "Book 04"},
188+
Book{Title: "Book 05"},
189+
Book{Title: "Book 06"},
190+
Book{Title: "Book 07"},
191+
Book{Title: "Book 08"},
192+
Book{Title: "Book 09"},
193+
Book{Title: "Book 10"},
194+
Book{Title: "Book 11"},
195+
Book{Title: "Book 12"},
196+
Book{Title: "Book 13"},
197+
Book{Title: "Book 14"},
198+
Book{Title: "Book 15"},
199+
Book{Title: "Book 16"},
200+
Book{Title: "Book 17"},
201+
Book{Title: "Book 18"},
202+
Book{Title: "Book 19"},
203+
Book{Title: "Book 20"},
204+
},
205+
"users": []interface{}{
206+
UserDoc{Name: "John", Age: 13},
207+
UserDoc{Name: "Jake", Age: 25},
208+
UserDoc{Name: "Clair", Age: 12},
209+
UserDoc{Name: "Johnny", Age: 42},
210+
UserDoc{Name: "Blair", Age: 67},
211+
UserDoc{Name: "Zz", Age: 12},
212+
},
213+
}
214+
ctx := context.Background()
215+
db := ensureDatabase(ctx, client, "cursur_test", nil, t)
216+
for colName, colDocs := range collectionData {
217+
col := ensureCollection(ctx, db, colName, nil, t)
218+
if _, _, err := col.CreateDocuments(ctx, colDocs); err != nil {
219+
t.Fatalf("Expected success, got %s", err)
220+
}
221+
}
222+
223+
// Setup tests
224+
tests := []queryTest{
225+
queryTest{
226+
Query: "FOR d IN books SORT d.Title RETURN d",
227+
ExpectSuccess: true,
228+
ExpectedDocuments: collectionData["books"],
229+
DocumentType: reflect.TypeOf(Book{}),
230+
},
231+
queryTest{
232+
Query: "FOR d IN books FILTER d.Title==@title SORT d.Title RETURN d",
233+
BindVars: map[string]interface{}{"title": "Book 02"},
234+
ExpectSuccess: true,
235+
ExpectedDocuments: []interface{}{collectionData["books"][1]},
236+
DocumentType: reflect.TypeOf(Book{}),
237+
},
238+
queryTest{
239+
Query: "FOR d IN books FILTER d.Title==@title SORT d.Title RETURN d",
240+
BindVars: map[string]interface{}{"somethingelse": "Book 02"},
241+
ExpectSuccess: false, // Unknown `@title`
242+
},
243+
queryTest{
244+
Query: "FOR u IN users FILTER u.age>100 SORT u.name RETURN u",
245+
ExpectSuccess: true,
246+
ExpectedDocuments: []interface{}{},
247+
DocumentType: reflect.TypeOf(UserDoc{}),
248+
},
249+
queryTest{
250+
Query: "FOR u IN users FILTER u.age<@maxAge SORT u.name RETURN u",
251+
BindVars: map[string]interface{}{"maxAge": 20},
252+
ExpectSuccess: true,
253+
ExpectedDocuments: []interface{}{collectionData["users"][2], collectionData["users"][0], collectionData["users"][5]},
254+
DocumentType: reflect.TypeOf(UserDoc{}),
255+
},
256+
queryTest{
257+
Query: "FOR u IN users FILTER u.age<@maxAge SORT u.name RETURN u",
258+
BindVars: map[string]interface{}{"maxage": 20},
259+
ExpectSuccess: false, // `@maxage` versus `@maxAge`
260+
},
261+
queryTest{
262+
Query: "FOR u IN users SORT u.age RETURN u.age",
263+
ExpectedDocuments: []interface{}{12, 12, 13, 25, 42, 67},
264+
DocumentType: reflect.TypeOf(12),
265+
ExpectSuccess: true,
266+
},
267+
queryTest{
268+
Query: "FOR p IN users COLLECT a = p.age WITH COUNT INTO c SORT a RETURN [a, c]",
269+
ExpectedDocuments: []interface{}{[]int{12, 2}, []int{13, 1}, []int{25, 1}, []int{42, 1}, []int{67, 1}},
270+
DocumentType: reflect.TypeOf([]int{}),
271+
ExpectSuccess: true,
272+
},
273+
queryTest{
274+
Query: "FOR u IN users SORT u.name RETURN u.name",
275+
ExpectedDocuments: []interface{}{"Blair", "Clair", "Jake", "John", "Johnny", "Zz"},
276+
DocumentType: reflect.TypeOf("foo"),
277+
ExpectSuccess: true,
278+
},
279+
}
280+
281+
// Setup context alternatives
282+
contexts := []queryTestContext{
283+
queryTestContext{nil, false},
284+
queryTestContext{context.Background(), false},
285+
queryTestContext{driver.WithQueryCount(nil), true},
286+
queryTestContext{driver.WithQueryCount(nil, true), true},
287+
queryTestContext{driver.WithQueryCount(nil, false), false},
288+
queryTestContext{driver.WithQueryBatchSize(nil, 1), false},
289+
queryTestContext{driver.WithQueryCache(nil), false},
290+
queryTestContext{driver.WithQueryCache(nil, true), false},
291+
queryTestContext{driver.WithQueryCache(nil, false), false},
292+
queryTestContext{driver.WithQueryMemoryLimit(nil, 60000), false},
293+
queryTestContext{driver.WithQueryTTL(nil, time.Minute), false},
294+
queryTestContext{driver.WithQueryBatchSize(driver.WithQueryCount(nil), 1), true},
295+
queryTestContext{driver.WithQueryCache(driver.WithQueryCount(driver.WithQueryBatchSize(nil, 2))), true},
296+
}
297+
298+
// Run tests for every context alternative
299+
for _, qctx := range contexts {
300+
ctx := qctx.Context
301+
for i, test := range tests {
302+
cursor, err := db.Query(ctx, test.Query, test.BindVars)
303+
if err == nil {
304+
// Close upon exit of the function
305+
defer cursor.Close()
306+
}
307+
if test.ExpectSuccess {
308+
if err != nil {
309+
t.Errorf("Expected success in query %d (%s), got '%s'", i, test.Query, err)
310+
continue
311+
}
312+
count := cursor.Count()
313+
if qctx.ExpectCount {
314+
if count != int64(len(test.ExpectedDocuments)) {
315+
t.Errorf("Expected count of %d, got %d in query %d (%s)", len(test.ExpectedDocuments), count, i, test.Query)
316+
}
317+
} else {
318+
if count != 0 {
319+
t.Errorf("Expected count of 0, got %d in query %d (%s)", count, i, test.Query)
320+
}
321+
}
322+
var result []interface{}
323+
for {
324+
hasMore := cursor.HasMore()
325+
doc := reflect.New(test.DocumentType)
326+
if _, err := cursor.ReadDocument(ctx, doc.Interface()); driver.IsNoMoreDocuments(err) {
327+
if hasMore {
328+
t.Error("HasMore returned true, but ReadDocument returns a IsNoMoreDocuments error")
329+
}
330+
break
331+
} else if err != nil {
332+
t.Errorf("Failed to result document %d: %s", len(result), err)
333+
}
334+
if !hasMore {
335+
t.Error("HasMore returned false, but ReadDocument returns a document")
336+
}
337+
result = append(result, doc.Elem().Interface())
338+
}
339+
if len(result) != len(test.ExpectedDocuments) {
340+
t.Errorf("Expected %d documents, got %d in query %d (%s)", len(test.ExpectedDocuments), len(result), i, test.Query)
341+
} else {
342+
for resultIdx, resultDoc := range result {
343+
if !reflect.DeepEqual(resultDoc, test.ExpectedDocuments[resultIdx]) {
344+
t.Errorf("Unexpected document in query %d (%s) at index %d: got %+v, expected %+v", i, test.Query, resultIdx, resultDoc, test.ExpectedDocuments[resultIdx])
345+
}
346+
}
347+
}
348+
// Close anyway (this tests calling Close more than once)
349+
if err := cursor.Close(); err != nil {
350+
t.Errorf("Expected success in Close of cursor from query %d (%s), got '%s'", i, test.Query, err)
351+
}
352+
} else {
353+
if err == nil {
354+
t.Errorf("Expected error in query %d (%s), got '%s'", i, test.Query, err)
355+
continue
356+
}
357+
}
358+
}
359+
}
360+
361+
}

0 commit comments

Comments
 (0)