1// Copyright (C) MongoDB, Inc. 2017-present. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7package unified 8 9import ( 10 "bytes" 11 "context" 12 "fmt" 13 14 "go.mongodb.org/mongo-driver/bson" 15 testhelpers "go.mongodb.org/mongo-driver/internal/testutil/helpers" 16 "go.mongodb.org/mongo-driver/mongo/integration/mtest" 17 "go.mongodb.org/mongo-driver/mongo/options" 18 "go.mongodb.org/mongo-driver/mongo/readconcern" 19 "go.mongodb.org/mongo-driver/mongo/readpref" 20) 21 22type CollectionData struct { 23 DatabaseName string `bson:"databaseName"` 24 CollectionName string `bson:"collectionName"` 25 Documents []bson.Raw `bson:"documents"` 26} 27 28// CreateCollection configures the collection represented by the receiver using the internal client. This function 29// first drops the collection and then creates it and inserts the seed data if needed. 30func (c *CollectionData) CreateCollection(ctx context.Context) error { 31 db := mtest.GlobalClient().Database(c.DatabaseName) 32 coll := db.Collection(c.CollectionName, options.Collection().SetWriteConcern(mtest.MajorityWc)) 33 if err := coll.Drop(ctx); err != nil { 34 return fmt.Errorf("error dropping collection: %v", err) 35 } 36 37 // If no data is given, create the collection with write concern "majority". 38 if len(c.Documents) == 0 { 39 // The write concern has to be manually specified in the command document because RunCommand does not honor 40 // the database's write concern. 41 create := bson.D{ 42 {"create", coll.Name()}, 43 {"writeConcern", bson.D{ 44 {"w", "majority"}, 45 }}, 46 } 47 if err := db.RunCommand(ctx, create).Err(); err != nil { 48 return fmt.Errorf("error creating collection: %v", err) 49 } 50 return nil 51 } 52 53 docs := testhelpers.RawSliceToInterfaceSlice(c.Documents) 54 if _, err := coll.InsertMany(ctx, docs); err != nil { 55 return fmt.Errorf("error inserting data: %v", err) 56 } 57 return nil 58} 59 60// VerifyContents asserts that the collection on the server represented by this CollectionData instance contains the 61// expected documents. 62func (c *CollectionData) VerifyContents(ctx context.Context) error { 63 collOpts := options.Collection(). 64 SetReadPreference(readpref.Primary()). 65 SetReadConcern(readconcern.Local()) 66 coll := mtest.GlobalClient().Database(c.DatabaseName).Collection(c.CollectionName, collOpts) 67 68 cursor, err := coll.Find(ctx, bson.D{}, options.Find().SetSort(bson.M{"_id": 1})) 69 if err != nil { 70 return fmt.Errorf("Find error: %v", err) 71 } 72 defer cursor.Close(ctx) 73 74 var docs []bson.Raw 75 if err := cursor.All(ctx, &docs); err != nil { 76 return fmt.Errorf("cursor iteration error: %v", err) 77 } 78 79 // Verify the slice lengths are equal. This also covers the case of asserting that the collection is empty if 80 // c.Documents is an empty slice. 81 if len(c.Documents) != len(docs) { 82 return fmt.Errorf("expected %d documents but found %d: %v", len(c.Documents), len(docs), docs) 83 } 84 85 // We can't use VerifyValuesMatch here because the rules for evaluating matches (e.g. flexible numeric comparisons 86 // and special $$ operators) do not apply when verifying collection outcomes. We have to permit variations in key 87 // order, though, so we sort documents before doing a byte-wise comparison. 88 for idx, expected := range c.Documents { 89 expected = SortDocument(expected) 90 actual := SortDocument(docs[idx]) 91 92 if !bytes.Equal(expected, actual) { 93 return fmt.Errorf("document comparison error at index %d: expected %s, got %s", idx, expected, actual) 94 } 95 } 96 return nil 97} 98 99func (c *CollectionData) Namespace() string { 100 return fmt.Sprintf("%s.%s", c.DatabaseName, c.CollectionName) 101} 102