mirror of
https://github.com/hanzoai/docdb.git
synced 2026-10-30 18:39:39 +00:00
zip's registration is a verb method on the group, so an operation reads as the group answering it rather than a function taking the group, and a route that is not a typed operation says which method it answers with. The package-level form and the Router interface it existed to accept any target through are both gone. The toolchain and the pin move together, deliberately. Below zip v1.36.50 the JSON encoder was selected on a build tag, so a repo whose go directive rises first writes a wire that is neither the old release's nor the new one's until the pin catches up. Co-authored-by: Hanzo Dev <[email protected]>
484 lines
14 KiB
Go
484 lines
14 KiB
Go
// Copyright 2021 DocDB Inc.
|
|
//
|
|
// 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
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package integration
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
|
|
"github.com/hanzoai/docdb/integration/setup"
|
|
"github.com/hanzoai/docdb/integration/shareddata"
|
|
)
|
|
|
|
// TestCreateIndexesCommandCompat tests specific behavior for index creation that can be only provided through RunCommand.
|
|
func TestCreateIndexesCommandCompat(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, targetCollections, compatCollections := setup.SetupCompat(t)
|
|
targetCollection := targetCollections[0]
|
|
compatCollection := compatCollections[0]
|
|
|
|
for name, tc := range map[string]struct { //nolint:vet // for readability
|
|
collectionName any
|
|
indexName any
|
|
key any
|
|
unique any
|
|
resultType CompatTestCaseResultType // defaults to NonEmptyResult
|
|
|
|
failsForDocDB string
|
|
}{
|
|
"InvalidCollectionName": {
|
|
collectionName: 42,
|
|
key: bson.D{{"v", -1}},
|
|
indexName: "custom-name",
|
|
resultType: EmptyResult,
|
|
},
|
|
"NilCollectionName": {
|
|
collectionName: nil,
|
|
key: bson.D{{"v", -1}},
|
|
indexName: "custom-name",
|
|
resultType: EmptyResult,
|
|
},
|
|
"EmptyCollectionName": {
|
|
collectionName: "",
|
|
key: bson.D{{"v", -1}},
|
|
indexName: "custom-name",
|
|
resultType: EmptyResult,
|
|
},
|
|
"IndexNameNotSet": {
|
|
collectionName: "test",
|
|
key: bson.D{{"v", -1}},
|
|
indexName: nil,
|
|
resultType: EmptyResult,
|
|
},
|
|
"EmptyIndexName": {
|
|
collectionName: "test",
|
|
key: bson.D{{"v", -1}},
|
|
indexName: "",
|
|
resultType: EmptyResult,
|
|
},
|
|
"NonStringIndexName": {
|
|
collectionName: "test",
|
|
key: bson.D{{"v", -1}},
|
|
indexName: 42,
|
|
resultType: EmptyResult,
|
|
},
|
|
"ExistingNameDifferentKeyLength": {
|
|
collectionName: "test",
|
|
key: bson.D{{"_id", 1}, {"v", 1}},
|
|
indexName: "_id_", // the same name as the default index
|
|
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/290",
|
|
},
|
|
"InvalidKey": {
|
|
collectionName: "test",
|
|
key: 42,
|
|
resultType: EmptyResult,
|
|
},
|
|
"EmptyKey": {
|
|
collectionName: "test",
|
|
key: bson.D{},
|
|
resultType: EmptyResult,
|
|
},
|
|
"KeyNotSet": {
|
|
collectionName: "test",
|
|
resultType: EmptyResult,
|
|
},
|
|
"UniqueFalse": {
|
|
collectionName: "unique_false",
|
|
key: bson.D{{"v", 1}},
|
|
indexName: "unique_false",
|
|
unique: false,
|
|
},
|
|
"UniqueTypeDocument": {
|
|
collectionName: "test",
|
|
key: bson.D{{"v", 1}},
|
|
indexName: "test",
|
|
unique: bson.D{},
|
|
resultType: EmptyResult,
|
|
},
|
|
} {
|
|
t.Run(name, func(tt *testing.T) {
|
|
tt.Helper()
|
|
tt.Parallel()
|
|
|
|
var t testing.TB = tt
|
|
|
|
if tc.failsForDocDB != "" {
|
|
t = setup.FailsForDocDB(tt, tc.failsForDocDB)
|
|
}
|
|
|
|
indexesDoc := bson.D{}
|
|
|
|
if tc.key != nil {
|
|
indexesDoc = append(indexesDoc, bson.E{Key: "key", Value: tc.key})
|
|
}
|
|
|
|
if tc.indexName != nil {
|
|
indexesDoc = append(indexesDoc, bson.E{"name", tc.indexName})
|
|
}
|
|
|
|
if tc.unique != nil {
|
|
indexesDoc = append(indexesDoc, bson.E{Key: "unique", Value: tc.unique})
|
|
}
|
|
|
|
var targetRes bson.D
|
|
targetErr := targetCollection.Database().RunCommand(ctx, bson.D{
|
|
{"createIndexes", tc.collectionName},
|
|
{"indexes", bson.A{indexesDoc}},
|
|
}).Decode(&targetRes)
|
|
|
|
var compatRes bson.D
|
|
compatErr := compatCollection.Database().RunCommand(ctx, bson.D{
|
|
{"createIndexes", tc.collectionName},
|
|
{"indexes", bson.A{indexesDoc}},
|
|
}).Decode(&compatRes)
|
|
|
|
if targetErr != nil {
|
|
t.Logf("Target error: %v", targetErr)
|
|
t.Logf("Compat error: %v", compatErr)
|
|
|
|
// error messages are intentionally not compared
|
|
AssertMatchesCommandError(t, compatErr, targetErr)
|
|
|
|
return
|
|
}
|
|
require.NoError(t, compatErr, "compat error; target returned no error")
|
|
|
|
if tc.resultType == EmptyResult {
|
|
require.Nil(t, targetRes)
|
|
require.Nil(t, compatRes)
|
|
}
|
|
|
|
AssertEqualDocuments(t, compatRes, targetRes)
|
|
|
|
targetCursor, targetErr := targetCollection.Indexes().List(ctx)
|
|
compatCursor, compatErr := compatCollection.Indexes().List(ctx)
|
|
|
|
if targetCursor != nil {
|
|
defer targetCursor.Close(ctx)
|
|
}
|
|
if compatCursor != nil {
|
|
defer compatCursor.Close(ctx)
|
|
}
|
|
|
|
require.NoError(t, targetErr)
|
|
require.NoError(t, compatErr)
|
|
|
|
targetListRes := FetchAll(t, ctx, targetCursor)
|
|
compatListRes := FetchAll(t, ctx, compatCursor)
|
|
|
|
assert.Equal(t, compatListRes, targetListRes)
|
|
|
|
targetSpec, targetErr := targetCollection.Indexes().ListSpecifications(ctx)
|
|
compatSpec, compatErr := compatCollection.Indexes().ListSpecifications(ctx)
|
|
|
|
require.NoError(t, compatErr)
|
|
require.NoError(t, targetErr)
|
|
|
|
assert.Equal(t, compatSpec, targetSpec)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestCreateIndexesCommandCompatCheckFields check that the response contains response's fields
|
|
// such as numIndexBefore, numIndexAfter, createdCollectionAutomatically
|
|
// contain the correct values.
|
|
func TestCreateIndexesCommandCompatCheckFields(tt *testing.T) {
|
|
tt.Parallel()
|
|
|
|
ctx, targetCollections, compatCollections := setup.SetupCompat(tt)
|
|
targetCollection := targetCollections[0]
|
|
compatCollection := compatCollections[0]
|
|
|
|
// Create an index for a non-existent collection, expect createdCollectionAutomatically to be true.
|
|
collectionName := "newCollection"
|
|
indexesDoc := bson.D{{"key", bson.D{{"v", 1}}}, {"name", "v_1"}}
|
|
|
|
var targetRes bson.D
|
|
targetErr := targetCollection.Database().RunCommand(ctx, bson.D{
|
|
{"createIndexes", collectionName},
|
|
{"indexes", bson.A{indexesDoc}},
|
|
}).Decode(&targetRes)
|
|
|
|
var compatRes bson.D
|
|
compatErr := compatCollection.Database().RunCommand(ctx, bson.D{
|
|
{"createIndexes", collectionName},
|
|
{"indexes", bson.A{indexesDoc}},
|
|
}).Decode(&compatRes)
|
|
|
|
require.NoError(tt, compatErr)
|
|
require.NoError(tt, targetErr, "target error; compat returned no error")
|
|
|
|
AssertEqualDocuments(tt, compatRes, targetRes)
|
|
|
|
// Now this collection exists, so we create another index and expect createdCollectionAutomatically to be false.
|
|
indexesDoc = bson.D{{"key", bson.D{{"foo", 1}}}, {"name", "foo_1"}}
|
|
|
|
targetErr = targetCollection.Database().RunCommand(ctx, bson.D{
|
|
{"createIndexes", collectionName},
|
|
{"indexes", bson.A{indexesDoc}},
|
|
}).Decode(&targetRes)
|
|
|
|
compatErr = compatCollection.Database().RunCommand(ctx, bson.D{
|
|
{"createIndexes", collectionName},
|
|
{"indexes", bson.A{indexesDoc}},
|
|
}).Decode(&compatRes)
|
|
|
|
require.NoError(tt, compatErr)
|
|
require.NoError(tt, targetErr, "target error; compat returned no error")
|
|
|
|
AssertEqualDocuments(tt, compatRes, targetRes)
|
|
|
|
// Call index creation for the index that already exists, expect note to be set.
|
|
indexesDoc = bson.D{{"key", bson.D{{"foo", 1}}}, {"name", "foo_1"}}
|
|
|
|
t := setup.FailsForDocDB(tt, "https://github.com/hanzoai/docdb-DocumentDB/issues/499")
|
|
|
|
targetErr = targetCollection.Database().RunCommand(ctx, bson.D{
|
|
{"createIndexes", collectionName},
|
|
{"indexes", bson.A{indexesDoc}},
|
|
}).Decode(&targetRes)
|
|
|
|
compatErr = compatCollection.Database().RunCommand(ctx, bson.D{
|
|
{"createIndexes", collectionName},
|
|
{"indexes", bson.A{indexesDoc}},
|
|
}).Decode(&compatRes)
|
|
|
|
require.NoError(t, compatErr)
|
|
require.NoError(t, targetErr, "target error; compat returned no error")
|
|
|
|
AssertEqualDocuments(t, compatRes, targetRes)
|
|
}
|
|
|
|
func TestDropIndexesCommandCompat(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for name, tc := range map[string]struct { //nolint:vet // for readability
|
|
toCreate []mongo.IndexModel // optional, if set, create the given indexes before drop is called
|
|
toDrop any // required, index to drop
|
|
|
|
resultType CompatTestCaseResultType // optional, defaults to NonEmptyResult
|
|
|
|
failsForDocDB string
|
|
}{
|
|
"MultipleIndexesByName": {
|
|
toCreate: []mongo.IndexModel{
|
|
{Keys: bson.D{{"v", -1}}},
|
|
{Keys: bson.D{{"v", 1}, {"foo", 1}}},
|
|
{Keys: bson.D{{"v.foo", -1}}},
|
|
},
|
|
toDrop: bson.A{"v_-1", "v_1_foo_1"},
|
|
failsForDocDB: "https://github.com/hanzoai/docdb/issues/5119",
|
|
},
|
|
"MultipleIndexesByKey": {
|
|
toCreate: []mongo.IndexModel{
|
|
{Keys: bson.D{{"v", -1}}},
|
|
{Keys: bson.D{{"v.foo", -1}}},
|
|
},
|
|
toDrop: bson.A{bson.D{{"v", -1}}, bson.D{{"v.foo", -1}}},
|
|
resultType: EmptyResult,
|
|
},
|
|
"NonExistentMultipleIndexes": {
|
|
toDrop: bson.A{"non-existent", "invalid"},
|
|
resultType: EmptyResult,
|
|
},
|
|
"MultipleIndexesWithDefault": {
|
|
toCreate: []mongo.IndexModel{
|
|
{Keys: bson.D{{"v", 1}}},
|
|
},
|
|
toDrop: bson.A{"v_1", "_id_"},
|
|
resultType: EmptyResult,
|
|
},
|
|
"InvalidMultipleIndexType": {
|
|
toDrop: bson.A{1},
|
|
resultType: EmptyResult,
|
|
},
|
|
"DocumentIndex": {
|
|
toCreate: []mongo.IndexModel{
|
|
{Keys: bson.D{{"v", -1}}},
|
|
},
|
|
toDrop: bson.D{{"v", -1}},
|
|
failsForDocDB: "https://github.com/hanzoai/docdb/issues/5119",
|
|
},
|
|
"SimilarIndexes": {
|
|
toCreate: []mongo.IndexModel{
|
|
{Keys: bson.D{{"v", 1}, {"foo", 1}}},
|
|
{Keys: bson.D{{"v", 1}, {"bar", 1}}},
|
|
},
|
|
toDrop: bson.D{{"v", 1}, {"bar", 1}},
|
|
failsForDocDB: "https://github.com/hanzoai/docdb/issues/5119",
|
|
},
|
|
"DropAllExpression": {
|
|
toCreate: []mongo.IndexModel{
|
|
{Keys: bson.D{{"v", -1}}},
|
|
{Keys: bson.D{{"foo.bar", 1}}},
|
|
{Keys: bson.D{{"foo", 1}, {"bar", 1}}},
|
|
},
|
|
toDrop: "*",
|
|
failsForDocDB: "https://github.com/hanzoai/docdb/issues/5119",
|
|
},
|
|
"WrongExpression": {
|
|
toCreate: []mongo.IndexModel{
|
|
{Keys: bson.D{{"v", -1}}},
|
|
{Keys: bson.D{{"foo.bar", 1}}},
|
|
{Keys: bson.D{{"foo", 1}, {"bar", 1}}},
|
|
},
|
|
toDrop: "***",
|
|
resultType: EmptyResult,
|
|
},
|
|
"NonExistentDescendingID": {
|
|
toDrop: bson.D{{"_id", -1}},
|
|
resultType: EmptyResult,
|
|
},
|
|
"MultipleKeyIndex": {
|
|
toCreate: []mongo.IndexModel{
|
|
{Keys: bson.D{{"_id", -1}, {"v", 1}}},
|
|
},
|
|
toDrop: bson.D{
|
|
{"_id", -1},
|
|
{"v", 1},
|
|
},
|
|
failsForDocDB: "https://github.com/hanzoai/docdb/issues/5119",
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
require.NotNil(t, tc.toDrop, "toDrop must not be nil")
|
|
|
|
// It's enough to use a single provider for drop indexes test as indexes work the same for different collections.
|
|
s := setup.SetupCompatWithOpts(t, &setup.SetupCompatOpts{
|
|
Providers: []shareddata.Provider{shareddata.Composites},
|
|
AddNonExistentCollection: true,
|
|
})
|
|
ctx, targetCollections, compatCollections := s.Ctx, s.TargetCollections, s.CompatCollections
|
|
|
|
var nonEmptyResults bool
|
|
for i := range targetCollections {
|
|
targetCollection := targetCollections[i]
|
|
compatCollection := compatCollections[i]
|
|
|
|
t.Run(targetCollection.Name(), func(tt *testing.T) {
|
|
var t testing.TB = tt
|
|
|
|
if tc.failsForDocDB != "" {
|
|
t = setup.FailsForDocDB(tt, tc.failsForDocDB)
|
|
}
|
|
|
|
if tc.toCreate != nil {
|
|
_, targetErr := targetCollection.Indexes().CreateMany(ctx, tc.toCreate)
|
|
_, compatErr := compatCollection.Indexes().CreateMany(ctx, tc.toCreate)
|
|
require.NoError(t, compatErr)
|
|
require.NoError(t, targetErr)
|
|
|
|
// List indexes to see they are identical after creation.
|
|
targetCursor, targetListErr := targetCollection.Indexes().List(ctx)
|
|
compatCursor, compatListErr := compatCollection.Indexes().List(ctx)
|
|
|
|
if targetCursor != nil {
|
|
defer targetCursor.Close(ctx)
|
|
}
|
|
if compatCursor != nil {
|
|
defer compatCursor.Close(ctx)
|
|
}
|
|
|
|
require.NoError(t, targetListErr)
|
|
require.NoError(t, compatListErr)
|
|
|
|
targetList := FetchAll(t, ctx, targetCursor)
|
|
compatList := FetchAll(t, ctx, compatCursor)
|
|
|
|
require.ElementsMatch(t, compatList, targetList)
|
|
}
|
|
|
|
targetCommand := bson.D{
|
|
{"dropIndexes", targetCollection.Name()},
|
|
{"index", tc.toDrop},
|
|
}
|
|
|
|
compatCommand := bson.D{
|
|
{"dropIndexes", compatCollection.Name()},
|
|
{"index", tc.toDrop},
|
|
}
|
|
|
|
var targetRes bson.D
|
|
targetErr := targetCollection.Database().RunCommand(ctx, targetCommand).Decode(&targetRes)
|
|
|
|
var compatRes bson.D
|
|
compatErr := compatCollection.Database().RunCommand(ctx, compatCommand).Decode(&compatRes)
|
|
|
|
if targetErr != nil {
|
|
t.Logf("Target error: %v", targetErr)
|
|
t.Logf("Compat error: %v", compatErr)
|
|
|
|
// error messages are intentionally not compared
|
|
AssertMatchesCommandError(t, compatErr, targetErr)
|
|
|
|
return
|
|
}
|
|
require.NoError(t, compatErr, "compat error; target returned no error")
|
|
|
|
if tc.resultType == EmptyResult {
|
|
require.Nil(t, targetRes)
|
|
require.Nil(t, compatRes)
|
|
}
|
|
|
|
AssertEqualDocuments(t, compatRes, targetRes)
|
|
|
|
if compatErr == nil {
|
|
nonEmptyResults = true
|
|
}
|
|
|
|
// List indexes to see they are identical after deletion.
|
|
targetCursor, targetListErr := targetCollection.Indexes().List(ctx)
|
|
compatCursor, compatListErr := compatCollection.Indexes().List(ctx)
|
|
|
|
if targetCursor != nil {
|
|
defer targetCursor.Close(ctx)
|
|
}
|
|
if compatCursor != nil {
|
|
defer compatCursor.Close(ctx)
|
|
}
|
|
|
|
require.NoError(t, targetListErr)
|
|
require.NoError(t, compatListErr)
|
|
|
|
targetList := FetchAll(t, ctx, targetCursor)
|
|
compatList := FetchAll(t, ctx, compatCursor)
|
|
|
|
assert.ElementsMatch(t, compatList, targetList)
|
|
})
|
|
}
|
|
|
|
switch tc.resultType {
|
|
case NonEmptyResult:
|
|
if tc.failsForDocDB != "" {
|
|
return
|
|
}
|
|
|
|
require.True(t, nonEmptyResults, "expected non-empty results (some indexes should be deleted)")
|
|
case EmptyResult:
|
|
require.False(t, nonEmptyResults, "expected empty results (no indexes should be deleted)")
|
|
default:
|
|
t.Fatalf("unknown result type %v", tc.resultType)
|
|
}
|
|
})
|
|
}
|
|
}
|