get.porter.sh/porter@v1.3.0/pkg/porter/list_test.go (about)

     1  package porter
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"get.porter.sh/porter/pkg/cnab"
     9  	"get.porter.sh/porter/pkg/printer"
    10  	"get.porter.sh/porter/pkg/secrets"
    11  	"get.porter.sh/porter/pkg/storage"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"reflect"
    16  )
    17  
    18  func TestNewDisplayInstallation(t *testing.T) {
    19  	t.Run("installation has been installed", func(t *testing.T) {
    20  		cp := storage.NewTestInstallationProvider(t)
    21  		defer cp.Close()
    22  
    23  		install := cp.CreateInstallation(storage.NewInstallation("", "wordpress"), func(i *storage.Installation) {
    24  			i.Status.Action = cnab.ActionUpgrade
    25  			i.Status.ResultStatus = cnab.StatusRunning
    26  		})
    27  
    28  		_, err := cp.GetInstallation(context.Background(), "", "wordpress")
    29  		require.NoError(t, err, "ReadInstallation failed")
    30  
    31  		displayInstall := NewDisplayInstallation(install)
    32  
    33  		require.Equal(t, displayInstall.Name, install.Name, "invalid installation name")
    34  		require.Equal(t, displayInstall.Status.Created, install.Status.Created, "invalid created time")
    35  		require.Equal(t, displayInstall.Status.Modified, install.Status.Modified, "invalid modified time")
    36  		require.Equal(t, cnab.ActionUpgrade, displayInstall.Status.Action, "invalid last action")
    37  		require.Equal(t, cnab.StatusRunning, displayInstall.Status.ResultStatus, "invalid last status")
    38  	})
    39  
    40  	t.Run("installation has not been installed", func(t *testing.T) {
    41  		cp := storage.NewTestInstallationProvider(t)
    42  		defer cp.Close()
    43  
    44  		install := cp.CreateInstallation(storage.NewInstallation("", "wordpress"))
    45  
    46  		_, err := cp.GetInstallation(context.Background(), "", "wordpress")
    47  		require.NoError(t, err, "GetInst failed")
    48  
    49  		displayInstall := NewDisplayInstallation(install)
    50  
    51  		require.Equal(t, displayInstall.Name, install.Name, "invalid installation name")
    52  		require.Equal(t, install.Status.Created, displayInstall.Status.Created, "invalid created time")
    53  		require.Equal(t, install.Status.Modified, displayInstall.Status.Modified, "invalid modified time")
    54  		require.Empty(t, displayInstall.Status.Action, "invalid last action")
    55  		require.Empty(t, displayInstall.Status.ResultStatus, "invalid last status")
    56  	})
    57  }
    58  
    59  func TestPorter_ListInstallations(t *testing.T) {
    60  	ctx := context.Background()
    61  	p := NewTestPorter(t)
    62  	defer p.Close()
    63  
    64  	i1 := storage.NewInstallation("", "shared-mysql")
    65  	i1.Parameters.Parameters = []secrets.SourceMap{ // Define a parameter that is stored in a secret, list should not retrieve it
    66  		{Name: "password", Source: secrets.Source{Strategy: "secret", Hint: "mypassword"}},
    67  	}
    68  	i1.Status.RunID = "10" // Add a run but don't populate the data for it, list should not retrieve it
    69  
    70  	p.TestInstallations.CreateInstallation(i1)
    71  	p.TestInstallations.CreateInstallation(storage.NewInstallation("dev", "carolyn-wordpress"))
    72  	p.TestInstallations.CreateInstallation(storage.NewInstallation("dev", "vaughn-wordpress"))
    73  	p.TestInstallations.CreateInstallation(storage.NewInstallation("test", "staging-wordpress"))
    74  	p.TestInstallations.CreateInstallation(storage.NewInstallation("test", "iat-wordpress"))
    75  	p.TestInstallations.CreateInstallation(storage.NewInstallation("test", "shared-mysql"))
    76  
    77  	t.Run("all-namespaces", func(t *testing.T) {
    78  		opts := ListOptions{AllNamespaces: true}
    79  		results, err := p.ListInstallations(ctx, opts)
    80  		require.NoError(t, err)
    81  		assert.Len(t, results, 6)
    82  
    83  		// Check that porter didn't go off and retrieve extra data for each installation
    84  		for _, r := range results {
    85  			assert.Empty(t, r.ResolvedParameters, "ListInstallations should not resolve secrets used by the installations")
    86  		}
    87  	})
    88  
    89  	t.Run("local namespace", func(t *testing.T) {
    90  		opts := ListOptions{Namespace: "dev"}
    91  		results, err := p.ListInstallations(ctx, opts)
    92  		require.NoError(t, err)
    93  		assert.Len(t, results, 2)
    94  
    95  		opts = ListOptions{Namespace: "test"}
    96  		results, err = p.ListInstallations(ctx, opts)
    97  		require.NoError(t, err)
    98  		assert.Len(t, results, 3)
    99  	})
   100  
   101  	t.Run("global namespace", func(t *testing.T) {
   102  		opts := ListOptions{Namespace: ""}
   103  		results, err := p.ListInstallations(ctx, opts)
   104  		require.NoError(t, err)
   105  		assert.Len(t, results, 1)
   106  	})
   107  
   108  	t.Run("empty namespace", func(t *testing.T) {
   109  		opts := ListOptions{Namespace: "nonexistent"}
   110  		results, err := p.ListInstallations(ctx, opts)
   111  		require.NoError(t, err)
   112  		assert.Len(t, results, 0)
   113  		assert.NotNil(t, results)
   114  	})
   115  }
   116  
   117  func TestPorter_ListInstallationsWithFieldSelector(t *testing.T) {
   118  	ctx := context.Background()
   119  	p := NewTestPorter(t)
   120  	defer p.Close()
   121  
   122  	i1 := storage.NewInstallation("", "installation1")
   123  	i1.Bundle.Version = "0.2.0"
   124  	i1.Status.Action = "install"
   125  	i1.Status.ResultStatus = "succeeded"
   126  
   127  	i2 := storage.NewInstallation("", "installation2")
   128  	i2.Bundle.Version = "0.2.1"
   129  	i2.Status.Action = "install"
   130  	i2.Status.ResultStatus = "succeeded"
   131  
   132  	i3 := storage.NewInstallation("", "installation3")
   133  	i3.Bundle.Version = "0.2.0"
   134  	i3.Status.Action = "install"
   135  	i3.Status.ResultStatus = "failed"
   136  
   137  	i4 := storage.NewInstallation("", "installation4")
   138  	i4.Bundle.Version = "0.2.5"
   139  	i4.Status.Action = "install"
   140  
   141  	i5 := storage.NewInstallation("", "installation5")
   142  	i5.Bundle.Version = "0.1.0"
   143  	i5.Status.Action = "install"
   144  	i5.Status.ResultStatus = "succeeded"
   145  
   146  	p.TestInstallations.CreateInstallation(i1)
   147  	p.TestInstallations.CreateInstallation(i2)
   148  	p.TestInstallations.CreateInstallation(i3)
   149  	p.TestInstallations.CreateInstallation(i4)
   150  	p.TestInstallations.CreateInstallation(i5)
   151  
   152  	t.Run("blank field selector", func(t *testing.T) {
   153  		opts := ListOptions{FieldSelector: ""}
   154  		results, err := p.ListInstallations(ctx, opts)
   155  		require.NoError(t, err)
   156  		assert.Len(t, results, 5)
   157  	})
   158  
   159  	t.Run("top level field selectors", func(t *testing.T) {
   160  		opts := ListOptions{FieldSelector: "name=installation1"}
   161  		results, err := p.ListInstallations(ctx, opts)
   162  		require.NoError(t, err)
   163  		assert.Len(t, results, 1)
   164  	})
   165  
   166  	t.Run("multi level field selectors", func(t *testing.T) {
   167  		opts := ListOptions{FieldSelector: "name=installation1,bundle.version=0.2.0,status.action=install,status.resultStatus=succeeded"}
   168  		results, err := p.ListInstallations(ctx, opts)
   169  		require.NoError(t, err)
   170  		assert.Len(t, results, 1)
   171  
   172  		opts = ListOptions{FieldSelector: "status.action=install,status.resultStatus=succeeded"}
   173  		results, err = p.ListInstallations(ctx, opts)
   174  		require.NoError(t, err)
   175  		assert.Len(t, results, 3)
   176  
   177  		opts = ListOptions{FieldSelector: "status.resultStatus=failed"}
   178  		results, err = p.ListInstallations(ctx, opts)
   179  		require.NoError(t, err)
   180  		assert.Len(t, results, 1)
   181  
   182  		opts = ListOptions{FieldSelector: "status.resultStatus"}
   183  		results, err = p.ListInstallations(ctx, opts)
   184  		require.Error(t, err)
   185  		assert.Len(t, results, 0)
   186  
   187  		opts = ListOptions{FieldSelector: "status.resultStatus=xyz"}
   188  		results, err = p.ListInstallations(ctx, opts)
   189  		require.NoError(t, err)
   190  		assert.Len(t, results, 0)
   191  
   192  	})
   193  }
   194  
   195  func TestDisplayInstallation_ConvertToInstallation(t *testing.T) {
   196  	cp := storage.NewTestInstallationProvider(t)
   197  	defer cp.Close()
   198  
   199  	install := cp.CreateInstallation(storage.NewInstallation("", "wordpress"), func(i *storage.Installation) {
   200  		i.Status.Action = cnab.ActionUpgrade
   201  		i.Status.ResultStatus = cnab.StatusRunning
   202  	})
   203  
   204  	_, err := cp.GetInstallation(context.Background(), "", "wordpress")
   205  	require.NoError(t, err, "ReadInstallation failed")
   206  
   207  	displayInstall := NewDisplayInstallation(install)
   208  
   209  	convertedInstallation, err := displayInstall.ConvertToInstallation()
   210  	require.NoError(t, err, "failed to convert display installation to installation record")
   211  
   212  	require.Equal(t, install.SchemaVersion, convertedInstallation.SchemaVersion, "invalid schema version")
   213  	require.Equal(t, install.Name, convertedInstallation.Name, "invalid installation name")
   214  	require.Equal(t, install.Namespace, convertedInstallation.Namespace, "invalid installation namespace")
   215  	require.Equal(t, install.Uninstalled, convertedInstallation.Uninstalled, "invalid installation uninstalled status")
   216  	require.Equal(t, install.Bundle.Digest, convertedInstallation.Bundle.Digest, "invalid installation bundle")
   217  
   218  	require.Equal(t, len(install.Labels), len(convertedInstallation.Labels))
   219  	for key := range displayInstall.Labels {
   220  		require.Equal(t, install.Labels[key], convertedInstallation.Labels[key], "invalid installation labels")
   221  	}
   222  
   223  	require.Equal(t, install.Custom, convertedInstallation.Custom, "invalid installation custom")
   224  
   225  	require.Equal(t, convertedInstallation.CredentialSets, install.CredentialSets, "invalid credential set")
   226  	require.Equal(t, convertedInstallation.ParameterSets, install.ParameterSets, "invalid parameter set")
   227  
   228  	require.Equal(t, install.Parameters.String(), convertedInstallation.Parameters.String(), "invalid parameters name")
   229  	require.Equal(t, len(install.Parameters.Parameters), len(convertedInstallation.Parameters.Parameters))
   230  
   231  	parametersMap := make(map[string]secrets.SourceMap, len(install.Parameters.Parameters))
   232  	for _, param := range install.Parameters.Parameters {
   233  		parametersMap[param.Name] = param
   234  	}
   235  
   236  	for _, param := range convertedInstallation.Parameters.Parameters {
   237  		expected := parametersMap[param.Name]
   238  		require.Equal(t, expected.ResolvedValue, param.ResolvedValue)
   239  		expectedSource, err := expected.Source.MarshalJSON()
   240  		require.NoError(t, err)
   241  		source, err := param.Source.MarshalJSON()
   242  		require.NoError(t, err)
   243  		require.Equal(t, expectedSource, source)
   244  	}
   245  
   246  	require.Equal(t, install.Status.Created, convertedInstallation.Status.Created, "invalid created time")
   247  	require.Equal(t, install.Status.Modified, convertedInstallation.Status.Modified, "invalid modified time")
   248  	require.Equal(t, cnab.ActionUpgrade, convertedInstallation.Status.Action, "invalid last action")
   249  	require.Equal(t, cnab.StatusRunning, convertedInstallation.Status.ResultStatus, "invalid last status")
   250  
   251  }
   252  
   253  func TestPorter_PrintInstallations(t *testing.T) {
   254  	t.Parallel()
   255  
   256  	testcases := []struct {
   257  		name       string
   258  		format     printer.Format
   259  		outputFile string
   260  	}{
   261  		{name: "plain", format: printer.FormatPlaintext, outputFile: "testdata/list/expected-output.txt"},
   262  		{name: "no reference, plain", format: printer.FormatPlaintext, outputFile: "testdata/list/no-reference-expected-output.txt"},
   263  		{name: "json", format: printer.FormatJson, outputFile: "testdata/list/expected-output.json"},
   264  		{name: "yaml", format: printer.FormatYaml, outputFile: "testdata/list/expected-output.yaml"},
   265  	}
   266  
   267  	for _, tc := range testcases {
   268  		t.Run(tc.name, func(t *testing.T) {
   269  			p := NewTestPorter(t)
   270  			defer p.Close()
   271  
   272  			opts := ListOptions{
   273  				Namespace: "dev",
   274  				Name:      "mywordpress",
   275  				PrintOptions: printer.PrintOptions{
   276  					Format: tc.format,
   277  				},
   278  			}
   279  
   280  			p.TestInstallations.CreateInstallation(storage.NewInstallation("dev", "mywordpress"), p.TestInstallations.SetMutableInstallationValues, func(i *storage.Installation) {
   281  				i.Status.BundleVersion = "v1.2.3"
   282  				i.Status.ResultStatus = cnab.StatusSucceeded
   283  			})
   284  
   285  			ctx := context.Background()
   286  
   287  			err := p.PrintInstallations(ctx, opts)
   288  			require.NoError(t, err, "PrintInstallation failed")
   289  			p.CompareGoldenFile(tc.outputFile, p.TestConfig.TestContext.GetOutput())
   290  		})
   291  	}
   292  }
   293  
   294  func TestPorter_getDisplayInstallationState(t *testing.T) {
   295  	p := NewTestPorter(t)
   296  	defer p.Close()
   297  
   298  	installation := p.TestInstallations.CreateInstallation(storage.NewInstallation("dev", "mywordpress"), p.TestInstallations.SetMutableInstallationValues)
   299  	displayInstallationState := getDisplayInstallationState(installation)
   300  	require.Equal(t, StateDefined, displayInstallationState)
   301  
   302  	bun := cnab.ExtendedBundle{}
   303  	run := p.TestInstallations.CreateRun(installation.NewRun(cnab.ActionInstall, bun), p.TestInstallations.SetMutableRunValues)
   304  	result := p.TestInstallations.CreateResult(run.NewResult(cnab.StatusSucceeded), p.TestInstallations.SetMutableResultValues)
   305  	installation.ApplyResult(run, result)
   306  	installTime := now.Add(-time.Second * 5)
   307  	installation.Status.Installed = &installTime
   308  	displayInstallationState = getDisplayInstallationState(installation)
   309  	require.Equal(t, StateInstalled, displayInstallationState)
   310  
   311  	run = p.TestInstallations.CreateRun(installation.NewRun(cnab.ActionUninstall, bun), p.TestInstallations.SetMutableRunValues)
   312  	result = p.TestInstallations.CreateResult(run.NewResult(cnab.StatusSucceeded), p.TestInstallations.SetMutableResultValues)
   313  	installation.ApplyResult(run, result)
   314  	installation.Status.Uninstalled = &now
   315  	displayInstallationState = getDisplayInstallationState(installation)
   316  	require.Equal(t, StateUninstalled, displayInstallationState)
   317  }
   318  
   319  func TestPorter_getDisplayInstallationStatus(t *testing.T) {
   320  	p := NewTestPorter(t)
   321  	defer p.Close()
   322  
   323  	installation := p.TestInstallations.CreateInstallation(storage.NewInstallation("dev", "mywordpress"), p.TestInstallations.SetMutableInstallationValues)
   324  	bun := cnab.ExtendedBundle{}
   325  	run := p.TestInstallations.CreateRun(installation.NewRun(cnab.ActionInstall, bun), p.TestInstallations.SetMutableRunValues)
   326  	result := p.TestInstallations.CreateResult(run.NewResult(cnab.StatusSucceeded), p.TestInstallations.SetMutableResultValues)
   327  	installation.ApplyResult(run, result)
   328  	displayInstallationStatus := getDisplayInstallationStatus(installation)
   329  	require.Equal(t, cnab.StatusSucceeded, displayInstallationStatus)
   330  
   331  	result = p.TestInstallations.CreateResult(run.NewResult(cnab.StatusFailed), p.TestInstallations.SetMutableResultValues)
   332  	installation.ApplyResult(run, result)
   333  	displayInstallationStatus = getDisplayInstallationStatus(installation)
   334  	require.Equal(t, cnab.StatusFailed, displayInstallationStatus)
   335  
   336  	run = p.TestInstallations.CreateRun(installation.NewRun(cnab.ActionInstall, bun), p.TestInstallations.SetMutableRunValues)
   337  	result = p.TestInstallations.CreateResult(run.NewResult(cnab.StatusRunning), p.TestInstallations.SetMutableResultValues)
   338  	installation.ApplyResult(run, result)
   339  	displayInstallationStatus = getDisplayInstallationStatus(installation)
   340  	require.Equal(t, StatusInstalling, displayInstallationStatus)
   341  
   342  	run = p.TestInstallations.CreateRun(installation.NewRun(cnab.ActionUninstall, bun), p.TestInstallations.SetMutableRunValues)
   343  	result = p.TestInstallations.CreateResult(run.NewResult(cnab.StatusRunning), p.TestInstallations.SetMutableResultValues)
   344  	installation.ApplyResult(run, result)
   345  	displayInstallationStatus = getDisplayInstallationStatus(installation)
   346  	require.Equal(t, StatusUninstalling, displayInstallationStatus)
   347  
   348  	run = p.TestInstallations.CreateRun(installation.NewRun(cnab.ActionUpgrade, bun), p.TestInstallations.SetMutableRunValues)
   349  	result = p.TestInstallations.CreateResult(run.NewResult(cnab.StatusRunning), p.TestInstallations.SetMutableResultValues)
   350  	installation.ApplyResult(run, result)
   351  	displayInstallationStatus = getDisplayInstallationStatus(installation)
   352  	require.Equal(t, StatusUpgrading, displayInstallationStatus)
   353  
   354  	run = p.TestInstallations.CreateRun(installation.NewRun("customaction", bun), p.TestInstallations.SetMutableRunValues)
   355  	result = p.TestInstallations.CreateResult(run.NewResult(cnab.StatusRunning), p.TestInstallations.SetMutableResultValues)
   356  	installation.ApplyResult(run, result)
   357  	installation.Status.Action = "customaction"
   358  	displayInstallationStatus = getDisplayInstallationStatus(installation)
   359  	require.Equal(t, "running customaction", displayInstallationStatus)
   360  }
   361  
   362  func Test_parseFieldSelector(t *testing.T) {
   363  	tests := []struct {
   364  		name           string
   365  		fieldSelector  string
   366  		want           map[string]string
   367  		wantErr        bool
   368  		expectedErrMsg string
   369  	}{
   370  		{
   371  			name:          "valid field selector",
   372  			fieldSelector: "bundle.version=0.2.0,status.action=install",
   373  			want: map[string]string{
   374  				"bundle.version": "0.2.0",
   375  				"status.action":  "install",
   376  			},
   377  			wantErr: false,
   378  		},
   379  		{
   380  			name:           "invalid field selector",
   381  			fieldSelector:  "bundle.version=0.2.0,status.action",
   382  			want:           nil,
   383  			wantErr:        true,
   384  			expectedErrMsg: "invalid field selector: bundle.version=0.2.0,status.action",
   385  		},
   386  	}
   387  	for _, tt := range tests {
   388  		t.Run(tt.name, func(t *testing.T) {
   389  			got, err := parseFieldSelector(tt.fieldSelector)
   390  			if (err != nil) != tt.wantErr {
   391  				t.Errorf("parseFieldSelector() error = %v, wantErr %v", err, tt.wantErr)
   392  				return
   393  			}
   394  			if !reflect.DeepEqual(got, tt.want) {
   395  				t.Errorf("parseFieldSelector() = %v, want %v", got, tt.want)
   396  			}
   397  			if err != nil && err.Error() != tt.expectedErrMsg {
   398  				t.Errorf("parseFieldSelector() error message = %v, expected error message %v", err.Error(), tt.expectedErrMsg)
   399  			}
   400  		})
   401  	}
   402  }
   403  
   404  func Test_doesInstallationMatchFieldSelectors(t *testing.T) {
   405  	type args struct {
   406  		installation     DisplayInstallation
   407  		fieldSelectorMap map[string]string
   408  	}
   409  	tests := []struct {
   410  		name string
   411  		args args
   412  		want bool
   413  	}{
   414  		{
   415  			name: "installation matches field selectors",
   416  			args: args{
   417  				installation: DisplayInstallation{
   418  					Name: "wordpress",
   419  					Status: storage.InstallationStatus{
   420  						Action:       "install",
   421  						ResultStatus: "succeeded",
   422  					},
   423  					Bundle: storage.OCIReferenceParts{
   424  						Version: "0.2.0",
   425  					},
   426  				},
   427  				fieldSelectorMap: map[string]string{
   428  					"name":                "wordpress",
   429  					"status.action":       "install",
   430  					"status.resultStatus": "succeeded",
   431  					"bundle.version":      "0.2.0",
   432  				},
   433  			},
   434  			want: true,
   435  		},
   436  		{
   437  			name: "installation matches field selectors",
   438  			args: args{
   439  				installation: DisplayInstallation{
   440  					Name: "wordpress",
   441  					Status: storage.InstallationStatus{
   442  						Action:       "install",
   443  						ResultStatus: "succeeded",
   444  					},
   445  					Bundle: storage.OCIReferenceParts{
   446  						Version: "0.2.1",
   447  					},
   448  				},
   449  				fieldSelectorMap: map[string]string{
   450  					"name":                "wordpress",
   451  					"status.action":       "install",
   452  					"status.resultStatus": "succeeded",
   453  					"bundle.version":      "0.2.0",
   454  				},
   455  			},
   456  			want: false,
   457  		},
   458  		{
   459  			name: "installation matches field selectors",
   460  			args: args{
   461  				installation: DisplayInstallation{
   462  					Name: "wordpress",
   463  					Status: storage.InstallationStatus{
   464  						Action:       "install",
   465  						ResultStatus: "failed",
   466  					},
   467  					Bundle: storage.OCIReferenceParts{
   468  						Version: "0.2.0",
   469  					},
   470  				},
   471  				fieldSelectorMap: map[string]string{
   472  					"name":                "wordpress",
   473  					"status.action":       "install",
   474  					"status.resultStatus": "succeeded",
   475  					"bundle.version":      "0.2.0",
   476  				},
   477  			},
   478  			want: false,
   479  		},
   480  	}
   481  	for _, tt := range tests {
   482  		t.Run(tt.name, func(t *testing.T) {
   483  			if got := doesInstallationMatchFieldSelectors(tt.args.installation, tt.args.fieldSelectorMap); got != tt.want {
   484  				t.Errorf("doesInstallationMatchFieldSelectors() = %v, want %v", got, tt.want)
   485  			}
   486  		})
   487  	}
   488  }
   489  
   490  func Test_installationHasFieldWithValue(t *testing.T) {
   491  	type args struct {
   492  		installation     DisplayInstallation
   493  		fieldJsonTagPath string
   494  		value            string
   495  	}
   496  	tests := []struct {
   497  		name string
   498  		args args
   499  		want bool
   500  	}{
   501  		{
   502  			name: "installation has field with value",
   503  			args: args{
   504  				installation: DisplayInstallation{
   505  					Name: "wordpress",
   506  					Status: storage.InstallationStatus{
   507  						Action:       "install",
   508  						ResultStatus: "succeeded",
   509  					},
   510  					Bundle: storage.OCIReferenceParts{
   511  						Version: "0.2.0",
   512  					},
   513  				},
   514  				fieldJsonTagPath: "name",
   515  				value:            "wordpress",
   516  			},
   517  			want: true,
   518  		},
   519  		{
   520  			name: "installation has nested field with value",
   521  			args: args{
   522  				installation: DisplayInstallation{
   523  					Name: "wordpress",
   524  					Status: storage.InstallationStatus{
   525  						Action:       "install",
   526  						ResultStatus: "succeeded",
   527  					},
   528  					Bundle: storage.OCIReferenceParts{
   529  						Version: "0.2.0",
   530  					},
   531  				},
   532  				fieldJsonTagPath: "bundle.version",
   533  				value:            "0.2.0",
   534  			},
   535  			want: true,
   536  		},
   537  		{
   538  			name: "installation does not have field with value",
   539  			args: args{
   540  				installation: DisplayInstallation{
   541  					Name: "wordpress",
   542  					Status: storage.InstallationStatus{
   543  						Action:       "install",
   544  						ResultStatus: "succeeded",
   545  					},
   546  					Bundle: storage.OCIReferenceParts{
   547  						Version: "0.2.0",
   548  					},
   549  				},
   550  				fieldJsonTagPath: "bundle.version",
   551  				value:            "0.2.1",
   552  			},
   553  			want: false,
   554  		},
   555  		{
   556  			name: "installation does not have field with value",
   557  			args: args{
   558  				installation: DisplayInstallation{
   559  					Name: "wordpress",
   560  					Status: storage.InstallationStatus{
   561  						Action:       "install",
   562  						ResultStatus: "succeeded",
   563  					},
   564  					Bundle: storage.OCIReferenceParts{
   565  						Version: "0.2.0",
   566  					},
   567  				},
   568  				fieldJsonTagPath: "xyz",
   569  				value:            "123",
   570  			},
   571  			want: false,
   572  		},
   573  	}
   574  	for _, tt := range tests {
   575  		t.Run(tt.name, func(t *testing.T) {
   576  			if got := installationHasFieldWithValue(tt.args.installation, tt.args.fieldJsonTagPath, tt.args.value); got != tt.want {
   577  				t.Errorf("installationHasFieldWithValue() = %v, want %v", got, tt.want)
   578  			}
   579  		})
   580  	}
   581  }