Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
henvic committed Dec 14, 2023
1 parent 25afee7 commit 0b7235e
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 30 deletions.
167 changes: 145 additions & 22 deletions integration/users/create_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,50 +15,173 @@
package users

import (
"context"
"testing"

"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"

"github.com/FerretDB/FerretDB/integration"
"github.com/FerretDB/FerretDB/integration/setup"
"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/must"
"github.com/FerretDB/FerretDB/internal/util/testutil"
"github.com/stretchr/testify/assert"
)

func TestCreateUser(t *testing.T) {
t.Parallel()

ctx, collection := setup.Setup(t)
// https://www.mongodb.com/docs/manual/reference/command/createUser/

_ = collection.Database().RunCommand(ctx, bson.D{
{"dropAllUsersFromDatabase", 1},
})

var res bson.D
err := collection.Database().RunCommand(ctx, bson.D{
{"createUser", "testuser"},
{"roles", bson.A{}},
{"pwd", "password"},
}).Decode(&res)

require.NoError(t, err)

actual := integration.ConvertDocument(t, res)
actual.Remove("$clusterTime")
actual.Remove("operationTime")

expected := integration.ConvertDocument(t, bson.D{{"ok", float64(1)}})

testutil.AssertEqual(t, expected, actual)
db := collection.Database()

err := db.RunCommand(ctx, bson.D{
{Key: "createUser", Value: "should_already_exist"},
{Key: "roles", Value: bson.A{}},
{Key: "pwd", Value: "password"},
}).Err()
assert.NoError(t, err)

testCases := map[string]struct {
payload bson.D
err *mongo.CommandError
altMessage string
expected bson.D
skip string
}{
"MissingRoles": {
payload: bson.D{
{Key: "createUser", Value: "missing_roles"},
},
err: &mongo.CommandError{
Code: 40414,
Name: "Location40414",
Message: "BSON field 'createUser.roles' is missing but a required field",
},
skip: "not implemented yet", // TODO: implement for FerretDB
},
"AlreadyExists": {
payload: bson.D{
{Key: "createUser", Value: "should_already_exist"},
{Key: "roles", Value: bson.A{}},
{Key: "pwd", Value: "password"},
},
err: &mongo.CommandError{
Code: 51003,
Name: "Location51003",
Message: "User \"should_already_exist@TestCreateUser\" already exists",
},
},
"MissingPwdOrExternal": {
payload: bson.D{
{Key: "createUser", Value: "mising_pwd_or_external"},
{Key: "roles", Value: bson.A{}},
},
err: &mongo.CommandError{
Code: 2,
Name: "BadValue",
Message: "Must provide a 'pwd' field for all user documents, except those with '$external' as the user's source db",
},
},
"Success": {
payload: bson.D{
{Key: "createUser", Value: "success_user"},
{Key: "roles", Value: bson.A{}},
{Key: "pwd", Value: "password"},
},
expected: bson.D{
{
Key: "ok", Value: float64(1),
},
},
},
"WithComment": {
payload: bson.D{
{Key: "createUser", Value: "with_comment_user"},
{Key: "roles", Value: bson.A{}},
{Key: "pwd", Value: "password"},
{Key: "comment", Value: "test string comment"},
},
expected: bson.D{
{
Key: "ok", Value: float64(1),
},
},
},
"WithCommentComposite": {
payload: bson.D{
{Key: "createUser", Value: "with_comment_composite"},
{Key: "roles", Value: bson.A{}},
{Key: "pwd", Value: "password"},
{
Key: "comment",
Value: bson.D{
{Key: "example", Value: "blah"},
{
Key: "complex",
Value: bson.A{
bson.D{{Key: "x", Value: "y"}},
},
},
},
},
},
expected: bson.D{
{
Key: "ok", Value: float64(1),
},
},
},
}
for name, tc := range testCases {
tc := tc
t.Run(name, func(t *testing.T) {
if tc.skip != "" {
t.Skip(tc.skip)
}
t.Parallel()
var res bson.D
err := db.RunCommand(ctx, tc.payload).Decode(&res)
if tc.err != nil {
integration.AssertEqualAltCommandError(t, *tc.err, tc.altMessage, err)
return
}

actual := integration.ConvertDocument(t, res)
actual.Remove("$clusterTime")
actual.Remove("operationTime")

expected := integration.ConvertDocument(t, tc.expected)
testutil.AssertEqual(t, expected, actual)
assert.NoError(t, err)
payload := integration.ConvertDocument(t, tc.payload)
assertUserExists(ctx, t, db, payload)
})
}
}

