github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/core/id/id_test.go (about) 1 // Copyright 2017-2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !plan9 6 // +build !plan9 7 8 package main 9 10 import ( 11 "bytes" 12 "errors" 13 "fmt" 14 "io" 15 "io/ioutil" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "sort" 20 "testing" 21 22 "github.com/mvdan/u-root-coreutils/pkg/testutil" 23 ) 24 25 var logPrefixLength = len("2009/11/10 23:00:00 ") 26 27 func TestBadFFiles(t *testing.T) { 28 var flags = &flags{} 29 30 d := t.TempDir() 31 n := filepath.Join(d, "nosuchfile") 32 f := filepath.Join(d, "afile") 33 if err := ioutil.WriteFile(f, []byte{}, 0666); err != nil { 34 t.Fatalf("writing %q: want nil, got %v", f, err) 35 } 36 if err := run(io.Discard, "root", flags, n, f); !errors.Is(err, os.ErrNotExist) { 37 t.Errorf("Using %q for passwd: want %v, got nil", n, os.ErrNotExist) 38 } 39 if err := run(io.Discard, "root", flags, f, n); !errors.Is(err, os.ErrNotExist) { 40 t.Errorf("Using %q for group: want %v, got nil", n, os.ErrNotExist) 41 } 42 } 43 44 // Run the command, with the optional args, and return a string 45 // for stdout, stderr, and an error. 46 func runHelper(c *exec.Cmd) (string, string, error) { 47 var o, e bytes.Buffer 48 c.Stdout, c.Stderr = &o, &e 49 err := c.Run() 50 return o.String(), e.String(), err 51 } 52 53 type passwd struct { 54 name string 55 uid int 56 gid int 57 } 58 59 var passwdShould = []passwd{ 60 {"root", 0, 0}, 61 {"bin", 1, 1}, 62 {"daemon", 2, 2}, 63 {"lary", 1000, 1000}, 64 {"curly", 1001, 1001}, 65 {"moe", 1002, 2002}, 66 } 67 68 var passwdShouldnt = []passwd{ 69 {"adm", 3, 4}, 70 } 71 72 var passwdFiles = []string{ 73 "testdata/passwd-simple.txt", 74 "testdata/passwd-comments.txt", 75 } 76 77 type group struct { 78 name string 79 gid int 80 } 81 82 var groupShould = []group{ 83 {"printadmin", 997}, 84 {"ssh_keys", 996}, 85 {"rpcuser", 29}, 86 {"nfsnobody", 65534}, 87 {"sshd", 74}, 88 {"wheel", 10}, 89 } 90 91 var groupShouldnt = []group{ 92 {"bad", 314}, 93 {"wrong", 996}, 94 {"wheel", 11}, 95 } 96 97 var groupFiles = []string{ 98 "testdata/group-simple.txt", 99 "testdata/group-comments.txt", 100 } 101 102 var groupMembers = map[string][]int{ 103 "larry": {10, 74}, 104 "curly": {10, 29}, 105 "moe": {10}, 106 "joe": {}, 107 } 108 109 func passwdSame(u *Users, us passwd) error { 110 var s string 111 var d int 112 var err error 113 d, err = u.GetUID(us.name) 114 if err != nil { 115 return fmt.Errorf("failed to GetUID expected user %s: %v", us.name, err) 116 } 117 if d != us.uid { 118 return fmt.Errorf("wrong UID for %s: got %d, expected %d", us.name, d, us.uid) 119 } 120 121 d, err = u.GetGID(us.uid) 122 if err != nil { 123 return fmt.Errorf("failed to GetGID expected uid %d: %v", us.uid, err) 124 } 125 if d != us.gid { 126 return fmt.Errorf("wrong GID for uid %d: got %d, expected %d", us.uid, d, us.gid) 127 } 128 129 s, err = u.GetUser(us.uid) 130 if err != nil { 131 return fmt.Errorf("failed to GetUser expected user %s: %v", us.name, err) 132 } 133 if s != us.name { 134 return fmt.Errorf("wrong username for %d: got %s, expected %s", us.uid, s, us.name) 135 } 136 return nil 137 } 138 139 func TestUsers(t *testing.T) { 140 t.Run("non-existent passwd file", func(t *testing.T) { 141 f := "testdata/does-not-exist" 142 u, e := NewUsers(f) 143 if e == nil { 144 t.Errorf("NewUser on non-existant file should return an error") 145 } 146 if u == nil { 147 t.Errorf("NewUser should return a valid Users object, even on error") 148 } 149 }) 150 t.Run("empty passwd file", func(t *testing.T) { 151 f := "testdata/passwd-empty.txt" 152 u, e := NewUsers(f) 153 if e != nil { 154 t.Errorf("NewUser should not report error for empty passwd file") 155 } 156 if u == nil { 157 t.Errorf("NewUser should return a valid Users object even if passwd file is empty") 158 } 159 }) 160 t.Run("almost empty passwd file", func(t *testing.T) { 161 f := "testdata/passwd-newline.txt" 162 u, e := NewUsers(f) 163 if e != nil { 164 t.Errorf("NewUser should not report error for empty passwd file") 165 } 166 if u == nil { 167 t.Errorf("NewUser should return a valid Users object even if passwd file is empty") 168 } 169 }) 170 for _, f := range passwdFiles { 171 t.Run(f, func(t *testing.T) { 172 u, e := NewUsers(f) 173 if e != nil { 174 t.Errorf("NewUser should not return an error on valid file") 175 } 176 if u == nil { 177 t.Errorf("NewUser should return a valid Users object on valid file") 178 } 179 for _, us := range passwdShould { 180 if e := passwdSame(u, us); e != nil { 181 t.Errorf("%v", e) 182 } 183 } 184 for _, us := range passwdShouldnt { 185 if e := passwdSame(u, us); e == nil { 186 t.Errorf("user %s matched when it shouldn't", us.name) 187 } 188 } 189 }) 190 } 191 } 192 193 func groupSame(g *Groups, gs group) error { 194 var s string 195 var d int 196 var err error 197 198 d, err = g.GetGID(gs.name) 199 if err != nil { 200 return fmt.Errorf("failed to GetGID expected group %s: %v", gs.name, err) 201 } 202 if d != gs.gid { 203 return fmt.Errorf("wrong GID for %s: got %d, expected %d", gs.name, d, gs.gid) 204 } 205 206 s, err = g.GetGroup(gs.gid) 207 if err != nil { 208 return fmt.Errorf("failed to GetGroup expected group %s: %v", gs.name, err) 209 } 210 if s != gs.name { 211 return fmt.Errorf("wrong groupname for %d: got %s, expected %s", gs.gid, s, gs.name) 212 } 213 return nil 214 } 215 216 func TestGroups(t *testing.T) { 217 t.Run("non-existent group file", func(t *testing.T) { 218 f := "testdata/does-not-exist" 219 g, e := NewGroups(f) 220 if e == nil { 221 t.Errorf("NewGroups jnon-existant file should return an error") 222 } 223 if g == nil { 224 t.Errorf("NewGroups should return a valid Groups object, even on error") 225 } 226 }) 227 t.Run("empty group file", func(t *testing.T) { 228 f := "testdata/group-empty.txt" 229 g, e := NewGroups(f) 230 if e != nil { 231 t.Errorf("NewGroups should not report error for empty passwd file") 232 } 233 if g == nil { 234 t.Errorf("NewGroups should return a valid Users object even if passwd file is empty") 235 } 236 }) 237 t.Run("almost empty group file", func(t *testing.T) { 238 f := "testdata/group-newline.txt" 239 g, e := NewGroups(f) 240 if e != nil { 241 t.Errorf("NewGroups should not report error for empty passwd file") 242 } 243 if g == nil { 244 t.Errorf("NewGroups should return a valid Users object even if passwd file is empty") 245 } 246 }) 247 for _, f := range groupFiles { 248 t.Run(f, func(t *testing.T) { 249 g, e := NewGroups(f) 250 if e != nil { 251 t.Errorf("NewGroups should not return an error on valid file") 252 } 253 if g == nil { 254 t.Errorf("NewGroups should return a valid Users object on valid file") 255 } 256 for _, gs := range groupShould { 257 if e := groupSame(g, gs); e != nil { 258 t.Errorf("%v", e) 259 } 260 } 261 for _, gs := range groupShouldnt { 262 if e := groupSame(g, gs); e == nil { 263 t.Errorf("group %s matched when it shouldn't", gs.name) 264 } 265 } 266 for u, is := range groupMembers { 267 js := g.UserGetGIDs(u) 268 if len(js) != len(is) { 269 t.Errorf("unequal gid lists for %s: %v vs %v", u, is, js) 270 } else { 271 sort.Ints(is) 272 sort.Ints(js) 273 for i := range is { 274 if is[i] != js[i] { 275 t.Errorf("unequal gid lists for %s: %v vs %v", u, is, js) 276 } 277 } 278 } 279 } 280 }) 281 } 282 } 283 284 func TestMain(m *testing.M) { 285 testutil.Run(m, main) 286 } 287 288 func TestFlags(t *testing.T) { 289 if !correctFlags(true, false, false) { 290 t.Errorf("correctFLags(true, false, false): got ! ok, want ok") 291 } 292 if correctFlags(true, true, false) { 293 t.Errorf("correctFLags(true, true, false): got ! ok, want ok") 294 } 295 296 tests := []struct { 297 testFlags *flags 298 wantError error 299 }{ 300 { 301 testFlags: &flags{name: true}, 302 wantError: errNotOnlyNames, 303 }, 304 { 305 testFlags: &flags{real: true}, 306 wantError: errNotOnlyNamesOrIDs, 307 }, 308 { 309 testFlags: &flags{group: true, groups: true}, 310 wantError: errOnlyOneChoice, 311 }, 312 { 313 testFlags: &flags{groups: true, user: true}, 314 wantError: errOnlyOneChoice, 315 }, 316 { 317 testFlags: &flags{group: true, user: true}, 318 wantError: errOnlyOneChoice, 319 }, 320 { 321 testFlags: &flags{group: true, groups: true, user: true}, 322 wantError: errOnlyOneChoice, 323 }, 324 } 325 326 for _, tt := range tests { 327 if err := run(io.Discard, "r", tt.testFlags, "", ""); !errors.Is(err, tt.wantError) { 328 t.Errorf(`run(%v, "", ""): got %v, want %v`, tt.testFlags, err, tt.wantError) 329 } 330 } 331 }