Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
347 changes: 347 additions & 0 deletions internal/integration/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2409,6 +2409,353 @@ func TestBypassEmptyTsReplacement(t *testing.T) {
})
}

// TestAddCommandFields verifies that the "addCommandFields" internal option
// injects arbitrary top-level fields into the command sent to the server for
// each collection- and index-level method that supports it. Each subtest
// injects a "comment" field (accepted by these commands on 4.4+) and asserts
// that it appears in the started command event.
Comment on lines +2412 to +2416
func TestAddCommandFields(t *testing.T) {
mt := mtest.New(t, mtest.NewOptions().MinServerVersion("4.4"))

const wantComment = "addCommandFieldsTest"

marshalValue := func(val interface{}) bson.RawValue {
t.Helper()

valType, data, err := bson.MarshalValue(val)
require.NoError(t, err, "MarshalValue error: %v", err)
return bson.RawValue{
Type: valType,
Value: data,
}
}

added := bson.D{{"comment", wantComment}}
empty := bson.RawValue{}
set := marshalValue(wantComment)

mt.Run("delete one", func(mt *mtest.T) {
newOpts := func() *options.DeleteOneOptionsBuilder {
opts := options.DeleteOne()
err := xoptions.SetInternalDeleteOneOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.DeleteOneOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
_, err := mt.Coll.DeleteOne(context.Background(), bson.D{{"x", 1}}, tc.opts)
require.NoError(mt, err, "DeleteOne error: %v", err)
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
mt.Run("delete many", func(mt *mtest.T) {
newOpts := func() *options.DeleteManyOptionsBuilder {
opts := options.DeleteMany()
err := xoptions.SetInternalDeleteManyOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.DeleteManyOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
_, err := mt.Coll.DeleteMany(context.Background(), bson.D{{"x", 1}}, tc.opts)
require.NoError(mt, err, "DeleteMany error: %v", err)
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
mt.Run("find", func(mt *mtest.T) {
newOpts := func() *options.FindOptionsBuilder {
opts := options.Find()
err := xoptions.SetInternalFindOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.FindOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
initCollection(mt, mt.Coll)
mt.ClearEvents()

cursor, err := mt.Coll.Find(context.Background(), bson.D{}, tc.opts)
require.NoError(mt, err, "Find error: %v", err)
_ = cursor.Close(context.Background())
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
mt.Run("find one", func(mt *mtest.T) {
newOpts := func() *options.FindOneOptionsBuilder {
opts := options.FindOne()
err := xoptions.SetInternalFindOneOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.FindOneOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
initCollection(mt, mt.Coll)
mt.ClearEvents()

err := mt.Coll.FindOne(context.Background(), bson.D{}, tc.opts).Err()
require.NoError(mt, err, "FindOne error: %v", err)
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
mt.Run("distinct", func(mt *mtest.T) {
newOpts := func() *options.DistinctOptionsBuilder {
opts := options.Distinct()
err := xoptions.SetInternalDistinctOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.DistinctOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
initCollection(mt, mt.Coll)
mt.ClearEvents()

err := mt.Coll.Distinct(context.Background(), "x", bson.D{}, tc.opts).Err()
require.NoError(mt, err, "Distinct error: %v", err)
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
mt.Run("count documents", func(mt *mtest.T) {
newOpts := func() *options.CountOptionsBuilder {
opts := options.Count()
err := xoptions.SetInternalCountOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.CountOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
initCollection(mt, mt.Coll)
mt.ClearEvents()

_, err := mt.Coll.CountDocuments(context.Background(), bson.D{}, tc.opts)
require.NoError(mt, err, "CountDocuments error: %v", err)
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
mt.Run("estimated document count", func(mt *mtest.T) {
newOpts := func() *options.EstimatedDocumentCountOptionsBuilder {
opts := options.EstimatedDocumentCount()
err := xoptions.SetInternalEstimatedDocumentCountOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.EstimatedDocumentCountOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
initCollection(mt, mt.Coll)
mt.ClearEvents()

_, err := mt.Coll.EstimatedDocumentCount(context.Background(), tc.opts)
require.NoError(mt, err, "EstimatedDocumentCount error: %v", err)
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
mt.Run("drop", func(mt *mtest.T) {
newOpts := func() *options.DropCollectionOptionsBuilder {
opts := options.DropCollection()
err := xoptions.SetInternalDropCollectionOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.DropCollectionOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
initCollection(mt, mt.Coll)
mt.ClearEvents()

var err error
if tc.opts == nil {
err = mt.Coll.Drop(context.Background())
} else {
err = mt.Coll.Drop(context.Background(), tc.opts)
}
require.NoError(mt, err, "Drop error: %v", err)
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
mt.Run("list indexes", func(mt *mtest.T) {
newOpts := func() *options.ListIndexesOptionsBuilder {
opts := options.ListIndexes()
err := xoptions.SetInternalListIndexesOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.ListIndexesOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
initCollection(mt, mt.Coll)
mt.ClearEvents()

cursor, err := mt.Coll.Indexes().List(context.Background(), tc.opts)
require.NoError(mt, err, "List error: %v", err)
_ = cursor.Close(context.Background())
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
mt.Run("create indexes", func(mt *mtest.T) {
newOpts := func() *options.CreateIndexesOptionsBuilder {
opts := options.CreateIndexes()
err := xoptions.SetInternalCreateIndexesOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.CreateIndexesOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
model := mongo.IndexModel{Keys: bson.D{{"x", 1}}}
_, err := mt.Coll.Indexes().CreateOne(context.Background(), model, tc.opts)
require.NoError(mt, err, "CreateOne error: %v", err)
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
mt.Run("drop indexes", func(mt *mtest.T) {
newOpts := func() *options.DropIndexesOptionsBuilder {
opts := options.DropIndexes()
err := xoptions.SetInternalDropIndexesOptions(opts, "addCommandFields", added)
require.NoError(mt, err, "unexpected error: %v", err)
return opts
}

testCases := []struct {
name string
opts *options.DropIndexesOptionsBuilder
expected bson.RawValue
}{
{"empty", nil, empty},
{"set", newOpts(), set},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
name, err := mt.Coll.Indexes().CreateOne(context.Background(),
mongo.IndexModel{Keys: bson.D{{"x", 1}}})
require.NoError(mt, err, "CreateOne error: %v", err)
mt.ClearEvents()

err = mt.Coll.Indexes().DropOne(context.Background(), name, tc.opts)
require.NoError(mt, err, "DropOne error: %v", err)
evt := mt.GetStartedEvent()
val := evt.Command.Lookup("comment")
assert.Equal(mt, tc.expected, val, "expected comment to be %s", tc.expected.String())
})
}
})
}

func initCollection(tb testing.TB, coll *mongo.Collection) {
tb.Helper()

Expand Down
2 changes: 1 addition & 1 deletion mongo/client_bulk_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ func (bw *clientBulkWrite) newCommand() func([]byte, description.SelectedServer)
if len(bw.additionalCmd) > 0 {
doc, err := bson.Marshal(bw.additionalCmd)
if err != nil {
return nil, err
return nil, fmt.Errorf("error marshaling additional command fields: %w", err)
}
dst = append(dst, doc[4:len(doc)-1]...)
}
Expand Down
Loading
Loading