// Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build aix darwin dragonfly freebsd !android,linux nacl netbsd openbsd solaris // +build !cgo package user import ( "reflect" "strings" "testing" ) const testGroupFile = `# See the opendirectoryd(8) man page for additional # information about Open Directory. ## nobody:*:-2: nogroup:*:-1: wheel:*:0:root emptyid:*::root invalidgid:*:notanumber:root +plussign:*:20:root -minussign:*:21:root daemon:*:1:root indented:*:7: # comment:*:4:found # comment:*:4:found kmem:*:2:root ` var groupTests = []struct { in string name string gid string }{ {testGroupFile, "nobody", "-2"}, {testGroupFile, "kmem", "2"}, {testGroupFile, "notinthefile", ""}, {testGroupFile, "comment", ""}, {testGroupFile, "plussign", ""}, {testGroupFile, "+plussign", ""}, {testGroupFile, "-minussign", ""}, {testGroupFile, "minussign", ""}, {testGroupFile, "emptyid", ""}, {testGroupFile, "invalidgid", ""}, {testGroupFile, "indented", "7"}, {testGroupFile, "# comment", ""}, {"", "emptyfile", ""}, } func TestFindGroupName(t *testing.T) { for _, tt := range groupTests { got, err := findGroupName(tt.name, strings.NewReader(tt.in)) if tt.gid == "" { if err == nil { t.Errorf("findGroupName(%s): got nil error, expected err", tt.name) continue } switch terr := err.(type) { case UnknownGroupError: if terr.Error() != "group: unknown group "+tt.name { t.Errorf("findGroupName(%s): got %v, want %v", tt.name, terr, tt.name) } default: t.Errorf("findGroupName(%s): got unexpected error %v", tt.name, terr) } } else { if err != nil { t.Fatalf("findGroupName(%s): got unexpected error %v", tt.name, err) } if got.Gid != tt.gid { t.Errorf("findGroupName(%s): got gid %v, want %s", tt.name, got.Gid, tt.gid) } if got.Name != tt.name { t.Errorf("findGroupName(%s): got name %s, want %s", tt.name, got.Name, tt.name) } } } } var groupIdTests = []struct { in string gid string name string }{ {testGroupFile, "-2", "nobody"}, {testGroupFile, "2", "kmem"}, {testGroupFile, "notinthefile", ""}, {testGroupFile, "comment", ""}, {testGroupFile, "7", "indented"}, {testGroupFile, "4", ""}, {testGroupFile, "20", ""}, // row starts with a plus {testGroupFile, "21", ""}, // row starts with a minus {"", "emptyfile", ""}, } func TestFindGroupId(t *testing.T) { for _, tt := range groupIdTests { got, err := findGroupId(tt.gid, strings.NewReader(tt.in)) if tt.name == "" { if err == nil { t.Errorf("findGroupId(%s): got nil error, expected err", tt.gid) continue } switch terr := err.(type) { case UnknownGroupIdError: if terr.Error() != "group: unknown groupid "+tt.gid { t.Errorf("findGroupId(%s): got %v, want %v", tt.name, terr, tt.name) } default: t.Errorf("findGroupId(%s): got unexpected error %v", tt.name, terr) } } else { if err != nil { t.Fatalf("findGroupId(%s): got unexpected error %v", tt.name, err) } if got.Gid != tt.gid { t.Errorf("findGroupId(%s): got gid %v, want %s", tt.name, got.Gid, tt.gid) } if got.Name != tt.name { t.Errorf("findGroupId(%s): got name %s, want %s", tt.name, got.Name, tt.name) } } } } const testUserFile = ` # Example user file root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:3:bin:/bin:/usr/sbin/nologin indented:x:3:3:indented:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync negative:x:-5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin allfields:x:6:12:mansplit,man2,man3,man4:/home/allfields:/usr/sbin/nologin +plussign:x:8:10:man:/var/cache/man:/usr/sbin/nologin -minussign:x:9:10:man:/var/cache/man:/usr/sbin/nologin malformed:x:27:12 # more:colons:after:comment struid:x:notanumber:12 # more:colons:after:comment # commented:x:28:12:commented:/var/cache/man:/usr/sbin/nologin # commentindented:x:29:12:commentindented:/var/cache/man:/usr/sbin/nologin struid2:x:30:badgid:struid2name:/home/struid:/usr/sbin/nologin ` var userIdTests = []struct { in string uid string name string }{ {testUserFile, "-5", "negative"}, {testUserFile, "2", "bin"}, {testUserFile, "100", ""}, // not in the file {testUserFile, "8", ""}, // plus sign, glibc doesn't find it {testUserFile, "9", ""}, // minus sign, glibc doesn't find it {testUserFile, "27", ""}, // malformed {testUserFile, "28", ""}, // commented out {testUserFile, "29", ""}, // commented out, indented {testUserFile, "3", "indented"}, {testUserFile, "30", ""}, // the Gid is not valid, shouldn't match {"", "1", ""}, } func TestInvalidUserId(t *testing.T) { _, err := findUserId("notanumber", strings.NewReader("")) if err == nil { t.Fatalf("findUserId('notanumber'): got nil error") } if want := "user: invalid userid notanumber"; err.Error() != want { t.Errorf("findUserId('notanumber'): got %v, want %s", err, want) } } func TestLookupUserId(t *testing.T) { for _, tt := range userIdTests { got, err := findUserId(tt.uid, strings.NewReader(tt.in)) if tt.name == "" { if err == nil { t.Errorf("findUserId(%s): got nil error, expected err", tt.uid) continue } switch terr := err.(type) { case UnknownUserIdError: if want := "user: unknown userid " + tt.uid; terr.Error() != want { t.Errorf("findUserId(%s): got %v, want %v", tt.name, terr, want) } default: t.Errorf("findUserId(%s): got unexpected error %v", tt.name, terr) } } else { if err != nil { t.Fatalf("findUserId(%s): got unexpected error %v", tt.name, err) } if got.Uid != tt.uid { t.Errorf("findUserId(%s): got uid %v, want %s", tt.name, got.Uid, tt.uid) } if got.Username != tt.name { t.Errorf("findUserId(%s): got name %s, want %s", tt.name, got.Username, tt.name) } } } } func TestLookupUserPopulatesAllFields(t *testing.T) { u, err := findUsername("allfields", strings.NewReader(testUserFile)) if err != nil { t.Fatal(err) } want := &User{ Username: "allfields", Uid: "6", Gid: "12", Name: "mansplit", HomeDir: "/home/allfields", } if !reflect.DeepEqual(u, want) { t.Errorf("findUsername: got %#v, want %#v", u, want) } } var userTests = []struct { in string name string uid string }{ {testUserFile, "negative", "-5"}, {testUserFile, "bin", "2"}, {testUserFile, "notinthefile", ""}, {testUserFile, "indented", "3"}, {testUserFile, "plussign", ""}, {testUserFile, "+plussign", ""}, {testUserFile, "minussign", ""}, {testUserFile, "-minussign", ""}, {testUserFile, " indented", ""}, {testUserFile, "commented", ""}, {testUserFile, "commentindented", ""}, {testUserFile, "malformed", ""}, {testUserFile, "# commented", ""}, {"", "emptyfile", ""}, } func TestLookupUser(t *testing.T) { for _, tt := range userTests { got, err := findUsername(tt.name, strings.NewReader(tt.in)) if tt.uid == "" { if err == nil { t.Errorf("lookupUser(%s): got nil error, expected err", tt.uid) continue } switch terr := err.(type) { case UnknownUserError: if want := "user: unknown user " + tt.name; terr.Error() != want { t.Errorf("lookupUser(%s): got %v, want %v", tt.name, terr, want) } default: t.Errorf("lookupUser(%s): got unexpected error %v", tt.name, terr) } } else { if err != nil { t.Fatalf("lookupUser(%s): got unexpected error %v", tt.name, err) } if got.Uid != tt.uid { t.Errorf("lookupUser(%s): got uid %v, want %s", tt.name, got.Uid, tt.uid) } if got.Username != tt.name { t.Errorf("lookupUser(%s): got name %s, want %s", tt.name, got.Username, tt.name) } } } }