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  }