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  }