diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 5c92a25e7e..11ba8e6627 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -198,7 +198,7 @@ functions: params: binary: bash env: - GO_BUILD_TAGS: cse + GO_BUILD_TAGS: "cse,mongointernal" include_expansions_in_env: ["TOPOLOGY", "AUTH", "SSL", "SKIP_CSOT_TESTS", "MONGODB_URI", "CRYPT_SHARED_LIB_PATH", "SKIP_CRYPT_SHARED_LIB", "RACE", "MONGO_GO_DRIVER_COMPRESSOR", "REQUIRE_API_VERSION", "LOAD_BALANCER"] args: [*task-runner, setup-test] - command: subprocess.exec diff --git a/internal/integration/mongointernal_test.go b/internal/integration/mongointernal_test.go new file mode 100644 index 0000000000..c6671b3cb2 --- /dev/null +++ b/internal/integration/mongointernal_test.go @@ -0,0 +1,98 @@ +// Copyright (C) MongoDB, Inc. 2025-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 + +//go:build mongointernal + +package integration + +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/integration/mtest" + "go.mongodb.org/mongo-driver/v2/internal/require" + "go.mongodb.org/mongo-driver/v2/mongo" + "go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore" +) + +func TestNewSessionWithLSID(t *testing.T) { + mt := mtest.New(t) + + mt.Run("can be used to pass a specific session ID to CRUD commands", func(mt *mtest.T) { + mt.Parallel() + + // Create a session ID document, which is a BSON document with field + // "id" containing a 16-byte UUID (binary subtype 4). + sessionID := bson.Raw(bsoncore.NewDocumentBuilder(). + AppendBinary("id", 4, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}). + Build()) + + sess := mongo.NewSessionWithLSID(mt.Client, sessionID) + + ctx := mongo.NewSessionContext(context.Background(), sess) + _, err := mt.Coll.InsertOne(ctx, bson.D{{"foo", "bar"}}) + require.NoError(mt, err) + + evt := mt.GetStartedEvent() + val, err := evt.Command.LookupErr("lsid") + require.NoError(mt, err, "lsid should be present in the command document") + + doc, ok := val.DocumentOK() + require.True(mt, ok, "lsid should be a document") + + assert.Equal(mt, sessionID, doc) + }) + + mt.Run("EndSession panics", func(mt *mtest.T) { + mt.Parallel() + + sessionID := bson.Raw(bsoncore.NewDocumentBuilder(). + AppendBinary("id", 4, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}). + Build()) + sess := mongo.NewSessionWithLSID(mt.Client, sessionID) + + // Use a defer-recover block to catch the expected panic and assert that + // the recovered error is not nil. + defer func() { + err := recover() + assert.NotNil(mt, err, "expected EndSession to panic") + }() + + // Expect this call to panic. + sess.EndSession(context.Background()) + + // We expect that calling EndSession on a Session returned by + // NewSessionWithLSID panics. This code will only be reached if EndSession + // doesn't panic. + t.Errorf("expected EndSession to panic") + }) + + mt.Run("ClientSession.SetServer panics", func(mt *mtest.T) { + mt.Parallel() + + sessionID := bson.Raw(bsoncore.NewDocumentBuilder(). + AppendBinary("id", 4, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}). + Build()) + sess := mongo.NewSessionWithLSID(mt.Client, sessionID) + + // Use a defer-recover block to catch the expected panic and assert that + // the recovered error is not nil. + defer func() { + err := recover() + assert.NotNil(mt, err, "expected ClientSession.SetServer to panic") + }() + + // Expect this call to panic. + sess.ClientSession().SetServer() + + // We expect that calling ClientSession.SetServer on a Session returned + // by NewSessionWithLSID panics. This code will only be reached if + // ClientSession.SetServer doesn't panic. + t.Errorf("expected ClientSession.SetServer to panic") + }) +} diff --git a/mongo/mongointernal.go b/mongo/mongointernal.go new file mode 100644 index 0000000000..31195d37e8 --- /dev/null +++ b/mongo/mongointernal.go @@ -0,0 +1,41 @@ +// Copyright (C) MongoDB, Inc. 2025-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 + +//go:build mongointernal + +package mongo + +import ( + "time" + + "go.mongodb.org/mongo-driver/v2/bson" + "go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore" + "go.mongodb.org/mongo-driver/v2/x/mongo/driver/session" +) + +// NewSessionWithLSID returns a Session with the given sessionID document. The +// sessionID is a BSON document with key "id" containing a 16-byte UUID (binary +// subtype 4). +// +// Sessions returned by NewSessionWithLSID are never added to the driver's +// session pool. Calling "EndSession" or "ClientSession.SetServer" on a Session +// returned by NewSessionWithLSID will panic. +// +// NewSessionWithLSID is intended only for internal use and may be changed or +// removed at any time. +func NewSessionWithLSID(client *Client, sessionID bson.Raw) *Session { + return &Session{ + clientSession: &session.Client{ + Server: &session.Server{ + SessionID: bsoncore.Document(sessionID), + LastUsed: time.Now(), + }, + ClientID: client.id, + }, + client: client, + deployment: client.deployment, + } +}