github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/db/db_utils_test.go (about) 1 package db 2 3 import ( 4 "io/ioutil" 5 "strings" 6 "testing" 7 8 "github.com/evergreen-ci/evergreen/testutil" 9 . "github.com/smartystreets/goconvey/convey" 10 "gopkg.in/mgo.v2" 11 "gopkg.in/mgo.v2/bson" 12 ) 13 14 var dbUtilsTestConf = testutil.TestConfig() 15 16 func TestDBUtils(t *testing.T) { 17 18 type insertableStruct struct { 19 FieldOne string `bson:"field_one"` 20 FieldTwo int `bson:"field_two"` 21 FieldThree string `bson:"field_three"` 22 } 23 24 SetGlobalSessionProvider(SessionFactoryFromConfig(dbUtilsTestConf)) 25 26 collection := "test_collection" 27 28 Convey("With a db and collection", t, func() { 29 30 So(Clear(collection), ShouldBeNil) 31 32 Convey("inserting an item into the collection should update the"+ 33 " database accordingly", func() { 34 35 in := &insertableStruct{ 36 FieldOne: "1", 37 FieldTwo: 1, 38 } 39 40 So(Insert(collection, in), ShouldBeNil) 41 42 out := &insertableStruct{} 43 err := FindOne( 44 collection, 45 bson.M{}, 46 NoProjection, 47 NoSort, 48 out, 49 ) 50 So(err, ShouldBeNil) 51 So(out, ShouldResemble, in) 52 53 }) 54 55 Convey("clearing a collection should remove all items from the"+ 56 " collection", func() { 57 58 in := &insertableStruct{ 59 FieldOne: "1", 60 FieldTwo: 1, 61 } 62 63 inTwo := &insertableStruct{ 64 FieldOne: "2", 65 FieldTwo: 2, 66 } 67 68 // insert, make sure both were inserted 69 So(Insert(collection, in), ShouldBeNil) 70 So(Insert(collection, inTwo), ShouldBeNil) 71 count, err := Count(collection, bson.M{}) 72 So(err, ShouldBeNil) 73 So(count, ShouldEqual, 2) 74 75 // clear and validate the collection is empty 76 So(Clear(collection), ShouldBeNil) 77 count, err = Count(collection, bson.M{}) 78 So(err, ShouldBeNil) 79 So(count, ShouldEqual, 0) 80 81 }) 82 83 Convey("after writing a gridfs file, reading it back should match the contents", func() { 84 85 So(Clear("testfiles.chunks"), ShouldBeNil) 86 So(Clear("testfiles.files"), ShouldBeNil) 87 id := bson.NewObjectId().Hex() 88 So(WriteGridFile("testfiles", id, strings.NewReader(id)), ShouldBeNil) 89 file, err := GetGridFile("testfiles", id) 90 So(err, ShouldBeNil) 91 raw, err := ioutil.ReadAll(file) 92 So(err, ShouldBeNil) 93 So(string(raw), ShouldEqual, id) 94 }) 95 96 Convey("removing an item from a collection should remove it and leave"+ 97 " the rest of the collection untouched", func() { 98 99 in := &insertableStruct{ 100 FieldOne: "1", 101 FieldTwo: 1, 102 } 103 104 inTwo := &insertableStruct{ 105 FieldOne: "2", 106 FieldTwo: 2, 107 } 108 109 // insert, make sure both were inserted 110 So(Insert(collection, in), ShouldBeNil) 111 So(Insert(collection, inTwo), ShouldBeNil) 112 count, err := Count(collection, bson.M{}) 113 So(err, ShouldBeNil) 114 So(count, ShouldEqual, 2) 115 116 // remove just the first 117 So(Remove(collection, bson.M{"field_one": "1"}), 118 ShouldBeNil) 119 count, err = Count(collection, bson.M{}) 120 So(err, ShouldBeNil) 121 So(count, ShouldEqual, 1) 122 123 out := &insertableStruct{} 124 err = FindOne( 125 collection, 126 bson.M{}, 127 NoProjection, 128 NoSort, 129 out, 130 ) 131 So(err, ShouldBeNil) 132 So(out, ShouldResemble, inTwo) 133 134 }) 135 136 Convey("removing multiple items from a collection should only remove"+ 137 " the matching ones", func() { 138 139 in := &insertableStruct{ 140 FieldOne: "1", 141 FieldTwo: 1, 142 } 143 144 inTwo := &insertableStruct{ 145 FieldOne: "2", 146 FieldTwo: 2, 147 } 148 149 inThree := &insertableStruct{ 150 FieldOne: "1", 151 FieldTwo: 2, 152 } 153 154 // insert, make sure all were inserted 155 So(Insert(collection, in), ShouldBeNil) 156 So(Insert(collection, inTwo), ShouldBeNil) 157 So(Insert(collection, inThree), ShouldBeNil) 158 count, err := Count(collection, bson.M{}) 159 So(err, ShouldBeNil) 160 So(count, ShouldEqual, 3) 161 162 // remove just the first 163 So(RemoveAll(collection, bson.M{"field_one": "1"}), 164 ShouldBeNil) 165 count, err = Count(collection, bson.M{}) 166 So(err, ShouldBeNil) 167 So(count, ShouldEqual, 1) 168 169 out := &insertableStruct{} 170 err = FindOne( 171 collection, 172 bson.M{}, 173 NoProjection, 174 NoSort, 175 out, 176 ) 177 So(err, ShouldBeNil) 178 So(out, ShouldResemble, inTwo) 179 }) 180 181 Convey("finding all matching items should use the correct filter, and"+ 182 " should respect the projection, sort, skip, and limit passed"+ 183 " in", func() { 184 185 in := &insertableStruct{ 186 FieldOne: "1", 187 FieldTwo: 1, 188 FieldThree: "x", 189 } 190 191 inTwo := &insertableStruct{ 192 FieldOne: "2", 193 FieldTwo: 1, 194 FieldThree: "y", 195 } 196 197 inThree := &insertableStruct{ 198 FieldOne: "3", 199 FieldTwo: 1, 200 FieldThree: "z", 201 } 202 203 inFour := &insertableStruct{ 204 FieldOne: "4", 205 FieldTwo: 2, 206 FieldThree: "z", 207 } 208 209 // insert, make sure all were inserted 210 So(Insert(collection, in), ShouldBeNil) 211 So(Insert(collection, inTwo), ShouldBeNil) 212 So(Insert(collection, inThree), ShouldBeNil) 213 So(Insert(collection, inFour), ShouldBeNil) 214 count, err := Count(collection, bson.M{}) 215 So(err, ShouldBeNil) 216 So(count, ShouldEqual, 4) 217 218 // run a find that should only match the first three, should not 219 // project field_three, should sort backwards on field_one, skip 220 // one and limit to one (meaning only the second struct should be 221 // returned) 222 out := []insertableStruct{} 223 err = FindAll( 224 collection, 225 bson.M{ 226 "field_two": 1, 227 }, 228 bson.M{ 229 "field_three": 0, 230 }, 231 []string{"-field_one"}, 232 1, 233 1, 234 &out, 235 ) 236 So(err, ShouldBeNil) 237 So(len(out), ShouldEqual, 1) 238 So(out[0].FieldOne, ShouldEqual, "2") 239 So(out[0].FieldThree, ShouldEqual, "") // not projected 240 241 }) 242 243 Convey("updating one item in a collection should apply the correct"+ 244 " update", func() { 245 246 in := &insertableStruct{ 247 FieldOne: "1", 248 FieldTwo: 1, 249 } 250 251 inTwo := &insertableStruct{ 252 FieldOne: "2", 253 FieldTwo: 2, 254 } 255 256 // insert, make sure both were inserted 257 So(Insert(collection, in), ShouldBeNil) 258 So(Insert(collection, inTwo), ShouldBeNil) 259 count, err := Count(collection, bson.M{}) 260 So(err, ShouldBeNil) 261 So(count, ShouldEqual, 2) 262 263 // update the second 264 err = Update( 265 collection, 266 bson.M{ 267 "field_one": "2", 268 }, 269 bson.M{ 270 "$set": bson.M{ 271 "field_two": 3, 272 }, 273 }, 274 ) 275 So(err, ShouldBeNil) 276 277 out := &insertableStruct{} 278 err = FindOne( 279 collection, 280 bson.M{ 281 "field_one": "2", 282 }, 283 NoProjection, 284 NoSort, 285 out, 286 ) 287 So(err, ShouldBeNil) 288 So(out.FieldTwo, ShouldEqual, 3) 289 290 }) 291 292 Convey("updating multiple items in a collection should update all of"+ 293 " the matched ones, and no others", func() { 294 295 in := &insertableStruct{ 296 FieldOne: "1", 297 FieldTwo: 1, 298 } 299 300 inTwo := &insertableStruct{ 301 FieldOne: "2", 302 FieldTwo: 2, 303 } 304 305 inThree := &insertableStruct{ 306 FieldOne: "1", 307 FieldTwo: 2, 308 } 309 310 // insert, make sure all were inserted 311 So(Insert(collection, in), ShouldBeNil) 312 So(Insert(collection, inTwo), ShouldBeNil) 313 So(Insert(collection, inThree), ShouldBeNil) 314 count, err := Count(collection, bson.M{}) 315 So(err, ShouldBeNil) 316 So(count, ShouldEqual, 3) 317 318 // update the first and third 319 _, err = UpdateAll( 320 collection, 321 bson.M{ 322 "field_one": "1", 323 }, 324 bson.M{ 325 "$set": bson.M{ 326 "field_two": 3, 327 }, 328 }, 329 ) 330 So(err, ShouldBeNil) 331 332 out := []insertableStruct{} 333 err = FindAll( 334 collection, 335 bson.M{ 336 "field_two": 3, 337 }, 338 NoProjection, 339 NoSort, 340 NoSkip, 341 NoLimit, 342 &out, 343 ) 344 So(err, ShouldBeNil) 345 So(len(out), ShouldEqual, 2) 346 347 }) 348 349 Convey("when upserting an item into the collection", func() { 350 351 Convey("if the item does not exist, it should be inserted", func() { 352 353 in := &insertableStruct{ 354 FieldOne: "1", 355 FieldTwo: 1, 356 } 357 358 _, err := Upsert( 359 collection, 360 bson.M{ 361 "field_one": in.FieldOne, 362 }, 363 bson.M{ 364 "$set": bson.M{ 365 "field_two": in.FieldTwo, 366 }, 367 }, 368 ) 369 So(err, ShouldBeNil) 370 371 out := &insertableStruct{} 372 err = FindOne( 373 collection, 374 bson.M{}, 375 NoProjection, 376 NoSort, 377 out, 378 ) 379 So(err, ShouldBeNil) 380 So(out, ShouldResemble, in) 381 382 }) 383 384 Convey("if the item already exists, it should be updated", func() { 385 386 in := &insertableStruct{ 387 FieldOne: "1", 388 FieldTwo: 1, 389 } 390 391 So(Insert(collection, in), ShouldBeNil) 392 in.FieldTwo = 2 393 394 _, err := Upsert( 395 collection, 396 bson.M{ 397 "field_one": in.FieldOne, 398 }, 399 bson.M{ 400 "$set": bson.M{ 401 "field_two": in.FieldTwo, 402 }, 403 }, 404 ) 405 So(err, ShouldBeNil) 406 407 out := &insertableStruct{} 408 err = FindOne( 409 collection, 410 bson.M{}, 411 NoProjection, 412 NoSort, 413 out, 414 ) 415 So(err, ShouldBeNil) 416 So(out, ShouldResemble, in) 417 }) 418 419 }) 420 421 Convey("finding and modifying in a collection should run the specified"+ 422 " find and modify", func() { 423 424 in := &insertableStruct{ 425 FieldOne: "1", 426 FieldTwo: 1, 427 } 428 429 So(Insert(collection, in), ShouldBeNil) 430 in.FieldTwo = 2 431 432 change := mgo.Change{ 433 Update: bson.M{ 434 "$set": bson.M{ 435 "field_two": in.FieldTwo, 436 }, 437 }, 438 ReturnNew: true, 439 } 440 441 out := &insertableStruct{} 442 cInfo, err := FindAndModify( 443 collection, 444 bson.M{ 445 "field_one": in.FieldOne, 446 }, 447 nil, 448 change, 449 out, 450 ) 451 So(err, ShouldBeNil) 452 So(cInfo.Updated, ShouldEqual, 1) 453 454 }) 455 456 Convey("a simple aggregation command should run successfully", func() { 457 458 in := &insertableStruct{ 459 FieldOne: "1", 460 FieldTwo: 1, 461 } 462 inTwo := &insertableStruct{ 463 FieldOne: "2", 464 FieldTwo: 2, 465 } 466 inThree := &insertableStruct{ 467 FieldOne: "2", 468 FieldTwo: 3, 469 } 470 So(Insert(collection, in), ShouldBeNil) 471 So(Insert(collection, inTwo), ShouldBeNil) 472 So(Insert(collection, inThree), ShouldBeNil) 473 474 testPipeline := []bson.M{ 475 {"$group": bson.M{ 476 "_id": "$field_one", 477 "total": bson.M{"$sum": "$field_two"}}}, 478 {"$sort": bson.M{"total": -1}}, 479 } 480 481 output := []bson.M{} 482 err := Aggregate(collection, testPipeline, &output) 483 So(err, ShouldBeNil) 484 So(len(output), ShouldEqual, 2) 485 So(output[0]["total"], ShouldEqual, 5) 486 So(output[0]["_id"], ShouldEqual, "2") 487 So(output[1]["total"], ShouldEqual, 1) 488 So(output[1]["_id"], ShouldEqual, "1") 489 490 Convey("and should be able to marshal results to a struct", func() { 491 type ResultStruct struct { 492 Id string `bson:"_id"` 493 TotalSum int `bson:"total"` 494 } 495 output := []ResultStruct{} 496 err := Aggregate(collection, testPipeline, &output) 497 So(err, ShouldBeNil) 498 So(len(output), ShouldEqual, 2) 499 So(output[0], ShouldResemble, ResultStruct{"2", 5}) 500 So(output[1], ShouldResemble, ResultStruct{"1", 1}) 501 }) 502 }) 503 }) 504 }