...

Source file src/sigs.k8s.io/release-utils/util/common_test.go

Documentation: sigs.k8s.io/release-utils/util

     1  /*
     2  Copyright 2019 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 util
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"path/filepath"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/blang/semver/v4"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestPackagesAvailableSuccess(t *testing.T) {
    32  	testcases := [][]string{
    33  		{"bash"},
    34  		{"bash", "curl", "grep"},
    35  		{},
    36  	}
    37  
    38  	for _, packages := range testcases {
    39  		available, err := PackagesAvailable(packages...)
    40  		require.Nil(t, err)
    41  		require.True(t, available)
    42  	}
    43  }
    44  
    45  func TestPackagesAvailableFailure(t *testing.T) {
    46  	testcases := [][]string{
    47  		{
    48  			"fakepackagefoo",
    49  		},
    50  		{
    51  			"fakepackagefoo",
    52  			"fakepackagebar",
    53  			"fakepackagebaz",
    54  		},
    55  		{
    56  			"bash",
    57  			"fakepackagefoo",
    58  			"fakepackagebar",
    59  		},
    60  	}
    61  
    62  	for _, packages := range testcases {
    63  		actual, err := PackagesAvailable(packages...)
    64  		require.Nil(t, err)
    65  		require.False(t, actual)
    66  	}
    67  }
    68  
    69  func TestMoreRecent(t *testing.T) {
    70  	baseTmpDir, err := os.MkdirTemp("", "")
    71  	require.Nil(t, err)
    72  
    73  	// Create test files.
    74  	testFileOne := filepath.Join(baseTmpDir, "testone.txt")
    75  	require.Nil(t, os.WriteFile(
    76  		testFileOne,
    77  		[]byte("file-one-contents"),
    78  		os.FileMode(0o644),
    79  	))
    80  
    81  	time.Sleep(1 * time.Second)
    82  
    83  	testFileTwo := filepath.Join(baseTmpDir, "testtwo.txt")
    84  	require.Nil(t, os.WriteFile(
    85  		testFileTwo,
    86  		[]byte("file-two-contents"),
    87  		os.FileMode(0o644),
    88  	))
    89  
    90  	notFile := filepath.Join(baseTmpDir, "noexist.txt")
    91  
    92  	defer cleanupTmp(t, baseTmpDir)
    93  
    94  	type args struct {
    95  		a string
    96  		b string
    97  	}
    98  	type want struct {
    99  		r   bool
   100  		err error
   101  	}
   102  	cases := map[string]struct {
   103  		args args
   104  		want want
   105  	}{
   106  		"AIsRecent": {
   107  			args: args{
   108  				a: testFileTwo,
   109  				b: testFileOne,
   110  			},
   111  			want: want{
   112  				r:   true,
   113  				err: nil,
   114  			},
   115  		},
   116  		"AIsNotRecent": {
   117  			args: args{
   118  				a: testFileOne,
   119  				b: testFileTwo,
   120  			},
   121  			want: want{
   122  				r:   false,
   123  				err: nil,
   124  			},
   125  		},
   126  		"ADoesNotExist": {
   127  			args: args{
   128  				a: notFile,
   129  				b: testFileTwo,
   130  			},
   131  			want: want{
   132  				r:   false,
   133  				err: nil,
   134  			},
   135  		},
   136  		"BDoesNotExist": {
   137  			args: args{
   138  				a: testFileOne,
   139  				b: notFile,
   140  			},
   141  			want: want{
   142  				r:   true,
   143  				err: nil,
   144  			},
   145  		},
   146  		"NeitherExists": {
   147  			args: args{
   148  				a: notFile,
   149  				b: notFile,
   150  			},
   151  			want: want{
   152  				r:   false,
   153  				err: errors.New("neither file exists"),
   154  			},
   155  		},
   156  	}
   157  
   158  	for name, tc := range cases {
   159  		t.Run(name, func(t *testing.T) {
   160  			more, err := MoreRecent(tc.args.a, tc.args.b)
   161  			require.IsType(t, tc.want.err, err)
   162  			require.Equal(t, tc.want.r, more)
   163  		})
   164  	}
   165  }
   166  
   167  func TestCopyFile(t *testing.T) {
   168  	srcDir, err := os.MkdirTemp("", "src")
   169  	require.Nil(t, err)
   170  	dstDir, err := os.MkdirTemp("", "dst")
   171  	require.Nil(t, err)
   172  
   173  	// Create test file.
   174  	srcFileOnePath := filepath.Join(srcDir, "testone.txt")
   175  	require.Nil(t, os.WriteFile(
   176  		srcFileOnePath,
   177  		[]byte("file-one-contents"),
   178  		os.FileMode(0o644),
   179  	))
   180  
   181  	dstFileOnePath := filepath.Join(dstDir, "testone.txt")
   182  
   183  	defer cleanupTmp(t, srcDir)
   184  	defer cleanupTmp(t, dstDir)
   185  
   186  	type args struct {
   187  		src      string
   188  		dst      string
   189  		required bool
   190  	}
   191  	cases := map[string]struct {
   192  		args        args
   193  		shouldError bool
   194  	}{
   195  		"CopyFileSuccess": {
   196  			args: args{
   197  				src:      srcFileOnePath,
   198  				dst:      dstFileOnePath,
   199  				required: true,
   200  			},
   201  			shouldError: false,
   202  		},
   203  		"CopyFileNotExistNotIgnore": {
   204  			args: args{
   205  				src:      "path/does/not/exit",
   206  				dst:      dstFileOnePath,
   207  				required: true,
   208  			},
   209  			shouldError: true,
   210  		},
   211  		"CopyFileNotExistIgnore": {
   212  			args: args{
   213  				src:      "path/does/not/exit",
   214  				dst:      dstFileOnePath,
   215  				required: false,
   216  			},
   217  			shouldError: false,
   218  		},
   219  	}
   220  
   221  	for name, tc := range cases {
   222  		t.Run(name, func(t *testing.T) {
   223  			copyErr := CopyFileLocal(tc.args.src, tc.args.dst, tc.args.required)
   224  			if tc.shouldError {
   225  				require.NotNil(t, copyErr)
   226  			} else {
   227  				require.Nil(t, copyErr)
   228  			}
   229  			if copyErr == nil {
   230  				_, err := os.Stat(tc.args.dst)
   231  				if err != nil && tc.args.required {
   232  					t.Fatal("file does not exist in destination")
   233  				}
   234  			}
   235  		})
   236  	}
   237  }
   238  
   239  func TestCopyDirContentLocal(t *testing.T) {
   240  	srcDir, err := os.MkdirTemp("", "src")
   241  	require.Nil(t, err)
   242  	dstDir, err := os.MkdirTemp("", "dst")
   243  	require.Nil(t, err)
   244  
   245  	// Create test file.
   246  	srcFileOnePath := filepath.Join(srcDir, "testone.txt")
   247  	require.Nil(t, os.WriteFile(
   248  		srcFileOnePath,
   249  		[]byte("file-one-contents"),
   250  		os.FileMode(0o644),
   251  	))
   252  
   253  	srcFileTwoPath := filepath.Join(srcDir, "testtwo.txt")
   254  	require.Nil(t, os.WriteFile(
   255  		srcFileTwoPath,
   256  		[]byte("file-two-contents"),
   257  		os.FileMode(0o644),
   258  	))
   259  
   260  	defer cleanupTmp(t, srcDir)
   261  	defer cleanupTmp(t, dstDir)
   262  
   263  	type args struct {
   264  		src string
   265  		dst string
   266  	}
   267  	type want struct {
   268  		err error
   269  	}
   270  	cases := map[string]struct {
   271  		args args
   272  		want want
   273  	}{
   274  		"CopyDirContentsSuccess": {
   275  			args: args{
   276  				src: srcDir,
   277  				dst: dstDir,
   278  			},
   279  			want: want{
   280  				err: nil,
   281  			},
   282  		},
   283  		"CopyDirContentsSuccessDstNotExist": {
   284  			args: args{
   285  				src: srcDir,
   286  				dst: filepath.Join(dstDir, "path-not-exist"),
   287  			},
   288  			want: want{
   289  				err: nil,
   290  			},
   291  		},
   292  	}
   293  
   294  	for name, tc := range cases {
   295  		t.Run(name, func(t *testing.T) {
   296  			copyErr := CopyDirContentsLocal(tc.args.src, tc.args.dst)
   297  			require.Equal(t, tc.want.err, copyErr)
   298  		})
   299  	}
   300  }
   301  
   302  func TestRemoveAndReplaceDir(t *testing.T) {
   303  	dir, err := os.MkdirTemp("", "rm")
   304  	require.Nil(t, err)
   305  
   306  	// Create test file.
   307  	fileOnePath := filepath.Join(dir, "testone.txt")
   308  	require.Nil(t, os.WriteFile(
   309  		fileOnePath,
   310  		[]byte("file-one-contents"),
   311  		os.FileMode(0o644),
   312  	))
   313  
   314  	fileTwoPath := filepath.Join(dir, "testtwo.txt")
   315  	require.Nil(t, os.WriteFile(
   316  		fileTwoPath,
   317  		[]byte("file-two-contents"),
   318  		os.FileMode(0o644),
   319  	))
   320  
   321  	defer cleanupTmp(t, dir)
   322  
   323  	type args struct {
   324  		dir string
   325  	}
   326  	type want struct {
   327  		err error
   328  	}
   329  	cases := map[string]struct {
   330  		args args
   331  		want want
   332  	}{
   333  		"RemoveAndReplaceSuccess": {
   334  			args: args{
   335  				dir: dir,
   336  			},
   337  			want: want{
   338  				err: nil,
   339  			},
   340  		},
   341  		"RemoveAndReplaceNotExist": {
   342  			args: args{
   343  				dir: filepath.Join(dir, "not-exit"),
   344  			},
   345  			want: want{
   346  				err: nil,
   347  			},
   348  		},
   349  	}
   350  
   351  	for name, tc := range cases {
   352  		t.Run(name, func(t *testing.T) {
   353  			err := RemoveAndReplaceDir(tc.args.dir)
   354  			require.Equal(t, tc.want.err, err)
   355  		})
   356  	}
   357  }
   358  
   359  func TestExist(t *testing.T) {
   360  	dir, err := os.MkdirTemp("", "rm")
   361  	require.Nil(t, err)
   362  
   363  	// Create test file.
   364  	fileOnePath := filepath.Join(dir, "testone.txt")
   365  	require.Nil(t, os.WriteFile(
   366  		fileOnePath,
   367  		[]byte("file-one-contents"),
   368  		os.FileMode(0o644),
   369  	))
   370  
   371  	defer cleanupTmp(t, dir)
   372  
   373  	type args struct {
   374  		dir string
   375  	}
   376  	type want struct {
   377  		exist bool
   378  	}
   379  	cases := map[string]struct {
   380  		args args
   381  		want want
   382  	}{
   383  		"DirExists": {
   384  			args: args{
   385  				dir: dir,
   386  			},
   387  			want: want{
   388  				exist: true,
   389  			},
   390  		},
   391  		"FileExists": {
   392  			args: args{
   393  				dir: fileOnePath,
   394  			},
   395  			want: want{
   396  				exist: true,
   397  			},
   398  		},
   399  		"DirNotExists": {
   400  			args: args{
   401  				dir: filepath.Join(dir, "path-not-exit"),
   402  			},
   403  			want: want{
   404  				exist: false,
   405  			},
   406  		},
   407  		"FileNotExists": {
   408  			args: args{
   409  				dir: filepath.Join(dir, "notexist.txt"),
   410  			},
   411  			want: want{
   412  				exist: false,
   413  			},
   414  		},
   415  	}
   416  
   417  	for name, tc := range cases {
   418  		t.Run(name, func(t *testing.T) {
   419  			exist := Exists(tc.args.dir)
   420  			require.Equal(t, tc.want.exist, exist)
   421  		})
   422  	}
   423  }
   424  
   425  func cleanupTmp(t *testing.T, dir string) {
   426  	require.Nil(t, os.RemoveAll(dir))
   427  }
   428  
   429  func TestTagStringToSemver(t *testing.T) {
   430  	// Success
   431  	version, err := TagStringToSemver("v1.2.3")
   432  	require.Nil(t, err)
   433  	require.Equal(t, version, semver.Version{Major: 1, Minor: 2, Patch: 3})
   434  
   435  	// No Major.Minor.Patch elements found
   436  	version, err = TagStringToSemver("invalid")
   437  	require.NotNil(t, err)
   438  	require.Equal(t, version, semver.Version{})
   439  
   440  	// Version string empty
   441  	version, err = TagStringToSemver("")
   442  	require.NotNil(t, err)
   443  	require.Equal(t, version, semver.Version{})
   444  }
   445  
   446  func TestSemverToTagString(t *testing.T) {
   447  	version := semver.Version{Major: 1, Minor: 2, Patch: 3}
   448  	require.Equal(t, SemverToTagString(version), "v1.2.3")
   449  }
   450  
   451  func TestAddTagPrefix(t *testing.T) {
   452  	require.Equal(t, "v0.0.0", AddTagPrefix("0.0.0"))
   453  	require.Equal(t, "v1.0.0", AddTagPrefix("v1.0.0"))
   454  }
   455  
   456  func TestTrimTagPrefix(t *testing.T) {
   457  	require.Equal(t, "0.0.0", TrimTagPrefix("0.0.0"))
   458  	require.Equal(t, "1.0.0", TrimTagPrefix("1.0.0"))
   459  }
   460  
   461  func TestWrapText(t *testing.T) {
   462  	//nolint: misspell
   463  	longText := `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut molestie accumsan orci, id congue nibh sollicitudin in. Nulla condimentum arcu eu est hendrerit tempus. Nunc risus nibh, aliquam in ultrices fringilla, aliquet ac purus. Aenean non nibh magna. Nunc lacinia suscipit malesuada. Vivamus porta a leo vel ornare. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi pellentesque orci magna, sed semper nulla fringilla at. Nam elementum ipsum maximus lectus tempor faucibus. Donec eu enim nulla. Integer egestas venenatis tristique. Curabitur id purus sem. Vivamus nec mollis lorem.`
   464  	wrappedText := "Lorem ipsum dolor sit amet, consectetur\n"
   465  	wrappedText += "adipiscing elit. Ut molestie accumsan\n"
   466  	wrappedText += "orci, id congue nibh sollicitudin in.\n"
   467  	wrappedText += "Nulla condimentum arcu eu est hendrerit\n"
   468  	wrappedText += "tempus. Nunc risus nibh, aliquam in\n"
   469  	wrappedText += "ultrices fringilla, aliquet ac purus.\n"
   470  	wrappedText += "Aenean non nibh magna. Nunc lacinia\n"
   471  	wrappedText += "suscipit malesuada. Vivamus porta a leo\n"
   472  	wrappedText += "vel ornare. Orci varius natoque\n"
   473  	wrappedText += "penatibus et magnis dis parturient\n"
   474  	wrappedText += "montes, nascetur ridiculus mus. Morbi\n" //nolint: misspell
   475  	wrappedText += "pellentesque orci magna, sed semper\n"
   476  	wrappedText += "nulla fringilla at. Nam elementum ipsum\n"
   477  	wrappedText += "maximus lectus tempor faucibus. Donec eu\n"
   478  	wrappedText += "enim nulla. Integer egestas venenatis\n"
   479  	wrappedText += "tristique. Curabitur id purus sem.\n"
   480  	wrappedText += "Vivamus nec mollis lorem."
   481  	require.Equal(t, WrapText(longText, 40), wrappedText)
   482  }
   483  
   484  func TestStripSensitiveData(t *testing.T) {
   485  	testCases := []struct {
   486  		text       string
   487  		mustChange bool
   488  	}{
   489  		{text: "a", mustChange: false},
   490  		{text: `s;!3Vc2]x~qL&'Sc/W/>^}8pau\.xr;;5uL:mL:h:x-oauth-basic`, mustChange: false},                                                                        // Non base64 token
   491  		{text: `ab0ff5efdbafcf1def98cac7bd4fa5856d53d000:x-oauth-basic`, mustChange: true},                                                                         // Visible token
   492  		{text: `X-Some-Header: ab0ff5efdbafcf1def98cac7bd4fa5856d53d000:x-oauth-basic;`, mustChange: true},                                                         // in string
   493  		{text: `error: failed to push some refs to 'https://git:538b8ca9618eaf316b8ca37bcf78da2c24639c14@github.com/kubernetes/kubernetes.git'`, mustChange: true}, // GitHub token
   494  		{text: `error: failed to push some refs to 'https://git:538b8c9618a316bca3bcf78da2c24639c35@github.com/kubernetes/kubernetes.git'`, mustChange: true},      // 35-char GitHub token
   495  	}
   496  	for _, tc := range testCases {
   497  		testBytes := []byte(tc.text)
   498  		if tc.mustChange {
   499  			require.NotEqual(t, StripSensitiveData(testBytes), testBytes, fmt.Sprintf("Failed sanitizing %s", tc.text))
   500  		} else {
   501  			require.ElementsMatch(t, StripSensitiveData(testBytes), testBytes)
   502  		}
   503  	}
   504  }
   505  
   506  func TestStripControlCharacters(t *testing.T) {
   507  	testCases := []struct {
   508  		text       []byte
   509  		mustChange bool
   510  	}{
   511  		{text: append([]byte{27}, []byte("[1m")...), mustChange: true},
   512  		{text: append([]byte{27}, []byte("[1K")...), mustChange: true},
   513  		{text: append([]byte{27}, []byte("[1B")...), mustChange: true},
   514  		{text: append([]byte{27}, []byte("(1B")...), mustChange: true},            // Parenthesis
   515  		{text: append([]byte{27}, []byte("[1;1m")...), mustChange: true},          // ; + 1 digit
   516  		{text: append([]byte{27}, []byte("[1;12m")...), mustChange: true},         // ; + 2 digits
   517  		{text: append([]byte{27}, []byte("[21K")...), mustChange: true},           //
   518  		{text: append([]byte{}, []byte("[1;13m")...), mustChange: false},          // No ESC
   519  		{text: append([]byte{27}, []byte("[1,13m")...), mustChange: false},        // No semicolon
   520  		{text: append([]byte("Test line"), []byte{13}...), mustChange: true},      // Bare CR
   521  		{text: append([]byte("Test line"), []byte{13, 15}...), mustChange: false}, // CRLF
   522  		{text: []byte("Test line"), mustChange: false},                            // Plain string
   523  	}
   524  	for _, tc := range testCases {
   525  		if tc.mustChange {
   526  			require.NotEqual(t, StripControlCharacters(tc.text), tc.text)
   527  		} else {
   528  			require.ElementsMatch(t, StripControlCharacters(tc.text), tc.text)
   529  		}
   530  	}
   531  }
   532  
   533  func TestCleanLogFile(t *testing.T) {
   534  	line1 := "This is a test log\n"
   535  	line2 := "It should not contain a test token here:\n"
   536  	line3 := "nor control characters o bare line feeds here:\n"
   537  	line4 := "Bare line feed: "
   538  	line5 := "\nControl Chars: "
   539  
   540  	// Create a token line
   541  	originalTokenLine := "7aa33bd2186c40849c4c2df321241e241def98ca:x-oauth-basic" //nolint: gosec
   542  	sanitizedTokenLine := string(StripSensitiveData([]byte(originalTokenLine)))
   543  	require.NotEqual(t, originalTokenLine, sanitizedTokenLine)
   544  
   545  	// Create the log
   546  	originalLog := line1 + line2 + originalTokenLine + line3 +
   547  		line4 + string([]byte{13}) + line5 +
   548  		string(append([]byte{27}, []byte("[1;1m")...)) + "\n"
   549  
   550  	// And expected output
   551  	cleanLog := line1 + line2 + sanitizedTokenLine + line3 + line4 + line5 + "\n"
   552  
   553  	logfile, err := os.CreateTemp("", "clean-log-test-")
   554  	require.Nil(t, err, "creating test logfile")
   555  	defer os.Remove(logfile.Name())
   556  	err = os.WriteFile(logfile.Name(), []byte(originalLog), os.FileMode(0o644))
   557  	require.Nil(t, err, "writing test file")
   558  
   559  	// Now, run the cleanLogFile
   560  	err = CleanLogFile(logfile.Name())
   561  	require.Nil(t, err, "running log cleaner")
   562  
   563  	resultingData, err := os.ReadFile(logfile.Name())
   564  	require.Nil(t, err, "reading modified file")
   565  	require.NotEmpty(t, resultingData)
   566  
   567  	// Must have changed
   568  	require.NotEqual(t, originalLog, string(resultingData))
   569  	require.Equal(t, cleanLog, string(resultingData))
   570  }
   571  

View as plain text