k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/import-boss/main_test.go (about)

     1  /*
     2  Copyright 2024 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"path/filepath"
    21  	"reflect"
    22  	goruntime "runtime"
    23  	"strings"
    24  	"testing"
    25  
    26  	"golang.org/x/tools/go/packages"
    27  )
    28  
    29  func TestRemoveLastDir(t *testing.T) {
    30  	table := map[string]struct{ newPath, removedDir string }{
    31  		"a/b/c": {"a/c", "b"},
    32  	}
    33  	for slashInput, expect := range table {
    34  		input := filepath.FromSlash(slashInput)
    35  
    36  		gotPath, gotRemoved := removeLastDir(input)
    37  		if e, a := filepath.FromSlash(expect.newPath), gotPath; e != a {
    38  			t.Errorf("%v: wanted %v, got %v", input, e, a)
    39  		}
    40  		if e, a := filepath.FromSlash(expect.removedDir), gotRemoved; e != a {
    41  			t.Errorf("%v: wanted %v, got %v", input, e, a)
    42  		}
    43  	}
    44  }
    45  
    46  func TestTransitiveClosure(t *testing.T) {
    47  	cases := []struct {
    48  		name     string
    49  		in       map[string][]string
    50  		expected map[string][]string
    51  	}{
    52  		{
    53  			name: "no transition",
    54  			in: map[string][]string{
    55  				"a": {"b"},
    56  				"c": {"d"},
    57  			},
    58  			expected: map[string][]string{
    59  				"a": {"b"},
    60  				"c": {"d"},
    61  			},
    62  		},
    63  		{
    64  			name: "simple",
    65  			in: map[string][]string{
    66  				"a": {"b"},
    67  				"b": {"c"},
    68  				"c": {"d"},
    69  			},
    70  			expected: map[string][]string{
    71  				"a": {"b", "c", "d"},
    72  				"b": {"c", "d"},
    73  				"c": {"d"},
    74  			},
    75  		},
    76  	}
    77  
    78  	for _, c := range cases {
    79  		t.Run(c.name, func(t *testing.T) {
    80  			out := transitiveClosure(c.in)
    81  			if !reflect.DeepEqual(c.expected, out) {
    82  				t.Errorf("expected: %#v, got %#v", c.expected, out)
    83  			}
    84  		})
    85  	}
    86  }
    87  
    88  func TestHasTestFiles(t *testing.T) {
    89  	cases := []struct {
    90  		input  []string
    91  		expect bool
    92  	}{{
    93  		input:  nil,
    94  		expect: false,
    95  	}, {
    96  		input:  []string{},
    97  		expect: false,
    98  	}, {
    99  		input:  []string{"foo.go"},
   100  		expect: false,
   101  	}, {
   102  		input:  []string{"foo.go", "bar.go"},
   103  		expect: false,
   104  	}, {
   105  		input:  []string{"foo_test.go"},
   106  		expect: true,
   107  	}, {
   108  		input:  []string{"foo.go", "foo_test.go"},
   109  		expect: true,
   110  	}, {
   111  		input:  []string{"foo.go", "foo_test.go", "bar.go", "bar_test.go"},
   112  		expect: true,
   113  	}}
   114  
   115  	for _, tc := range cases {
   116  		ret := hasTestFiles(tc.input)
   117  		if ret != tc.expect {
   118  			t.Errorf("expected %v, got %v: %q", tc.expect, ret, tc.input)
   119  		}
   120  	}
   121  }
   122  
   123  func TestPackageDir(t *testing.T) {
   124  	if goruntime.GOOS == "windows" {
   125  		// TODO: remove skip once the failing test has been fixed.
   126  		t.Skip("Skip failing test on Windows.")
   127  	}
   128  	cases := []struct {
   129  		input  *packages.Package
   130  		expect string
   131  	}{{
   132  		input: &packages.Package{
   133  			PkgPath:      "example.com/foo/bar/qux",
   134  			GoFiles:      []string{"/src/prj/file.go"},
   135  			IgnoredFiles: []string{"/otherdir/file.go"},
   136  		},
   137  		expect: "/src/prj",
   138  	}, {
   139  		input: &packages.Package{
   140  			PkgPath:      "example.com/foo/bar/qux",
   141  			IgnoredFiles: []string{"/src/prj/file.go"},
   142  		},
   143  		expect: "/src/prj",
   144  	}, {
   145  		input: &packages.Package{
   146  			PkgPath: "example.com/foo/bar/qux",
   147  		},
   148  		expect: "",
   149  	}}
   150  
   151  	for i, tc := range cases {
   152  		ret := packageDir(tc.input)
   153  		if ret != tc.expect {
   154  			t.Errorf("[%d] expected %v, got %v: %q", i, tc.expect, ret, tc.input)
   155  		}
   156  	}
   157  }
   158  
   159  func TestHasPathPrefix(t *testing.T) {
   160  	if goruntime.GOOS == "windows" {
   161  		// TODO: remove skip once the failing test has been fixed.
   162  		t.Skip("Skip failing test on Windows.")
   163  	}
   164  	cases := []struct {
   165  		base   string
   166  		pfx    string
   167  		expect bool
   168  	}{{
   169  		base:   "",
   170  		pfx:    "",
   171  		expect: true,
   172  	}, {
   173  		base:   "/foo/bar",
   174  		pfx:    "",
   175  		expect: true,
   176  	}, {
   177  		base:   "",
   178  		pfx:    "/foo",
   179  		expect: false,
   180  	}, {
   181  		base:   "/foo",
   182  		pfx:    "/foo",
   183  		expect: true,
   184  	}, {
   185  		base:   "/foo/bar",
   186  		pfx:    "/foo",
   187  		expect: true,
   188  	}, {
   189  		base:   "/foobar/qux",
   190  		pfx:    "/foo",
   191  		expect: false,
   192  	}, {
   193  		base:   "/foo/bar/bat/qux/zrb",
   194  		pfx:    "/foo/bar/bat",
   195  		expect: true,
   196  	}}
   197  
   198  	for _, tc := range cases {
   199  		ret := hasPathPrefix(tc.base, tc.pfx)
   200  		if ret != tc.expect {
   201  			t.Errorf("expected %v, got %v: (%q, %q)", tc.expect, ret, tc.base, tc.pfx)
   202  		}
   203  	}
   204  }
   205  
   206  func checkAllErrorStrings(t *testing.T, errs []error, expect []string) {
   207  	t.Helper()
   208  	if len(errs) != len(expect) {
   209  		t.Fatalf("expected %d errors, got %d: %q", len(expect), len(errs), errs)
   210  	}
   211  
   212  	for _, str := range expect {
   213  		found := false
   214  		for _, err := range errs {
   215  			if strings.HasPrefix(err.Error(), str) {
   216  				found = true
   217  				break
   218  			}
   219  		}
   220  		if !found {
   221  			t.Errorf("did not find error %q", str)
   222  			t.Logf("\tseek: %s\n\t  in:", str)
   223  			for _, err := range errs {
   224  				t.Logf("\t      %s", err.Error())
   225  			}
   226  		}
   227  	}
   228  }
   229  
   230  func TestSimpleForward(t *testing.T) {
   231  	if goruntime.GOOS == "windows" {
   232  		// TODO: remove skip once the failing test has been fixed.
   233  		t.Skip("Skip failing test on Windows.")
   234  	}
   235  	pkgs, err := loadPkgs("./testdata/simple-fwd/aaa")
   236  	if err != nil {
   237  		t.Fatalf("unexpected failure: %v", err)
   238  	}
   239  	if len(pkgs) != 1 {
   240  		t.Fatalf("expected 1 pkg result, got %d", len(pkgs))
   241  	}
   242  	if pkgs[0].PkgPath != "k8s.io/kubernetes/cmd/import-boss/testdata/simple-fwd/aaa" {
   243  		t.Fatalf("wrong PkgPath: %q", pkgs[0].PkgPath)
   244  	}
   245  
   246  	boss := newBoss(pkgs)
   247  	errs := boss.Verify(pkgs[0])
   248  
   249  	expect := []string{
   250  		`"k8s.io/kubernetes/cmd/import-boss/testdata/simple-fwd/aaa" -> "k8s.io/kubernetes/cmd/import-boss/testdata/simple-fwd/forbidden" is forbidden`,
   251  		`"k8s.io/kubernetes/cmd/import-boss/testdata/simple-fwd/aaa" -> "k8s.io/kubernetes/cmd/import-boss/testdata/simple-fwd/forbidden/f1" is forbidden`,
   252  		`"k8s.io/kubernetes/cmd/import-boss/testdata/simple-fwd/aaa" -> "k8s.io/kubernetes/cmd/import-boss/testdata/simple-fwd/neither" did not match any rule`,
   253  		`"k8s.io/kubernetes/cmd/import-boss/testdata/simple-fwd/aaa" -> "k8s.io/kubernetes/cmd/import-boss/testdata/simple-fwd/neither/n1" did not match any rule`,
   254  	}
   255  
   256  	checkAllErrorStrings(t, errs, expect)
   257  }
   258  
   259  func TestNestedForward(t *testing.T) {
   260  	pkgs, err := loadPkgs("./testdata/nested-fwd/aaa")
   261  	if err != nil {
   262  		t.Fatalf("unexpected failure: %v", err)
   263  	}
   264  	if len(pkgs) != 1 {
   265  		t.Fatalf("expected 1 pkg result, got %d", len(pkgs))
   266  	}
   267  	if pkgs[0].PkgPath != "k8s.io/kubernetes/cmd/import-boss/testdata/nested-fwd/aaa" {
   268  		t.Fatalf("wrong PkgPath: %q", pkgs[0].PkgPath)
   269  	}
   270  
   271  	boss := newBoss(pkgs)
   272  	errs := boss.Verify(pkgs[0])
   273  
   274  	expect := []string{
   275  		`"k8s.io/kubernetes/cmd/import-boss/testdata/nested-fwd/aaa" -> "k8s.io/kubernetes/cmd/import-boss/testdata/nested-fwd/forbidden-by-both" is forbidden`,
   276  		`"k8s.io/kubernetes/cmd/import-boss/testdata/nested-fwd/aaa" -> "k8s.io/kubernetes/cmd/import-boss/testdata/nested-fwd/forbidden-by-root" is forbidden`,
   277  		`"k8s.io/kubernetes/cmd/import-boss/testdata/nested-fwd/aaa" -> "k8s.io/kubernetes/cmd/import-boss/testdata/nested-fwd/forbidden-by-sub" is forbidden`,
   278  		`"k8s.io/kubernetes/cmd/import-boss/testdata/nested-fwd/aaa" -> "k8s.io/kubernetes/cmd/import-boss/testdata/nested-fwd/neither/n1" did not match any rule`,
   279  	}
   280  
   281  	checkAllErrorStrings(t, errs, expect)
   282  }
   283  
   284  func TestInverse(t *testing.T) {
   285  	pkgs, err := loadPkgs("./testdata/inverse/...")
   286  	if err != nil {
   287  		t.Fatalf("unexpected failure: %v", err)
   288  	}
   289  	if len(pkgs) != 10 {
   290  		t.Fatalf("expected 10 pkg results, got %d", len(pkgs))
   291  	}
   292  
   293  	boss := newBoss(pkgs)
   294  
   295  	var errs []error
   296  	for _, pkg := range pkgs {
   297  		errs = append(errs, boss.Verify(pkg)...)
   298  	}
   299  
   300  	expect := []string{
   301  		`"k8s.io/kubernetes/cmd/import-boss/testdata/inverse/forbidden" <- "k8s.io/kubernetes/cmd/import-boss/testdata/inverse/aaa" is forbidden`,
   302  		`"k8s.io/kubernetes/cmd/import-boss/testdata/inverse/forbidden/f1" <- "k8s.io/kubernetes/cmd/import-boss/testdata/inverse/aaa" is forbidden`,
   303  		`"k8s.io/kubernetes/cmd/import-boss/testdata/inverse/allowed/a2" <- "k8s.io/kubernetes/cmd/import-boss/testdata/inverse/allowed" did not match any rule`,
   304  		`"k8s.io/kubernetes/cmd/import-boss/testdata/inverse/forbidden/f2" <- "k8s.io/kubernetes/cmd/import-boss/testdata/inverse/allowed" did not match any rule`,
   305  	}
   306  
   307  	checkAllErrorStrings(t, errs, expect)
   308  }
   309  
   310  func TestTransitive(t *testing.T) {
   311  	pkgs, err := loadPkgs("./testdata/transitive/...")
   312  	if err != nil {
   313  		t.Fatalf("unexpected failure: %v", err)
   314  	}
   315  	if len(pkgs) != 10 {
   316  		t.Fatalf("expected 10 pkg results, got %d", len(pkgs))
   317  	}
   318  
   319  	boss := newBoss(pkgs)
   320  
   321  	var errs []error
   322  	for _, pkg := range pkgs {
   323  		errs = append(errs, boss.Verify(pkg)...)
   324  	}
   325  
   326  	expect := []string{
   327  		`"k8s.io/kubernetes/cmd/import-boss/testdata/transitive/forbidden" <- "k8s.io/kubernetes/cmd/import-boss/testdata/transitive/aaa" is forbidden`,
   328  		`"k8s.io/kubernetes/cmd/import-boss/testdata/transitive/forbidden/f1" <- "k8s.io/kubernetes/cmd/import-boss/testdata/transitive/aaa" is forbidden`,
   329  		`"k8s.io/kubernetes/cmd/import-boss/testdata/transitive/forbidden/f2" <-- "k8s.io/kubernetes/cmd/import-boss/testdata/transitive/aaa" is forbidden`,
   330  		`"k8s.io/kubernetes/cmd/import-boss/testdata/transitive/allowed/a2" <- "k8s.io/kubernetes/cmd/import-boss/testdata/transitive/allowed" did not match any rule`,
   331  		`"k8s.io/kubernetes/cmd/import-boss/testdata/transitive/forbidden/f2" <- "k8s.io/kubernetes/cmd/import-boss/testdata/transitive/allowed" did not match any rule`,
   332  	}
   333  
   334  	checkAllErrorStrings(t, errs, expect)
   335  }