From b0add0cac034d7fc2331d74ed3c030513fced7cc Mon Sep 17 00:00:00 2001 From: h3n4l Date: Tue, 27 Jan 2026 11:27:40 +0800 Subject: [PATCH] fix: return JSON objects from SHOW COLLECTIONS/DATABASES commands SHOW COLLECTIONS, SHOW DATABASES, and db.getCollectionNames() returned plain strings which couldn't be parsed by the frontend's lossless-json. Now returns JSON objects with a "name" field: {"name": "collection_name"} Co-Authored-By: Claude Opus 4.5 --- admin_test.go | 29 +++++++++++++++++++++++------ database_test.go | 32 +++++++++++++++++++++++++------- internal/executor/database.go | 11 +++++++++-- internal/executor/server.go | 11 +++++++++-- 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/admin_test.go b/admin_test.go index 4c2ac58..c7206b6 100644 --- a/admin_test.go +++ b/admin_test.go @@ -3,7 +3,6 @@ package gomongo_test import ( "context" "fmt" - "slices" "strings" "testing" @@ -13,6 +12,24 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" ) +// containsCollectionName checks if the rows contain a JSON object with the given collection name. +func containsCollectionName(rows []string, name string) bool { + for _, row := range rows { + var doc bson.M + if err := bson.UnmarshalExtJSON([]byte(row), false, &doc); err == nil { + if doc["name"] == name { + return true + } + } + } + return false +} + +// containsDatabaseName checks if the rows contain a JSON object with the given database name. +func containsDatabaseName(rows []string, name string) bool { + return containsCollectionName(rows, name) // Same logic +} + func TestCreateIndex(t *testing.T) { testutil.RunOnAllDBs(t, func(t *testing.T, db testutil.TestDB) { dbName := fmt.Sprintf("testdb_create_idx_%s", db.Name) @@ -163,7 +180,7 @@ func TestCreateCollection(t *testing.T) { collResult, err := gc.Execute(ctx, dbName, `show collections`) require.NoError(t, err) require.Equal(t, 1, collResult.RowCount) - require.Equal(t, "newcollection", collResult.Rows[0]) + require.True(t, containsCollectionName(collResult.Rows, "newcollection"), "expected 'newcollection' in result") }) } @@ -183,7 +200,7 @@ func TestDropDatabase(t *testing.T) { // Verify database exists result, err := gc.Execute(ctx, dbName, `show dbs`) require.NoError(t, err) - require.True(t, slices.Contains(result.Rows, dbName), "database should exist before drop") + require.True(t, containsDatabaseName(result.Rows, dbName), "database should exist before drop") // Drop the database result, err = gc.Execute(ctx, dbName, `db.dropDatabase()`) @@ -216,7 +233,7 @@ func TestRenameCollection(t *testing.T) { collResult, err := gc.Execute(ctx, dbName, `show collections`) require.NoError(t, err) require.Equal(t, 1, collResult.RowCount) - require.Equal(t, "newname", collResult.Rows[0]) + require.True(t, containsCollectionName(collResult.Rows, "newname"), "expected 'newname' in result") // Verify data is preserved findResult, err := gc.Execute(ctx, dbName, `db.newname.find()`) @@ -252,7 +269,7 @@ func TestRenameCollectionWithDropTarget(t *testing.T) { collResult, err := gc.Execute(ctx, dbName, `show collections`) require.NoError(t, err) require.Equal(t, 1, collResult.RowCount) - require.Equal(t, "target", collResult.Rows[0]) + require.True(t, containsCollectionName(collResult.Rows, "target"), "expected 'target' in result") // Verify it has source data, not old target data findResult, err := gc.Execute(ctx, dbName, `db.target.find()`) @@ -280,7 +297,7 @@ func TestCreateCollectionWithOptions(t *testing.T) { collResult, err := gc.Execute(ctx, dbName, `show collections`) require.NoError(t, err) require.Equal(t, 1, collResult.RowCount) - require.Equal(t, "cappedcoll", collResult.Rows[0]) + require.True(t, containsCollectionName(collResult.Rows, "cappedcoll"), "expected 'cappedcoll' in result") }) } diff --git a/database_test.go b/database_test.go index b271f5f..cb1a001 100644 --- a/database_test.go +++ b/database_test.go @@ -3,7 +3,6 @@ package gomongo_test import ( "context" "fmt" - "slices" "testing" "github.com/bytebase/gomongo" @@ -40,8 +39,17 @@ func TestShowDatabases(t *testing.T) { require.NotNil(t, result) require.GreaterOrEqual(t, result.RowCount, 1) - // Check that dbName is in the result - require.True(t, slices.Contains(result.Rows, dbName), "expected '%s' in database list, got: %v", dbName, result.Rows) + // Check that dbName is in the result (as JSON object with "name" field) + found := false + for _, row := range result.Rows { + var doc bson.M + err := bson.UnmarshalExtJSON([]byte(row), false, &doc) + if err == nil && doc["name"] == dbName { + found = true + break + } + } + require.True(t, found, "expected database '%s' in result, got: %v", dbName, result.Rows) }) } }) @@ -67,10 +75,15 @@ func TestShowCollections(t *testing.T) { require.NotNil(t, result) require.Equal(t, 2, result.RowCount) - // Check that both collections are in the result + // Check that both collections are in the result (as JSON objects with "name" field) collectionSet := make(map[string]bool) for _, row := range result.Rows { - collectionSet[row] = true + var doc bson.M + err := bson.UnmarshalExtJSON([]byte(row), false, &doc) + require.NoError(t, err, "row should be valid JSON: %s", row) + if name, ok := doc["name"].(string); ok { + collectionSet[name] = true + } } require.True(t, collectionSet["users"], "expected 'users' collection") require.True(t, collectionSet["orders"], "expected 'orders' collection") @@ -97,10 +110,15 @@ func TestGetCollectionNames(t *testing.T) { require.NotNil(t, result) require.Equal(t, 2, result.RowCount) - // Check that both collections are in the result + // Check that both collections are in the result (as JSON objects with "name" field) collectionSet := make(map[string]bool) for _, row := range result.Rows { - collectionSet[row] = true + var doc bson.M + err := bson.UnmarshalExtJSON([]byte(row), false, &doc) + require.NoError(t, err, "row should be valid JSON: %s", row) + if name, ok := doc["name"].(string); ok { + collectionSet[name] = true + } } require.True(t, collectionSet["products"], "expected 'products' collection") require.True(t, collectionSet["categories"], "expected 'categories' collection") diff --git a/internal/executor/database.go b/internal/executor/database.go index d1bc257..44a4df9 100644 --- a/internal/executor/database.go +++ b/internal/executor/database.go @@ -17,8 +17,15 @@ func executeShowCollections(ctx context.Context, client *mongo.Client, database return nil, fmt.Errorf("list collections failed: %w", err) } - rows := make([]string, len(names)) - copy(rows, names) + rows := make([]string, 0, len(names)) + for _, name := range names { + doc := bson.M{"name": name} + jsonBytes, err := bson.MarshalExtJSONIndent(doc, false, false, "", " ") + if err != nil { + return nil, fmt.Errorf("marshal failed: %w", err) + } + rows = append(rows, string(jsonBytes)) + } return &Result{ Rows: rows, diff --git a/internal/executor/server.go b/internal/executor/server.go index bb3c236..510ed89 100644 --- a/internal/executor/server.go +++ b/internal/executor/server.go @@ -15,8 +15,15 @@ func executeShowDatabases(ctx context.Context, client *mongo.Client) (*Result, e return nil, fmt.Errorf("list databases failed: %w", err) } - rows := make([]string, len(names)) - copy(rows, names) + rows := make([]string, 0, len(names)) + for _, name := range names { + doc := bson.M{"name": name} + jsonBytes, err := bson.MarshalExtJSONIndent(doc, false, false, "", " ") + if err != nil { + return nil, fmt.Errorf("marshal failed: %w", err) + } + rows = append(rows, string(jsonBytes)) + } return &Result{ Rows: rows,