github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/pkg/packager/common_test.go (about)

     1  package packager
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/Racer159/jackal/src/config"
     9  	"github.com/Racer159/jackal/src/config/lang"
    10  	"github.com/Racer159/jackal/src/pkg/cluster"
    11  	"github.com/Racer159/jackal/src/pkg/k8s"
    12  	"github.com/Racer159/jackal/src/types"
    13  	"github.com/stretchr/testify/require"
    14  	v1 "k8s.io/api/core/v1"
    15  	"k8s.io/apimachinery/pkg/runtime"
    16  	"k8s.io/client-go/kubernetes/fake"
    17  	k8sTesting "k8s.io/client-go/testing"
    18  )
    19  
    20  // TestValidatePackageArchitecture verifies that Jackal validates package architecture against cluster architecture correctly.
    21  func TestValidatePackageArchitecture(t *testing.T) {
    22  	t.Parallel()
    23  
    24  	type testCase struct {
    25  		name          string
    26  		pkgArch       string
    27  		clusterArchs  []string
    28  		images        []string
    29  		expectedError error
    30  		getArchError  error
    31  	}
    32  
    33  	testCases := []testCase{
    34  		{
    35  			name:          "architecture match",
    36  			pkgArch:       "amd64",
    37  			clusterArchs:  []string{"amd64"},
    38  			images:        []string{"nginx"},
    39  			expectedError: nil,
    40  		},
    41  		{
    42  			name:          "architecture mismatch",
    43  			pkgArch:       "arm64",
    44  			clusterArchs:  []string{"amd64"},
    45  			images:        []string{"nginx"},
    46  			expectedError: fmt.Errorf(lang.CmdPackageDeployValidateArchitectureErr, "arm64", "amd64"),
    47  		},
    48  		{
    49  			name:          "multiple cluster architectures",
    50  			pkgArch:       "arm64",
    51  			clusterArchs:  []string{"amd64", "arm64"},
    52  			images:        []string{"nginx"},
    53  			expectedError: nil,
    54  		},
    55  		{
    56  			name:          "ignore validation when package arch equals 'multi'",
    57  			pkgArch:       "multi",
    58  			clusterArchs:  []string{"not evaluated"},
    59  			expectedError: nil,
    60  		},
    61  		{
    62  			name:          "ignore validation when a package doesn't contain images",
    63  			pkgArch:       "amd64",
    64  			images:        []string{},
    65  			clusterArchs:  []string{"not evaluated"},
    66  			expectedError: nil,
    67  		},
    68  		{
    69  			name:          "test the error path when fetching cluster architecture fails",
    70  			pkgArch:       "amd64",
    71  			images:        []string{"nginx"},
    72  			getArchError:  errors.New("error fetching cluster architecture"),
    73  			expectedError: lang.ErrUnableToCheckArch,
    74  		},
    75  	}
    76  
    77  	for _, testCase := range testCases {
    78  		testCase := testCase
    79  
    80  		t.Run(testCase.name, func(t *testing.T) {
    81  			t.Parallel()
    82  
    83  			mockClient := fake.NewSimpleClientset()
    84  			logger := func(string, ...interface{}) {}
    85  
    86  			// Create a Packager instance with package architecture set and a mock Kubernetes client.
    87  			p := &Packager{
    88  				cluster: &cluster.Cluster{
    89  					K8s: &k8s.K8s{
    90  						Clientset: mockClient,
    91  						Log:       logger,
    92  					},
    93  				},
    94  				cfg: &types.PackagerConfig{
    95  					Pkg: types.JackalPackage{
    96  						Metadata: types.JackalMetadata{Architecture: testCase.pkgArch},
    97  						Components: []types.JackalComponent{
    98  							{
    99  								Images: testCase.images,
   100  							},
   101  						},
   102  					},
   103  				},
   104  			}
   105  
   106  			// Set up test data for fetching cluster architecture.
   107  			mockClient.Fake.PrependReactor("list", "nodes", func(_ k8sTesting.Action) (bool, runtime.Object, error) {
   108  				// Return an error for cases that test this error path.
   109  				if testCase.getArchError != nil {
   110  					return true, nil, testCase.getArchError
   111  				}
   112  
   113  				nodeItems := []v1.Node{}
   114  
   115  				for _, arch := range testCase.clusterArchs {
   116  					nodeItems = append(nodeItems, v1.Node{
   117  						Status: v1.NodeStatus{
   118  							NodeInfo: v1.NodeSystemInfo{
   119  								Architecture: arch,
   120  							},
   121  						},
   122  					})
   123  				}
   124  
   125  				// Create a NodeList object to fetch cluster architecture with the mock client.
   126  				nodeList := &v1.NodeList{
   127  					Items: nodeItems,
   128  				}
   129  				return true, nodeList, nil
   130  			})
   131  
   132  			err := p.validatePackageArchitecture()
   133  
   134  			require.Equal(t, testCase.expectedError, err)
   135  		})
   136  	}
   137  }
   138  
   139  // TestValidateLastNonBreakingVersion verifies that Jackal validates the lastNonBreakingVersion of packages against the CLI version correctly.
   140  func TestValidateLastNonBreakingVersion(t *testing.T) {
   141  	t.Parallel()
   142  
   143  	type testCase struct {
   144  		name                   string
   145  		cliVersion             string
   146  		lastNonBreakingVersion string
   147  		expectedErrorMessage   string
   148  		expectedWarningMessage string
   149  		returnError            bool
   150  		throwWarning           bool
   151  	}
   152  
   153  	testCases := []testCase{
   154  		{
   155  			name:                   "CLI version less than lastNonBreakingVersion",
   156  			cliVersion:             "v0.26.4",
   157  			lastNonBreakingVersion: "v0.27.0",
   158  			returnError:            false,
   159  			throwWarning:           true,
   160  			expectedWarningMessage: fmt.Sprintf(
   161  				lang.CmdPackageDeployValidateLastNonBreakingVersionWarn,
   162  				"v0.26.4",
   163  				"v0.27.0",
   164  				"v0.27.0",
   165  			),
   166  		},
   167  		{
   168  			name:                   "invalid semantic version (CLI version)",
   169  			cliVersion:             "invalidSemanticVersion",
   170  			lastNonBreakingVersion: "v0.0.1",
   171  			returnError:            false,
   172  			throwWarning:           true,
   173  			expectedWarningMessage: fmt.Sprintf(lang.CmdPackageDeployInvalidCLIVersionWarn, "invalidSemanticVersion"),
   174  		},
   175  		{
   176  			name:                   "invalid semantic version (lastNonBreakingVersion)",
   177  			cliVersion:             "v0.0.1",
   178  			lastNonBreakingVersion: "invalidSemanticVersion",
   179  			throwWarning:           false,
   180  			returnError:            true,
   181  			expectedErrorMessage:   "unable to parse lastNonBreakingVersion",
   182  		},
   183  		{
   184  			name:                   "CLI version greater than lastNonBreakingVersion",
   185  			cliVersion:             "v0.28.2",
   186  			lastNonBreakingVersion: "v0.27.0",
   187  			returnError:            false,
   188  			throwWarning:           false,
   189  		},
   190  		{
   191  			name:                   "CLI version equal to lastNonBreakingVersion",
   192  			cliVersion:             "v0.27.0",
   193  			lastNonBreakingVersion: "v0.27.0",
   194  			returnError:            false,
   195  			throwWarning:           false,
   196  		},
   197  		{
   198  			name:                   "empty lastNonBreakingVersion",
   199  			cliVersion:             "this shouldn't get evaluated when the lastNonBreakingVersion is empty",
   200  			lastNonBreakingVersion: "",
   201  			returnError:            false,
   202  			throwWarning:           false,
   203  		},
   204  	}
   205  
   206  	for _, testCase := range testCases {
   207  		testCase := testCase
   208  
   209  		t.Run(testCase.name, func(t *testing.T) {
   210  			config.CLIVersion = testCase.cliVersion
   211  
   212  			p := &Packager{
   213  				cfg: &types.PackagerConfig{
   214  					Pkg: types.JackalPackage{
   215  						Build: types.JackalBuildData{
   216  							LastNonBreakingVersion: testCase.lastNonBreakingVersion,
   217  						},
   218  					},
   219  				},
   220  			}
   221  
   222  			err := p.validateLastNonBreakingVersion()
   223  
   224  			switch {
   225  			case testCase.returnError:
   226  				require.ErrorContains(t, err, testCase.expectedErrorMessage)
   227  				require.Empty(t, p.warnings, "Expected no warnings for test case: %s", testCase.name)
   228  			case testCase.throwWarning:
   229  				require.Contains(t, p.warnings, testCase.expectedWarningMessage)
   230  				require.NoError(t, err, "Expected no error for test case: %s", testCase.name)
   231  			default:
   232  				require.NoError(t, err, "Expected no error for test case: %s", testCase.name)
   233  				require.Empty(t, p.warnings, "Expected no warnings for test case: %s", testCase.name)
   234  			}
   235  		})
   236  	}
   237  }