Skip to content

Commit 5e3ae90

Browse files
h3n4lclaude
andauthored
feat(test): add comprehensive multi-database testing infrastructure (#18)
* feat(test): add comprehensive multi-database testing infrastructure - Add multi-container support for MongoDB 4.4, MongoDB 8.0, and DocumentDB - Migrate all existing tests to run against all three databases - Add unicode test suite covering CJK, Arabic, emoji, and special characters - Add complex query tests (nested $and/$or, $elemMatch, cursor modifiers) - Add BSON helper function tests (ObjectId, ISODate, NumberLong, etc.) - Add fuzz tests for edge case discovery - Add shared test fixtures (users, unicode samples, complex documents) - Add fixture loader utility for test data management Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address PR review comments - Pin DocumentDB image to version 1.0.0 - Fix port spec consistency ("10260/tcp") - Add slices.SortFunc for stable DB ordering - Add ping verification to setupMongoDB - Require all 3 DBs in TestMultiContainer - Fix distinct test to assert both values explicitly - Remove fuzz tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent d4856f4 commit 5e3ae90

13 files changed

Lines changed: 3462 additions & 2499 deletions

collection_test.go

Lines changed: 2069 additions & 1816 deletions
Large diffs are not rendered by default.

database_test.go

Lines changed: 230 additions & 218 deletions
Large diffs are not rendered by default.

error_test.go

Lines changed: 100 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package gomongo_test
22

33
import (
44
"context"
5+
"fmt"
56
"testing"
67

78
"github.com/bytebase/gomongo"
@@ -10,69 +11,73 @@ import (
1011
)
1112

1213
func TestParseError(t *testing.T) {
13-
client := testutil.GetClient(t)
14-
dbName := "testdb_parse_error"
15-
defer testutil.CleanupDatabase(t, client, dbName)
14+
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
15+
dbName := fmt.Sprintf("testdb_parse_error_%s", db.Name)
16+
defer testutil.CleanupDatabase(t, db.Client, dbName)
1617

17-
gc := gomongo.NewClient(client)
18-
ctx := context.Background()
18+
gc := gomongo.NewClient(db.Client)
19+
ctx := context.Background()
1920

20-
_, err := gc.Execute(ctx, dbName, "db.users.find({ name: })")
21-
require.Error(t, err)
21+
_, err := gc.Execute(ctx, dbName, "db.users.find({ name: })")
22+
require.Error(t, err)
2223

23-
var parseErr *gomongo.ParseError
24-
require.ErrorAs(t, err, &parseErr)
24+
var parseErr *gomongo.ParseError
25+
require.ErrorAs(t, err, &parseErr)
26+
})
2527
}
2628

2729
func TestPlannedOperation(t *testing.T) {
28-
client := testutil.GetClient(t)
29-
dbName := "testdb_planned_op"
30-
defer testutil.CleanupDatabase(t, client, dbName)
30+
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
31+
dbName := fmt.Sprintf("testdb_planned_op_%s", db.Name)
32+
defer testutil.CleanupDatabase(t, db.Client, dbName)
3133

32-
gc := gomongo.NewClient(client)
33-
ctx := context.Background()
34+
gc := gomongo.NewClient(db.Client)
35+
ctx := context.Background()
3436

35-
// createIndex is a planned M3 operation - should return PlannedOperationError
36-
_, err := gc.Execute(ctx, dbName, "db.users.createIndex({ name: 1 })")
37-
require.Error(t, err)
37+
// createIndex is a planned M3 operation - should return PlannedOperationError
38+
_, err := gc.Execute(ctx, dbName, "db.users.createIndex({ name: 1 })")
39+
require.Error(t, err)
3840

39-
var plannedErr *gomongo.PlannedOperationError
40-
require.ErrorAs(t, err, &plannedErr)
41-
require.Equal(t, "createIndex()", plannedErr.Operation)
41+
var plannedErr *gomongo.PlannedOperationError
42+
require.ErrorAs(t, err, &plannedErr)
43+
require.Equal(t, "createIndex()", plannedErr.Operation)
44+
})
4245
}
4346

4447
func TestUnsupportedOperation(t *testing.T) {
45-
client := testutil.GetClient(t)
46-
dbName := "testdb_unsup_op"
47-
defer testutil.CleanupDatabase(t, client, dbName)
48+
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
49+
dbName := fmt.Sprintf("testdb_unsup_op_%s", db.Name)
50+
defer testutil.CleanupDatabase(t, db.Client, dbName)
4851

49-
gc := gomongo.NewClient(client)
50-
ctx := context.Background()
52+
gc := gomongo.NewClient(db.Client)
53+
ctx := context.Background()
5154

52-
// createSearchIndex is NOT in the registry - should return UnsupportedOperationError
53-
_, err := gc.Execute(ctx, dbName, `db.movies.createSearchIndex({ name: "default", definition: { mappings: { dynamic: true } } })`)
54-
require.Error(t, err)
55+
// createSearchIndex is NOT in the registry - should return UnsupportedOperationError
56+
_, err := gc.Execute(ctx, dbName, `db.movies.createSearchIndex({ name: "default", definition: { mappings: { dynamic: true } } })`)
57+
require.Error(t, err)
5558

56-
var unsupportedErr *gomongo.UnsupportedOperationError
57-
require.ErrorAs(t, err, &unsupportedErr)
58-
require.Equal(t, "createSearchIndex()", unsupportedErr.Operation)
59+
var unsupportedErr *gomongo.UnsupportedOperationError
60+
require.ErrorAs(t, err, &unsupportedErr)
61+
require.Equal(t, "createSearchIndex()", unsupportedErr.Operation)
62+
})
5963
}
6064

