...

Source file src/go.mongodb.org/mongo-driver/internal/integtest/integtest.go

Documentation: go.mongodb.org/mongo-driver/internal/integtest

     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 integtest
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"fmt"
    13  	"math"
    14  	"os"
    15  	"reflect"
    16  	"strconv"
    17  	"strings"
    18  	"sync"
    19  	"testing"
    20  
    21  	"go.mongodb.org/mongo-driver/event"
    22  	"go.mongodb.org/mongo-driver/internal/require"
    23  	"go.mongodb.org/mongo-driver/mongo/description"
    24  	"go.mongodb.org/mongo-driver/mongo/options"
    25  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    26  	"go.mongodb.org/mongo-driver/x/mongo/driver"
    27  	"go.mongodb.org/mongo-driver/x/mongo/driver/connstring"
    28  	"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
    29  	"go.mongodb.org/mongo-driver/x/mongo/driver/topology"
    30  )
    31  
    32  var connectionString *connstring.ConnString
    33  var connectionStringOnce sync.Once
    34  var connectionStringErr error
    35  var liveTopology *topology.Topology
    36  var liveTopologyOnce sync.Once
    37  var liveTopologyErr error
    38  
    39  // AddOptionsToURI appends connection string options to a URI.
    40  func AddOptionsToURI(uri string, opts ...string) string {
    41  	if !strings.ContainsRune(uri, '?') {
    42  		if uri[len(uri)-1] != '/' {
    43  			uri += "/"
    44  		}
    45  
    46  		uri += "?"
    47  	} else {
    48  		uri += "&"
    49  	}
    50  
    51  	for _, opt := range opts {
    52  		uri += opt
    53  	}
    54  
    55  	return uri
    56  }
    57  
    58  // AddTLSConfigToURI checks for the environmental variable indicating that the tests are being run
    59  // on an SSL-enabled server, and if so, returns a new URI with the necessary configuration.
    60  func AddTLSConfigToURI(uri string) string {
    61  	caFile := os.Getenv("MONGO_GO_DRIVER_CA_FILE")
    62  	if len(caFile) == 0 {
    63  		return uri
    64  	}
    65  
    66  	return AddOptionsToURI(uri, "ssl=true&sslCertificateAuthorityFile=", caFile)
    67  }
    68  
    69  // AddCompressorToURI checks for the environment variable indicating that the tests are being run with compression
    70  // enabled. If so, it returns a new URI with the necessary configuration
    71  func AddCompressorToURI(uri string) string {
    72  	comp := os.Getenv("MONGO_GO_DRIVER_COMPRESSOR")
    73  	if len(comp) == 0 {
    74  		return uri
    75  	}
    76  
    77  	return AddOptionsToURI(uri, "compressors=", comp)
    78  }
    79  
    80  // AddTestServerAPIVersion adds the latest server API version in a ServerAPIOptions to passed-in opts.
    81  func AddTestServerAPIVersion(opts *options.ClientOptions) {
    82  	if os.Getenv("REQUIRE_API_VERSION") == "true" {
    83  		opts.SetServerAPIOptions(options.ServerAPI(driver.TestServerAPIVersion))
    84  	}
    85  }
    86  
    87  // MonitoredTopology returns a new topology with the command monitor attached
    88  func MonitoredTopology(t *testing.T, dbName string, monitor *event.CommandMonitor) *topology.Topology {
    89  	uri, err := MongoDBURI()
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  	cfg, err := topology.NewConfig(options.Client().ApplyURI(uri).SetMonitor(monitor), nil)
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  
    98  	monitoredTopology, err := topology.New(cfg)
    99  	if err != nil {
   100  		t.Fatal(err)
   101  	} else {
   102  		_ = monitoredTopology.Connect()
   103  
   104  		err = operation.NewCommand(bsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, "dropDatabase", 1))).
   105  			Database(dbName).ServerSelector(description.WriteSelector()).Deployment(monitoredTopology).Execute(context.Background())
   106  
   107  		require.NoError(t, err)
   108  	}
   109  
   110  	return monitoredTopology
   111  }
   112  
   113  // Topology gets the globally configured topology.
   114  func Topology(t *testing.T) *topology.Topology {
   115  	uri, err := MongoDBURI()
   116  	require.NoError(t, err, "error constructing mongodb URI: %v", err)
   117  	cfg, err := topology.NewConfig(options.Client().ApplyURI(uri), nil)
   118  	require.NoError(t, err, "error constructing topology config: %v", err)
   119  
   120  	liveTopologyOnce.Do(func() {
   121  		var err error
   122  		liveTopology, err = topology.New(cfg)
   123  		if err != nil {
   124  			liveTopologyErr = err
   125  		} else {
   126  			_ = liveTopology.Connect()
   127  
   128  			err = operation.NewCommand(bsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, "dropDatabase", 1))).
   129  				Database(DBName(t)).ServerSelector(description.WriteSelector()).Deployment(liveTopology).Execute(context.Background())
   130  			require.NoError(t, err)
   131  		}
   132  	})
   133  
   134  	if liveTopologyErr != nil {
   135  		t.Fatal(liveTopologyErr)
   136  	}
   137  
   138  	return liveTopology
   139  }
   140  
   141  // TopologyWithCredential takes an "options.Credential" object and returns a connected topology.
   142  func TopologyWithCredential(t *testing.T, credential options.Credential) *topology.Topology {
   143  	uri, err := MongoDBURI()
   144  	if err != nil {
   145  		t.Fatalf("error constructing mongodb URI: %v", err)
   146  	}
   147  	cfg, err := topology.NewConfig(options.Client().ApplyURI(uri).SetAuth(credential), nil)
   148  	if err != nil {
   149  		t.Fatalf("error constructing topology config: %v", err)
   150  	}
   151  	topology, err := topology.New(cfg)
   152  	if err != nil {
   153  		t.Fatal("Could not construct topology")
   154  	}
   155  	err = topology.Connect()
   156  	if err != nil {
   157  		t.Fatal("Could not start topology connection")
   158  	}
   159  	return topology
   160  }
   161  
   162  // ColName gets a collection name that should be unique
   163  // to the currently executing test.
   164  func ColName(t *testing.T) string {
   165  	// Get this indirectly to avoid copying a mutex
   166  	v := reflect.Indirect(reflect.ValueOf(t))
   167  	name := v.FieldByName("name")
   168  	return name.String()
   169  }
   170  
   171  // MongoDBURI will construct the MongoDB URI from the MONGODB_URI environment variable for testing. The default host is
   172  // "localhost" and the default port is "27017"
   173  func MongoDBURI() (string, error) {
   174  	uri := os.Getenv("MONGODB_URI")
   175  	if uri == "" {
   176  		uri = "mongodb://localhost:27017"
   177  	}
   178  
   179  	uri = AddTLSConfigToURI(uri)
   180  	uri = AddCompressorToURI(uri)
   181  	uri, err := AddServerlessAuthCredentials(uri)
   182  	return uri, err
   183  }
   184  
   185  // AddServerlessAuthCredentials will attempt to construct the serverless auth credentials for a URI.
   186  func AddServerlessAuthCredentials(uri string) (string, error) {
   187  	if os.Getenv("SERVERLESS") != "serverless" {
   188  		return uri, nil
   189  	}
   190  	user := os.Getenv("SERVERLESS_ATLAS_USER")
   191  	if user == "" {
   192  		return "", fmt.Errorf("serverless expects SERVERLESS_ATLAS_USER to be set")
   193  	}
   194  	password := os.Getenv("SERVERLESS_ATLAS_PASSWORD")
   195  	if password == "" {
   196  		return "", fmt.Errorf("serverless expects SERVERLESS_ATLAS_PASSWORD to be set")
   197  	}
   198  
   199  	var scheme string
   200  	// remove the scheme
   201  	if strings.HasPrefix(uri, "mongodb+srv://") {
   202  		scheme = "mongodb+srv://"
   203  	} else if strings.HasPrefix(uri, "mongodb://") {
   204  		scheme = "mongodb://"
   205  	} else {
   206  		return "", errors.New(`scheme must be "mongodb" or "mongodb+srv"`)
   207  	}
   208  
   209  	uri = scheme + user + ":" + password + "@" + uri[len(scheme):]
   210  	return uri, nil
   211  }
   212  
   213  // ConnString gets the globally configured connection string.
   214  func ConnString(t *testing.T) *connstring.ConnString {
   215  	connectionStringOnce.Do(func() {
   216  		uri, err := MongoDBURI()
   217  		require.NoError(t, err, "error constructing mongodb URI: %v", err)
   218  
   219  		connectionString, err = connstring.ParseAndValidate(uri)
   220  		if err != nil {
   221  			connectionStringErr = err
   222  		}
   223  	})
   224  	if connectionStringErr != nil {
   225  		t.Fatal(connectionStringErr)
   226  	}
   227  
   228  	return connectionString
   229  }
   230  
   231  func GetConnString() (*connstring.ConnString, error) {
   232  	mongodbURI := os.Getenv("MONGODB_URI")
   233  	if mongodbURI == "" {
   234  		mongodbURI = "mongodb://localhost:27017"
   235  	}
   236  
   237  	mongodbURI = AddTLSConfigToURI(mongodbURI)
   238  
   239  	cs, err := connstring.ParseAndValidate(mongodbURI)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	return cs, nil
   245  }
   246  
   247  // DBName gets the globally configured database name.
   248  func DBName(t *testing.T) string {
   249  	return GetDBName(ConnString(t))
   250  }
   251  
   252  func GetDBName(cs *connstring.ConnString) string {
   253  	if cs.Database != "" {
   254  		return cs.Database
   255  	}
   256  
   257  	return fmt.Sprintf("mongo-go-driver-%d", os.Getpid())
   258  }
   259  
   260  // CompareVersions compares two version number strings (i.e. positive integers separated by
   261  // periods). Comparisons are done to the lesser precision of the two versions. For example, 3.2 is
   262  // considered equal to 3.2.11, whereas 3.2.0 is considered less than 3.2.11.
   263  //
   264  // Returns a positive int if version1 is greater than version2, a negative int if version1 is less
   265  // than version2, and 0 if version1 is equal to version2.
   266  func CompareVersions(t *testing.T, v1 string, v2 string) int {
   267  	n1 := strings.Split(v1, ".")
   268  	n2 := strings.Split(v2, ".")
   269  
   270  	for i := 0; i < int(math.Min(float64(len(n1)), float64(len(n2)))); i++ {
   271  		i1, err := strconv.Atoi(n1[i])
   272  		require.NoError(t, err)
   273  
   274  		i2, err := strconv.Atoi(n2[i])
   275  		require.NoError(t, err)
   276  
   277  		difference := i1 - i2
   278  		if difference != 0 {
   279  			return difference
   280  		}
   281  	}
   282  
   283  	return 0
   284  }
   285  

View as plain text