1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
15 "git.arvados.org/arvados.git/sdk/go/arvados"
19 // Gocheck boilerplate
20 func Test(t *testing.T) {
24 type TestSuite struct {
27 users map[string]arvados.User
30 func (s *TestSuite) SetUpTest(c *C) {
31 s.ac = arvados.NewClientFromEnv()
32 u, err := s.ac.CurrentUser()
34 c.Assert(u.IsAdmin, Equals, true)
36 s.users = make(map[string]arvados.User)
37 ul := arvados.UserList{}
38 s.ac.RequestAndDecode(&ul, "GET", "/arvados/v1/users", nil, arvados.ResourceListParams{})
39 c.Assert(ul.ItemsAvailable, Not(Equals), 0)
40 s.users = make(map[string]arvados.User)
41 for _, u := range ul.Items {
45 // Set up command config
46 os.Args = []string{"cmd", "somefile.csv"}
47 config, err := GetConfig()
52 func (s *TestSuite) TearDownTest(c *C) {
54 // Reset database to fixture state after every test run.
55 err := s.cfg.Client.RequestAndDecode(&dst, "POST", "/database/reset", nil, nil)
59 var _ = Suite(&TestSuite{})
61 // MakeTempCSVFile creates a temp file with data as comma separated values
62 func MakeTempCSVFile(data [][]string) (f *os.File, err error) {
63 f, err = ioutil.TempFile("", "test_sync_users")
67 for _, line := range data {
68 fmt.Fprintf(f, "%s\n", strings.Join(line, ","))
74 func ListUsers(ac *arvados.Client) ([]arvados.User, error) {
75 var ul arvados.UserList
76 err := ac.RequestAndDecode(&ul, "GET", "/arvados/v1/users", nil, arvados.ResourceListParams{})
83 func (s *TestSuite) TestParseFlagsWithoutPositionalArgument(c *C) {
84 os.Args = []string{"cmd", "-verbose"}
85 err := ParseFlags(&ConfigParams{})
89 func (s *TestSuite) TestParseFlagsWithPositionalArgument(c *C) {
91 os.Args = []string{"cmd", "/tmp/somefile.csv"}
92 err := ParseFlags(&cfg)
94 c.Assert(cfg.Path, Equals, "/tmp/somefile.csv")
95 c.Assert(cfg.Verbose, Equals, false)
96 c.Assert(cfg.DeactivateUnlisted, Equals, false)
99 func (s *TestSuite) TestParseFlagsWithOptionalFlags(c *C) {
100 cfg := ConfigParams{}
101 os.Args = []string{"cmd", "-verbose", "-deactivate-unlisted", "/tmp/somefile.csv"}
102 err := ParseFlags(&cfg)
104 c.Assert(cfg.Path, Equals, "/tmp/somefile.csv")
105 c.Assert(cfg.Verbose, Equals, true)
106 c.Assert(cfg.DeactivateUnlisted, Equals, true)
109 func (s *TestSuite) TestGetConfig(c *C) {
110 os.Args = []string{"cmd", "/tmp/somefile.csv"}
111 cfg, err := GetConfig()
113 c.Assert(cfg.AnonUserUUID, Not(Equals), "")
114 c.Assert(cfg.SysUserUUID, Not(Equals), "")
115 c.Assert(cfg.CurrentUser, Not(Equals), "")
116 c.Assert(cfg.ClusterID, Not(Equals), "")
117 c.Assert(cfg.Client, NotNil)
120 func (s *TestSuite) TestFailOnEmptyFields(c *C) {
121 records := [][]string{
122 {"", "first-name", "last-name", "1", "0"},
123 {"user@example", "", "last-name", "1", "0"},
124 {"user@example", "first-name", "", "1", "0"},
125 {"user@example", "first-name", "last-name", "", "0"},
126 {"user@example", "first-name", "last-name", "1", ""},
128 for _, record := range records {
129 data := [][]string{record}
130 tmpfile, err := MakeTempCSVFile(data)
132 defer os.Remove(tmpfile.Name())
133 s.cfg.Path = tmpfile.Name()
135 c.Assert(err, NotNil)
136 c.Assert(err, ErrorMatches, ".*fields cannot be empty.*")
140 func (s *TestSuite) TestIgnoreSpaces(c *C) {
141 // Make sure users aren't already there from fixtures
142 for _, user := range s.users {
144 found := e == "user1@example.com" || e == "user2@example.com" || e == "user3@example.com"
145 c.Assert(found, Equals, false)
147 // Use CSV data with leading/trailing whitespaces, confirm that they get ignored
149 {" user1@example.com", " Example", " User1", "1", "0"},
150 {"user2@example.com ", "Example ", "User2 ", "1", "0"},
151 {" user3@example.com ", " Example ", " User3 ", "1", "0"},
153 tmpfile, err := MakeTempCSVFile(data)
155 defer os.Remove(tmpfile.Name())
156 s.cfg.Path = tmpfile.Name()
159 users, err := ListUsers(s.cfg.Client)
161 for _, userNr := range []int{1, 2, 3} {
163 for _, user := range users {
164 if user.Email == fmt.Sprintf("user%d@example.com", userNr) &&
165 user.LastName == fmt.Sprintf("User%d", userNr) &&
166 user.FirstName == "Example" && user.IsActive == true {
171 c.Assert(found, Equals, true)
175 // Error out when records have != 5 records
176 func (s *TestSuite) TestWrongNumberOfFields(c *C) {
177 for _, testCase := range [][][]string{
178 {{"user1@example.com", "Example", "User1", "1"}},
179 {{"user1@example.com", "Example", "User1", "1", "0", "extra data"}},
181 tmpfile, err := MakeTempCSVFile(testCase)
183 defer os.Remove(tmpfile.Name())
184 s.cfg.Path = tmpfile.Name()
186 c.Assert(err, NotNil)
187 c.Assert(err, ErrorMatches, ".*expected 5 fields, found.*")
191 // Error out when records have incorrect data types
192 func (s *TestSuite) TestWrongDataFields(c *C) {
193 for _, testCase := range [][][]string{
194 {{"user1@example.com", "Example", "User1", "yep", "0"}},
195 {{"user1@example.com", "Example", "User1", "1", "nope"}},
197 tmpfile, err := MakeTempCSVFile(testCase)
199 defer os.Remove(tmpfile.Name())
200 s.cfg.Path = tmpfile.Name()
202 c.Assert(err, NotNil)
203 c.Assert(err, ErrorMatches, ".*parsing error at line.*[active|admin] status not recognized.*")
207 // Activate and deactivate users
208 func (s *TestSuite) TestUserCreationAndUpdate(c *C) {
209 testCases := []struct {
216 Email: "user1@example.com",
217 FirstName: "Example",
222 Email: "admin1@example.com",
223 FirstName: "Example",
228 // Make sure users aren't already there from fixtures
229 for _, user := range s.users {
231 found := e == testCases[0].Email || e == testCases[1].Email
232 c.Assert(found, Equals, false)
236 {testCases[0].Email, testCases[0].FirstName, testCases[0].LastName, fmt.Sprintf("%t", testCases[0].Active), fmt.Sprintf("%t", testCases[0].Admin)},
237 {testCases[1].Email, testCases[1].FirstName, testCases[1].LastName, fmt.Sprintf("%t", testCases[1].Active), fmt.Sprintf("%t", testCases[1].Admin)},
239 tmpfile, err := MakeTempCSVFile(data)
241 defer os.Remove(tmpfile.Name())
242 s.cfg.Path = tmpfile.Name()
246 users, err := ListUsers(s.cfg.Client)
248 for _, tc := range testCases {
249 var foundUser arvados.User
250 for _, user := range users {
251 if user.Email == tc.Email {
256 c.Assert(foundUser, NotNil)
257 c.Logf("Checking recently created user %q", foundUser.Email)
258 c.Assert(foundUser.FirstName, Equals, tc.FirstName)
259 c.Assert(foundUser.LastName, Equals, tc.LastName)
260 c.Assert(foundUser.IsActive, Equals, true)
261 c.Assert(foundUser.IsAdmin, Equals, tc.Admin)
264 testCases[0].Active = false
265 testCases[1].Active = false
267 {testCases[0].Email, testCases[0].FirstName, testCases[0].LastName, fmt.Sprintf("%t", testCases[0].Active), fmt.Sprintf("%t", testCases[0].Admin)},
268 {testCases[1].Email, testCases[1].FirstName, testCases[1].LastName, fmt.Sprintf("%t", testCases[1].Active), fmt.Sprintf("%t", testCases[1].Admin)},
270 tmpfile, err = MakeTempCSVFile(data)
272 defer os.Remove(tmpfile.Name())
273 s.cfg.Path = tmpfile.Name()
277 users, err = ListUsers(s.cfg.Client)
279 for _, tc := range testCases {
280 var foundUser arvados.User
281 for _, user := range users {
282 if user.Email == tc.Email {
287 c.Assert(foundUser, NotNil)
288 c.Logf("Checking recently deactivated user %q", foundUser.Email)
289 c.Assert(foundUser.FirstName, Equals, tc.FirstName)
290 c.Assert(foundUser.LastName, Equals, tc.LastName)
291 c.Assert(foundUser.IsActive, Equals, false)
292 c.Assert(foundUser.IsAdmin, Equals, false) // inactive users cannot be admins
296 func (s *TestSuite) TestDeactivateUnlisted(c *C) {
297 localUserUuidRegex := regexp.MustCompile(fmt.Sprintf("^%s-tpzed-[0-9a-z]{15}$", s.cfg.ClusterID))
298 users, err := ListUsers(s.cfg.Client)
300 previouslyActiveUsers := 0
301 for _, u := range users {
302 if u.UUID == fmt.Sprintf("%s-tpzed-anonymouspublic", s.cfg.ClusterID) && !u.IsActive {
303 // Make sure the anonymous user is active for this test
305 err := UpdateUser(s.cfg.Client, u.UUID, &au, map[string]string{"is_active": "true"})
307 c.Assert(au.IsActive, Equals, true)
309 if localUserUuidRegex.MatchString(u.UUID) && u.IsActive {
310 previouslyActiveUsers++
313 // At least 3 active users: System root, Anonymous and the current user.
314 // Other active users should exist from fixture.
315 c.Logf("Initial active users count: %d", previouslyActiveUsers)
316 c.Assert(previouslyActiveUsers > 3, Equals, true)
318 s.cfg.DeactivateUnlisted = true
321 {"user1@example.com", "Example", "User1", "0", "0"},
323 tmpfile, err := MakeTempCSVFile(data)
325 defer os.Remove(tmpfile.Name())
326 s.cfg.Path = tmpfile.Name()
330 users, err = ListUsers(s.cfg.Client)
332 currentlyActiveUsers := 0
333 acceptableActiveUUIDs := map[string]bool{
334 fmt.Sprintf("%s-tpzed-000000000000000", s.cfg.ClusterID): true,
335 fmt.Sprintf("%s-tpzed-anonymouspublic", s.cfg.ClusterID): true,
336 s.cfg.CurrentUser.UUID: true,
338 remainingActiveUUIDs := map[string]bool{}
339 seenUserEmails := map[string]bool{}
340 for _, u := range users {
341 if _, ok := seenUserEmails[u.Email]; ok {
342 c.Errorf("Duplicated email address %q in user list (probably from fixtures). This test requires unique email addresses.", u.Email)
344 seenUserEmails[u.Email] = true
345 if localUserUuidRegex.MatchString(u.UUID) && u.IsActive {
346 c.Logf("Found remaining active user %q (%s)", u.Email, u.UUID)
347 _, ok := acceptableActiveUUIDs[u.UUID]
348 c.Assert(ok, Equals, true)
349 remainingActiveUUIDs[u.UUID] = true
350 currentlyActiveUsers++
353 // 3 active users remaining: System root, Anonymous and the current user.
354 c.Logf("Active local users remaining: %v", remainingActiveUUIDs)
355 c.Assert(currentlyActiveUsers, Equals, 3)
358 func (s *TestSuite) TestFailOnDuplicatedEmails(c *C) {
359 for i := range []int{1, 2} {
361 err := CreateUser(s.cfg.Client, &arvados.User{}, map[string]string{
362 "email": "somedupedemail@example.com",
363 "first_name": fmt.Sprintf("Duped %d", i),
364 "username": fmt.Sprintf("dupedemail%d", i),
367 "is_admin": fmt.Sprintf("%t", isAdmin),
373 {"user1@example.com", "Example", "User1", "0", "0"},
375 tmpfile, err := MakeTempCSVFile(data)
377 defer os.Remove(tmpfile.Name())
378 s.cfg.Path = tmpfile.Name()
380 c.Assert(err, NotNil)
381 c.Assert(err, ErrorMatches, "skipped.*duplicated email address.*")