func assertUserExists(ctx context.Context, t testing.TB, db *mongo.Database, payload *types.Document) {
t.Helper()
var rec bson.D
err = collection.Database().Collection("system.users").FindOne(ctx, bson.D{{"user", "testuser"}}).Decode(&rec)
require.NoError(t, err)
err := db.Collection("system.users").FindOne(ctx, bson.D{
{Key: "user", Value: must.NotFail(payload.Get("createUser"))},
}).Decode(&rec)
assert.NoError(t, err)

var x any
t.Error(db.ListCollections(ctx, x))
t.Error(rec)
err = db.Collection("system.users").FindOne(ctx, bson.D{}).Decode(&rec)
assert.NoError(t, err)

actualRecorded := integration.ConvertDocument(t, rec)
expectedRec := integration.ConvertDocument(t, bson.D{{"ok", float64(1)}})
expectedRec := integration.ConvertDocument(t, bson.D{{Key: "ok", Value: float64(1)}})

testutil.AssertEqual(t, expectedRec, actualRecorded)

// TODO compare other data
}
43 changes: 35 additions & 8 deletions internal/handler/msg_createuser.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/FerretDB/FerretDB/internal/backends"
"github.com/FerretDB/FerretDB/internal/handler/common"
"github.com/FerretDB/FerretDB/internal/handler/handlererrors"
"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/internal/util/must"
Expand All @@ -39,7 +40,7 @@ func (h *Handler) MsgCreateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpM
return nil, err
}

Check warning on line 41 in internal/handler/msg_createuser.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_createuser.go#L38-L41

Added lines #L38 - L41 were not covered by tests

_, err = common.GetRequiredParam[string](document, "pwd")
_, err = common.GetOptionalParam[string](document, "pwd", "")
if err != nil {
return nil, err
}

Check warning on line 46 in internal/handler/msg_createuser.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_createuser.go#L43-L46

Added lines #L43 - L46 were not covered by tests
Expand All @@ -51,11 +52,43 @@ func (h *Handler) MsgCreateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpM
return nil, err
}

Check warning on line 53 in internal/handler/msg_createuser.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_createuser.go#L48-L53

Added lines #L48 - L53 were not covered by tests

common.Ignored(document, h.L, "roles", "writeConcern", "authenticationRestrictions", "mechanisms")

saved := must.NotFail(types.NewDocument(
"user", username,
"roles", must.NotFail(types.NewArray()), // Non-default value is currently ignored.
"pwd", "password", // TODO: hash the password.
))

if document.Has("customData") {
customData, err := common.GetOptionalParam[*types.Document](document, "customData", nil)
if err != nil {
return nil, err
}
saved.Set("customData", customData)

Check warning on line 68 in internal/handler/msg_createuser.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_createuser.go#L55-L68

Added lines #L55 - L68 were not covered by tests
}

if document.Has("digestPassword") {
digestPassword, err := common.GetOptionalParam[bool](document, "digestPassword", true)
if err != nil {
return nil, err
}
saved.Set("digestPassword", digestPassword)

Check warning on line 76 in internal/handler/msg_createuser.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_createuser.go#L71-L76

Added lines #L71 - L76 were not covered by tests
}

if document.Has("comment") {
saved.Set("comment", must.NotFail(document.Get("comment")))
}

Check warning on line 81 in internal/handler/msg_createuser.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_createuser.go#L79-L81

Added lines #L79 - L81 were not covered by tests

dbName, err := common.GetRequiredParam[string](document, "$db")
if err != nil {
return nil, err
}

Check warning on line 86 in internal/handler/msg_createuser.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_createuser.go#L83-L86

Added lines #L83 - L86 were not covered by tests

if dbName != "$external" && !document.Has("pwd") {
return nil, handlererrors.NewCommandErrorMsg(handlererrors.ErrBadValue, "Must provide a 'pwd' field for all user documents, except those with '$external' as the user's source db")
}

Check warning on line 90 in internal/handler/msg_createuser.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_createuser.go#L88-L90

Added lines #L88 - L90 were not covered by tests

db, err := h.b.Database(dbName)
if err != nil {
return nil, lazyerrors.Error(err)
Expand All @@ -67,18 +100,12 @@ func (h *Handler) MsgCreateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpM
}

Check warning on line 100 in internal/handler/msg_createuser.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_createuser.go#L97-L100

Added lines #L97 - L100 were not covered by tests

_, err = collection.InsertAll(ctx, &backends.InsertAllParams{
Docs: []*types.Document{must.NotFail(types.NewDocument(
"createdUser", username,
"roles", []string{},
"pwd", "password", // TODO: hash the password.
)),
},
Docs: []*types.Document{saved},
})
if err != nil {
return nil, lazyerrors.Error(err)
}

Check warning on line 107 in internal/handler/msg_createuser.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_createuser.go#L102-L107

Added lines #L102 - L107 were not covered by tests

// TODO https://github.com/FerretDB/FerretDB/issues/1491
var reply wire.OpMsg
must.NoError(reply.SetSections(wire.OpMsgSection{
Documents: []*types.Document{must.NotFail(types.NewDocument(
Expand Down

0 comments on commit 0b7235e

Please sign in to comment.