6165
func TestUnsupportedOptionError(t *testing.T) {
62-
client := testutil.GetClient(t)
63-
dbName := "testdb_unsup_opt_err"
64-
defer testutil.CleanupDatabase(t, client, dbName)
66+
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
67+
dbName := fmt.Sprintf("testdb_unsup_opt_err_%s", db.Name)
68+
defer testutil.CleanupDatabase(t, db.Client, dbName)
6569

66-
ctx := context.Background()
70+
ctx := context.Background()
6771

68-
gc := gomongo.NewClient(client)
72+
gc := gomongo.NewClient(db.Client)
6973

70-
// find() with unsupported option 'collation'
71-
_, err := gc.Execute(ctx, dbName, `db.users.find({}, {}, { collation: { locale: "en" } })`)
72-
var optErr *gomongo.UnsupportedOptionError
73-
require.ErrorAs(t, err, &optErr)
74-
require.Equal(t, "find()", optErr.Method)
75-
require.Equal(t, "collation", optErr.Option)
74+
// find() with unsupported option 'collation'
75+
_, err := gc.Execute(ctx, dbName, `db.users.find({}, {}, { collation: { locale: "en" } })`)
76+
var optErr *gomongo.UnsupportedOptionError
77+
require.ErrorAs(t, err, &optErr)
78+
require.Equal(t, "find()", optErr.Method)
79+
require.Equal(t, "collation", optErr.Option)
80+
})
7681
}
7782

7883
func TestMethodRegistryStats(t *testing.T) {
@@ -87,79 +92,84 @@ func TestMethodRegistryStats(t *testing.T) {
8792
}
8893

8994
func TestFindOneUnsupportedOption(t *testing.T) {
90-
client := testutil.GetClient(t)
91-
dbName := "testdb_findone_unsup_opt"
92-
defer testutil.CleanupDatabase(t, client, dbName)
95+
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
96+
dbName := fmt.Sprintf("testdb_findone_unsup_opt_%s", db.Name)
97+
defer testutil.CleanupDatabase(t, db.Client, dbName)
9398

94-
ctx := context.Background()
99+
ctx := context.Background()
95100

96-
gc := gomongo.NewClient(client)
101+
gc := gomongo.NewClient(db.Client)
97102

98-
_, err := gc.Execute(ctx, dbName, `db.users.findOne({}, {}, { collation: { locale: "en" } })`)
99-
var optErr *gomongo.UnsupportedOptionError
100-
require.ErrorAs(t, err, &optErr)
101-
require.Equal(t, "findOne()", optErr.Method)
102-
require.Equal(t, "collation", optErr.Option)
103+
_, err := gc.Execute(ctx, dbName, `db.users.findOne({}, {}, { collation: { locale: "en" } })`)
104+
var optErr *gomongo.UnsupportedOptionError
105+
require.ErrorAs(t, err, &optErr)
106+
require.Equal(t, "findOne()", optErr.Method)
107+
require.Equal(t, "collation", optErr.Option)
108+
})
103109
}
104110

105111
func TestAggregateUnsupportedOption(t *testing.T) {
106-
client := testutil.GetClient(t)
107-
dbName := "testdb_agg_unsup_opt"
108-
defer testutil.CleanupDatabase(t, client, dbName)
112+
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
113+
dbName := fmt.Sprintf("testdb_agg_unsup_opt_%s", db.Name)
114+
defer testutil.CleanupDatabase(t, db.Client, dbName)
109115

110-
ctx := context.Background()
116+
ctx := context.Background()
111117

112-
gc := gomongo.NewClient(client)
118+
gc := gomongo.NewClient(db.Client)
113119

114-
_, err := gc.Execute(ctx, dbName, `db.users.aggregate([], { allowDiskUse: true })`)
115-
var optErr *gomongo.UnsupportedOptionError
116-
require.ErrorAs(t, err, &optErr)
117-
require.Equal(t, "aggregate()", optErr.Method)
118-
require.Equal(t, "allowDiskUse", optErr.Option)
120+
_, err := gc.Execute(ctx, dbName, `db.users.aggregate([], { allowDiskUse: true })`)
121+
var optErr *gomongo.UnsupportedOptionError
122+
require.ErrorAs(t, err, &optErr)
123+
require.Equal(t, "aggregate()", optErr.Method)
124+
require.Equal(t, "allowDiskUse", optErr.Option)
125+
})
119126
}
120127

