...

Source file src/go.mongodb.org/mongo-driver/mongo/integration/crud_spec_test.go

Documentation: go.mongodb.org/mongo-driver/mongo/integration

     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  
     7  package integration
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"path"
    15  	"reflect"
    16  	"strings"
    17  	"testing"
    18  
    19  	"go.mongodb.org/mongo-driver/bson"
    20  	"go.mongodb.org/mongo-driver/internal/assert"
    21  	"go.mongodb.org/mongo-driver/internal/bsonutil"
    22  	"go.mongodb.org/mongo-driver/mongo"
    23  	"go.mongodb.org/mongo-driver/mongo/integration/mtest"
    24  )
    25  
    26  const (
    27  	crudTestsDir = "../../testdata/crud"
    28  	crudReadDir  = "v1/read"
    29  	crudWriteDir = "v1/write"
    30  )
    31  
    32  type crudTestFile struct {
    33  	Data             []bson.Raw `bson:"data"`
    34  	MinServerVersion string     `bson:"minServerVersion"`
    35  	MaxServerVersion string     `bson:"maxServerVersion"`
    36  	Serverless       string     `bson:"serverless"`
    37  	Tests            []crudTest `bson:"tests"`
    38  }
    39  
    40  type crudTest struct {
    41  	Description string        `bson:"description"`
    42  	SkipReason  string        `bson:"skipReason"`
    43  	Operation   crudOperation `bson:"operation"`
    44  	Outcome     crudOutcome   `bson:"outcome"`
    45  }
    46  
    47  type crudOperation struct {
    48  	Name      string   `bson:"name"`
    49  	Arguments bson.Raw `bson:"arguments"`
    50  }
    51  
    52  type crudOutcome struct {
    53  	Error      bool               `bson:"error"` // only used by retryable writes tests
    54  	Result     interface{}        `bson:"result"`
    55  	Collection *outcomeCollection `bson:"collection"`
    56  }
    57  
    58  var crudRegistry = bson.NewRegistryBuilder().
    59  	RegisterTypeMapEntry(bson.TypeEmbeddedDocument, reflect.TypeOf(bson.Raw{})).Build()
    60  
    61  func TestCrudSpec(t *testing.T) {
    62  	for _, dir := range []string{crudReadDir, crudWriteDir} {
    63  		for _, file := range jsonFilesInDir(t, path.Join(crudTestsDir, dir)) {
    64  			t.Run(file, func(t *testing.T) {
    65  				runCrudFile(t, path.Join(crudTestsDir, dir, file))
    66  			})
    67  		}
    68  	}
    69  }
    70  
    71  func verifyServerlessConstraint(mt *mtest.T, expected string) error {
    72  	serverless := os.Getenv("SERVERLESS") == "serverless"
    73  
    74  	switch expected {
    75  	case "require":
    76  		if !serverless {
    77  			return fmt.Errorf("test requires serverless")
    78  		}
    79  	case "forbid":
    80  		if serverless {
    81  			return fmt.Errorf("test forbids serverless")
    82  		}
    83  	case "allow", "":
    84  	default:
    85  		mt.Fatalf("invalid value for serverless: %s", expected)
    86  	}
    87  	return nil
    88  }
    89  
    90  func runCrudFile(t *testing.T, file string) {
    91  	content, err := ioutil.ReadFile(file)
    92  	assert.Nil(t, err, "ReadFile error for %v: %v", file, err)
    93  
    94  	var testFile crudTestFile
    95  	err = bson.UnmarshalExtJSONWithRegistry(crudRegistry, content, false, &testFile)
    96  	assert.Nil(t, err, "UnmarshalExtJSONWithRegistry error: %v", err)
    97  
    98  	mt := mtest.New(t, mtest.NewOptions().MinServerVersion(testFile.MinServerVersion).MaxServerVersion(testFile.MaxServerVersion))
    99  
   100  	// Skip test if serverless requirements are not met.
   101  	if err = verifyServerlessConstraint(mt, testFile.Serverless); err != nil {
   102  		mt.Skipf("%v", err)
   103  	}
   104  
   105  	for _, test := range testFile.Tests {
   106  		mt.Run(test.Description, func(mt *mtest.T) {
   107  			runCrudTest(mt, test, testFile)
   108  		})
   109  	}
   110  }
   111  
   112  func runCrudTest(mt *mtest.T, test crudTest, testFile crudTestFile) {
   113  	if len(testFile.Data) > 0 {
   114  		docs := bsonutil.RawToInterfaces(testFile.Data...)
   115  		_, err := mt.Coll.InsertMany(context.Background(), docs)
   116  		assert.Nil(mt, err, "InsertMany error: %v", err)
   117  	}
   118  
   119  	runCrudOperation(mt, test.Description, test.Operation, test.Outcome)
   120  }
   121  
   122  func verifyCrudError(mt *mtest.T, outcome crudOutcome, err error) {
   123  	opError := errorFromResult(mt, outcome.Result)
   124  	if opError == nil {
   125  		return
   126  	}
   127  	verificationErr := verifyError(opError, err)
   128  	assert.Nil(mt, verificationErr, "error mismatch: %v", verificationErr)
   129  }
   130  
   131  // run a CRUD operation and verify errors and outcomes.
   132  // the test description is needed to see determine if the test is an aggregate with $out
   133  func runCrudOperation(mt *mtest.T, testDescription string, operation crudOperation, outcome crudOutcome) {
   134  	switch operation.Name {
   135  	case "aggregate":
   136  		cursor, err := executeAggregate(mt, mt.Coll, nil, operation.Arguments)
   137  		if outcome.Error {
   138  			assert.NotNil(mt, err, "expected Aggregate error, got nil")
   139  			verifyCrudError(mt, outcome, err)
   140  			break
   141  		}
   142  		assert.Nil(mt, err, "Aggregate error: %v", err)
   143  		// only verify cursor contents for pipelines without $out
   144  		if !strings.Contains(testDescription, "$out") {
   145  			verifyCursorResult(mt, cursor, outcome.Result)
   146  		}
   147  	case "bulkWrite":
   148  		res, err := executeBulkWrite(mt, nil, operation.Arguments)
   149  		if outcome.Error {
   150  			assert.NotNil(mt, err, "expected BulkWrite error, got nil")
   151  			verifyCrudError(mt, outcome, err)
   152  			break
   153  		}
   154  		assert.Nil(mt, err, "BulkWrite error: %v", err)
   155  		verifyBulkWriteResult(mt, res, outcome.Result)
   156  	case "count":
   157  		res, err := executeCountDocuments(mt, nil, operation.Arguments)
   158  		if outcome.Error {
   159  			assert.NotNil(mt, err, "expected CountDocuments error, got nil")
   160  			verifyCrudError(mt, outcome, err)
   161  			break
   162  		}
   163  		assert.Nil(mt, err, "CountDocuments error: %v", err)
   164  		verifyCountResult(mt, res, outcome.Result)
   165  	case "distinct":
   166  		res, err := executeDistinct(mt, nil, operation.Arguments)
   167  		if outcome.Error {
   168  			assert.NotNil(mt, err, "expected Distinct error, got nil")
   169  			verifyCrudError(mt, outcome, err)
   170  			break
   171  		}
   172  		assert.Nil(mt, err, "Distinct error: %v", err)
   173  		verifyDistinctResult(mt, res, outcome.Result)
   174  	case "find":
   175  		cursor, err := executeFind(mt, nil, operation.Arguments)
   176  		if outcome.Error {
   177  			assert.NotNil(mt, err, "expected Find error, got nil")
   178  			verifyCrudError(mt, outcome, err)
   179  			break
   180  		}
   181  		assert.Nil(mt, err, "Find error: %v", err)
   182  		verifyCursorResult(mt, cursor, outcome.Result)
   183  	case "deleteOne":
   184  		res, err := executeDeleteOne(mt, nil, operation.Arguments)
   185  		if outcome.Error {
   186  			assert.NotNil(mt, err, "expected DeleteOne error, got nil")
   187  			verifyCrudError(mt, outcome, err)
   188  			break
   189  		}
   190  		assert.Nil(mt, err, "DeleteOne error: %v", err)
   191  		verifyDeleteResult(mt, res, outcome.Result)
   192  	case "deleteMany":
   193  		res, err := executeDeleteMany(mt, nil, operation.Arguments)
   194  		if outcome.Error {
   195  			assert.NotNil(mt, err, "expected DeleteMany error, got nil")
   196  			verifyCrudError(mt, outcome, err)
   197  			break
   198  		}
   199  		assert.Nil(mt, err, "DeleteMany error: %v", err)
   200  		verifyDeleteResult(mt, res, outcome.Result)
   201  	case "findOneAndDelete":
   202  		res := executeFindOneAndDelete(mt, nil, operation.Arguments)
   203  		err := res.Err()
   204  		if outcome.Error {
   205  			assert.NotNil(mt, err, "expected FindOneAndDelete error, got nil")
   206  			verifyCrudError(mt, outcome, err)
   207  			break
   208  		}
   209  		if outcome.Result == nil {
   210  			assert.Equal(mt, mongo.ErrNoDocuments, err, "expected error %v, got %v", mongo.ErrNoDocuments, err)
   211  			break
   212  		}
   213  		assert.Nil(mt, err, "FindOneAndDelete error: %v", err)
   214  		verifySingleResult(mt, res, outcome.Result)
   215  	case "findOneAndReplace":
   216  		res := executeFindOneAndReplace(mt, nil, operation.Arguments)
   217  		err := res.Err()
   218  		if outcome.Error {
   219  			assert.NotNil(mt, err, "expected FindOneAndReplace error, got nil")
   220  			verifyCrudError(mt, outcome, err)
   221  			break
   222  		}
   223  		if outcome.Result == nil {
   224  			assert.Equal(mt, mongo.ErrNoDocuments, err, "expected error %v, got %v", mongo.ErrNoDocuments, err)
   225  			break
   226  		}
   227  		assert.Nil(mt, err, "FindOneAndReplace error: %v", err)
   228  		verifySingleResult(mt, res, outcome.Result)
   229  	case "findOneAndUpdate":
   230  		res := executeFindOneAndUpdate(mt, nil, operation.Arguments)
   231  		err := res.Err()
   232  		if outcome.Error {
   233  			assert.NotNil(mt, err, "expected FindOneAndUpdate error, got nil")
   234  			verifyCrudError(mt, outcome, err)
   235  			break
   236  		}
   237  		if outcome.Result == nil {
   238  			assert.Equal(mt, mongo.ErrNoDocuments, err, "expected error %v, got %v", mongo.ErrNoDocuments, err)
   239  			break
   240  		}
   241  		assert.Nil(mt, err, "FindOneAndUpdate error: %v", err)
   242  		verifySingleResult(mt, res, outcome.Result)
   243  	case "insertOne":
   244  		res, err := executeInsertOne(mt, nil, operation.Arguments)
   245  		if outcome.Error {
   246  			assert.NotNil(mt, err, "expected InsertOne error, got nil")
   247  			verifyCrudError(mt, outcome, err)
   248  			break
   249  		}
   250  		assert.Nil(mt, err, "InsertOne error: %v", err)
   251  		verifyInsertOneResult(mt, res, outcome.Result)
   252  	case "insertMany":
   253  		res, err := executeInsertMany(mt, nil, operation.Arguments)
   254  		if outcome.Error {
   255  			assert.NotNil(mt, err, "expected InsertMany error, got nil")
   256  			verifyCrudError(mt, outcome, err)
   257  			break
   258  		}
   259  		assert.Nil(mt, err, "InsertMany error: %v", err)
   260  		verifyInsertManyResult(mt, res, outcome.Result)
   261  	case "replaceOne":
   262  		res, err := executeReplaceOne(mt, nil, operation.Arguments)
   263  		if outcome.Error {
   264  			assert.NotNil(mt, err, "expected ReplaceOne error, got nil")
   265  			verifyCrudError(mt, outcome, err)
   266  			break
   267  		}
   268  		assert.Nil(mt, err, "ReplaceOne error: %v", err)
   269  		verifyUpdateResult(mt, res, outcome.Result)
   270  	case "updateOne":
   271  		res, err := executeUpdateOne(mt, nil, operation.Arguments)
   272  		if outcome.Error {
   273  			assert.NotNil(mt, err, "expected UpdateOne error, got nil")
   274  			verifyCrudError(mt, outcome, err)
   275  			break
   276  		}
   277  		assert.Nil(mt, err, "UpdateOne error: %v", err)
   278  		verifyUpdateResult(mt, res, outcome.Result)
   279  	case "updateMany":
   280  		res, err := executeUpdateMany(mt, nil, operation.Arguments)
   281  		if outcome.Error {
   282  			assert.NotNil(mt, err, "expected UpdateMany error, got nil")
   283  			verifyCrudError(mt, outcome, err)
   284  			break
   285  		}
   286  		assert.Nil(mt, err, "UpdateMany error: %v", err)
   287  		verifyUpdateResult(mt, res, outcome.Result)
   288  	case "estimatedDocumentCount":
   289  		res, err := executeEstimatedDocumentCount(mt, nil, operation.Arguments)
   290  		if outcome.Error {
   291  			assert.NotNil(mt, err, "expected EstimatedDocumentCount error, got nil")
   292  			verifyCrudError(mt, outcome, err)
   293  			break
   294  		}
   295  		assert.Nil(mt, err, "EstimatedDocumentCount error: %v", err)
   296  		verifyCountResult(mt, res, outcome.Result)
   297  	case "countDocuments":
   298  		res, err := executeCountDocuments(mt, nil, operation.Arguments)
   299  		if outcome.Error {
   300  			assert.NotNil(mt, err, "expected CountDocuments error, got nil")
   301  			verifyCrudError(mt, outcome, err)
   302  			break
   303  		}
   304  		assert.Nil(mt, err, "CountDocuments error: %v", err)
   305  		verifyCountResult(mt, res, outcome.Result)
   306  	default:
   307  		mt.Fatalf("unrecognized operation: %v", operation.Name)
   308  	}
   309  
   310  	if outcome.Collection != nil {
   311  		verifyTestOutcome(mt, outcome.Collection)
   312  	}
   313  }
   314  

View as plain text