// Copyright (C) MongoDB, Inc. 2017-present. // // Licensed under the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 package unified import ( "bytes" "context" "fmt" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/internal/bsonutil" "go.mongodb.org/mongo-driver/mongo/integration/mtest" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/readconcern" "go.mongodb.org/mongo-driver/mongo/readpref" ) type collectionData struct { DatabaseName string `bson:"databaseName"` CollectionName string `bson:"collectionName"` Documents []bson.Raw `bson:"documents"` Options *createOptions `bson:"createOptions"` } type createOptions struct { Capped *bool `bson:"capped"` SizeInBytes *int64 `bson:"size"` } // createCollection configures the collection represented by the receiver using the internal client. This function // first drops the collection and then creates it with specified options (if any) and inserts the seed data if needed. func (c *collectionData) createCollection(ctx context.Context) error { db := mtest.GlobalClient().Database(c.DatabaseName, options.Database().SetWriteConcern(mtest.MajorityWc)) coll := db.Collection(c.CollectionName) if err := coll.Drop(ctx); err != nil { return fmt.Errorf("error dropping collection: %w", err) } // Explicitly create collection if Options are specified. if c.Options != nil { createOpts := options.CreateCollection() if c.Options.Capped != nil { createOpts = createOpts.SetCapped(*c.Options.Capped) } if c.Options.SizeInBytes != nil { createOpts = createOpts.SetSizeInBytes(*c.Options.SizeInBytes) } if err := db.CreateCollection(ctx, c.CollectionName, createOpts); err != nil { return fmt.Errorf("error creating collection: %w", err) } } // If neither documents nor options are provided, still create the collection with write concern "majority". if len(c.Documents) == 0 && c.Options == nil { // The write concern has to be manually specified in the command document because RunCommand does not honor // the database's write concern. create := bson.D{ {"create", coll.Name()}, {"writeConcern", bson.D{ {"w", "majority"}, }}, } if err := db.RunCommand(ctx, create).Err(); err != nil { return fmt.Errorf("error creating collection: %w", err) } return nil } docs := bsonutil.RawToInterfaces(c.Documents...) if _, err := coll.InsertMany(ctx, docs); err != nil { return fmt.Errorf("error inserting data: %w", err) } return nil } // verifyContents asserts that the collection on the server represented by this collectionData instance contains the // expected documents. func (c *collectionData) verifyContents(ctx context.Context) error { collOpts := options.Collection(). SetReadPreference(readpref.Primary()). SetReadConcern(readconcern.Local()) coll := mtest.GlobalClient().Database(c.DatabaseName).Collection(c.CollectionName, collOpts) cursor, err := coll.Find(ctx, bson.D{}, options.Find().SetSort(bson.M{"_id": 1})) if err != nil { return fmt.Errorf("Find error: %w", err) } defer cursor.Close(ctx) var docs []bson.Raw if err := cursor.All(ctx, &docs); err != nil { return fmt.Errorf("cursor iteration error: %w", err) } // Verify the slice lengths are equal. This also covers the case of asserting that the collection is empty if // c.Documents is an empty slice. if len(c.Documents) != len(docs) { return fmt.Errorf("expected %d documents but found %d: %v", len(c.Documents), len(docs), docs) } // We can't use verifyValuesMatch here because the rules for evaluating matches (e.g. flexible numeric comparisons // and special $$ operators) do not apply when verifying collection outcomes. We have to permit variations in key // order, though, so we sort documents before doing a byte-wise comparison. for idx, expected := range c.Documents { expected = sortDocument(expected) actual := sortDocument(docs[idx]) if !bytes.Equal(expected, actual) { return fmt.Errorf("document comparison error at index %d: expected %s, got %s", idx, expected, actual) } } return nil } func (c *collectionData) namespace() string { return fmt.Sprintf("%s.%s", c.DatabaseName, c.CollectionName) }