...

Source file src/github.com/redhat-developer/odo/pkg/libdevfile/libdevfile_test.go

Documentation: github.com/redhat-developer/odo/pkg/libdevfile

     1  package libdevfile
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
    10  	devfilepkg "github.com/devfile/api/v2/pkg/devfile"
    11  	"github.com/devfile/library/v2/pkg/devfile/parser"
    12  	"github.com/devfile/library/v2/pkg/devfile/parser/data"
    13  	devfileFileSystem "github.com/devfile/library/v2/pkg/testingutil/filesystem"
    14  	dfutil "github.com/devfile/library/v2/pkg/util"
    15  	"github.com/golang/mock/gomock"
    16  	"github.com/google/go-cmp/cmp"
    17  	"k8s.io/utils/pointer"
    18  
    19  	"github.com/redhat-developer/odo/pkg/libdevfile/generator"
    20  	"github.com/redhat-developer/odo/pkg/testingutil"
    21  	"github.com/redhat-developer/odo/pkg/util"
    22  )
    23  
    24  var buildGroup = v1alpha2.BuildCommandGroupKind
    25  var runGroup = v1alpha2.RunCommandGroupKind
    26  
    27  func TestGetCommand(t *testing.T) {
    28  
    29  	commands := [...]string{"ls -la", "pwd"}
    30  	components := [...]string{"alias1", "alias2"}
    31  
    32  	tests := []struct {
    33  		name           string
    34  		requestedType  []v1alpha2.CommandGroupKind
    35  		execCommands   []v1alpha2.Command
    36  		compCommands   []v1alpha2.Command
    37  		reqCommandName string
    38  		retCommandName string
    39  		wantErr        bool
    40  		wantPresent    bool
    41  	}{
    42  		{
    43  			name: "Case 1: Valid devfile",
    44  			execCommands: []v1alpha2.Command{
    45  				getExecCommand("build", buildGroup),
    46  				getExecCommand("run", runGroup),
    47  			},
    48  			requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
    49  			wantErr:       false,
    50  			wantPresent:   true,
    51  		},
    52  		{
    53  			name: "Case 2: Valid devfile with devrun and devbuild",
    54  			execCommands: []v1alpha2.Command{
    55  				getExecCommand("build", buildGroup),
    56  				getExecCommand("run", runGroup),
    57  			},
    58  			requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
    59  			wantErr:       false,
    60  			wantPresent:   true,
    61  		},
    62  		{
    63  			name: "Case 3: Valid devfile with empty workdir",
    64  			execCommands: []v1alpha2.Command{
    65  				{
    66  					CommandUnion: v1alpha2.CommandUnion{
    67  						Exec: &v1alpha2.ExecCommand{
    68  							LabeledCommand: v1alpha2.LabeledCommand{
    69  								BaseCommand: v1alpha2.BaseCommand{
    70  									Group: &v1alpha2.CommandGroup{Kind: runGroup},
    71  								},
    72  							},
    73  							CommandLine: commands[0],
    74  							Component:   components[0],
    75  						},
    76  					},
    77  				},
    78  			},
    79  			requestedType: []v1alpha2.CommandGroupKind{runGroup},
    80  			wantErr:       false,
    81  			wantPresent:   true,
    82  		},
    83  		{
    84  			name: "Case 4.1: Mismatched command type",
    85  			execCommands: []v1alpha2.Command{
    86  				{
    87  					Id: "build command",
    88  					CommandUnion: v1alpha2.CommandUnion{
    89  						Exec: &v1alpha2.ExecCommand{
    90  							LabeledCommand: v1alpha2.LabeledCommand{
    91  								BaseCommand: v1alpha2.BaseCommand{
    92  									Group: &v1alpha2.CommandGroup{Kind: runGroup},
    93  								},
    94  							},
    95  							CommandLine: commands[0],
    96  							Component:   components[0],
    97  						},
    98  					},
    99  				},
   100  			},
   101  			reqCommandName: "build command",
   102  			requestedType:  []v1alpha2.CommandGroupKind{buildGroup},
   103  			wantErr:        true,
   104  		},
   105  		{
   106  			name: "Case 4.2: Matching command by name and type",
   107  			execCommands: []v1alpha2.Command{
   108  				{
   109  					Id: "build command",
   110  					CommandUnion: v1alpha2.CommandUnion{
   111  						Exec: &v1alpha2.ExecCommand{
   112  							LabeledCommand: v1alpha2.LabeledCommand{
   113  								BaseCommand: v1alpha2.BaseCommand{
   114  									Group: &v1alpha2.CommandGroup{Kind: buildGroup},
   115  								},
   116  							},
   117  							CommandLine: commands[0],
   118  							Component:   components[0],
   119  						},
   120  					},
   121  				},
   122  			},
   123  			reqCommandName: "build command",
   124  			requestedType:  []v1alpha2.CommandGroupKind{buildGroup},
   125  			wantErr:        false,
   126  			wantPresent:    true,
   127  		},
   128  		{
   129  			name: "Case 5: Default command is returned",
   130  			execCommands: []v1alpha2.Command{
   131  				{
   132  					Id: "defaultRunCommand",
   133  					CommandUnion: v1alpha2.CommandUnion{
   134  						Exec: &v1alpha2.ExecCommand{
   135  							LabeledCommand: v1alpha2.LabeledCommand{
   136  								BaseCommand: v1alpha2.BaseCommand{
   137  									Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
   138  								},
   139  							},
   140  							CommandLine: commands[0],
   141  							Component:   components[0],
   142  						},
   143  					},
   144  				},
   145  				{
   146  					Id: "runCommand",
   147  					CommandUnion: v1alpha2.CommandUnion{
   148  						Exec: &v1alpha2.ExecCommand{
   149  							LabeledCommand: v1alpha2.LabeledCommand{
   150  								BaseCommand: v1alpha2.BaseCommand{
   151  									Group: &v1alpha2.CommandGroup{Kind: runGroup},
   152  								},
   153  							},
   154  							CommandLine: commands[0],
   155  							Component:   components[0],
   156  						},
   157  					},
   158  				},
   159  			},
   160  			retCommandName: "defaultRunCommand",
   161  			requestedType:  []v1alpha2.CommandGroupKind{runGroup},
   162  			wantErr:        false,
   163  			wantPresent:    true,
   164  		},
   165  		{
   166  			name: "Case 5.1: if only one command is present, it is returned and assumed as default",
   167  			execCommands: []v1alpha2.Command{
   168  				{
   169  					Id: "defaultRunCommand",
   170  					CommandUnion: v1alpha2.CommandUnion{
   171  						Exec: &v1alpha2.ExecCommand{
   172  							LabeledCommand: v1alpha2.LabeledCommand{
   173  								BaseCommand: v1alpha2.BaseCommand{
   174  									Group: &v1alpha2.CommandGroup{Kind: runGroup},
   175  								},
   176  							},
   177  							CommandLine: commands[0],
   178  							Component:   components[0],
   179  						},
   180  					},
   181  				},
   182  			},
   183  			retCommandName: "defaultRunCommand",
   184  			requestedType:  []v1alpha2.CommandGroupKind{runGroup},
   185  			wantErr:        false,
   186  			wantPresent:    true,
   187  		},
   188  		{
   189  			name: "Case 5.2: if multiple default commands are present, error is returned",
   190  			execCommands: []v1alpha2.Command{
   191  				{
   192  					Id: "runCommand1",
   193  					CommandUnion: v1alpha2.CommandUnion{
   194  						Exec: &v1alpha2.ExecCommand{
   195  							LabeledCommand: v1alpha2.LabeledCommand{
   196  								BaseCommand: v1alpha2.BaseCommand{
   197  									Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
   198  								},
   199  							},
   200  							CommandLine: commands[0],
   201  							Component:   components[0],
   202  						},
   203  					},
   204  				}, {
   205  					Id: "runCommand2",
   206  					CommandUnion: v1alpha2.CommandUnion{
   207  						Exec: &v1alpha2.ExecCommand{
   208  							LabeledCommand: v1alpha2.LabeledCommand{
   209  								BaseCommand: v1alpha2.BaseCommand{
   210  									Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
   211  								},
   212  							},
   213  							CommandLine: commands[0],
   214  							Component:   components[0],
   215  						},
   216  					},
   217  				},
   218  			},
   219  
   220  			requestedType: []v1alpha2.CommandGroupKind{runGroup},
   221  			wantErr:       true,
   222  		},
   223  		{
   224  			name: "Case 5.2: if multiple default commands are present, error is returned",
   225  			execCommands: []v1alpha2.Command{
   226  				{
   227  					Id: "runCommand1",
   228  					CommandUnion: v1alpha2.CommandUnion{
   229  						Exec: &v1alpha2.ExecCommand{
   230  							LabeledCommand: v1alpha2.LabeledCommand{
   231  								BaseCommand: v1alpha2.BaseCommand{
   232  									Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
   233  								},
   234  							},
   235  							CommandLine: commands[0],
   236  							Component:   components[0],
   237  						},
   238  					},
   239  				}, {
   240  					Id: "runCommand2",
   241  					CommandUnion: v1alpha2.CommandUnion{
   242  						Exec: &v1alpha2.ExecCommand{
   243  							LabeledCommand: v1alpha2.LabeledCommand{
   244  								BaseCommand: v1alpha2.BaseCommand{
   245  									Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
   246  								},
   247  							},
   248  							CommandLine: commands[0],
   249  							Component:   components[0],
   250  						},
   251  					},
   252  				},
   253  			},
   254  
   255  			requestedType: []v1alpha2.CommandGroupKind{runGroup},
   256  			wantErr:       true,
   257  		},
   258  		{
   259  			name: "Case 6: Composite command is returned",
   260  			execCommands: []v1alpha2.Command{
   261  				{
   262  					Id: "build",
   263  					CommandUnion: v1alpha2.CommandUnion{
   264  						Exec: &v1alpha2.ExecCommand{
   265  							LabeledCommand: v1alpha2.LabeledCommand{
   266  								BaseCommand: v1alpha2.BaseCommand{
   267  									Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBool(false)},
   268  								},
   269  							},
   270  							CommandLine: commands[0],
   271  							Component:   components[0],
   272  						},
   273  					},
   274  				},
   275  				{
   276  					Id: "run",
   277  					CommandUnion: v1alpha2.CommandUnion{
   278  						Exec: &v1alpha2.ExecCommand{
   279  							LabeledCommand: v1alpha2.LabeledCommand{
   280  								BaseCommand: v1alpha2.BaseCommand{
   281  									Group: &v1alpha2.CommandGroup{Kind: runGroup},
   282  								},
   283  							},
   284  							CommandLine: commands[0],
   285  							Component:   components[0],
   286  						},
   287  					},
   288  				},
   289  			},
   290  			compCommands: []v1alpha2.Command{
   291  				{
   292  					Id: "myComposite",
   293  					CommandUnion: v1alpha2.CommandUnion{
   294  						Composite: &v1alpha2.CompositeCommand{
   295  							LabeledCommand: v1alpha2.LabeledCommand{
   296  								BaseCommand: v1alpha2.BaseCommand{
   297  									Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBool(true)},
   298  								},
   299  							},
   300  							Commands: []string{"build", "run"},
   301  						},
   302  					},
   303  				},
   304  			},
   305  			retCommandName: "myComposite",
   306  			requestedType:  []v1alpha2.CommandGroupKind{buildGroup},
   307  			wantErr:        false,
   308  			wantPresent:    true,
   309  		},
   310  	}
   311  	for _, tt := range tests {
   312  		t.Run(tt.name, func(t *testing.T) {
   313  			components := []v1alpha2.Component{testingutil.GetFakeContainerComponent(tt.execCommands[0].Exec.Component)}
   314  			devObj := parser.DevfileObj{
   315  				Data: func() data.DevfileData {
   316  					devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
   317  					if err != nil {
   318  						t.Error(err)
   319  					}
   320  					err = devfileData.AddCommands(tt.execCommands)
   321  					if err != nil {
   322  						t.Error(err)
   323  					}
   324  					err = devfileData.AddCommands(tt.compCommands)
   325  					if err != nil {
   326  						t.Error(err)
   327  					}
   328  					err = devfileData.AddComponents(components)
   329  					if err != nil {
   330  						t.Error(err)
   331  					}
   332  					return devfileData
   333  				}(),
   334  			}
   335  
   336  			for _, gtype := range tt.requestedType {
   337  				cmd, ok, err := GetCommand(devObj, tt.reqCommandName, gtype)
   338  				if tt.wantErr != (err != nil) {
   339  					t.Errorf("TestGetCommand unexpected error for command: %v wantErr: %v err: %v", gtype, tt.wantErr, err)
   340  					return
   341  				} else if tt.wantErr {
   342  					return
   343  				}
   344  				if tt.wantPresent != ok {
   345  					t.Errorf("TestGetCommand unexpected presence for command: %v wantPresent: %v ok: %v", gtype, tt.wantPresent, ok)
   346  					return
   347  				}
   348  
   349  				if len(tt.retCommandName) > 0 && cmd.Id != tt.retCommandName {
   350  					t.Errorf("TestGetCommand error: command names do not match expected: %v actual: %v", tt.retCommandName, cmd.Id)
   351  				}
   352  			}
   353  		})
   354  	}
   355  
   356  }
   357  
   358  func TestDeploy(t *testing.T) {
   359  	deployDefault1 := generator.GetCompositeCommand(generator.CompositeCommandParams{
   360  		Kind:      v1alpha2.DeployCommandGroupKind,
   361  		Id:        "deploy-default-1",
   362  		IsDefault: pointer.Bool(true),
   363  		Commands:  []string{"image-command", "deployment-command", "service-command"},
   364  	})
   365  	applyImageCommand := generator.GetApplyCommand(generator.ApplyCommandParams{
   366  		Kind:      v1alpha2.DeployCommandGroupKind,
   367  		Id:        "image-command",
   368  		IsDefault: pointer.Bool(false),
   369  		Component: "image-component",
   370  	})
   371  	applyDeploymentCommand := generator.GetApplyCommand(generator.ApplyCommandParams{
   372  		Kind:      v1alpha2.DeployCommandGroupKind,
   373  		Id:        "deployment-command",
   374  		IsDefault: pointer.Bool(false),
   375  		Component: "deployment-component",
   376  	})
   377  	applyServiceCommand := generator.GetApplyCommand(generator.ApplyCommandParams{
   378  		Kind:      v1alpha2.DeployCommandGroupKind,
   379  		Id:        "service-command",
   380  		IsDefault: pointer.Bool(false),
   381  		Component: "service-component",
   382  	})
   383  
   384  	imageComponent := generator.GetImageComponent(generator.ImageComponentParams{
   385  		Name: "image-component",
   386  		Image: v1alpha2.Image{
   387  			ImageName: "an-image-name",
   388  		},
   389  	})
   390  	deploymentComponent := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
   391  		Name:       "deployment-component",
   392  		Kubernetes: &v1alpha2.KubernetesComponent{},
   393  	})
   394  	serviceComponent := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
   395  		Name:       "service-component",
   396  		Kubernetes: &v1alpha2.KubernetesComponent{},
   397  	})
   398  
   399  	type args struct {
   400  		devfileObj func() parser.DevfileObj
   401  		handler    func(ctrl *gomock.Controller) Handler
   402  	}
   403  	tests := []struct {
   404  		name    string
   405  		args    args
   406  		wantErr bool
   407  	}{
   408  		{
   409  			name: "deploy an image and two kubernetes components",
   410  			args: args{
   411  				devfileObj: func() parser.DevfileObj {
   412  					dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   413  					_ = dData.AddCommands([]v1alpha2.Command{deployDefault1, applyImageCommand, applyDeploymentCommand, applyServiceCommand})
   414  					_ = dData.AddComponents([]v1alpha2.Component{imageComponent, deploymentComponent, serviceComponent})
   415  					return parser.DevfileObj{
   416  						Data: dData,
   417  					}
   418  				},
   419  				handler: func(ctrl *gomock.Controller) Handler {
   420  					h := NewMockHandler(ctrl)
   421  					h.EXPECT().ApplyImage(imageComponent)
   422  					h.EXPECT().ApplyKubernetes(deploymentComponent, v1alpha2.DeployCommandGroupKind)
   423  					h.EXPECT().ApplyKubernetes(serviceComponent, v1alpha2.DeployCommandGroupKind)
   424  					return h
   425  				},
   426  			},
   427  		},
   428  		{
   429  			name: "deploy with multiple deploy and no default",
   430  			args: args{
   431  				devfileObj: func() parser.DevfileObj {
   432  					dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   433  					_ = dData.AddCommands([]v1alpha2.Command{applyServiceCommand, applyDeploymentCommand})
   434  					_ = dData.AddComponents([]v1alpha2.Component{deploymentComponent, serviceComponent})
   435  					return parser.DevfileObj{
   436  						Data: dData,
   437  					}
   438  				},
   439  				handler: func(ctrl *gomock.Controller) Handler {
   440  					return NewMockHandler(ctrl)
   441  				},
   442  			},
   443  			wantErr: true,
   444  		},
   445  		// TODO: Add test cases.
   446  	}
   447  	for _, tt := range tests {
   448  		t.Run(tt.name, func(t *testing.T) {
   449  			ctrl := gomock.NewController(t)
   450  			if err := Deploy(context.Background(), tt.args.devfileObj(), tt.args.handler(ctrl)); (err != nil) != tt.wantErr {
   451  				t.Errorf("Deploy() error = %v, wantErr %v", err, tt.wantErr)
   452  			}
   453  		})
   454  	}
   455  }
   456  
   457  func TestBuild(t *testing.T) {
   458  	containerComp := v1alpha2.Component{
   459  		Name: "my-container",
   460  		ComponentUnion: v1alpha2.ComponentUnion{
   461  			Container: &v1alpha2.ContainerComponent{
   462  				Container: v1alpha2.Container{
   463  					Image: "my-image",
   464  				},
   465  			},
   466  		},
   467  	}
   468  	defaultBuildCommand := generator.GetExecCommand(generator.ExecCommandParams{
   469  		Kind:        v1alpha2.BuildCommandGroupKind,
   470  		Id:          "my-default-build-command",
   471  		IsDefault:   pointer.Bool(true),
   472  		CommandLine: "build my-app",
   473  		Component:   containerComp.Name,
   474  	})
   475  	nonDefaultBuildCommandExplicit := generator.GetExecCommand(generator.ExecCommandParams{
   476  		Kind:        v1alpha2.BuildCommandGroupKind,
   477  		Id:          "my-explicit-non-default-build-command",
   478  		IsDefault:   pointer.Bool(false),
   479  		CommandLine: "build my-app",
   480  		Component:   containerComp.Name,
   481  	})
   482  	nonDefaultBuildCommandImplicit := generator.GetExecCommand(generator.ExecCommandParams{
   483  		Kind:        v1alpha2.BuildCommandGroupKind,
   484  		Id:          "my-implicit-non-default-build-command",
   485  		CommandLine: "build my-app",
   486  		Component:   containerComp.Name,
   487  	})
   488  
   489  	nonDefaultRunCommand := generator.GetExecCommand(generator.ExecCommandParams{
   490  		Kind:        v1alpha2.RunCommandGroupKind,
   491  		Id:          "my-non-default-run-command",
   492  		CommandLine: "run my-app",
   493  		Component:   containerComp.Name,
   494  	})
   495  	defaultBuildCommandComposite := generator.GetCompositeCommand(generator.CompositeCommandParams{
   496  		Kind:      v1alpha2.BuildCommandGroupKind,
   497  		Id:        "my-default-build-command-composite",
   498  		IsDefault: pointer.Bool(true),
   499  		Commands:  []string{"my-non-default-run-command"},
   500  	})
   501  	type args struct {
   502  		devfileObj func() parser.DevfileObj
   503  		handler    func(ctrl *gomock.Controller) Handler
   504  		cmdName    string
   505  	}
   506  	for _, tt := range []struct {
   507  		name    string
   508  		args    args
   509  		wantErr bool
   510  	}{
   511  		{
   512  			name: "missing default command",
   513  			args: args{
   514  				devfileObj: func() parser.DevfileObj {
   515  					dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   516  					_ = dData.AddCommands([]v1alpha2.Command{nonDefaultBuildCommandExplicit, nonDefaultBuildCommandImplicit})
   517  					_ = dData.AddComponents([]v1alpha2.Component{containerComp})
   518  					return parser.DevfileObj{
   519  						Data: dData,
   520  					}
   521  				},
   522  				handler: func(ctrl *gomock.Controller) Handler {
   523  					h := NewMockHandler(ctrl)
   524  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(defaultBuildCommand)).Times(0)
   525  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandExplicit)).Times(0)
   526  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandImplicit)).Times(0)
   527  					return h
   528  				},
   529  			},
   530  		},
   531  		{
   532  			name: "with default command",
   533  			args: args{
   534  				devfileObj: func() parser.DevfileObj {
   535  					dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   536  					_ = dData.AddCommands([]v1alpha2.Command{defaultBuildCommand, nonDefaultBuildCommandExplicit, nonDefaultBuildCommandImplicit})
   537  					_ = dData.AddComponents([]v1alpha2.Component{containerComp})
   538  					return parser.DevfileObj{
   539  						Data: dData,
   540  					}
   541  				},
   542  				handler: func(ctrl *gomock.Controller) Handler {
   543  					h := NewMockHandler(ctrl)
   544  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(defaultBuildCommand)).Times(1)
   545  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandExplicit)).Times(0)
   546  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandImplicit)).Times(0)
   547  					return h
   548  				},
   549  			},
   550  		},
   551  		{
   552  			name: "missing custom command",
   553  			args: args{
   554  				devfileObj: func() parser.DevfileObj {
   555  					dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   556  					_ = dData.AddCommands([]v1alpha2.Command{defaultBuildCommand})
   557  					_ = dData.AddComponents([]v1alpha2.Component{containerComp})
   558  					return parser.DevfileObj{
   559  						Data: dData,
   560  					}
   561  				},
   562  				handler: func(ctrl *gomock.Controller) Handler {
   563  					h := NewMockHandler(ctrl)
   564  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(defaultBuildCommand)).Times(0)
   565  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandExplicit)).Times(0)
   566  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandImplicit)).Times(0)
   567  					return h
   568  				},
   569  				cmdName: "my-explicit-non-default-build-command",
   570  			},
   571  			wantErr: true,
   572  		},
   573  		{
   574  			name: "with custom command",
   575  			args: args{
   576  				devfileObj: func() parser.DevfileObj {
   577  					dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   578  					_ = dData.AddCommands([]v1alpha2.Command{nonDefaultBuildCommandExplicit, nonDefaultBuildCommandImplicit})
   579  					_ = dData.AddComponents([]v1alpha2.Component{containerComp})
   580  					return parser.DevfileObj{
   581  						Data: dData,
   582  					}
   583  				},
   584  				handler: func(ctrl *gomock.Controller) Handler {
   585  					h := NewMockHandler(ctrl)
   586  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(defaultBuildCommand)).Times(0)
   587  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandExplicit)).Times(1)
   588  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandImplicit)).Times(0)
   589  					return h
   590  				},
   591  				cmdName: "my-explicit-non-default-build-command",
   592  			},
   593  		},
   594  		{
   595  			name: "with default composite command",
   596  			args: args{
   597  				devfileObj: func() parser.DevfileObj {
   598  					dData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   599  					_ = dData.AddCommands([]v1alpha2.Command{defaultBuildCommandComposite, nonDefaultRunCommand})
   600  					_ = dData.AddComponents([]v1alpha2.Component{containerComp})
   601  					return parser.DevfileObj{
   602  						Data: dData,
   603  					}
   604  				},
   605  				handler: func(ctrl *gomock.Controller) Handler {
   606  					h := NewMockHandler(ctrl)
   607  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultRunCommand)).Times(1)
   608  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandExplicit)).Times(0)
   609  					h.EXPECT().ExecuteTerminatingCommand(gomock.Any(), gomock.Eq(nonDefaultBuildCommandImplicit)).Times(0)
   610  					return h
   611  				},
   612  			},
   613  		},
   614  	} {
   615  		t.Run(tt.name, func(t *testing.T) {
   616  			err := Build(context.Background(), tt.args.devfileObj(), tt.args.cmdName, tt.args.handler(gomock.NewController(t)))
   617  			if (err != nil) != tt.wantErr {
   618  				t.Errorf("Build() error = %v, wantErr %v", err, tt.wantErr)
   619  			}
   620  		})
   621  	}
   622  }
   623  
   624  func TestGetContainerEndpointMapping(t *testing.T) {
   625  	type args struct {
   626  		containers   []v1alpha2.Component
   627  		includeDebug bool
   628  	}
   629  
   630  	imageComponent := generator.GetImageComponent(generator.ImageComponentParams{
   631  		Name: "image-component",
   632  		Image: v1alpha2.Image{
   633  			ImageName: "an-image-name",
   634  		},
   635  	})
   636  
   637  	containerWithNoEndpoints := generator.GetContainerComponent(generator.ContainerComponentParams{
   638  		Name:      "container 1",
   639  		Endpoints: nil,
   640  	})
   641  
   642  	containerWithOnePublicEndpoint := generator.GetContainerComponent(generator.ContainerComponentParams{
   643  		Name: "container 2",
   644  		Endpoints: []v1alpha2.Endpoint{
   645  			{
   646  				Name:       "ep1",
   647  				TargetPort: 8080,
   648  				Exposure:   v1alpha2.PublicEndpointExposure,
   649  			},
   650  		},
   651  	})
   652  
   653  	containerWithOneInternalEndpoint := generator.GetContainerComponent(generator.ContainerComponentParams{
   654  		Name: "container 3",
   655  		Endpoints: []v1alpha2.Endpoint{
   656  			{
   657  				Name:       "ep2",
   658  				TargetPort: 9090,
   659  				Exposure:   v1alpha2.InternalEndpointExposure,
   660  			},
   661  		},
   662  	})
   663  
   664  	containerWithOneNoneDebugEndpoint := generator.GetContainerComponent(generator.ContainerComponentParams{
   665  		Name: "container-none-endpoint",
   666  		Endpoints: []v1alpha2.Endpoint{
   667  			{
   668  				Name:       "debug",
   669  				TargetPort: 9099,
   670  				Exposure:   v1alpha2.NoneEndpointExposure,
   671  			},
   672  		},
   673  	})
   674  	multiportContainer1 := generator.GetContainerComponent(generator.ContainerComponentParams{
   675  		Name: "container-multiple-endpoints-1",
   676  		Endpoints: []v1alpha2.Endpoint{
   677  			{
   678  				Name:       "http-3000",
   679  				TargetPort: 3000,
   680  				Exposure:   v1alpha2.PublicEndpointExposure,
   681  			},
   682  			{
   683  				Name:       "http-8888",
   684  				TargetPort: 8888,
   685  				Exposure:   v1alpha2.InternalEndpointExposure,
   686  			},
   687  			{
   688  				Name:       "debug",
   689  				TargetPort: 5005,
   690  				Exposure:   v1alpha2.NoneEndpointExposure,
   691  			},
   692  			{
   693  				Name:       "debug-udp",
   694  				TargetPort: 15005,
   695  				Exposure:   v1alpha2.NoneEndpointExposure,
   696  				Protocol:   v1alpha2.UDPEndpointProtocol,
   697  			},
   698  			{
   699  				Name:       "debug-ws",
   700  				TargetPort: 25005,
   701  				Exposure:   v1alpha2.NoneEndpointExposure,
   702  				Protocol:   v1alpha2.WSEndpointProtocol,
   703  			},
   704  		},
   705  	})
   706  	multiportContainer2 := generator.GetContainerComponent(generator.ContainerComponentParams{
   707  		Name: "container-multiple-endpoints-2",
   708  		Endpoints: []v1alpha2.Endpoint{
   709  			{
   710  				Name:       "http-8080",
   711  				TargetPort: 8080,
   712  				Exposure:   v1alpha2.PublicEndpointExposure,
   713  			},
   714  			{
   715  				Name:       "http-9100",
   716  				TargetPort: 9100,
   717  				Exposure:   v1alpha2.InternalEndpointExposure,
   718  			},
   719  			{
   720  				Name:       "debug",
   721  				TargetPort: 18080,
   722  				Exposure:   v1alpha2.NoneEndpointExposure,
   723  			},
   724  			{
   725  				Name:       "debug-udp",
   726  				TargetPort: 19100,
   727  				Exposure:   v1alpha2.NoneEndpointExposure,
   728  				Protocol:   v1alpha2.UDPEndpointProtocol,
   729  			},
   730  		},
   731  	})
   732  
   733  	tests := []struct {
   734  		name string
   735  		args args
   736  		want map[string][]v1alpha2.Endpoint
   737  	}{
   738  		{
   739  			name: "invalid input - image components instead of container components",
   740  			args: args{
   741  				containers: []v1alpha2.Component{imageComponent},
   742  			},
   743  			want: map[string][]v1alpha2.Endpoint{},
   744  		},
   745  		{
   746  			name: "one container with no endpoints exposed",
   747  			args: args{
   748  				containers: []v1alpha2.Component{containerWithNoEndpoints},
   749  			},
   750  			want: map[string][]v1alpha2.Endpoint{},
   751  		},
   752  		{
   753  			name: "multiple containers with varying types of endpoints - without debug",
   754  			args: args{
   755  				containers: []v1alpha2.Component{
   756  					containerWithNoEndpoints,
   757  					containerWithOnePublicEndpoint,
   758  					containerWithOneInternalEndpoint,
   759  					containerWithOneNoneDebugEndpoint,
   760  					multiportContainer1,
   761  					multiportContainer2,
   762  				},
   763  			},
   764  			want: map[string][]v1alpha2.Endpoint{
   765  				containerWithOnePublicEndpoint.Name: {
   766  					v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: "public"},
   767  				},
   768  				containerWithOneInternalEndpoint.Name: {
   769  					v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: "internal"},
   770  				},
   771  				multiportContainer1.Name: {
   772  					v1alpha2.Endpoint{Name: "http-3000", TargetPort: 3000, Exposure: "public"},
   773  					v1alpha2.Endpoint{Name: "http-8888", TargetPort: 8888, Exposure: "internal"},
   774  				},
   775  				multiportContainer2.Name: {
   776  					v1alpha2.Endpoint{Name: "http-8080", TargetPort: 8080, Exposure: "public"},
   777  					v1alpha2.Endpoint{Name: "http-9100", TargetPort: 9100, Exposure: "internal"},
   778  				},
   779  			},
   780  		},
   781  		{
   782  			name: "multiple containers with varying types of endpoints - with debug",
   783  			args: args{
   784  				containers: []v1alpha2.Component{
   785  					containerWithNoEndpoints,
   786  					containerWithOnePublicEndpoint,
   787  					containerWithOneInternalEndpoint,
   788  					containerWithOneNoneDebugEndpoint,
   789  					multiportContainer1,
   790  					multiportContainer2,
   791  				},
   792  				includeDebug: true,
   793  			},
   794  			want: map[string][]v1alpha2.Endpoint{
   795  				containerWithOnePublicEndpoint.Name: {
   796  					v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: "public"},
   797  				},
   798  				containerWithOneInternalEndpoint.Name: {
   799  					v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: "internal"},
   800  				},
   801  				containerWithOneNoneDebugEndpoint.Name: {
   802  					v1alpha2.Endpoint{Name: "debug", TargetPort: 9099, Exposure: "none"},
   803  				},
   804  				multiportContainer1.Name: {
   805  					v1alpha2.Endpoint{Name: "http-3000", TargetPort: 3000, Exposure: "public"},
   806  					v1alpha2.Endpoint{Name: "http-8888", TargetPort: 8888, Exposure: "internal"},
   807  					v1alpha2.Endpoint{Name: "debug", TargetPort: 5005, Exposure: "none"},
   808  					v1alpha2.Endpoint{Name: "debug-udp", TargetPort: 15005, Exposure: "none", Protocol: "udp"},
   809  					v1alpha2.Endpoint{Name: "debug-ws", TargetPort: 25005, Exposure: "none", Protocol: "ws"},
   810  				},
   811  				multiportContainer2.Name: {
   812  					v1alpha2.Endpoint{Name: "http-8080", TargetPort: 8080, Exposure: "public"},
   813  					v1alpha2.Endpoint{Name: "http-9100", TargetPort: 9100, Exposure: "internal"},
   814  					v1alpha2.Endpoint{Name: "debug", TargetPort: 18080, Exposure: "none"},
   815  					v1alpha2.Endpoint{Name: "debug-udp", TargetPort: 19100, Exposure: "none", Protocol: "udp"},
   816  				},
   817  			},
   818  		},
   819  		{
   820  			name: "invalid input - one image component with rest being containers - without debug",
   821  			args: args{
   822  				containers: []v1alpha2.Component{
   823  					containerWithNoEndpoints,
   824  					containerWithOnePublicEndpoint,
   825  					containerWithOneInternalEndpoint,
   826  					containerWithOneNoneDebugEndpoint,
   827  					imageComponent,
   828  				},
   829  			},
   830  			want: map[string][]v1alpha2.Endpoint{
   831  				containerWithOnePublicEndpoint.Name: {
   832  					v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: "public"},
   833  				},
   834  				containerWithOneInternalEndpoint.Name: {
   835  					v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: "internal"},
   836  				},
   837  			},
   838  		},
   839  		{
   840  			name: "invalid input - one image component with rest being containers - with debug",
   841  			args: args{
   842  				containers: []v1alpha2.Component{
   843  					containerWithNoEndpoints,
   844  					containerWithOnePublicEndpoint,
   845  					containerWithOneInternalEndpoint,
   846  					containerWithOneNoneDebugEndpoint,
   847  					imageComponent,
   848  				},
   849  				includeDebug: true,
   850  			},
   851  			want: map[string][]v1alpha2.Endpoint{
   852  				containerWithOnePublicEndpoint.Name: {
   853  					v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: "public"},
   854  				},
   855  				containerWithOneInternalEndpoint.Name: {
   856  					v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: "internal"},
   857  				},
   858  				containerWithOneNoneDebugEndpoint.Name: {
   859  					v1alpha2.Endpoint{Name: "debug", TargetPort: 9099, Exposure: "none"},
   860  				},
   861  			},
   862  		},
   863  	}
   864  	for _, tt := range tests {
   865  		t.Run(tt.name, func(t *testing.T) {
   866  			got := GetContainerEndpointMapping(tt.args.containers, tt.args.includeDebug)
   867  
   868  			if diff := cmp.Diff(tt.want, got); diff != "" {
   869  				t.Errorf("GetContainerEndpointMapping() mismatch (-want +got):\n%s", diff)
   870  			}
   871  		})
   872  	}
   873  }
   874  
   875  func TestGetEndpointsFromDevfile(t *testing.T) {
   876  	type args struct {
   877  		devfileObj      func() parser.DevfileObj
   878  		ignoreExposures []v1alpha2.EndpointExposure
   879  	}
   880  	ep1 := v1alpha2.Endpoint{Name: "ep1", TargetPort: 8080, Exposure: v1alpha2.NoneEndpointExposure}
   881  	ep2 := v1alpha2.Endpoint{Name: "ep2", TargetPort: 9090, Exposure: v1alpha2.InternalEndpointExposure}
   882  	ep3 := v1alpha2.Endpoint{Name: "ep3", TargetPort: 8888, Exposure: v1alpha2.PublicEndpointExposure}
   883  
   884  	container := generator.GetContainerComponent(generator.ContainerComponentParams{
   885  		Name:      "container-1",
   886  		Endpoints: []v1alpha2.Endpoint{ep1, ep2, ep3},
   887  	})
   888  	tests := []struct {
   889  		name    string
   890  		args    args
   891  		want    []v1alpha2.Endpoint
   892  		wantErr bool
   893  	}{
   894  		{
   895  			name: "Ignore exposure of type none",
   896  			args: args{
   897  				devfileObj: func() parser.DevfileObj {
   898  					data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   899  					_ = data.AddComponents([]v1alpha2.Component{container})
   900  					return parser.DevfileObj{
   901  						Data: data,
   902  					}
   903  				},
   904  				ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.NoneEndpointExposure},
   905  			},
   906  			want: []v1alpha2.Endpoint{ep2, ep3},
   907  		},
   908  		{
   909  			name: "Ignore exposure of type public",
   910  			args: args{
   911  				devfileObj: func() parser.DevfileObj {
   912  					data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   913  					_ = data.AddComponents([]v1alpha2.Component{container})
   914  					return parser.DevfileObj{
   915  						Data: data,
   916  					}
   917  				},
   918  				ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.PublicEndpointExposure},
   919  			},
   920  			want: []v1alpha2.Endpoint{ep1, ep2},
   921  		},
   922  		{
   923  			name: "Ignore exposure of type internal",
   924  			args: args{
   925  				devfileObj: func() parser.DevfileObj {
   926  					data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   927  					_ = data.AddComponents([]v1alpha2.Component{container})
   928  					return parser.DevfileObj{
   929  						Data: data,
   930  					}
   931  				},
   932  				ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.InternalEndpointExposure},
   933  			},
   934  			want: []v1alpha2.Endpoint{ep1, ep3},
   935  		},
   936  		{
   937  			name: "Ignore none",
   938  			args: args{
   939  				devfileObj: func() parser.DevfileObj {
   940  					data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   941  					_ = data.AddComponents([]v1alpha2.Component{container})
   942  					return parser.DevfileObj{
   943  						Data: data,
   944  					}
   945  				},
   946  				ignoreExposures: []v1alpha2.EndpointExposure{},
   947  			},
   948  			want: []v1alpha2.Endpoint{ep1, ep2, ep3},
   949  		},
   950  		{
   951  			name: "Ignore all exposure types",
   952  			args: args{
   953  				devfileObj: func() parser.DevfileObj {
   954  					data, _ := data.NewDevfileData(string(data.APISchemaVersion200))
   955  					_ = data.AddComponents([]v1alpha2.Component{container})
   956  					return parser.DevfileObj{
   957  						Data: data,
   958  					}
   959  				},
   960  				ignoreExposures: []v1alpha2.EndpointExposure{v1alpha2.InternalEndpointExposure, v1alpha2.NoneEndpointExposure, v1alpha2.PublicEndpointExposure},
   961  			},
   962  			want: nil,
   963  		},
   964  	}
   965  	for _, tt := range tests {
   966  		t.Run(tt.name, func(t *testing.T) {
   967  			got, err := GetEndpointsFromDevfile(tt.args.devfileObj(), tt.args.ignoreExposures)
   968  			if (err != nil) != tt.wantErr {
   969  				t.Errorf("GetEndpointsFromDevfile() error = %v, wantErr %v", err, tt.wantErr)
   970  				return
   971  			}
   972  			if diff := cmp.Diff(tt.want, got); diff != "" {
   973  				t.Errorf("GetEndpointsFromDevfile() mismatch (-want +got):\n%s", diff)
   974  			}
   975  		})
   976  	}
   977  }
   978  
   979  func TestIsDebugEndpoint(t *testing.T) {
   980  	type args struct {
   981  		endpoint v1alpha2.Endpoint
   982  	}
   983  	for _, tt := range []struct {
   984  		name string
   985  		args args
   986  		want bool
   987  	}{
   988  		{
   989  			name: "exactly debug",
   990  			args: args{endpoint: v1alpha2.Endpoint{Name: "debug", TargetPort: 5005}},
   991  			want: true,
   992  		},
   993  		{
   994  			name: "exactly debug - case-sensitive",
   995  			args: args{endpoint: v1alpha2.Endpoint{Name: "DEBUG", TargetPort: 5005}},
   996  		},
   997  		{
   998  			name: "starting with debug",
   999  			args: args{endpoint: v1alpha2.Endpoint{Name: "debug-port", TargetPort: 5005}},
  1000  			want: true,
  1001  		},
  1002  		{
  1003  			name: "starting with debug - case sensitive",
  1004  			args: args{endpoint: v1alpha2.Endpoint{Name: "DEBUG-PORT", TargetPort: 5005}},
  1005  		},
  1006  		{
  1007  			name: "containing debug",
  1008  			args: args{endpoint: v1alpha2.Endpoint{Name: "my-debug", TargetPort: 5005}},
  1009  		},
  1010  		{
  1011  			name: "containing debug prefix",
  1012  			args: args{endpoint: v1alpha2.Endpoint{Name: "my-debug-port", TargetPort: 5005}},
  1013  		},
  1014  		{
  1015  			name: "any other string",
  1016  			args: args{endpoint: v1alpha2.Endpoint{Name: "lorem-ipsum", TargetPort: 5005}},
  1017  		},
  1018  	} {
  1019  		t.Run(tt.name, func(t *testing.T) {
  1020  			got := IsDebugEndpoint(tt.args.endpoint)
  1021  			if diff := cmp.Diff(tt.want, got); diff != "" {
  1022  				t.Errorf("IsDebugEndpoint() mismatch (-want +got):\n%s", diff)
  1023  			}
  1024  		})
  1025  	}
  1026  }
  1027  
  1028  func TestGetDebugEndpointsForComponent(t *testing.T) {
  1029  	type args struct {
  1030  		cmpProvider func() v1alpha2.Component
  1031  	}
  1032  
  1033  	for _, tt := range []struct {
  1034  		name    string
  1035  		args    args
  1036  		wantErr bool
  1037  		want    []v1alpha2.Endpoint
  1038  	}{
  1039  		{
  1040  			name: "not a container component",
  1041  			args: args{
  1042  				cmpProvider: func() v1alpha2.Component { return testingutil.GetFakeVolumeComponent("vol-comp", "1Gi") },
  1043  			},
  1044  			wantErr: true,
  1045  		},
  1046  		{
  1047  			name: "no endpoints in container component",
  1048  			args: args{
  1049  				cmpProvider: func() v1alpha2.Component { return testingutil.GetFakeContainerComponent("my-container-comp") },
  1050  			},
  1051  		},
  1052  		{
  1053  			name: "mix of debug and non-debug endpoints in container component",
  1054  			args: args{
  1055  				cmpProvider: func() v1alpha2.Component {
  1056  					comp := testingutil.GetFakeContainerComponent("my-container-comp", 8080, 3000, 9090)
  1057  					comp.Container.Endpoints = append(comp.Container.Endpoints, v1alpha2.Endpoint{
  1058  						Name:       "debug",
  1059  						TargetPort: 5005,
  1060  						Exposure:   v1alpha2.NoneEndpointExposure,
  1061  					})
  1062  					comp.Container.Endpoints = append(comp.Container.Endpoints, v1alpha2.Endpoint{
  1063  						Name:       "debug1",
  1064  						TargetPort: 15005,
  1065  						Exposure:   v1alpha2.InternalEndpointExposure,
  1066  					})
  1067  					comp.Container.Endpoints = append(comp.Container.Endpoints, v1alpha2.Endpoint{
  1068  						Name:       "debug-port2",
  1069  						TargetPort: 5006,
  1070  						Exposure:   v1alpha2.InternalEndpointExposure,
  1071  					})
  1072  					comp.Container.Endpoints = append(comp.Container.Endpoints, v1alpha2.Endpoint{
  1073  						Name:       "debug-port3-public",
  1074  						TargetPort: 5007,
  1075  						Exposure:   v1alpha2.PublicEndpointExposure,
  1076  					})
  1077  					return comp
  1078  				},
  1079  			},
  1080  			want: []v1alpha2.Endpoint{
  1081  				{
  1082  					Name:       "debug",
  1083  					TargetPort: 5005,
  1084  					Exposure:   v1alpha2.NoneEndpointExposure,
  1085  				},
  1086  				{
  1087  					Name:       "debug-port2",
  1088  					TargetPort: 5006,
  1089  					Exposure:   v1alpha2.InternalEndpointExposure,
  1090  				},
  1091  				{
  1092  					Name:       "debug-port3-public",
  1093  					TargetPort: 5007,
  1094  					Exposure:   v1alpha2.PublicEndpointExposure,
  1095  				},
  1096  			},
  1097  		},
  1098  	} {
  1099  		t.Run(tt.name, func(t *testing.T) {
  1100  			got, err := GetDebugEndpointsForComponent(tt.args.cmpProvider())
  1101  
  1102  			if tt.wantErr != (err != nil) {
  1103  				t.Errorf("GetDebugEndpointsForComponent(), wantErr: %v, err: %v", tt.wantErr, err)
  1104  			}
  1105  			if diff := cmp.Diff(tt.want, got); diff != "" {
  1106  				t.Errorf("GetDebugEndpointsForComponent() mismatch (-want +got):\n%s", diff)
  1107  			}
  1108  		})
  1109  	}
  1110  }
  1111  
  1112  func TestGetK8sManifestsWithVariablesSubstituted(t *testing.T) {
  1113  	fakeFs := devfileFileSystem.NewFakeFs()
  1114  	cmpName := "my-cmp-1"
  1115  	for _, tt := range []struct {
  1116  		name           string
  1117  		setupFunc      func() error
  1118  		devfileObjFunc func() parser.DevfileObj
  1119  		wantErr        bool
  1120  		want           string
  1121  	}{
  1122  		{
  1123  			name: "Missing Component",
  1124  			devfileObjFunc: func() parser.DevfileObj {
  1125  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1126  				cmp := generator.GetContainerComponent(generator.ContainerComponentParams{
  1127  					Name: "a-different-component",
  1128  				})
  1129  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1130  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1131  						Components: []v1alpha2.Component{cmp},
  1132  					},
  1133  				}
  1134  				devfileData.SetDevfileWorkspaceSpec(s)
  1135  				return parser.DevfileObj{
  1136  					Data: devfileData,
  1137  				}
  1138  			},
  1139  			wantErr: true,
  1140  		},
  1141  		{
  1142  			name: "Multiple Components with the same name",
  1143  			devfileObjFunc: func() parser.DevfileObj {
  1144  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1145  				cmp1 := generator.GetContainerComponent(generator.ContainerComponentParams{
  1146  					Name: cmpName,
  1147  				})
  1148  				cmp2 := generator.GetImageComponent(generator.ImageComponentParams{
  1149  					Name: cmpName,
  1150  				})
  1151  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1152  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1153  						Components: []v1alpha2.Component{cmp1, cmp2},
  1154  					},
  1155  				}
  1156  				devfileData.SetDevfileWorkspaceSpec(s)
  1157  				return parser.DevfileObj{
  1158  					Data: devfileData,
  1159  				}
  1160  			},
  1161  			wantErr: true,
  1162  		},
  1163  		{
  1164  			name: "Container Component",
  1165  			devfileObjFunc: func() parser.DevfileObj {
  1166  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1167  				cmp := generator.GetContainerComponent(generator.ContainerComponentParams{
  1168  					Name: cmpName,
  1169  				})
  1170  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1171  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1172  						Components: []v1alpha2.Component{cmp},
  1173  					},
  1174  				}
  1175  				devfileData.SetDevfileWorkspaceSpec(s)
  1176  				return parser.DevfileObj{
  1177  					Data: devfileData,
  1178  				}
  1179  			},
  1180  			wantErr: true,
  1181  		},
  1182  		{
  1183  			name: "Image Component",
  1184  			devfileObjFunc: func() parser.DevfileObj {
  1185  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1186  				cmp := generator.GetImageComponent(generator.ImageComponentParams{
  1187  					Name: cmpName,
  1188  				})
  1189  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1190  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1191  						Components: []v1alpha2.Component{cmp},
  1192  					},
  1193  				}
  1194  				devfileData.SetDevfileWorkspaceSpec(s)
  1195  				return parser.DevfileObj{
  1196  					Data: devfileData,
  1197  				}
  1198  			},
  1199  			wantErr: true,
  1200  		},
  1201  		{
  1202  			name: "Kubernetes Component - Inlined with no variables",
  1203  			devfileObjFunc: func() parser.DevfileObj {
  1204  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1205  				cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
  1206  					Name: cmpName,
  1207  					Kubernetes: &v1alpha2.KubernetesComponent{
  1208  						K8sLikeComponent: v1alpha2.K8sLikeComponent{
  1209  							K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
  1210  								Inlined: "some-text-inlined",
  1211  							},
  1212  						},
  1213  					},
  1214  				})
  1215  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1216  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1217  						Components: []v1alpha2.Component{cmp},
  1218  					},
  1219  				}
  1220  				devfileData.SetDevfileWorkspaceSpec(s)
  1221  				return parser.DevfileObj{
  1222  					Data: devfileData,
  1223  				}
  1224  			},
  1225  			wantErr: false,
  1226  			want:    "some-text-inlined",
  1227  		},
  1228  		{
  1229  			name: "Kubernetes Component - Inlined with variables",
  1230  			devfileObjFunc: func() parser.DevfileObj {
  1231  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1232  				cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
  1233  					Name: cmpName,
  1234  					Kubernetes: &v1alpha2.KubernetesComponent{
  1235  						K8sLikeComponent: v1alpha2.K8sLikeComponent{
  1236  							K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
  1237  								Inlined: "image: {{MY_CONTAINER_IMAGE}}",
  1238  							},
  1239  						},
  1240  					},
  1241  				})
  1242  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1243  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1244  						Variables: map[string]string{
  1245  							"MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image:1.2.3",
  1246  						},
  1247  						Components: []v1alpha2.Component{cmp},
  1248  					},
  1249  				}
  1250  				devfileData.SetDevfileWorkspaceSpec(s)
  1251  				return parser.DevfileObj{
  1252  					Data: devfileData,
  1253  				}
  1254  			},
  1255  			wantErr: false,
  1256  			want:    "image: quay.io/unknown-account/my-image:1.2.3",
  1257  		},
  1258  		{
  1259  			name: "Kubernetes Component - Inlined with unknown variables",
  1260  			devfileObjFunc: func() parser.DevfileObj {
  1261  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1262  				cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
  1263  					Name: cmpName,
  1264  					Kubernetes: &v1alpha2.KubernetesComponent{
  1265  						K8sLikeComponent: v1alpha2.K8sLikeComponent{
  1266  							K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
  1267  								Inlined: "image: {{MY_CONTAINER_IMAGE}}:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}",
  1268  							},
  1269  						},
  1270  					},
  1271  				})
  1272  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1273  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1274  						Variables: map[string]string{
  1275  							"MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image",
  1276  						},
  1277  						Components: []v1alpha2.Component{cmp},
  1278  					},
  1279  				}
  1280  				devfileData.SetDevfileWorkspaceSpec(s)
  1281  				return parser.DevfileObj{
  1282  					Data: devfileData,
  1283  				}
  1284  			},
  1285  			wantErr: true,
  1286  			want:    "image: quay.io/unknown-account/my-image:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}",
  1287  		},
  1288  		{
  1289  			name: "Kubernetes Component - non-existing external file",
  1290  			devfileObjFunc: func() parser.DevfileObj {
  1291  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1292  				cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
  1293  					Name: cmpName,
  1294  					Kubernetes: &v1alpha2.KubernetesComponent{
  1295  						K8sLikeComponent: v1alpha2.K8sLikeComponent{
  1296  							K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
  1297  								Uri: "kubernetes/my-external-file-with-should-not-exist",
  1298  							},
  1299  						},
  1300  					},
  1301  				})
  1302  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1303  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1304  						Components: []v1alpha2.Component{cmp},
  1305  					},
  1306  				}
  1307  				devfileData.SetDevfileWorkspaceSpec(s)
  1308  				return parser.DevfileObj{
  1309  					Data: devfileData,
  1310  				}
  1311  			},
  1312  			wantErr: true,
  1313  		},
  1314  		{
  1315  			name: "Kubernetes Component - URI with no variables",
  1316  			setupFunc: func() error {
  1317  				return fakeFs.WriteFile("kubernetes/my-external-file",
  1318  					[]byte("some-text-with-no-variables"),
  1319  					os.ModePerm)
  1320  			},
  1321  			devfileObjFunc: func() parser.DevfileObj {
  1322  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1323  				cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
  1324  					Name: cmpName,
  1325  					Kubernetes: &v1alpha2.KubernetesComponent{
  1326  						K8sLikeComponent: v1alpha2.K8sLikeComponent{
  1327  							K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
  1328  								Uri: "kubernetes/my-external-file",
  1329  							},
  1330  						},
  1331  					},
  1332  				})
  1333  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1334  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1335  						Components: []v1alpha2.Component{cmp},
  1336  					},
  1337  				}
  1338  				devfileData.SetDevfileWorkspaceSpec(s)
  1339  				return parser.DevfileObj{
  1340  					Data: devfileData,
  1341  				}
  1342  			},
  1343  			wantErr: false,
  1344  			want:    "some-text-with-no-variables",
  1345  		},
  1346  		{
  1347  			name: "Kubernetes Component - URI with variables",
  1348  			setupFunc: func() error {
  1349  				return fakeFs.WriteFile("kubernetes/my-deployment.yaml",
  1350  					[]byte("image: {{ MY_CONTAINER_IMAGE }}:{{MY_CONTAINER_IMAGE_VERSION}}"),
  1351  					os.ModePerm)
  1352  			},
  1353  			devfileObjFunc: func() parser.DevfileObj {
  1354  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1355  				cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
  1356  					Name: cmpName,
  1357  					Kubernetes: &v1alpha2.KubernetesComponent{
  1358  						K8sLikeComponent: v1alpha2.K8sLikeComponent{
  1359  							K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
  1360  								Uri: "kubernetes/my-deployment.yaml",
  1361  							},
  1362  						},
  1363  					},
  1364  				})
  1365  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1366  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1367  						Variables: map[string]string{
  1368  							"MY_CONTAINER_IMAGE":         "quay.io/unknown-account/my-image",
  1369  							"MY_CONTAINER_IMAGE_VERSION": "1.2.3",
  1370  						},
  1371  						Components: []v1alpha2.Component{cmp},
  1372  					},
  1373  				}
  1374  				devfileData.SetDevfileWorkspaceSpec(s)
  1375  				return parser.DevfileObj{
  1376  					Data: devfileData,
  1377  				}
  1378  			},
  1379  			wantErr: false,
  1380  			want:    "image: quay.io/unknown-account/my-image:1.2.3",
  1381  		},
  1382  		{
  1383  			name: "Kubernetes Component - URI with unknown variables",
  1384  			setupFunc: func() error {
  1385  				return fakeFs.WriteFile("kubernetes/my-external-file.yaml",
  1386  					[]byte("image: {{MY_CONTAINER_IMAGE}}:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}"),
  1387  					os.ModePerm)
  1388  			},
  1389  			devfileObjFunc: func() parser.DevfileObj {
  1390  				devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1391  				cmp := generator.GetKubernetesComponent(generator.KubernetesComponentParams{
  1392  					Name: cmpName,
  1393  					Kubernetes: &v1alpha2.KubernetesComponent{
  1394  						K8sLikeComponent: v1alpha2.K8sLikeComponent{
  1395  							K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
  1396  								Uri: "kubernetes/my-external-file.yaml",
  1397  							},
  1398  						},
  1399  					},
  1400  				})
  1401  				s := v1alpha2.DevWorkspaceTemplateSpec{
  1402  					DevWorkspaceTemplateSpecContent: v1alpha2.DevWorkspaceTemplateSpecContent{
  1403  						Variables: map[string]string{
  1404  							"MY_CONTAINER_IMAGE": "quay.io/unknown-account/my-image",
  1405  						},
  1406  						Components: []v1alpha2.Component{cmp},
  1407  					},
  1408  				}
  1409  				devfileData.SetDevfileWorkspaceSpec(s)
  1410  				return parser.DevfileObj{
  1411  					Data: devfileData,
  1412  				}
  1413  			},
  1414  			wantErr: true,
  1415  			want:    "image: quay.io/unknown-account/my-image:{{ MY_CONTAINER_IMAGE_VERSION_UNKNOWN }}",
  1416  		},
  1417  	} {
  1418  		t.Run(tt.name, func(t *testing.T) {
  1419  			if tt.setupFunc != nil {
  1420  				if err := tt.setupFunc(); err != nil {
  1421  					t.Errorf("setup function returned an error: %v", err)
  1422  					return
  1423  				}
  1424  			}
  1425  			if tt.devfileObjFunc == nil {
  1426  				t.Error("devfileObjFunc function not defined for test case")
  1427  				return
  1428  			}
  1429  
  1430  			got, err := GetK8sManifestsWithVariablesSubstituted(tt.devfileObjFunc(), cmpName, "", fakeFs)
  1431  			if (err != nil) != tt.wantErr {
  1432  				t.Errorf("GetK8sManifestsWithVariablesSubstituted() error = %v, wantErr %v",
  1433  					err, tt.wantErr)
  1434  				return
  1435  			}
  1436  			if got != tt.want {
  1437  				t.Errorf("GetK8sManifestsWithVariablesSubstituted() got = %v, want %v",
  1438  					got, tt.want)
  1439  			}
  1440  		})
  1441  	}
  1442  }
  1443  
  1444  func TestValidateAndGetCommand(t *testing.T) {
  1445  
  1446  	commands := [...]string{"ls -la", "pwd"}
  1447  	components := [...]string{"alias1", "alias2"}
  1448  
  1449  	tests := []struct {
  1450  		name           string
  1451  		requestedType  []v1alpha2.CommandGroupKind
  1452  		execCommands   []v1alpha2.Command
  1453  		compCommands   []v1alpha2.Command
  1454  		reqCommandName string
  1455  		retCommandName []string
  1456  		wantErr        bool
  1457  	}{
  1458  		{
  1459  			name: "Case 1: Valid devfile, default command returned even if it is not marked as IsDefault",
  1460  			execCommands: []v1alpha2.Command{
  1461  				getExecCommand("build", buildGroup),
  1462  				getExecCommand("run", runGroup),
  1463  			},
  1464  			requestedType:  []v1alpha2.CommandGroupKind{buildGroup, runGroup},
  1465  			wantErr:        false,
  1466  			retCommandName: []string{"build", "run"},
  1467  		},
  1468  		{
  1469  			name: "Case 2: Valid devfile, but error returned because multiple build commands without default",
  1470  			execCommands: []v1alpha2.Command{
  1471  				getExecCommand("build", buildGroup),
  1472  				getExecCommand("build2", buildGroup),
  1473  				getExecCommand("run", runGroup),
  1474  			},
  1475  			requestedType: []v1alpha2.CommandGroupKind{buildGroup, runGroup},
  1476  			wantErr:       true,
  1477  		},
  1478  		{
  1479  			name: "Case 3: Valid devfile with empty workdir",
  1480  			execCommands: []v1alpha2.Command{
  1481  				{
  1482  					Id: "run",
  1483  					CommandUnion: v1alpha2.CommandUnion{
  1484  						Exec: &v1alpha2.ExecCommand{
  1485  							LabeledCommand: v1alpha2.LabeledCommand{
  1486  								BaseCommand: v1alpha2.BaseCommand{
  1487  									Group: &v1alpha2.CommandGroup{Kind: runGroup},
  1488  								},
  1489  							},
  1490  							CommandLine: commands[0],
  1491  							Component:   components[0],
  1492  						},
  1493  					},
  1494  				},
  1495  			},
  1496  			requestedType:  []v1alpha2.CommandGroupKind{runGroup},
  1497  			wantErr:        false,
  1498  			retCommandName: []string{"run"},
  1499  		},
  1500  		{
  1501  			name: "Case 4.1: Mismatched command type",
  1502  			execCommands: []v1alpha2.Command{
  1503  				{
  1504  					Id: "build command",
  1505  					CommandUnion: v1alpha2.CommandUnion{
  1506  						Exec: &v1alpha2.ExecCommand{
  1507  							LabeledCommand: v1alpha2.LabeledCommand{
  1508  								BaseCommand: v1alpha2.BaseCommand{
  1509  									Group: &v1alpha2.CommandGroup{Kind: runGroup},
  1510  								},
  1511  							},
  1512  							CommandLine: commands[0],
  1513  							Component:   components[0],
  1514  						},
  1515  					},
  1516  				},
  1517  			},
  1518  			reqCommandName: "build command",
  1519  			requestedType:  []v1alpha2.CommandGroupKind{buildGroup},
  1520  			wantErr:        true,
  1521  		},
  1522  		{
  1523  			name: "Case 4.2: Matching command by name and type",
  1524  			execCommands: []v1alpha2.Command{
  1525  				{
  1526  					Id: "build command",
  1527  					CommandUnion: v1alpha2.CommandUnion{
  1528  						Exec: &v1alpha2.ExecCommand{
  1529  							LabeledCommand: v1alpha2.LabeledCommand{
  1530  								BaseCommand: v1alpha2.BaseCommand{
  1531  									Group: &v1alpha2.CommandGroup{Kind: buildGroup},
  1532  								},
  1533  							},
  1534  							CommandLine: commands[0],
  1535  							Component:   components[0],
  1536  						},
  1537  					},
  1538  				},
  1539  			},
  1540  			reqCommandName: "build command",
  1541  			requestedType:  []v1alpha2.CommandGroupKind{buildGroup},
  1542  			retCommandName: []string{"build command"},
  1543  			wantErr:        false,
  1544  		},
  1545  		{
  1546  			name: "Case 5: Default command is returned",
  1547  			execCommands: []v1alpha2.Command{
  1548  				{
  1549  					Id: "defaultRunCommand",
  1550  					CommandUnion: v1alpha2.CommandUnion{
  1551  						Exec: &v1alpha2.ExecCommand{
  1552  							LabeledCommand: v1alpha2.LabeledCommand{
  1553  								BaseCommand: v1alpha2.BaseCommand{
  1554  									Group: &v1alpha2.CommandGroup{Kind: runGroup, IsDefault: util.GetBool(true)},
  1555  								},
  1556  							},
  1557  							CommandLine: commands[0],
  1558  							Component:   components[0],
  1559  						},
  1560  					},
  1561  				},
  1562  				{
  1563  					Id: "runCommand",
  1564  					CommandUnion: v1alpha2.CommandUnion{
  1565  						Exec: &v1alpha2.ExecCommand{
  1566  							LabeledCommand: v1alpha2.LabeledCommand{
  1567  								BaseCommand: v1alpha2.BaseCommand{
  1568  									Group: &v1alpha2.CommandGroup{Kind: runGroup},
  1569  								},
  1570  							},
  1571  							CommandLine: commands[0],
  1572  							Component:   components[0],
  1573  						},
  1574  					},
  1575  				},
  1576  			},
  1577  			retCommandName: []string{"defaultRunCommand"},
  1578  			requestedType:  []v1alpha2.CommandGroupKind{runGroup},
  1579  			wantErr:        false,
  1580  		},
  1581  		{
  1582  			name: "Case 6: Composite command is returned",
  1583  			execCommands: []v1alpha2.Command{
  1584  				{
  1585  					Id: "build",
  1586  					CommandUnion: v1alpha2.CommandUnion{
  1587  						Exec: &v1alpha2.ExecCommand{
  1588  							LabeledCommand: v1alpha2.LabeledCommand{
  1589  								BaseCommand: v1alpha2.BaseCommand{
  1590  									Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBool(false)},
  1591  								},
  1592  							},
  1593  							CommandLine: commands[0],
  1594  							Component:   components[0],
  1595  						},
  1596  					},
  1597  				},
  1598  				{
  1599  					Id: "run",
  1600  					CommandUnion: v1alpha2.CommandUnion{
  1601  						Exec: &v1alpha2.ExecCommand{
  1602  							LabeledCommand: v1alpha2.LabeledCommand{
  1603  								BaseCommand: v1alpha2.BaseCommand{
  1604  									Group: &v1alpha2.CommandGroup{Kind: runGroup},
  1605  								},
  1606  							},
  1607  							CommandLine: commands[0],
  1608  							Component:   components[0],
  1609  						},
  1610  					},
  1611  				},
  1612  			},
  1613  			compCommands: []v1alpha2.Command{
  1614  				{
  1615  					Id: "myComposite",
  1616  					CommandUnion: v1alpha2.CommandUnion{
  1617  						Composite: &v1alpha2.CompositeCommand{
  1618  							LabeledCommand: v1alpha2.LabeledCommand{
  1619  								BaseCommand: v1alpha2.BaseCommand{
  1620  									Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBool(true)},
  1621  								},
  1622  							},
  1623  							Commands: []string{"build", "run"},
  1624  						},
  1625  					},
  1626  				},
  1627  			},
  1628  			retCommandName: []string{"myComposite"},
  1629  			requestedType:  []v1alpha2.CommandGroupKind{buildGroup},
  1630  			wantErr:        false,
  1631  		},
  1632  	}
  1633  	for _, tt := range tests {
  1634  		t.Run(tt.name, func(t *testing.T) {
  1635  
  1636  			if !tt.wantErr && (len(tt.requestedType) != len(tt.retCommandName)) {
  1637  				t.Errorf("Invalid test definition %q requestedType length must match retCommandName length.", tt.name)
  1638  			}
  1639  			components := []v1alpha2.Component{testingutil.GetFakeContainerComponent(tt.execCommands[0].Exec.Component)}
  1640  			devObj := parser.DevfileObj{
  1641  				Data: func() data.DevfileData {
  1642  					devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
  1643  					if err != nil {
  1644  						t.Error(err)
  1645  					}
  1646  					err = devfileData.AddCommands(tt.execCommands)
  1647  					if err != nil {
  1648  						t.Error(err)
  1649  					}
  1650  					err = devfileData.AddCommands(tt.compCommands)
  1651  					if err != nil {
  1652  						t.Error(err)
  1653  					}
  1654  					err = devfileData.AddComponents(components)
  1655  					if err != nil {
  1656  						t.Error(err)
  1657  					}
  1658  					return devfileData
  1659  				}(),
  1660  			}
  1661  
  1662  			for i, gtype := range tt.requestedType {
  1663  				cmd, err := ValidateAndGetCommand(devObj, tt.reqCommandName, gtype)
  1664  				if tt.wantErr != (err != nil) {
  1665  					t.Errorf("TestGetCommand unexpected error for command: %v wantErr: %v err: %v", gtype, tt.wantErr, err)
  1666  					return
  1667  				} else if tt.wantErr {
  1668  					return
  1669  				}
  1670  
  1671  				if cmd.Id != tt.retCommandName[i] {
  1672  					t.Errorf("TestGetCommand error: command names do not match expected: %v actual: %v", tt.retCommandName[i], cmd.Id)
  1673  				}
  1674  			}
  1675  		})
  1676  	}
  1677  
  1678  }
  1679  
  1680  func TestValidateAndGetPushCommands(t *testing.T) {
  1681  
  1682  	command := "ls -la"
  1683  	component := "alias1"
  1684  	workDir := "/"
  1685  	emptyString := ""
  1686  
  1687  	execCommands := []v1alpha2.Command{
  1688  		{
  1689  			Id: "run command",
  1690  			CommandUnion: v1alpha2.CommandUnion{
  1691  				Exec: &v1alpha2.ExecCommand{
  1692  					LabeledCommand: v1alpha2.LabeledCommand{
  1693  						BaseCommand: v1alpha2.BaseCommand{
  1694  							Group: &v1alpha2.CommandGroup{
  1695  								Kind:      runGroup,
  1696  								IsDefault: util.GetBool(true),
  1697  							},
  1698  						},
  1699  					},
  1700  					CommandLine: command,
  1701  					Component:   component,
  1702  					WorkingDir:  workDir,
  1703  				},
  1704  			},
  1705  		},
  1706  
  1707  		{
  1708  			Id: "build command",
  1709  			CommandUnion: v1alpha2.CommandUnion{
  1710  				Exec: &v1alpha2.ExecCommand{
  1711  					LabeledCommand: v1alpha2.LabeledCommand{
  1712  						BaseCommand: v1alpha2.BaseCommand{
  1713  							Group: &v1alpha2.CommandGroup{Kind: buildGroup},
  1714  						},
  1715  					},
  1716  					CommandLine: command,
  1717  					Component:   component,
  1718  					WorkingDir:  workDir,
  1719  				},
  1720  			},
  1721  		},
  1722  
  1723  		{
  1724  			Id: "customcommand",
  1725  			CommandUnion: v1alpha2.CommandUnion{
  1726  				Exec: &v1alpha2.ExecCommand{
  1727  					LabeledCommand: v1alpha2.LabeledCommand{
  1728  						BaseCommand: v1alpha2.BaseCommand{
  1729  							Group: &v1alpha2.CommandGroup{Kind: runGroup},
  1730  						},
  1731  					},
  1732  					CommandLine: command,
  1733  					Component:   component,
  1734  					WorkingDir:  workDir,
  1735  				},
  1736  			},
  1737  		},
  1738  	}
  1739  
  1740  	defaultBuildCommand := v1alpha2.Command{
  1741  		Id: "default build command",
  1742  		CommandUnion: v1alpha2.CommandUnion{
  1743  			Exec: &v1alpha2.ExecCommand{
  1744  				LabeledCommand: v1alpha2.LabeledCommand{
  1745  					BaseCommand: v1alpha2.BaseCommand{
  1746  						Group: &v1alpha2.CommandGroup{Kind: buildGroup, IsDefault: util.GetBool(true)},
  1747  					},
  1748  				},
  1749  				CommandLine: command,
  1750  				Component:   component,
  1751  				WorkingDir:  workDir,
  1752  			},
  1753  		},
  1754  	}
  1755  
  1756  	wrongCompTypeCmd := v1alpha2.Command{
  1757  
  1758  		Id: "wrong",
  1759  		CommandUnion: v1alpha2.CommandUnion{
  1760  			Exec: &v1alpha2.ExecCommand{
  1761  				LabeledCommand: v1alpha2.LabeledCommand{
  1762  					BaseCommand: v1alpha2.BaseCommand{
  1763  						Group: &v1alpha2.CommandGroup{Kind: runGroup},
  1764  					},
  1765  				},
  1766  				CommandLine: command,
  1767  				Component:   "",
  1768  				WorkingDir:  workDir,
  1769  			},
  1770  		},
  1771  	}
  1772  
  1773  	tests := []struct {
  1774  		name                string
  1775  		buildCommand        string
  1776  		runCommand          string
  1777  		execCommands        []v1alpha2.Command
  1778  		numberOfCommands    int
  1779  		missingBuildCommand bool
  1780  		wantErr             bool
  1781  	}{
  1782  		{
  1783  			name:             "Case 1: Default Devfile Commands",
  1784  			buildCommand:     emptyString,
  1785  			runCommand:       emptyString,
  1786  			execCommands:     execCommands,
  1787  			numberOfCommands: 2,
  1788  			wantErr:          false,
  1789  		},
  1790  		{
  1791  			name:         "Case 2: Default Build Command, and Provided Run Command",
  1792  			buildCommand: emptyString,
  1793  			runCommand:   "customcommand",
  1794  			execCommands: execCommands,
  1795  			// only the specified run command is returned, because the build command is not marked as default
  1796  			numberOfCommands: 2,
  1797  			wantErr:          false,
  1798  		},
  1799  		{
  1800  			name:             "Case 2.2: Default Build Command, and Default Run Command",
  1801  			buildCommand:     emptyString,
  1802  			runCommand:       emptyString,
  1803  			execCommands:     append(execCommands, defaultBuildCommand),
  1804  			numberOfCommands: 2,
  1805  			wantErr:          false,
  1806  		},
  1807  		{
  1808  			name:             "Case 3: Empty Component",
  1809  			buildCommand:     "customcommand",
  1810  			runCommand:       "customcommand",
  1811  			execCommands:     append(execCommands, wrongCompTypeCmd),
  1812  			numberOfCommands: 0,
  1813  			wantErr:          true,
  1814  		},
  1815  		{
  1816  			name:             "Case 4: Provided Wrong Build Command and Provided Run Command",
  1817  			buildCommand:     "customcommand123",
  1818  			runCommand:       "customcommand",
  1819  			execCommands:     execCommands,
  1820  			numberOfCommands: 1,
  1821  			wantErr:          true,
  1822  		},
  1823  		{
  1824  			name:         "Case 5: Missing Build Command, and Provided Run Command",
  1825  			buildCommand: emptyString,
  1826  			runCommand:   "customcommand",
  1827  			execCommands: []v1alpha2.Command{
  1828  				{
  1829  					Id: "customcommand",
  1830  					CommandUnion: v1alpha2.CommandUnion{
  1831  						Exec: &v1alpha2.ExecCommand{
  1832  							LabeledCommand: v1alpha2.LabeledCommand{
  1833  								BaseCommand: v1alpha2.BaseCommand{
  1834  									Group: &v1alpha2.CommandGroup{Kind: runGroup},
  1835  								},
  1836  							},
  1837  							Component:   component,
  1838  							CommandLine: command,
  1839  						},
  1840  					},
  1841  				},
  1842  			},
  1843  			numberOfCommands: 1,
  1844  			wantErr:          false,
  1845  		},
  1846  	}
  1847  
  1848  	for _, tt := range tests {
  1849  		t.Run(tt.name, func(t *testing.T) {
  1850  			devObj := parser.DevfileObj{
  1851  				Data: func() data.DevfileData {
  1852  					devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
  1853  					if err != nil {
  1854  						t.Error(err)
  1855  					}
  1856  					err = devfileData.AddCommands(tt.execCommands)
  1857  					if err != nil {
  1858  						t.Error(err)
  1859  					}
  1860  					err = devfileData.AddComponents(([]v1alpha2.Component{testingutil.GetFakeContainerComponent(component)}))
  1861  					if err != nil {
  1862  						t.Error(err)
  1863  					}
  1864  					return devfileData
  1865  				}(),
  1866  			}
  1867  
  1868  			pushCommands, err := ValidateAndGetPushCommands(devObj, tt.buildCommand, tt.runCommand)
  1869  			if !tt.wantErr == (err != nil) {
  1870  				t.Errorf("TestValidateAndGetPushDevfileCommands unexpected error when validating commands wantErr: %v err: %v", tt.wantErr, err)
  1871  			} else if tt.wantErr && err != nil {
  1872  				return
  1873  			}
  1874  
  1875  			if len(pushCommands) != tt.numberOfCommands {
  1876  				t.Errorf("TestValidateAndGetPushDevfileCommands error: wrong number of validated commands expected: %v actual :%v", tt.numberOfCommands, len(pushCommands))
  1877  			}
  1878  		})
  1879  	}
  1880  
  1881  }
  1882  
  1883  func TestGetContainerComponentsForCommand(t *testing.T) {
  1884  	devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion220))
  1885  	devfileData.SetMetadata(devfilepkg.DevfileMetadata{Name: "my-app"})
  1886  	_ = devfileData.AddComponents([]v1alpha2.Component{
  1887  		{
  1888  			Name: "my-container1",
  1889  			ComponentUnion: v1alpha2.ComponentUnion{
  1890  				Container: &v1alpha2.ContainerComponent{
  1891  					Container: v1alpha2.Container{Image: "my-image"},
  1892  				},
  1893  			},
  1894  		},
  1895  		{
  1896  			Name: "my-container2",
  1897  			ComponentUnion: v1alpha2.ComponentUnion{
  1898  				Container: &v1alpha2.ContainerComponent{
  1899  					Container: v1alpha2.Container{Image: "my-image"},
  1900  				},
  1901  			},
  1902  		},
  1903  		{
  1904  			Name: "my-container3",
  1905  			ComponentUnion: v1alpha2.ComponentUnion{
  1906  				Container: &v1alpha2.ContainerComponent{
  1907  					Container: v1alpha2.Container{Image: "my-image"},
  1908  				},
  1909  			},
  1910  		},
  1911  		{
  1912  			Name: "my-image1",
  1913  			ComponentUnion: v1alpha2.ComponentUnion{
  1914  				Image: &v1alpha2.ImageComponent{
  1915  					Image: v1alpha2.Image{
  1916  						ImageName: "my-image",
  1917  					},
  1918  				},
  1919  			},
  1920  		},
  1921  		{
  1922  			Name: "my-k8s",
  1923  			ComponentUnion: v1alpha2.ComponentUnion{
  1924  				Kubernetes: &v1alpha2.KubernetesComponent{
  1925  					K8sLikeComponent: v1alpha2.K8sLikeComponent{
  1926  						K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
  1927  							Inlined: "---",
  1928  						},
  1929  					},
  1930  				},
  1931  			},
  1932  		},
  1933  	})
  1934  
  1935  	customCmd := v1alpha2.Command{
  1936  		Id: "custom",
  1937  		CommandUnion: v1alpha2.CommandUnion{
  1938  			Custom: &v1alpha2.CustomCommand{},
  1939  		},
  1940  	}
  1941  	execCmdCont1 := v1alpha2.Command{
  1942  		Id: "execCmdCont1",
  1943  		CommandUnion: v1alpha2.CommandUnion{
  1944  			Exec: &v1alpha2.ExecCommand{Component: "my-container1"},
  1945  		},
  1946  	}
  1947  	execCmdCont2 := v1alpha2.Command{
  1948  		Id: "execCmdCont2",
  1949  		CommandUnion: v1alpha2.CommandUnion{
  1950  			Exec: &v1alpha2.ExecCommand{Component: "my-container2"},
  1951  		},
  1952  	}
  1953  	execCmdCont3 := v1alpha2.Command{
  1954  		Id: "execCmdCont3",
  1955  		CommandUnion: v1alpha2.CommandUnion{
  1956  			Exec: &v1alpha2.ExecCommand{Component: "my-container3"},
  1957  		},
  1958  	}
  1959  	applyCmdCont1 := v1alpha2.Command{
  1960  		Id: "applyCmdCont1",
  1961  		CommandUnion: v1alpha2.CommandUnion{
  1962  			Apply: &v1alpha2.ApplyCommand{Component: "my-container1"},
  1963  		},
  1964  	}
  1965  	applyCmdImg1 := v1alpha2.Command{
  1966  		Id: "applyCmdImg1",
  1967  		CommandUnion: v1alpha2.CommandUnion{
  1968  			Apply: &v1alpha2.ApplyCommand{Component: "my-image1"},
  1969  		},
  1970  	}
  1971  	applyK8s1 := v1alpha2.Command{
  1972  		Id: "applyK8s1",
  1973  		CommandUnion: v1alpha2.CommandUnion{
  1974  			Apply: &v1alpha2.ApplyCommand{Component: "my-k8s"},
  1975  		},
  1976  	}
  1977  	childCompositeCmd := v1alpha2.Command{
  1978  		Id: "child-composite",
  1979  		CommandUnion: v1alpha2.CommandUnion{
  1980  			Composite: &v1alpha2.CompositeCommand{
  1981  				Commands: []string{execCmdCont3.Id, applyK8s1.Id},
  1982  			},
  1983  		},
  1984  	}
  1985  
  1986  	_ = devfileData.AddCommands([]v1alpha2.Command{
  1987  		customCmd, execCmdCont1, execCmdCont2, execCmdCont3, applyCmdCont1, applyCmdImg1, applyK8s1, childCompositeCmd})
  1988  
  1989  	devfileObj := parser.DevfileObj{Data: devfileData}
  1990  
  1991  	type args struct {
  1992  		cmd v1alpha2.Command
  1993  	}
  1994  	for _, tt := range []struct {
  1995  		name    string
  1996  		args    args
  1997  		wantErr bool
  1998  		want    []string
  1999  	}{
  2000  		{
  2001  			name: "zero -value command",
  2002  			args: args{cmd: v1alpha2.Command{}},
  2003  		},
  2004  		{
  2005  			name:    "GetCommandType returning an error",
  2006  			args:    args{cmd: v1alpha2.Command{Id: "unknown"}},
  2007  			wantErr: true,
  2008  		},
  2009  		{
  2010  			name:    "non-supported command type",
  2011  			args:    args{cmd: customCmd},
  2012  			wantErr: true,
  2013  		},
  2014  		{
  2015  			name: "exec command matching existing container component",
  2016  			args: args{cmd: execCmdCont1},
  2017  			want: []string{"my-container1"},
  2018  		},
  2019  		{
  2020  			name: "exec command not matching existing container component",
  2021  			args: args{
  2022  				cmd: v1alpha2.Command{
  2023  					CommandUnion: v1alpha2.CommandUnion{
  2024  						Exec: &v1alpha2.ExecCommand{Component: "my-k8s"},
  2025  					},
  2026  				},
  2027  			},
  2028  		},
  2029  		{
  2030  			name: "apply command matching existing container component",
  2031  			args: args{cmd: applyCmdCont1},
  2032  			want: []string{"my-container1"},
  2033  		},
  2034  		{
  2035  			name: "apply command not matching existing container component",
  2036  			args: args{cmd: applyCmdImg1},
  2037  		},
  2038  		{
  2039  			name: "composite command with one command missing not declared in Devfile commands",
  2040  			args: args{
  2041  				cmd: v1alpha2.Command{
  2042  					CommandUnion: v1alpha2.CommandUnion{
  2043  						Composite: &v1alpha2.CompositeCommand{
  2044  							Commands: []string{execCmdCont1.Id, "a-command-not-found-in-devfile"},
  2045  						},
  2046  					},
  2047  				},
  2048  			},
  2049  			wantErr: true,
  2050  		},
  2051  		{
  2052  			name: "composite command with at least one unsupported component",
  2053  			args: args{
  2054  				cmd: v1alpha2.Command{
  2055  					CommandUnion: v1alpha2.CommandUnion{
  2056  						Composite: &v1alpha2.CompositeCommand{
  2057  							Commands: []string{childCompositeCmd.Id, customCmd.Id, applyCmdImg1.Id},
  2058  						},
  2059  					},
  2060  				},
  2061  			},
  2062  			wantErr: true,
  2063  		},
  2064  		{
  2065  			name: "composite command with no non-unsupported component",
  2066  			args: args{
  2067  				cmd: v1alpha2.Command{
  2068  					CommandUnion: v1alpha2.CommandUnion{
  2069  						Composite: &v1alpha2.CompositeCommand{
  2070  							Commands: []string{childCompositeCmd.Id, applyCmdImg1.Id, execCmdCont1.Id},
  2071  						},
  2072  					},
  2073  				},
  2074  			},
  2075  			want: []string{"my-container3", "my-container1"},
  2076  		},
  2077  	} {
  2078  		t.Run(tt.name, func(t *testing.T) {
  2079  			got, err := GetContainerComponentsForCommand(devfileObj, tt.args.cmd)
  2080  
  2081  			if tt.wantErr != (err != nil) {
  2082  				t.Errorf("unexpected error, wantErr: %v, err: %v", tt.wantErr, err)
  2083  			}
  2084  			if diff := cmp.Diff(tt.want, got); diff != "" {
  2085  				t.Errorf("GetContainerComponentsForCommand() mismatch (-want +got):\n%s", diff)
  2086  			}
  2087  		})
  2088  	}
  2089  }
  2090  
  2091  func getExecCommand(id string, group v1alpha2.CommandGroupKind) v1alpha2.Command {
  2092  	if len(id) == 0 {
  2093  		id = fmt.Sprintf("%s-%s", "cmd", dfutil.GenerateRandomString(10))
  2094  	}
  2095  	commands := [...]string{"ls -la", "pwd"}
  2096  	components := [...]string{"alias1", "alias2"}
  2097  	workDir := [...]string{"/", "/root"}
  2098  
  2099  	return v1alpha2.Command{
  2100  		Id: id,
  2101  		CommandUnion: v1alpha2.CommandUnion{
  2102  			Exec: &v1alpha2.ExecCommand{
  2103  				LabeledCommand: v1alpha2.LabeledCommand{
  2104  					BaseCommand: v1alpha2.BaseCommand{
  2105  						Group: &v1alpha2.CommandGroup{Kind: group},
  2106  					},
  2107  				},
  2108  				CommandLine: commands[0],
  2109  				Component:   components[0],
  2110  				WorkingDir:  workDir[0],
  2111  			},
  2112  		},
  2113  	}
  2114  
  2115  }
  2116  

View as plain text