1
2
3
4
5
6
7 package integration
8
9 import (
10 "context"
11 "fmt"
12 "io/ioutil"
13 "os"
14 "path"
15 "reflect"
16 "strings"
17 "testing"
18
19 "go.mongodb.org/mongo-driver/bson"
20 "go.mongodb.org/mongo-driver/internal/assert"
21 "go.mongodb.org/mongo-driver/internal/bsonutil"
22 "go.mongodb.org/mongo-driver/mongo"
23 "go.mongodb.org/mongo-driver/mongo/integration/mtest"
24 )
25
26 const (
27 crudTestsDir = "../../testdata/crud"
28 crudReadDir = "v1/read"
29 crudWriteDir = "v1/write"
30 )
31
32 type crudTestFile struct {
33 Data []bson.Raw `bson:"data"`
34 MinServerVersion string `bson:"minServerVersion"`
35 MaxServerVersion string `bson:"maxServerVersion"`
36 Serverless string `bson:"serverless"`
37 Tests []crudTest `bson:"tests"`
38 }
39
40 type crudTest struct {
41 Description string `bson:"description"`
42 SkipReason string `bson:"skipReason"`
43 Operation crudOperation `bson:"operation"`
44 Outcome crudOutcome `bson:"outcome"`
45 }
46
47 type crudOperation struct {
48 Name string `bson:"name"`
49 Arguments bson.Raw `bson:"arguments"`
50 }
51
52 type crudOutcome struct {
53 Error bool `bson:"error"`
54 Result interface{} `bson:"result"`
55 Collection *outcomeCollection `bson:"collection"`
56 }
57
58 var crudRegistry = bson.NewRegistryBuilder().
59 RegisterTypeMapEntry(bson.TypeEmbeddedDocument, reflect.TypeOf(bson.Raw{})).Build()
60
61 func TestCrudSpec(t *testing.T) {
62 for _, dir := range []string{crudReadDir, crudWriteDir} {
63 for _, file := range jsonFilesInDir(t, path.Join(crudTestsDir, dir)) {
64 t.Run(file, func(t *testing.T) {
65 runCrudFile(t, path.Join(crudTestsDir, dir, file))
66 })
67 }
68 }
69 }
70
71 func verifyServerlessConstraint(mt *mtest.T, expected string) error {
72 serverless := os.Getenv("SERVERLESS") == "serverless"
73
74 switch expected {
75 case "require":
76 if !serverless {
77 return fmt.Errorf("test requires serverless")
78 }
79 case "forbid":
80 if serverless {
81 return fmt.Errorf("test forbids serverless")
82 }
83 case "allow", "":
84 default:
85 mt.Fatalf("invalid value for serverless: %s", expected)
86 }
87 return nil
88 }
89
90 func runCrudFile(t *testing.T, file string) {
91 content, err := ioutil.ReadFile(file)
92 assert.Nil(t, err, "ReadFile error for %v: %v", file, err)
93
94 var testFile crudTestFile
95 err = bson.UnmarshalExtJSONWithRegistry(crudRegistry, content, false, &testFile)
96 assert.Nil(t, err, "UnmarshalExtJSONWithRegistry error: %v", err)
97
98 mt := mtest.New(t, mtest.NewOptions().MinServerVersion(testFile.MinServerVersion).MaxServerVersion(testFile.MaxServerVersion))
99
100
101 if err = verifyServerlessConstraint(mt, testFile.Serverless); err != nil {
102 mt.Skipf("%v", err)
103 }
104
105 for _, test := range testFile.Tests {
106 mt.Run(test.Description, func(mt *mtest.T) {
107 runCrudTest(mt, test, testFile)
108 })
109 }
110 }
111
112 func runCrudTest(mt *mtest.T, test crudTest, testFile crudTestFile) {
113 if len(testFile.Data) > 0 {
114 docs := bsonutil.RawToInterfaces(testFile.Data...)
115 _, err := mt.Coll.InsertMany(context.Background(), docs)
116 assert.Nil(mt, err, "InsertMany error: %v", err)
117 }
118
119 runCrudOperation(mt, test.Description, test.Operation, test.Outcome)
120 }
121
122 func verifyCrudError(mt *mtest.T, outcome crudOutcome, err error) {
123 opError := errorFromResult(mt, outcome.Result)
124 if opError == nil {
125 return
126 }
127 verificationErr := verifyError(opError, err)
128 assert.Nil(mt, verificationErr, "error mismatch: %v", verificationErr)
129 }
130
131
132
133 func runCrudOperation(mt *mtest.T, testDescription string, operation crudOperation, outcome crudOutcome) {
134 switch operation.Name {
135 case "aggregate":
136 cursor, err := executeAggregate(mt, mt.Coll, nil, operation.Arguments)
137 if outcome.Error {
138 assert.NotNil(mt, err, "expected Aggregate error, got nil")
139 verifyCrudError(mt, outcome, err)
140 break
141 }
142 assert.Nil(mt, err, "Aggregate error: %v", err)
143
144 if !strings.Contains(testDescription, "$out") {
145 verifyCursorResult(mt, cursor, outcome.Result)
146 }
147 case "bulkWrite":
148 res, err := executeBulkWrite(mt, nil, operation.Arguments)
149 if outcome.Error {
150 assert.NotNil(mt, err, "expected BulkWrite error, got nil")
151 verifyCrudError(mt, outcome, err)
152 break
153 }
154 assert.Nil(mt, err, "BulkWrite error: %v", err)
155 verifyBulkWriteResult(mt, res, outcome.Result)
156 case "count":
157 res, err := executeCountDocuments(mt, nil, operation.Arguments)
158 if outcome.Error {
159 assert.NotNil(mt, err, "expected CountDocuments error, got nil")
160 verifyCrudError(mt, outcome, err)
161 break
162 }
163 assert.Nil(mt, err, "CountDocuments error: %v", err)
164 verifyCountResult(mt, res, outcome.Result)
165 case "distinct":
166 res, err := executeDistinct(mt, nil, operation.Arguments)
167 if outcome.Error {
168 assert.NotNil(mt, err, "expected Distinct error, got nil")
169 verifyCrudError(mt, outcome, err)
170 break
171 }
172 assert.Nil(mt, err, "Distinct error: %v", err)
173 verifyDistinctResult(mt, res, outcome.Result)
174 case "find":
175 cursor, err := executeFind(mt, nil, operation.Arguments)
176 if outcome.Error {
177 assert.NotNil(mt, err, "expected Find error, got nil")
178 verifyCrudError(mt, outcome, err)
179 break
180 }
181 assert.Nil(mt, err, "Find error: %v", err)
182 verifyCursorResult(mt, cursor, outcome.Result)
183 case "deleteOne":
184 res, err := executeDeleteOne(mt, nil, operation.Arguments)
185 if outcome.Error {
186 assert.NotNil(mt, err, "expected DeleteOne error, got nil")
187 verifyCrudError(mt, outcome, err)
188 break
189 }
190 assert.Nil(mt, err, "DeleteOne error: %v", err)
191 verifyDeleteResult(mt, res, outcome.Result)
192 case "deleteMany":
193 res, err := executeDeleteMany(mt, nil, operation.Arguments)
194 if outcome.Error {
195 assert.NotNil(mt, err, "expected DeleteMany error, got nil")
196 verifyCrudError(mt, outcome, err)
197 break
198 }
199 assert.Nil(mt, err, "DeleteMany error: %v", err)
200 verifyDeleteResult(mt, res, outcome.Result)
201 case "findOneAndDelete":
202 res := executeFindOneAndDelete(mt, nil, operation.Arguments)
203 err := res.Err()
204 if outcome.Error {
205 assert.NotNil(mt, err, "expected FindOneAndDelete error, got nil")
206 verifyCrudError(mt, outcome, err)
207 break
208 }
209 if outcome.Result == nil {
210 assert.Equal(mt, mongo.ErrNoDocuments, err, "expected error %v, got %v", mongo.ErrNoDocuments, err)
211 break
212 }
213 assert.Nil(mt, err, "FindOneAndDelete error: %v", err)
214 verifySingleResult(mt, res, outcome.Result)
215 case "findOneAndReplace":
216 res := executeFindOneAndReplace(mt, nil, operation.Arguments)
217 err := res.Err()
218 if outcome.Error {
219 assert.NotNil(mt, err, "expected FindOneAndReplace error, got nil")
220 verifyCrudError(mt, outcome, err)
221 break
222 }
223 if outcome.Result == nil {
224 assert.Equal(mt, mongo.ErrNoDocuments, err, "expected error %v, got %v", mongo.ErrNoDocuments, err)
225 break
226 }
227 assert.Nil(mt, err, "FindOneAndReplace error: %v", err)
228 verifySingleResult(mt, res, outcome.Result)
229 case "findOneAndUpdate":
230 res := executeFindOneAndUpdate(mt, nil, operation.Arguments)
231 err := res.Err()
232 if outcome.Error {
233 assert.NotNil(mt, err, "expected FindOneAndUpdate error, got nil")
234 verifyCrudError(mt, outcome, err)
235 break
236 }
237 if outcome.Result == nil {
238 assert.Equal(mt, mongo.ErrNoDocuments, err, "expected error %v, got %v", mongo.ErrNoDocuments, err)
239 break
240 }
241 assert.Nil(mt, err, "FindOneAndUpdate error: %v", err)
242 verifySingleResult(mt, res, outcome.Result)
243 case "insertOne":
244 res, err := executeInsertOne(mt, nil, operation.Arguments)
245 if outcome.Error {
246 assert.NotNil(mt, err, "expected InsertOne error, got nil")
247 verifyCrudError(mt, outcome, err)
248 break
249 }
250 assert.Nil(mt, err, "InsertOne error: %v", err)
251 verifyInsertOneResult(mt, res, outcome.Result)
252 case "insertMany":
253 res, err := executeInsertMany(mt, nil, operation.Arguments)
254 if outcome.Error {
255 assert.NotNil(mt, err, "expected InsertMany error, got nil")
256 verifyCrudError(mt, outcome, err)
257 break
258 }
259 assert.Nil(mt, err, "InsertMany error: %v", err)
260 verifyInsertManyResult(mt, res, outcome.Result)
261 case "replaceOne":
262 res, err := executeReplaceOne(mt, nil, operation.Arguments)
263 if outcome.Error {
264 assert.NotNil(mt, err, "expected ReplaceOne error, got nil")
265 verifyCrudError(mt, outcome, err)
266 break
267 }
268 assert.Nil(mt, err, "ReplaceOne error: %v", err)
269 verifyUpdateResult(mt, res, outcome.Result)
270 case "updateOne":
271 res, err := executeUpdateOne(mt, nil, operation.Arguments)
272 if outcome.Error {
273 assert.NotNil(mt, err, "expected UpdateOne error, got nil")
274 verifyCrudError(mt, outcome, err)
275 break
276 }
277 assert.Nil(mt, err, "UpdateOne error: %v", err)
278 verifyUpdateResult(mt, res, outcome.Result)
279 case "updateMany":
280 res, err := executeUpdateMany(mt, nil, operation.Arguments)
281 if outcome.Error {
282 assert.NotNil(mt, err, "expected UpdateMany error, got nil")
283 verifyCrudError(mt, outcome, err)
284 break
285 }
286 assert.Nil(mt, err, "UpdateMany error: %v", err)
287 verifyUpdateResult(mt, res, outcome.Result)
288 case "estimatedDocumentCount":
289 res, err := executeEstimatedDocumentCount(mt, nil, operation.Arguments)
290 if outcome.Error {
291 assert.NotNil(mt, err, "expected EstimatedDocumentCount error, got nil")
292 verifyCrudError(mt, outcome, err)
293 break
294 }
295 assert.Nil(mt, err, "EstimatedDocumentCount error: %v", err)
296 verifyCountResult(mt, res, outcome.Result)
297 case "countDocuments":
298 res, err := executeCountDocuments(mt, nil, operation.Arguments)
299 if outcome.Error {
300 assert.NotNil(mt, err, "expected CountDocuments error, got nil")
301 verifyCrudError(mt, outcome, err)
302 break
303 }
304 assert.Nil(mt, err, "CountDocuments error: %v", err)
305 verifyCountResult(mt, res, outcome.Result)
306 default:
307 mt.Fatalf("unrecognized operation: %v", operation.Name)
308 }
309
310 if outcome.Collection != nil {
311 verifyTestOutcome(mt, outcome.Collection)
312 }
313 }
314
View as plain text