diff --git a/internal/driverutil/description.go b/internal/driverutil/description.go index 0d06aee4f7..df3adc3692 100644 --- a/internal/driverutil/description.go +++ b/internal/driverutil/description.go @@ -20,6 +20,11 @@ import ( "go.mongodb.org/mongo-driver/v2/x/mongo/driver/description" ) +const ( + MinWireVersion = 6 + MaxWireVersion = 25 +) + func equalWireVersion(wv1, wv2 *description.VersionRange) bool { if wv1 == nil && wv2 == nil { return true diff --git a/internal/integration/collection_test.go b/internal/integration/collection_test.go index e65ff45235..b370b09418 100644 --- a/internal/integration/collection_test.go +++ b/internal/integration/collection_test.go @@ -19,6 +19,7 @@ import ( "go.mongodb.org/mongo-driver/v2/mongo/options" "go.mongodb.org/mongo-driver/v2/mongo/writeconcern" "go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore" + "go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest" ) const ( @@ -1819,7 +1820,7 @@ func TestCollection(t *testing.T) { }) mt.RunOpts("insert and delete with batches", mtest.NewOptions().ClientType(mtest.Mock), func(mt *mtest.T) { // grouped together because delete requires the documents to be inserted - maxBatchCount := int(mtest.MockDescription.MaxBatchCount) + maxBatchCount := int(drivertest.MockDescription.MaxBatchCount) numDocs := maxBatchCount + 50 var insertModels []mongo.WriteModel var deleteModels []mongo.WriteModel @@ -1870,7 +1871,7 @@ func TestCollection(t *testing.T) { assert.True(mt, deletes > 1, "expected multiple batches, got %v", deletes) }) mt.RunOpts("update with batches", mtest.NewOptions().ClientType(mtest.Mock), func(mt *mtest.T) { - maxBatchCount := int(mtest.MockDescription.MaxBatchCount) + maxBatchCount := int(drivertest.MockDescription.MaxBatchCount) numModels := maxBatchCount + 50 var models []mongo.WriteModel diff --git a/internal/integration/mtest/mongotest.go b/internal/integration/mtest/mongotest.go index 65a15a60c7..be1da51aeb 100644 --- a/internal/integration/mtest/mongotest.go +++ b/internal/integration/mtest/mongotest.go @@ -29,6 +29,7 @@ import ( "go.mongodb.org/mongo-driver/v2/mongo/writeconcern" "go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore" "go.mongodb.org/mongo-driver/v2/x/mongo/driver" + "go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest" ) var ( @@ -62,7 +63,7 @@ type T struct { createClient *bool createCollection *bool runOn []RunOnBlock - mockDeployment *mockDeployment // nil if the test is not being run against a mock + mockDeployment *drivertest.MockDeployment // nil if the test is not being run against a mock mockResponses []bson.D createdColls []*Collection // collections created in this test proxyDialer *proxyDialer @@ -235,12 +236,12 @@ func (t *T) RunOpts(name string, opts *Options, callback func(mt *T)) { // AddMockResponses adds responses to be returned by the mock deployment. This should only be used if T is being run // against a mock deployment. func (t *T) AddMockResponses(responses ...bson.D) { - t.mockDeployment.addResponses(responses...) + t.mockDeployment.AddResponses(responses...) } // ClearMockResponses clears all responses in the mock deployment. func (t *T) ClearMockResponses() { - t.mockDeployment.clearResponses() + t.mockDeployment.ClearResponses() } // GetStartedEvent returns the least recent CommandStartedEvent, or nil if one is not present. @@ -661,7 +662,7 @@ func (t *T) createTestClient() { args.PoolMonitor = nil - t.mockDeployment = newMockDeployment() + t.mockDeployment = drivertest.NewMockDeployment() args.Deployment = t.mockDeployment opts := mongoutil.NewOptionsLister(args, nil) diff --git a/internal/integration/mtest/opmsg_deployment.go b/x/mongo/driver/drivertest/opmsg_deployment.go similarity index 78% rename from internal/integration/mtest/opmsg_deployment.go rename to x/mongo/driver/drivertest/opmsg_deployment.go index 31220918f6..84fdb308df 100644 --- a/internal/integration/mtest/opmsg_deployment.go +++ b/x/mongo/driver/drivertest/opmsg_deployment.go @@ -4,7 +4,7 @@ // 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 mtest +package drivertest import ( "context" @@ -13,12 +13,12 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/csot" + "go.mongodb.org/mongo-driver/v2/internal/driverutil" "go.mongodb.org/mongo-driver/v2/mongo/address" "go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore" "go.mongodb.org/mongo-driver/v2/x/mongo/driver" "go.mongodb.org/mongo-driver/v2/x/mongo/driver/description" "go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet" - "go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology" "go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage" ) @@ -42,7 +42,7 @@ var ( SessionTimeoutMinutes: &sessionTimeoutMinutes, Kind: description.ServerKindRSPrimary, WireVersion: &description.VersionRange{ - Max: topology.SupportedWireVersions.Max, + Max: driverutil.MaxWireVersion, }, } ) @@ -122,60 +122,60 @@ func (*connection) Stale() bool { return false } -// mockDeployment wraps a connection and implements the driver.Deployment interface. -type mockDeployment struct { +// MockDeployment wraps a connection and implements the driver.Deployment interface. +type MockDeployment struct { conn *connection updates chan description.Topology } -var _ driver.Deployment = &mockDeployment{} -var _ driver.Server = &mockDeployment{} -var _ driver.Connector = &mockDeployment{} -var _ driver.Disconnector = &mockDeployment{} -var _ driver.Subscriber = &mockDeployment{} +var _ driver.Deployment = &MockDeployment{} +var _ driver.Server = &MockDeployment{} +var _ driver.Connector = &MockDeployment{} +var _ driver.Disconnector = &MockDeployment{} +var _ driver.Subscriber = &MockDeployment{} // SelectServer implements the Deployment interface. This method does not use the // description.SelectedServer provided and instead returns itself. The Connections returned from the // Connection method have a no-op Close method. -func (md *mockDeployment) SelectServer(context.Context, description.ServerSelector) (driver.Server, error) { +func (md *MockDeployment) SelectServer(context.Context, description.ServerSelector) (driver.Server, error) { return md, nil } // GetServerSelectionTimeout returns zero as a server selection timeout is not // applicable for mock deployments. -func (*mockDeployment) GetServerSelectionTimeout() time.Duration { +func (*MockDeployment) GetServerSelectionTimeout() time.Duration { return 0 } // Kind implements the Deployment interface. It always returns description.TopologyKindSingle. -func (md *mockDeployment) Kind() description.TopologyKind { +func (md *MockDeployment) Kind() description.TopologyKind { return description.TopologyKindSingle } // Connection implements the driver.Server interface. -func (md *mockDeployment) Connection(context.Context) (*mnet.Connection, error) { +func (md *MockDeployment) Connection(context.Context) (*mnet.Connection, error) { return mnet.NewConnection(md.conn), nil } // RTTMonitor implements the driver.Server interface. -func (md *mockDeployment) RTTMonitor() driver.RTTMonitor { +func (md *MockDeployment) RTTMonitor() driver.RTTMonitor { return &csot.ZeroRTTMonitor{} } // Connect is a no-op method which implements the driver.Connector interface. -func (md *mockDeployment) Connect() error { +func (md *MockDeployment) Connect() error { return nil } // Disconnect is a no-op method which implements the driver.Disconnector interface { -func (md *mockDeployment) Disconnect(context.Context) error { +func (md *MockDeployment) Disconnect(context.Context) error { close(md.updates) return nil } // Subscribe returns a subscription from which new topology descriptions can be retrieved. // Subscribe implements the driver.Subscriber interface. -func (md *mockDeployment) Subscribe() (*driver.Subscription, error) { +func (md *MockDeployment) Subscribe() (*driver.Subscription, error) { if md.updates == nil { md.updates = make(chan description.Topology, 1) @@ -190,23 +190,24 @@ func (md *mockDeployment) Subscribe() (*driver.Subscription, error) { } // Unsubscribe is a no-op method which implements the driver.Subscriber interface. -func (md *mockDeployment) Unsubscribe(*driver.Subscription) error { +func (md *MockDeployment) Unsubscribe(*driver.Subscription) error { return nil } -// addResponses adds responses to this mock deployment. -func (md *mockDeployment) addResponses(responses ...bson.D) { +// AddResponses adds responses to this mock deployment. +func (md *MockDeployment) AddResponses(responses ...bson.D) { md.conn.responses = append(md.conn.responses, responses...) } -// clearResponses clears all remaining responses in this mock deployment. -func (md *mockDeployment) clearResponses() { +// ClearResponses clears all remaining responses in this mock deployment. +func (md *MockDeployment) ClearResponses() { md.conn.responses = md.conn.responses[:0] } -// newMockDeployment returns a mock driver.Deployment that responds with OP_MSG wire messages. -func newMockDeployment(responses ...bson.D) *mockDeployment { - return &mockDeployment{ +// NewMockDeployment returns a mock driver.Deployment that responds with OP_MSG wire messages. +// However, for most use cases, we suggest testing with an actual database. +func NewMockDeployment(responses ...bson.D) *MockDeployment { + return &MockDeployment{ conn: &connection{ responses: responses, }, diff --git a/x/mongo/driver/drivertest/opmsg_deployment_test.go b/x/mongo/driver/drivertest/opmsg_deployment_test.go new file mode 100644 index 0000000000..24e5294e99 --- /dev/null +++ b/x/mongo/driver/drivertest/opmsg_deployment_test.go @@ -0,0 +1,59 @@ +// 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 drivertest + +import ( + "context" + "testing" + + "go.mongodb.org/mongo-driver/v2/bson" + "go.mongodb.org/mongo-driver/v2/internal/assert" + "go.mongodb.org/mongo-driver/v2/internal/require" + "go.mongodb.org/mongo-driver/v2/mongo" + "go.mongodb.org/mongo-driver/v2/mongo/options" +) + +func TestOPMSGMockDeployment(t *testing.T) { + md := NewMockDeployment() + + opts := options.Client() + opts.Opts = append(opts.Opts, func(co *options.ClientOptions) error { + co.Deployment = md + + return nil + }) + client, err := mongo.Connect(opts) + + t.Run("NewMockDeployment connect to client", func(t *testing.T) { + require.NoError(t, err, "unexpected error from connect to mockdeployment") + }) + t.Run("AddResponses with one", func(t *testing.T) { + res := bson.D{{"ok", 1}} + md.AddResponses(res) + assert.NotNil(t, md.conn.responses, "expected non-nil responses") + assert.Len(t, md.conn.responses, 1, "expected 1 response, got %v", len(md.conn.responses)) + err = client.Ping(context.Background(), nil) + require.NoError(t, err) + }) + t.Run("AddResponses with multiple", func(t *testing.T) { + res1 := bson.D{{"ok", 1}} + res2 := bson.D{{"ok", 2}} + res3 := bson.D{{"ok", 3}} + md.AddResponses(res1, res2, res3) + assert.NotNil(t, md.conn.responses, "expected non-nil responses") + assert.Len(t, md.conn.responses, 3, "expected 3 responses, got %v", len(md.conn.responses)) + err = client.Ping(context.Background(), nil) + require.NoError(t, err) + }) + t.Run("ClearResponses", func(t *testing.T) { + md.ClearResponses() + assert.NotNil(t, md.conn.responses, "expected non-nil responses") + assert.Len(t, md.conn.responses, 0, "expected 0 responses, got %v", len(md.conn.responses)) + err = client.Ping(context.Background(), nil) + require.Error(t, err, "expected Ping error, got nil") + }) +} diff --git a/x/mongo/driver/topology/fsm.go b/x/mongo/driver/topology/fsm.go index 17f8628afa..3cc06656fc 100644 --- a/x/mongo/driver/topology/fsm.go +++ b/x/mongo/driver/topology/fsm.go @@ -23,7 +23,7 @@ var ( MinSupportedMongoDBVersion = "3.6" // SupportedWireVersions is the range of wire versions supported by the driver. - SupportedWireVersions = driverutil.NewVersionRange(6, 25) + SupportedWireVersions = driverutil.NewVersionRange(driverutil.MinWireVersion, driverutil.MaxWireVersion) ) type fsm struct {