Skip to content

Commit d42a247

Browse files
committed
User tests
1 parent c81aa75 commit d42a247

File tree

8 files changed

+337
-12
lines changed

8 files changed

+337
-12
lines changed

client.go

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ type Client interface {
3232

3333
// Database functions
3434
ClientDatabases
35+
36+
// User functions
37+
ClientUsers
3538
}
3639

3740
// ClientConfig contains all settings needed to create a client.

client_users.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type ClientUsers interface {
3737
Users(ctx context.Context) ([]User, error)
3838

3939
// CreateUser creates a new user with given name and opens a connection to it.
40-
// If a user with given name already exists, a DuplicateError is returned.
40+
// If a user with given name already exists, a Conflict error is returned.
4141
CreateUser(ctx context.Context, name string, options *UserOptions) (User, error)
4242
}
4343

client_users_impl.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ package driver
2424

2525
import (
2626
"context"
27+
"net/url"
2728
"path"
2829
)
2930

3031
// User opens a connection to an existing user.
3132
// If no user with given name exists, an NotFoundError is returned.
3233
func (c *client) User(ctx context.Context, name string) (User, error) {
33-
req, err := c.conn.NewRequest("GET", path.Join("_api/user", name))
34+
encodedName := url.QueryEscape(name)
35+
req, err := c.conn.NewRequest("GET", path.Join("_api/user", encodedName))
3436
if err != nil {
3537
return nil, WithStack(err)
3638
}
@@ -54,7 +56,8 @@ func (c *client) User(ctx context.Context, name string) (User, error) {
5456

5557
// UserExists returns true if a database with given name exists.
5658
func (c *client) UserExists(ctx context.Context, name string) (bool, error) {
57-
req, err := c.conn.NewRequest("GET", path.Join("_api/user", name))
59+
encodedName := url.QueryEscape(name)
60+
req, err := c.conn.NewRequest("GET", path.Join("_api/user", encodedName))
5861
if err != nil {
5962
return false, WithStack(err)
6063
}

error.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func IsNotFound(err error) bool {
8282

8383
// IsConflict returns true if the given error is an ArangoError with code 409, indicating a conflict.
8484
func IsConflict(err error) bool {
85-
return IsArangoErrorWithCode(err, 409)
85+
return IsArangoErrorWithCode(err, 409) || IsArangoErrorWithErrorNum(err, 1702)
8686
}
8787

8888
// IsPreconditionFailed returns true if the given error is an ArangoError with code 412, indicating a failed precondition.

test/user_test.go

+283
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package test
24+
25+
import (
26+
"context"
27+
"encoding/json"
28+
"reflect"
29+
"testing"
30+
31+
driver "github.com/arangodb/go-driver"
32+
)
33+
34+
// ensureUser is a helper to check if a user exists and create it if needed.
35+
// It will fail the test when an error occurs.
36+
func ensureUser(ctx context.Context, c driver.Client, name string, options *driver.UserOptions, t *testing.T) driver.User {
37+
u, err := c.User(ctx, name)
38+
if driver.IsNotFound(err) {
39+
u, err = c.CreateUser(ctx, name, options)
40+
if err != nil {
41+
t.Fatalf("Failed to create user '%s': %s", name, describe(err))
42+
}
43+
} else if err != nil {
44+
t.Fatalf("Failed to open user '%s': %s", name, describe(err))
45+
}
46+
return u
47+
}
48+
49+
// TestCreateUser creates a user and then checks that it exists.
50+
func TestCreateUser(t *testing.T) {
51+
c := createClientFromEnv(t, true)
52+
53+
tests := map[string]*driver.UserOptions{
54+
"jan1": nil,
55+
"george": &driver.UserOptions{Password: "foo", Active: boolRef(false)},
56+
"candy": &driver.UserOptions{Password: "ARANGODB_DEFAULT_ROOT_PASSWORD", Active: boolRef(true)},
57+
"joe": &driver.UserOptions{Extra: map[string]interface{}{"key": "value", "x": 5}},
58+
// Some strange names
59+
"ewout/foo": nil,
60+
"admin@api": nil,
61+
"測試用例": nil,
62+
"測試用例@foo": nil,
63+
"_": nil,
64+
" ": nil,
65+
"/": nil,
66+
}
67+
68+
for name, options := range tests {
69+
if _, err := c.CreateUser(nil, name, options); err != nil {
70+
t.Fatalf("Failed to create user '%s': %s", name, describe(err))
71+
}
72+
// User must exist now
73+
if found, err := c.UserExists(nil, name); err != nil {
74+
t.Errorf("UserExists('%s') failed: %s", name, describe(err))
75+
} else if !found {
76+
t.Errorf("UserExists('%s') return false, expected true", name)
77+
}
78+
79+
// Must be able to open user
80+
if u, err := c.User(nil, name); err != nil {
81+
t.Errorf("Failed to open user '%s': %s", name, describe(err))
82+
} else {
83+
if u.Name() != name {
84+
t.Errorf("Invalid name, expected '%s', got '%s'", name, u.Name())
85+
}
86+
if options != nil {
87+
if options.Active != nil {
88+
if u.IsActive() != *options.Active {
89+
t.Errorf("Invalid active, expected '%v', got '%v'", *options.Active, u.IsActive())
90+
}
91+
}
92+
var extra map[string]interface{}
93+
if err := u.Extra(&extra); err != nil {
94+
t.Errorf("Expected success, got %s", describe(err))
95+
} else {
96+
if options.Extra == nil {
97+
if len(extra) != 0 {
98+
t.Errorf("Invalid extra, expected 'nil', got '%+v'", extra)
99+
}
100+
} else {
101+
expected, _ := json.Marshal(options.Extra)
102+
got, _ := json.Marshal(extra)
103+
if string(expected) != string(got) {
104+
t.Errorf("Invalid extra, expected '%s', got '%s'", string(expected), string(got))
105+
}
106+
}
107+
}
108+
}
109+
if u.IsPasswordChangeNeeded() != false {
110+
t.Errorf("Invalid passwordChangeNeeded, expected 'false', got '%v'", u.IsPasswordChangeNeeded())
111+
}
112+
}
113+
114+
// Create again (must fail)
115+
if _, err := c.CreateUser(nil, name, options); !driver.IsConflict(err) {
116+
t.Fatalf("Expected ConflictError, got %s", describe(err))
117+
}
118+
}
119+
120+
// Fetch all users
121+
users, err := c.Users(nil)
122+
if err != nil {
123+
t.Fatalf("Failed to fetch users: %s", describe(err))
124+
}
125+
for userName := range tests {
126+
foundUser := false
127+
for _, u := range users {
128+
if u.Name() == userName {
129+
foundUser = true
130+
break
131+
}
132+
}
133+
if !foundUser {
134+
t.Errorf("Cannot find user '%s'", userName)
135+
}
136+
}
137+
138+
// Now remove the users
139+
for userName := range tests {
140+
u, err := c.User(nil, userName)
141+
if err != nil {
142+
t.Errorf("Expected success, got %s", describe(err))
143+
} else {
144+
if err := u.Remove(context.Background()); err != nil {
145+
t.Errorf("Failed to remove user '%s': %s", userName, describe(err))
146+
}
147+
148+
// User must no longer exist
149+
if found, err := c.UserExists(nil, userName); err != nil {
150+
t.Errorf("Expected success, got %s", describe(err))
151+
} else if found {
152+
t.Errorf("Expected user '%s' to be NOT found, but it was found", userName)
153+
}
154+
}
155+
}
156+
}
157+
158+
// TestUpdateUser creates a user and performs various updates.
159+
func TestUpdateUser(t *testing.T) {
160+
c := createClientFromEnv(t, true)
161+
u := ensureUser(nil, c, "update_user", nil, t)
162+
163+
if err := u.Update(context.TODO(), driver.UserOptions{}); err != nil {
164+
t.Errorf("Cannot update user with empty options: %s", describe(err))
165+
}
166+
167+
if u.IsActive() != true {
168+
t.Errorf("Expected IsActive to be true, got false")
169+
}
170+
if err := u.Update(context.TODO(), driver.UserOptions{
171+
Active: boolRef(false),
172+
}); err != nil {
173+
t.Errorf("Cannot update user with Active in options: %s", describe(err))
174+
}
175+
if u.IsActive() != false {
176+
t.Errorf("Expected IsActive to be false, got true")
177+
}
178+
179+
if err := u.Update(context.TODO(), driver.UserOptions{
180+
Active: boolRef(true),
181+
}); err != nil {
182+
t.Errorf("Cannot update user with Active in options: %s", describe(err))
183+
}
184+
if u.IsActive() != true {
185+
t.Errorf("Expected IsActive to be true, got false")
186+
}
187+
188+
book := Book{Title: "Testing is fun"}
189+
if err := u.Update(context.TODO(), driver.UserOptions{
190+
Extra: book,
191+
}); err != nil {
192+
t.Errorf("Cannot update user with Extra in options: %s", describe(err))
193+
}
194+
var readBook Book
195+
if err := u.Extra(&readBook); err != nil {
196+
t.Errorf("Failed to read extra: %s", describe(err))
197+
} else if !reflect.DeepEqual(book, readBook) {
198+
t.Errorf("Extra differs; expected '%+v', got '%+v'", book, readBook)
199+
}
200+
}
201+
202+
// TestReplaceUser creates a user and performs various replacements.
203+
func TestReplaceUser(t *testing.T) {
204+
c := createClientFromEnv(t, true)
205+
u := ensureUser(nil, c, "replace_user", nil, t)
206+
207+
if err := u.Replace(context.TODO(), driver.UserOptions{}); err != nil {
208+
t.Errorf("Cannot replace user with empty options: %s", describe(err))
209+
}
210+
211+
if u.IsActive() != true {
212+
t.Errorf("Expected IsActive to be true, got false")
213+
}
214+
if err := u.Replace(context.TODO(), driver.UserOptions{
215+
Active: boolRef(false),
216+
}); err != nil {
217+
t.Errorf("Cannot replace user with Active in options: %s", describe(err))
218+
}
219+
if u.IsActive() != false {
220+
t.Errorf("Expected IsActive to be false, got true")
221+
}
222+
223+
book := Book{Title: "Testing is fun"}
224+
if err := u.Replace(context.TODO(), driver.UserOptions{
225+
Extra: book,
226+
}); err != nil {
227+
t.Errorf("Cannot replace user with Extra in options: %s", describe(err))
228+
}
229+
var readBook Book
230+
if err := u.Extra(&readBook); err != nil {
231+
t.Errorf("Failed to read extra: %s", describe(err))
232+
} else if !reflect.DeepEqual(book, readBook) {
233+
t.Errorf("Extra differs; expected '%+v', got '%+v'", book, readBook)
234+
}
235+
if u.IsActive() != true {
236+
t.Errorf("Expected IsActive to be true, got false")
237+
}
238+
}
239+
240+
// TestUpdateUserPasswordMyself creates a user and tries to update the password of the authenticated user.
241+
func TestUpdateUserPasswordMyself(t *testing.T) {
242+
c := createClientFromEnv(t, true)
243+
ensureUser(nil, c, "user@TestUpdateUserPasswordMyself", &driver.UserOptions{Password: "foo"}, t)
244+
245+
authClient, err := driver.NewClient(driver.ClientConfig{
246+
Connection: createConnectionFromEnv(t),
247+
Authentication: driver.BasicAuthentication("user@TestUpdateUserPasswordMyself", "foo"),
248+
})
249+
if err != nil {
250+
t.Fatalf("Expected success, got %s", describe(err))
251+
}
252+
253+
u, err := authClient.User(nil, "user@TestUpdateUserPasswordMyself")
254+
if err != nil {
255+
t.Fatalf("Expected success, got %s", describe(err))
256+
}
257+
if err := u.Update(context.TODO(), driver.UserOptions{Password: "something"}); err != nil {
258+
t.Errorf("Expected success, got %s", describe(err))
259+
}
260+
}
261+
262+
// TestUpdateUserPasswordOtherUser creates a user and tries to update the password of another user.
263+
func TestUpdateUserPasswordOtherUser(t *testing.T) {
264+
c := createClientFromEnv(t, true)
265+
ensureUser(nil, c, "user1", &driver.UserOptions{Password: "foo"}, t)
266+
ensureUser(nil, c, "user2", nil, t)
267+
268+
authClient, err := driver.NewClient(driver.ClientConfig{
269+
Connection: createConnectionFromEnv(t),
270+
Authentication: driver.BasicAuthentication("user1", "foo"),
271+
})
272+
if err != nil {
273+
t.Fatalf("Expected success, got %s", describe(err))
274+
}
275+
276+
u2, err := authClient.User(nil, "user2")
277+
if err != nil {
278+
t.Fatalf("Expected success, got %s", describe(err))
279+
}
280+
if err := u2.Update(context.TODO(), driver.UserOptions{Password: "something"}); err == nil {
281+
t.Errorf("Expected failure, got %s", describe(err))
282+
}
283+
}

test/util.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package test
24+
25+
// boolRef returns a reference to a given boolean
26+
func boolRef(v bool) *bool {
27+
return &v
28+
}

user.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type User interface {
3636
IsPasswordChangeNeeded() bool
3737

3838
// Get extra information about this user that was passed during its creation/update/replacement
39-
Extra() interface{}
39+
Extra(result interface{}) error
4040

4141
// Remove removes the user.
4242
// If the user does not exist, a NotFoundError is returned.

0 commit comments

Comments
 (0)