File: collection_data.go

package info (click to toggle)
golang-mongodb-mongo-driver 1.17.1%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 25,988 kB
  • sloc: perl: 533; ansic: 491; python: 432; sh: 327; makefile: 174
file content (122 lines) | stat: -rw-r--r-- 4,407 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// 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)
}