121128
func TestCountDocumentsUnsupportedOption(t *testing.T) {
122-
dbName := "testdb_count_unsup"
123-
client := testutil.GetClient(t)
124-
defer testutil.CleanupDatabase(t, client, dbName)
129+
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
130+
dbName := fmt.Sprintf("testdb_count_unsup_%s", db.Name)
131+
defer testutil.CleanupDatabase(t, db.Client, dbName)
125132

126-
ctx := context.Background()
133+
ctx := context.Background()
127134

128-
gc := gomongo.NewClient(client)
135+
gc := gomongo.NewClient(db.Client)
129136

130-
_, err := gc.Execute(ctx, dbName, `db.users.countDocuments({}, { collation: { locale: "en" } })`)
131-
var optErr *gomongo.UnsupportedOptionError
132-
require.ErrorAs(t, err, &optErr)
133-
require.Equal(t, "countDocuments()", optErr.Method)
137+
_, err := gc.Execute(ctx, dbName, `db.users.countDocuments({}, { collation: { locale: "en" } })`)
138+
var optErr *gomongo.UnsupportedOptionError
139+
require.ErrorAs(t, err, &optErr)
140+
require.Equal(t, "countDocuments()", optErr.Method)
141+
})
134142
}
135143

136144
func TestDistinctUnsupportedOption(t *testing.T) {
137-
dbName := "testdb_distinct_unsup"
138-
client := testutil.GetClient(t)
139-
defer testutil.CleanupDatabase(t, client, dbName)
145+
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
146+
dbName := fmt.Sprintf("testdb_distinct_unsup_%s", db.Name)
147+
defer testutil.CleanupDatabase(t, db.Client, dbName)
140148

141-
ctx := context.Background()
149+
ctx := context.Background()
142150

143-
gc := gomongo.NewClient(client)
151+
gc := gomongo.NewClient(db.Client)
144152

145-
_, err := gc.Execute(ctx, dbName, `db.users.distinct("city", {}, { collation: { locale: "en" } })`)
146-
var optErr *gomongo.UnsupportedOptionError
147-
require.ErrorAs(t, err, &optErr)
148-
require.Equal(t, "distinct()", optErr.Method)
153+
_, err := gc.Execute(ctx, dbName, `db.users.distinct("city", {}, { collation: { locale: "en" } })`)
154+
var optErr *gomongo.UnsupportedOptionError
155+
require.ErrorAs(t, err, &optErr)
156+
require.Equal(t, "distinct()", optErr.Method)
157+
})
149158
}
150159

151160
func TestEstimatedDocumentCountUnsupportedOption(t *testing.T) {
152-
dbName := "testdb_est_count_unsup"
153-
client := testutil.GetClient(t)
154-
defer testutil.CleanupDatabase(t, client, dbName)
161+
testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) {
162+
dbName := fmt.Sprintf("testdb_est_count_unsup_%s", db.Name)
163+
defer testutil.CleanupDatabase(t, db.Client, dbName)
155164

156-
ctx := context.Background()
165+
ctx := context.Background()
157166

158-
gc := gomongo.NewClient(client)
167+
gc := gomongo.NewClient(db.Client)
159168

160-
_, err := gc.Execute(ctx, dbName, `db.users.estimatedDocumentCount({ comment: "test" })`)
161-
var optErr *gomongo.UnsupportedOptionError
162-
require.ErrorAs(t, err, &optErr)
163-
require.Equal(t, "estimatedDocumentCount()", optErr.Method)
164-
require.Equal(t, "comment", optErr.Option)
169+
_, err := gc.Execute(ctx, dbName, `db.users.estimatedDocumentCount({ comment: "test" })`)
170+
var optErr *gomongo.UnsupportedOptionError
171+
require.ErrorAs(t, err, &optErr)
172+
require.Equal(t, "estimatedDocumentCount()", optErr.Method)
173+
require.Equal(t, "comment", optErr.Option)
174+
})
165175
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/bytebase/parser v0.0.0-20260121030202-698704919f24
88
github.com/google/uuid v1.6.0
99
github.com/stretchr/testify v1.11.1
10+
github.com/testcontainers/testcontainers-go v0.40.0
1011
github.com/testcontainers/testcontainers-go/modules/mongodb v0.40.0
1112
go.mongodb.org/mongo-driver/v2 v2.4.1
1213
)
@@ -51,7 +52,6 @@ require (
5152
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
5253
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
5354
github.com/sirupsen/logrus v1.9.3 // indirect
54-
github.com/testcontainers/testcontainers-go v0.40.0 // indirect
5555
github.com/tklauser/go-sysconf v0.3.12 // indirect
5656
github.com/tklauser/numcpus v0.6.1 // indirect
5757
github.com/xdg-go/pbkdf2 v1.0.0 // indirect

0 commit comments

Comments
 (0)