diff --git a/.github/codecov.yaml b/.github/codecov.yaml index 57bca0caab0..d7463d08fb0 100644 --- a/.github/codecov.yaml +++ b/.github/codecov.yaml @@ -15,7 +15,3 @@ comment: layout: "reach,diff,flags,tree" behavior: default require_changes: yes - -ignore: - # opmsg_deployment is copied from mongo-go-driver. - - "instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/opmsg_deployment.go" diff --git a/Makefile b/Makefile index 0def5aaf787..72c404a9d2e 100644 --- a/Makefile +++ b/Makefile @@ -186,15 +186,9 @@ vanity-import-check: | $(PORTO) .PHONY: lint lint: go-mod-tidy golangci-lint misspell govulncheck -# The following file is a third-party copy from the mongo-go-driver: -# ./instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/opmsg_deployment.go .PHONY: license-check license-check: - @licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) \ - ! -path './vendor/*' \ - ! -path './exporters/otlp/internal/opentelemetry-proto/*' \ - ! -path './instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/opmsg_deployment.go') ; do \ - awk '/Copyright The OpenTelemetry Authors|generated|GENERATED/ && NR<=4 { found=1; next } END { if (!found) print FILENAME }' $$f; \ + @licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path './vendor/*' ! -path './exporters/otlp/internal/opentelemetry-proto/*') ; do \ done); \ if [ -n "$${licRes}" ]; then \ echo "license header checking failed:"; echo "$${licRes}"; \ diff --git a/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/go.mod b/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/go.mod index 8989d91a429..769bdb3a9fe 100644 --- a/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/go.mod +++ b/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/go.mod @@ -3,6 +3,8 @@ module go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/m go 1.21 +replace go.mongodb.org/mongo-driver => /Users/preston.vasquez/Developer/mongo-go-driver-2 + require ( github.com/stretchr/testify v1.9.0 go.mongodb.org/mongo-driver v1.15.0 diff --git a/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/mongo_test.go b/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/mongo_test.go index f1141de9b45..10413393a21 100644 --- a/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/mongo_test.go +++ b/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/mongo_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/integration/mtest" "go.mongodb.org/mongo-driver/mongo/options" "go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo" // nolint:staticcheck // deprecated. @@ -24,6 +25,8 @@ import ( type validator func(sdktrace.ReadOnlySpan) bool func TestDBCrudOperation(t *testing.T) { + t.Parallel() + commonValidators := []validator{ func(s sdktrace.ReadOnlySpan) bool { return assert.Equal(t, "test-collection.insert", s.Name(), "expected %s", s.Name()) @@ -80,13 +83,19 @@ func TestDBCrudOperation(t *testing.T) { }, } for _, tc := range tt { + tc := tc + title := tc.title if tc.excludeCommand { title = title + "/excludeCommand" } else { title = title + "/includeCommand" } - t.Run(title, func(t *testing.T) { + + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + mt.Run(title, func(mt *mtest.T) { + mt.Parallel() + sr := tracetest.NewSpanRecorder() provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) @@ -103,56 +112,43 @@ func TestDBCrudOperation(t *testing.T) { ) opts.ApplyURI(addr) - mock := newMockDeployment() - - // nolint:staticcheck - // - // Deployment is not part of the stable API guarantee of the - // mongo-go-driver and is therefore marked as deprecated. - // - // See https://jira.mongodb.org/browse/GODRIVER-3241 for a long-term solution. - opts.Deployment = mock - - client, err := mongo.Connect(ctx, opts) - if err != nil { - t.Fatal(err) - } - - mock.addResponses(tc.mockResponses...) - t.Cleanup(mock.clearResponses) + mt.ResetClient(opts) + mt.AddMockResponses(tc.mockResponses...) - _, err = tc.operation(ctx, client.Database("test-database")) + _, err := tc.operation(ctx, mt.Client.Database("test-database")) if err != nil { - t.Error(err) + mt.Error(err) } span.End() spans := sr.Ended() - if !assert.Len(t, spans, 2, "expected 2 spans, received %d", len(spans)) { - t.FailNow() + if !assert.Len(mt, spans, 2, "expected 2 spans, received %d", len(spans)) { + mt.FailNow() } - assert.Len(t, spans, 2) - assert.Equal(t, spans[0].SpanContext().TraceID(), spans[1].SpanContext().TraceID()) - assert.Equal(t, spans[0].Parent().SpanID(), spans[1].SpanContext().SpanID()) - assert.Equal(t, span.SpanContext().SpanID(), spans[1].SpanContext().SpanID()) + assert.Len(mt, spans, 2) + assert.Equal(mt, spans[0].SpanContext().TraceID(), spans[1].SpanContext().TraceID()) + assert.Equal(mt, spans[0].Parent().SpanID(), spans[1].SpanContext().SpanID()) + assert.Equal(mt, span.SpanContext().SpanID(), spans[1].SpanContext().SpanID()) s := spans[0] - assert.Equal(t, trace.SpanKindClient, s.SpanKind()) + assert.Equal(mt, trace.SpanKindClient, s.SpanKind()) attrs := s.Attributes() - assert.Contains(t, attrs, attribute.String("db.system", "mongodb")) - assert.Contains(t, attrs, attribute.String("net.peer.name", "")) - assert.Contains(t, attrs, attribute.Int64("net.peer.port", int64(27017))) - assert.Contains(t, attrs, attribute.String("net.transport", "ip_tcp")) - assert.Contains(t, attrs, attribute.String("db.name", "test-database")) + assert.Contains(mt, attrs, attribute.String("db.system", "mongodb")) + assert.Contains(mt, attrs, attribute.String("net.peer.name", "")) + assert.Contains(mt, attrs, attribute.Int64("net.peer.port", int64(27017))) + assert.Contains(mt, attrs, attribute.String("net.transport", "ip_tcp")) + assert.Contains(mt, attrs, attribute.String("db.name", "test-database")) for _, v := range tc.validators { - assert.True(t, v(s)) + assert.True(mt, v(s)) } }) } } func TestDBCollectionAttribute(t *testing.T) { + t.Parallel() + tt := []struct { title string operation func(context.Context, *mongo.Database) (interface{}, error) @@ -205,7 +201,12 @@ func TestDBCollectionAttribute(t *testing.T) { }, } for _, tc := range tt { - t.Run(tc.title, func(t *testing.T) { + tc := tc + + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + mt.Run(tc.title, func(mt *mtest.T) { + mt.Parallel() + sr := tracetest.NewSpanRecorder() provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) @@ -222,50 +223,35 @@ func TestDBCollectionAttribute(t *testing.T) { ) opts.ApplyURI(addr) - mock := newMockDeployment() - - // nolint:staticcheck - // - // Deployment is not part of the stable API guarantee of the - // mongo-go-driver and is therefore marked as deprecated. - // - // See https://jira.mongodb.org/browse/GODRIVER-3241 for a long-term solution. - opts.Deployment = mock - - client, err := mongo.Connect(ctx, opts) - if err != nil { - t.Fatal(err) - } - - mock.addResponses(tc.mockResponses...) - t.Cleanup(mock.clearResponses) + mt.ResetClient(opts) + mt.AddMockResponses(tc.mockResponses...) - _, err = tc.operation(ctx, client.Database("test-database")) + _, err := tc.operation(ctx, mt.Client.Database("test-database")) if err != nil { - t.Error(err) + mt.Error(err) } span.End() spans := sr.Ended() - if !assert.Len(t, spans, 2, "expected 2 spans, received %d", len(spans)) { - t.FailNow() + if !assert.Len(mt, spans, 2, "expected 2 spans, received %d", len(spans)) { + mt.FailNow() } - assert.Len(t, spans, 2) - assert.Equal(t, spans[0].SpanContext().TraceID(), spans[1].SpanContext().TraceID()) - assert.Equal(t, spans[0].Parent().SpanID(), spans[1].SpanContext().SpanID()) - assert.Equal(t, span.SpanContext().SpanID(), spans[1].SpanContext().SpanID()) + assert.Len(mt, spans, 2) + assert.Equal(mt, spans[0].SpanContext().TraceID(), spans[1].SpanContext().TraceID()) + assert.Equal(mt, spans[0].Parent().SpanID(), spans[1].SpanContext().SpanID()) + assert.Equal(mt, span.SpanContext().SpanID(), spans[1].SpanContext().SpanID()) s := spans[0] - assert.Equal(t, trace.SpanKindClient, s.SpanKind()) + assert.Equal(mt, trace.SpanKindClient, s.SpanKind()) attrs := s.Attributes() - assert.Contains(t, attrs, attribute.String("db.system", "mongodb")) - assert.Contains(t, attrs, attribute.String("net.peer.name", "")) - assert.Contains(t, attrs, attribute.Int64("net.peer.port", int64(27017))) - assert.Contains(t, attrs, attribute.String("net.transport", "ip_tcp")) - assert.Contains(t, attrs, attribute.String("db.name", "test-database")) + assert.Contains(mt, attrs, attribute.String("db.system", "mongodb")) + assert.Contains(mt, attrs, attribute.String("net.peer.name", "")) + assert.Contains(mt, attrs, attribute.Int64("net.peer.port", int64(27017))) + assert.Contains(mt, attrs, attribute.String("net.transport", "ip_tcp")) + assert.Contains(mt, attrs, attribute.String("db.name", "test-database")) for _, v := range tc.validators { - assert.True(t, v(s)) + assert.True(mt, v(s)) } }) } diff --git a/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/opmsg_deployment.go b/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/opmsg_deployment.go deleted file mode 100644 index 6510f816e4c..00000000000 --- a/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test/opmsg_deployment.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copied from https://github.com/mongodb/mongo-go-driver/blob/d46f29c34e9ff3968a5c00dcdea8fb41a20a2b2f/mongo/integration/mtest/opmsg_deployment.go - -// 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 test // import "go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo/test" - -import ( - "context" - "errors" - "time" - - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo/address" - "go.mongodb.org/mongo-driver/mongo/description" - "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" - "go.mongodb.org/mongo-driver/x/mongo/driver" - "go.mongodb.org/mongo-driver/x/mongo/driver/topology" - "go.mongodb.org/mongo-driver/x/mongo/driver/wiremessage" -) - -const ( - serverAddress = address.Address("127.0.0.1:27017") - maxDocumentSize uint32 = 16777216 - maxMessageSize uint32 = 48000000 - maxBatchCount uint32 = 100000 -) - -var ( - sessionTimeoutMinutes uint32 = 30 - sessionTimeoutMinutesInt64 = int64(sessionTimeoutMinutes) - - // MockDescription is the server description used for the mock deployment. Each mocked connection returns this - // value from its Description method. - MockDescription = description.Server{ - CanonicalAddr: serverAddress, - MaxDocumentSize: maxDocumentSize, - MaxMessageSize: maxMessageSize, - MaxBatchCount: maxBatchCount, - // TODO(GODRIVER-2885): This can be removed once legacy - // SessionTimeoutMinutes is removed. - SessionTimeoutMinutes: sessionTimeoutMinutes, - SessionTimeoutMinutesPtr: &sessionTimeoutMinutesInt64, - Kind: description.RSPrimary, - WireVersion: &description.VersionRange{ - Max: topology.SupportedWireVersions.Max, - }, - } -) - -// connection implements the driver.Connection interface and responds to wire messages with pre-configured responses. -type connection struct { - responses []bson.D // responses to send when ReadWireMessage is called -} - -var _ driver.Connection = &connection{} - -// WriteWireMessage is a no-op. -func (c *connection) WriteWireMessage(context.Context, []byte) error { - return nil -} - -// ReadWireMessage returns the next response in the connection's list of responses. -func (c *connection) ReadWireMessage(_ context.Context) ([]byte, error) { - var dst []byte - if len(c.responses) == 0 { - return dst, errors.New("no responses remaining") - } - nextRes := c.responses[0] - c.responses = c.responses[1:] - - var wmindex int32 - wmindex, dst = wiremessage.AppendHeaderStart(dst, wiremessage.NextRequestID(), 0, wiremessage.OpMsg) - dst = wiremessage.AppendMsgFlags(dst, 0) - dst = wiremessage.AppendMsgSectionType(dst, wiremessage.SingleDocument) - resBytes, _ := bson.Marshal(nextRes) - dst = append(dst, resBytes...) - dst = bsoncore.UpdateLength(dst, wmindex, int32(len(dst[wmindex:]))) - return dst, nil -} - -// Description returns a fixed server description for the connection. -func (c *connection) Description() description.Server { - return MockDescription -} - -// Close is a no-op operation. -func (*connection) Close() error { - return nil -} - -// ID returns a fixed identifier for the connection. -func (*connection) ID() string { - return "" -} - -// DriverConnectionID returns a fixed identifier for the driver pool connection. -// TODO(GODRIVER-2824): replace return type with int64. -func (*connection) DriverConnectionID() uint64 { - return 0 -} - -// ServerConnectionID returns a fixed identifier for the server connection. -func (*connection) ServerConnectionID() *int64 { - serverConnectionID := int64(42) - return &serverConnectionID -} - -// Address returns a fixed address for the connection. -func (*connection) Address() address.Address { - return serverAddress -} - -// Stale returns if the connection is stale. -func (*connection) Stale() bool { - return false -} - -// mockDeployment wraps a connection and implements the driver.Deployment interface. -type mockDeployment struct { - conn *connection - updates chan description.Topology -} - -var ( - _ driver.Deployment = &mockDeployment{} - _ driver.Server = &mockDeployment{} - _ driver.Connector = &mockDeployment{} - _ driver.Disconnector = &mockDeployment{} - _ 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) { - return md, nil -} - -// Kind implements the Deployment interface. It always returns description.Single. -func (md *mockDeployment) Kind() description.TopologyKind { - return description.Single -} - -// Connection implements the driver.Server interface. -func (md *mockDeployment) Connection(context.Context) (driver.Connection, error) { - return md.conn, nil -} - -// ZeroRTTMonitor implements the RTTMonitor interface and is used internally for testing. It returns 0 for all -// RTT calculations and an empty string for RTT statistics. -type ZeroRTTMonitor struct{} - -var _ driver.RTTMonitor = &ZeroRTTMonitor{} - -// EWMA implements the RTT monitor interface. -func (zrm *ZeroRTTMonitor) EWMA() time.Duration { - return 0 -} - -// Min implements the RTT monitor interface. -func (zrm *ZeroRTTMonitor) Min() time.Duration { - return 0 -} - -// P90 implements the RTT monitor interface. -func (zrm *ZeroRTTMonitor) P90() time.Duration { - return 0 -} - -// Stats implements the RTT monitor interface. -func (zrm *ZeroRTTMonitor) Stats() string { - return "" -} - -// RTTMonitor implements the driver.Server interface. -func (md *mockDeployment) RTTMonitor() driver.RTTMonitor { - return &ZeroRTTMonitor{} -} - -// Connect is a no-op method which implements the driver.Connector interface. -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 { - 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) { - if md.updates == nil { - md.updates = make(chan description.Topology, 1) - - md.updates <- description.Topology{ - SessionTimeoutMinutesPtr: &sessionTimeoutMinutesInt64, - - // TODO(GODRIVER-2885): This can be removed once legacy - // SessionTimeoutMinutes is removed. - SessionTimeoutMinutes: sessionTimeoutMinutes, - } - } - - return &driver.Subscription{ - Updates: md.updates, - }, nil -} - -// Unsubscribe is a no-op method which implements the driver.Subscriber interface. -func (md *mockDeployment) Unsubscribe(*driver.Subscription) error { - return nil -} - -// 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() { - 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{ - conn: &connection{ - responses: responses, - }, - } -}