...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/core/workflow_test.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/core

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package core
    15  
    16  import (
    17  	"reflect"
    18  	"testing"
    19  
    20  	corev1 "k8s.io/api/core/v1"
    21  
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  
    24  	"github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
    25  )
    26  
    27  func Test_convertWorkflow(t *testing.T) {
    28  	type args struct {
    29  		kubeWorkflow v1alpha1.Workflow
    30  	}
    31  	tests := []struct {
    32  		name string
    33  		args args
    34  		want WorkflowMeta
    35  	}{
    36  		{
    37  			name: "simple workflow",
    38  			args: args{
    39  				v1alpha1.Workflow{
    40  					TypeMeta: metav1.TypeMeta{},
    41  					ObjectMeta: metav1.ObjectMeta{
    42  						Namespace: "fake-namespace",
    43  						Name:      "fake-workflow-0",
    44  					},
    45  					Spec: v1alpha1.WorkflowSpec{
    46  						Entry: "an-entry",
    47  					},
    48  					Status: v1alpha1.WorkflowStatus{},
    49  				},
    50  			},
    51  			want: WorkflowMeta{
    52  				Namespace: "fake-namespace",
    53  				Name:      "fake-workflow-0",
    54  				Entry:     "an-entry",
    55  				Status:    WorkflowUnknown,
    56  			},
    57  		}, {
    58  			name: "running workflow",
    59  			args: args{
    60  				v1alpha1.Workflow{
    61  					TypeMeta: metav1.TypeMeta{},
    62  					ObjectMeta: metav1.ObjectMeta{
    63  						Namespace: "fake-namespace",
    64  						Name:      "fake-workflow-0",
    65  					},
    66  					Spec: v1alpha1.WorkflowSpec{
    67  						Entry: "an-entry",
    68  					},
    69  					Status: v1alpha1.WorkflowStatus{
    70  						Conditions: []v1alpha1.WorkflowCondition{
    71  							{
    72  								Type:   v1alpha1.WorkflowConditionScheduled,
    73  								Status: corev1.ConditionTrue,
    74  								Reason: "",
    75  							},
    76  						},
    77  					},
    78  				},
    79  			},
    80  			want: WorkflowMeta{
    81  				Namespace: "fake-namespace",
    82  				Name:      "fake-workflow-0",
    83  				Entry:     "an-entry",
    84  				Status:    WorkflowRunning,
    85  			},
    86  		}, {
    87  			name: "running workflow",
    88  			args: args{
    89  				v1alpha1.Workflow{
    90  					TypeMeta: metav1.TypeMeta{},
    91  					ObjectMeta: metav1.ObjectMeta{
    92  						Namespace: "fake-namespace",
    93  						Name:      "fake-workflow-0",
    94  					},
    95  					Spec: v1alpha1.WorkflowSpec{
    96  						Entry: "an-entry",
    97  					},
    98  					Status: v1alpha1.WorkflowStatus{
    99  						Conditions: []v1alpha1.WorkflowCondition{
   100  							{
   101  								Type:   v1alpha1.WorkflowConditionAccomplished,
   102  								Status: corev1.ConditionUnknown,
   103  								Reason: "",
   104  							},
   105  							{
   106  								Type:   v1alpha1.WorkflowConditionScheduled,
   107  								Status: corev1.ConditionTrue,
   108  								Reason: "",
   109  							},
   110  						},
   111  					},
   112  				},
   113  			},
   114  			want: WorkflowMeta{
   115  				Namespace: "fake-namespace",
   116  				Name:      "fake-workflow-0",
   117  				Entry:     "an-entry",
   118  				Status:    WorkflowRunning,
   119  			},
   120  		}, {
   121  			name: "running workflow",
   122  			args: args{
   123  				v1alpha1.Workflow{
   124  					TypeMeta: metav1.TypeMeta{},
   125  					ObjectMeta: metav1.ObjectMeta{
   126  						Namespace: "fake-namespace",
   127  						Name:      "fake-workflow-0",
   128  					},
   129  					Spec: v1alpha1.WorkflowSpec{
   130  						Entry: "an-entry",
   131  					},
   132  					Status: v1alpha1.WorkflowStatus{
   133  						Conditions: []v1alpha1.WorkflowCondition{
   134  							{
   135  								Type:   v1alpha1.WorkflowConditionAccomplished,
   136  								Status: corev1.ConditionFalse,
   137  								Reason: "",
   138  							},
   139  							{
   140  								Type:   v1alpha1.WorkflowConditionScheduled,
   141  								Status: corev1.ConditionTrue,
   142  								Reason: "",
   143  							},
   144  						},
   145  					},
   146  				},
   147  			},
   148  			want: WorkflowMeta{
   149  				Namespace: "fake-namespace",
   150  				Name:      "fake-workflow-0",
   151  				Entry:     "an-entry",
   152  				Status:    WorkflowRunning,
   153  			},
   154  		}, {
   155  			name: "succeed workflow",
   156  			args: args{
   157  				v1alpha1.Workflow{
   158  					TypeMeta: metav1.TypeMeta{},
   159  					ObjectMeta: metav1.ObjectMeta{
   160  						Namespace: "fake-namespace",
   161  						Name:      "fake-workflow-0",
   162  					},
   163  					Spec: v1alpha1.WorkflowSpec{
   164  						Entry: "an-entry",
   165  					},
   166  					Status: v1alpha1.WorkflowStatus{
   167  						Conditions: []v1alpha1.WorkflowCondition{
   168  							{
   169  								Type:   v1alpha1.WorkflowConditionAccomplished,
   170  								Status: corev1.ConditionTrue,
   171  								Reason: "",
   172  							},
   173  							{
   174  								Type:   v1alpha1.WorkflowConditionScheduled,
   175  								Status: corev1.ConditionTrue,
   176  								Reason: "",
   177  							},
   178  						},
   179  					},
   180  				},
   181  			},
   182  			want: WorkflowMeta{
   183  				Namespace: "fake-namespace",
   184  				Name:      "fake-workflow-0",
   185  				Entry:     "an-entry",
   186  				Status:    WorkflowSucceed,
   187  			},
   188  		}, {
   189  			name: "converting UID",
   190  			args: args{
   191  				v1alpha1.Workflow{
   192  					TypeMeta: metav1.TypeMeta{},
   193  					ObjectMeta: metav1.ObjectMeta{
   194  						Namespace: "fake-namespace",
   195  						Name:      "fake-workflow-0",
   196  						UID:       "uid-of-workflow",
   197  					},
   198  					Spec: v1alpha1.WorkflowSpec{
   199  						Entry: "an-entry",
   200  					},
   201  					Status: v1alpha1.WorkflowStatus{
   202  						Conditions: []v1alpha1.WorkflowCondition{
   203  							{
   204  								Type:   v1alpha1.WorkflowConditionAccomplished,
   205  								Status: corev1.ConditionTrue,
   206  								Reason: "",
   207  							},
   208  							{
   209  								Type:   v1alpha1.WorkflowConditionScheduled,
   210  								Status: corev1.ConditionTrue,
   211  								Reason: "",
   212  							},
   213  						},
   214  					},
   215  				},
   216  			},
   217  			want: WorkflowMeta{
   218  				Namespace: "fake-namespace",
   219  				Name:      "fake-workflow-0",
   220  				Entry:     "an-entry",
   221  				Status:    WorkflowSucceed,
   222  				UID:       "uid-of-workflow",
   223  			},
   224  		},
   225  	}
   226  	for _, tt := range tests {
   227  		t.Run(tt.name, func(t *testing.T) {
   228  			if got := convertWorkflow(tt.args.kubeWorkflow); !reflect.DeepEqual(got, tt.want) {
   229  				t.Errorf("convertWorkflow() = %v, want %v", got, tt.want)
   230  			}
   231  		})
   232  	}
   233  }
   234  
   235  func Test_convertWorkflowDetail(t *testing.T) {
   236  	type args struct {
   237  		kubeWorkflow v1alpha1.Workflow
   238  		kubeNodes    []v1alpha1.WorkflowNode
   239  	}
   240  	tests := []struct {
   241  		name    string
   242  		args    args
   243  		want    WorkflowDetail
   244  		wantErr bool
   245  	}{
   246  		{
   247  			name: "simple workflow detail with no nodes",
   248  			args: args{
   249  				kubeWorkflow: v1alpha1.Workflow{
   250  					TypeMeta: metav1.TypeMeta{},
   251  					ObjectMeta: metav1.ObjectMeta{
   252  						Namespace: "another-namespace",
   253  						Name:      "another-fake-workflow",
   254  					},
   255  					Spec: v1alpha1.WorkflowSpec{
   256  						Entry:     "another-entry",
   257  						Templates: nil,
   258  					},
   259  					Status: v1alpha1.WorkflowStatus{},
   260  				},
   261  				kubeNodes: nil,
   262  			},
   263  			want: WorkflowDetail{
   264  				WorkflowMeta: WorkflowMeta{
   265  					Namespace: "another-namespace",
   266  					Name:      "another-fake-workflow",
   267  					Entry:     "another-entry",
   268  					Status:    WorkflowUnknown,
   269  				},
   270  				Topology: Topology{
   271  					Nodes: []Node{},
   272  				},
   273  				KubeObject: KubeObjectDesc{
   274  					Meta: KubeObjectMeta{
   275  						Name:      "another-fake-workflow",
   276  						Namespace: "another-namespace",
   277  					},
   278  					Spec: v1alpha1.WorkflowSpec{
   279  						Entry: "another-entry",
   280  					},
   281  				},
   282  			},
   283  		},
   284  		// TODO: Add test cases.
   285  	}
   286  	for _, tt := range tests {
   287  		t.Run(tt.name, func(t *testing.T) {
   288  			got, err := convertWorkflowDetail(tt.args.kubeWorkflow, tt.args.kubeNodes)
   289  			if (err != nil) != tt.wantErr {
   290  				t.Errorf("convertWorkflowDetail() error = %v, wantErr %v", err, tt.wantErr)
   291  				return
   292  			}
   293  			if !reflect.DeepEqual(got, tt.want) {
   294  				t.Errorf("convertWorkflowDetail() got = %v, want %v", got, tt.want)
   295  			}
   296  		})
   297  	}
   298  }
   299  
   300  func Test_convertWorkflowNode(t *testing.T) {
   301  	type args struct {
   302  		kubeWorkflowNode v1alpha1.WorkflowNode
   303  	}
   304  	tests := []struct {
   305  		name    string
   306  		args    args
   307  		want    Node
   308  		wantErr bool
   309  	}{
   310  		{
   311  			name: "simple node",
   312  			args: args{kubeWorkflowNode: v1alpha1.WorkflowNode{
   313  				TypeMeta: metav1.TypeMeta{},
   314  				ObjectMeta: metav1.ObjectMeta{
   315  					Namespace: "fake-namespace",
   316  					Name:      "fake-node-0",
   317  				},
   318  				Spec: v1alpha1.WorkflowNodeSpec{
   319  					WorkflowName: "fake-workflow-0",
   320  					TemplateName: "fake-template-0",
   321  					Type:         v1alpha1.TypeJVMChaos,
   322  				},
   323  				Status: v1alpha1.WorkflowNodeStatus{},
   324  			}},
   325  			want: Node{
   326  				Name:     "fake-node-0",
   327  				Type:     ChaosNode,
   328  				Serial:   nil,
   329  				Parallel: nil,
   330  				Template: "fake-template-0",
   331  				State:    NodeRunning,
   332  			},
   333  		}, {
   334  			name: "serial node",
   335  			args: args{
   336  				kubeWorkflowNode: v1alpha1.WorkflowNode{
   337  					TypeMeta: metav1.TypeMeta{},
   338  					ObjectMeta: metav1.ObjectMeta{
   339  						Namespace: "fake-namespace",
   340  						Name:      "fake-serial-node-0",
   341  					},
   342  					Spec: v1alpha1.WorkflowNodeSpec{
   343  						TemplateName: "fake-serial-node",
   344  						WorkflowName: "fake-workflow-0",
   345  						Type:         v1alpha1.TypeSerial,
   346  						Children:     []string{"child-0", "child-1"},
   347  					},
   348  					Status: v1alpha1.WorkflowNodeStatus{},
   349  				},
   350  			},
   351  			want: Node{
   352  				Name: "fake-serial-node-0",
   353  				Type: SerialNode,
   354  				Serial: &NodeSerial{
   355  					Children: []NodeNameWithTemplate{
   356  						{Name: "", Template: "child-0"},
   357  						{Name: "", Template: "child-1"},
   358  					},
   359  				},
   360  				Parallel: nil,
   361  				Template: "fake-serial-node",
   362  				State:    NodeRunning,
   363  			},
   364  		},
   365  		{
   366  			name: "parallel node",
   367  			args: args{
   368  				kubeWorkflowNode: v1alpha1.WorkflowNode{
   369  					TypeMeta: metav1.TypeMeta{},
   370  					ObjectMeta: metav1.ObjectMeta{
   371  						Namespace: "fake-namespace",
   372  						Name:      "parallel-node-0",
   373  					},
   374  					Spec: v1alpha1.WorkflowNodeSpec{
   375  						TemplateName: "parallel-node",
   376  						WorkflowName: "another-fake-workflow",
   377  						Type:         v1alpha1.TypeParallel,
   378  						Children:     []string{"child-1", "child-0"},
   379  					},
   380  					Status: v1alpha1.WorkflowNodeStatus{},
   381  				},
   382  			},
   383  			want: Node{
   384  				Name:   "parallel-node-0",
   385  				Type:   ParallelNode,
   386  				Serial: nil,
   387  				Parallel: &NodeParallel{
   388  					Children: []NodeNameWithTemplate{
   389  						{Name: "", Template: "child-1"},
   390  						{Name: "", Template: "child-0"},
   391  					},
   392  				},
   393  				Template: "parallel-node",
   394  				State:    NodeRunning,
   395  			},
   396  		},
   397  		{
   398  			name: "some chaos",
   399  			args: args{
   400  				kubeWorkflowNode: v1alpha1.WorkflowNode{
   401  					TypeMeta: metav1.TypeMeta{},
   402  					ObjectMeta: metav1.ObjectMeta{
   403  						Namespace: "fake-namespace",
   404  						Name:      "io-chaos-0",
   405  					},
   406  					Spec: v1alpha1.WorkflowNodeSpec{
   407  						TemplateName: "io-chaos",
   408  						WorkflowName: "another-workflow-0",
   409  						Type:         v1alpha1.TypeIOChaos,
   410  						EmbedChaos: &v1alpha1.EmbedChaos{
   411  							IOChaos: &v1alpha1.IOChaosSpec{
   412  								ContainerSelector: v1alpha1.ContainerSelector{
   413  									PodSelector: v1alpha1.PodSelector{
   414  										Mode: v1alpha1.OnePodMode,
   415  									},
   416  								},
   417  								Action:     "delay",
   418  								Delay:      "100ms",
   419  								Path:       "/fake/path",
   420  								Percent:    100,
   421  								VolumePath: "/fake/path",
   422  							},
   423  						},
   424  					},
   425  					Status: v1alpha1.WorkflowNodeStatus{},
   426  				},
   427  			},
   428  			want: Node{
   429  				Name:     "io-chaos-0",
   430  				Type:     ChaosNode,
   431  				Serial:   nil,
   432  				Parallel: nil,
   433  				Template: "io-chaos",
   434  				State:    NodeRunning,
   435  			},
   436  		},
   437  		{
   438  			name: "accomplished node",
   439  			args: args{
   440  				kubeWorkflowNode: v1alpha1.WorkflowNode{
   441  					TypeMeta: metav1.TypeMeta{},
   442  					ObjectMeta: metav1.ObjectMeta{
   443  						Namespace: "fake-namespace",
   444  						Name:      "the-entry-0",
   445  					},
   446  					Spec: v1alpha1.WorkflowNodeSpec{
   447  						TemplateName: "the-entry",
   448  						WorkflowName: "fake-workflow-0",
   449  						Type:         v1alpha1.TypeSerial,
   450  						Children:     []string{"unimportant-task-0"},
   451  					},
   452  					Status: v1alpha1.WorkflowNodeStatus{
   453  						Conditions: []v1alpha1.WorkflowNodeCondition{
   454  							{
   455  								Type:   v1alpha1.ConditionAccomplished,
   456  								Status: corev1.ConditionTrue,
   457  								Reason: "unit test mocked true",
   458  							},
   459  						},
   460  					},
   461  				},
   462  			},
   463  			want: Node{
   464  				Name:  "the-entry-0",
   465  				Type:  SerialNode,
   466  				State: NodeSucceed,
   467  				Serial: &NodeSerial{
   468  					Children: []NodeNameWithTemplate{
   469  						{Name: "", Template: "unimportant-task-0"},
   470  					},
   471  				},
   472  				Parallel: nil,
   473  				Template: "the-entry",
   474  			},
   475  		},
   476  		{
   477  			name: "deadline exceed node",
   478  			args: args{kubeWorkflowNode: v1alpha1.WorkflowNode{
   479  				TypeMeta: metav1.TypeMeta{},
   480  				ObjectMeta: metav1.ObjectMeta{
   481  					Namespace: "fake-namespace",
   482  					Name:      "deadline-exceed-node-0",
   483  				},
   484  				Spec: v1alpha1.WorkflowNodeSpec{
   485  					TemplateName: "deadline-exceed-node",
   486  					WorkflowName: "some-workflow",
   487  					Type:         v1alpha1.TypePodChaos,
   488  				},
   489  				Status: v1alpha1.WorkflowNodeStatus{
   490  					Conditions: []v1alpha1.WorkflowNodeCondition{
   491  						{
   492  							Type:   v1alpha1.ConditionDeadlineExceed,
   493  							Status: corev1.ConditionTrue,
   494  							Reason: "unit test mocked true",
   495  						},
   496  					},
   497  				},
   498  			}},
   499  			want: Node{
   500  				Name:     "deadline-exceed-node-0",
   501  				Type:     ChaosNode,
   502  				State:    NodeSucceed,
   503  				Serial:   nil,
   504  				Parallel: nil,
   505  				Template: "deadline-exceed-node",
   506  			},
   507  		},
   508  		{
   509  			name: "appending uid",
   510  			args: args{
   511  				kubeWorkflowNode: v1alpha1.WorkflowNode{
   512  					TypeMeta: metav1.TypeMeta{},
   513  					ObjectMeta: metav1.ObjectMeta{
   514  						Namespace: "fake-namespace",
   515  						Name:      "the-entry-0",
   516  						UID:       "uid-of-workflow-node",
   517  					},
   518  					Spec: v1alpha1.WorkflowNodeSpec{
   519  						TemplateName: "the-entry",
   520  						WorkflowName: "fake-workflow-0",
   521  						Type:         v1alpha1.TypeSerial,
   522  						Children:     []string{"unimportant-task-0"},
   523  					},
   524  					Status: v1alpha1.WorkflowNodeStatus{
   525  						Conditions: []v1alpha1.WorkflowNodeCondition{
   526  							{
   527  								Type:   v1alpha1.ConditionAccomplished,
   528  								Status: corev1.ConditionTrue,
   529  								Reason: "unit test mocked true",
   530  							},
   531  						},
   532  					},
   533  				},
   534  			},
   535  			want: Node{
   536  				Name:  "the-entry-0",
   537  				Type:  SerialNode,
   538  				State: NodeSucceed,
   539  				Serial: &NodeSerial{
   540  					Children: []NodeNameWithTemplate{
   541  						{Name: "", Template: "unimportant-task-0"},
   542  					},
   543  				},
   544  				Parallel: nil,
   545  				Template: "the-entry",
   546  				UID:      "uid-of-workflow-node",
   547  			},
   548  		},
   549  		{
   550  			name: "task node",
   551  			args: args{
   552  				kubeWorkflowNode: v1alpha1.WorkflowNode{
   553  					ObjectMeta: metav1.ObjectMeta{
   554  						Name:      "mocking-task-node-0",
   555  						Namespace: "mocked-namespace",
   556  					},
   557  					Spec: v1alpha1.WorkflowNodeSpec{
   558  						TemplateName: "mocking-task-node",
   559  						WorkflowName: "fake-workflow-0",
   560  						Type:         v1alpha1.TypeTask,
   561  						ConditionalBranches: []v1alpha1.ConditionalBranch{
   562  							{
   563  								Target:     "one-node",
   564  								Expression: "exitCode == 0",
   565  							},
   566  							{
   567  								Target:     "another-node",
   568  								Expression: "exitCode != 0",
   569  							},
   570  						},
   571  					},
   572  					Status: v1alpha1.WorkflowNodeStatus{
   573  						ConditionalBranchesStatus: &v1alpha1.ConditionalBranchesStatus{
   574  							Branches: []v1alpha1.ConditionalBranchStatus{
   575  								{
   576  									Target:           "one-node",
   577  									EvaluationResult: corev1.ConditionFalse,
   578  								},
   579  								{
   580  									Target:           "another-node",
   581  									EvaluationResult: corev1.ConditionTrue,
   582  								},
   583  							},
   584  							Context: nil,
   585  						},
   586  						ActiveChildren: []corev1.LocalObjectReference{
   587  							{
   588  								Name: "another-node-0",
   589  							},
   590  						},
   591  					},
   592  				},
   593  			},
   594  			want: Node{
   595  				Name:  "mocking-task-node-0",
   596  				Type:  TaskNode,
   597  				State: NodeRunning,
   598  				ConditionalBranches: []ConditionalBranch{
   599  					{
   600  						NodeNameWithTemplate: NodeNameWithTemplate{
   601  							Template: "one-node",
   602  							Name:     "",
   603  						},
   604  						Expression: "exitCode == 0",
   605  					},
   606  					{
   607  						NodeNameWithTemplate: NodeNameWithTemplate{
   608  							Template: "another-node",
   609  							Name:     "another-node-0",
   610  						},
   611  						Expression: "exitCode != 0",
   612  					},
   613  				},
   614  				Template: "mocking-task-node",
   615  			},
   616  			wantErr: false,
   617  		},
   618  	}
   619  	for _, tt := range tests {
   620  		t.Run(tt.name, func(t *testing.T) {
   621  			got, err := convertWorkflowNode(tt.args.kubeWorkflowNode)
   622  			if (err != nil) != tt.wantErr {
   623  				t.Errorf("convertWorkflowNode() error = %v, wantErr %v", err, tt.wantErr)
   624  				return
   625  			}
   626  			if !reflect.DeepEqual(got, tt.want) {
   627  				t.Errorf("convertWorkflowNode() got = %v, want %v", got, tt.want)
   628  			}
   629  		})
   630  	}
   631  }
   632  
   633  func Test_composeTaskAndNodes(t *testing.T) {
   634  	type args struct {
   635  		children []string
   636  		nodes    []string
   637  	}
   638  	tests := []struct {
   639  		name string
   640  		args args
   641  		want []NodeNameWithTemplate
   642  	}{
   643  		{
   644  			name: "ordered with serial",
   645  			args: args{
   646  				children: []string{"node-0", "node-1", "node-0", "node-2", "node-3"},
   647  				nodes:    []string{"node-0-instance", "node-1-instance", "node-0-another_instance"},
   648  			},
   649  			want: []NodeNameWithTemplate{
   650  				{
   651  					Name:     "node-0-instance",
   652  					Template: "node-0",
   653  				}, {
   654  					Name:     "node-1-instance",
   655  					Template: "node-1",
   656  				}, {
   657  					Name:     "node-0-another_instance",
   658  					Template: "node-0",
   659  				}, {
   660  					Name:     "",
   661  					Template: "node-2",
   662  				}, {
   663  					Name:     "",
   664  					Template: "node-3",
   665  				},
   666  			},
   667  		},
   668  	}
   669  	for _, tt := range tests {
   670  		t.Run(tt.name, func(t *testing.T) {
   671  			if got := composeSerialTaskAndNodes(tt.args.children, tt.args.nodes); !reflect.DeepEqual(got, tt.want) {
   672  				t.Errorf("composeSerialTaskAndNodes() = %v, want %v", got, tt.want)
   673  			}
   674  		})
   675  	}
   676  }
   677  
   678  func Test_composeParallelTaskAndNodes(t *testing.T) {
   679  	type args struct {
   680  		children []string
   681  		nodes    []string
   682  	}
   683  	tests := []struct {
   684  		name string
   685  		args args
   686  		want []NodeNameWithTemplate
   687  	}{
   688  		{
   689  			name: "parallel",
   690  			args: args{
   691  				children: []string{"node-a", "node-b", "node-a", "node-c", "node-d"},
   692  				nodes:    []string{"node-a-instance", "node-a-another_instance", "node-d-instance"},
   693  			},
   694  			want: []NodeNameWithTemplate{
   695  				{
   696  					Name:     "node-a-instance",
   697  					Template: "node-a",
   698  				}, {
   699  					Name:     "",
   700  					Template: "node-b",
   701  				}, {
   702  					Name:     "node-a-another_instance",
   703  					Template: "node-a",
   704  				}, {
   705  					Name:     "",
   706  					Template: "node-c",
   707  				}, {
   708  					Name:     "node-d-instance",
   709  					Template: "node-d",
   710  				},
   711  			},
   712  		},
   713  	}
   714  	for _, tt := range tests {
   715  		t.Run(tt.name, func(t *testing.T) {
   716  			if got := composeParallelTaskAndNodes(tt.args.children, tt.args.nodes); !reflect.DeepEqual(got, tt.want) {
   717  				t.Errorf("composeParallelTaskAndNodes() = %v, want %v", got, tt.want)
   718  			}
   719  		})
   720  	}
   721  }
   722  
   723  func Test_composeTaskConditionalBranches(t *testing.T) {
   724  	type args struct {
   725  		conditionalBranches []v1alpha1.ConditionalBranch
   726  		nodes               []string
   727  	}
   728  	tests := []struct {
   729  		name string
   730  		args args
   731  		want []ConditionalBranch
   732  	}{
   733  		{
   734  			name: "task node all of the branch is selected",
   735  			args: args{
   736  				conditionalBranches: []v1alpha1.ConditionalBranch{
   737  					{
   738  						Target:     "template-a",
   739  						Expression: "a: whatever valid or not",
   740  					},
   741  					{
   742  						Target:     "template-b",
   743  						Expression: "b: whatever valid or not",
   744  					},
   745  					{
   746  						Target:     "template-c",
   747  						Expression: "c: whatever valid or not",
   748  					},
   749  				},
   750  				nodes: []string{
   751  					"template-a-0",
   752  					"template-b-0",
   753  					"template-c-0",
   754  				},
   755  			},
   756  			want: []ConditionalBranch{
   757  				{
   758  					NodeNameWithTemplate: NodeNameWithTemplate{
   759  						Name:     "template-a-0",
   760  						Template: "template-a",
   761  					},
   762  					Expression: "a: whatever valid or not",
   763  				},
   764  				{
   765  					NodeNameWithTemplate: NodeNameWithTemplate{
   766  						Name:     "template-b-0",
   767  						Template: "template-b",
   768  					},
   769  					Expression: "b: whatever valid or not",
   770  				},
   771  				{
   772  					NodeNameWithTemplate: NodeNameWithTemplate{
   773  						Name:     "template-c-0",
   774  						Template: "template-c",
   775  					},
   776  					Expression: "c: whatever valid or not",
   777  				},
   778  			},
   779  		},
   780  		{
   781  			name: "none of the branch is selected",
   782  			args: args{
   783  				conditionalBranches: []v1alpha1.ConditionalBranch{
   784  					{
   785  						Target:     "template-a",
   786  						Expression: "a: whatever valid or not",
   787  					},
   788  					{
   789  						Target:     "template-b",
   790  						Expression: "b: whatever valid or not",
   791  					},
   792  					{
   793  						Target:     "template-c",
   794  						Expression: "c: whatever valid or not",
   795  					},
   796  				},
   797  				nodes: []string{},
   798  			},
   799  			want: []ConditionalBranch{
   800  				{
   801  					NodeNameWithTemplate: NodeNameWithTemplate{
   802  						Name:     "",
   803  						Template: "template-a",
   804  					},
   805  					Expression: "a: whatever valid or not",
   806  				},
   807  				{
   808  					NodeNameWithTemplate: NodeNameWithTemplate{
   809  						Name:     "",
   810  						Template: "template-b",
   811  					},
   812  					Expression: "b: whatever valid or not",
   813  				},
   814  				{
   815  					NodeNameWithTemplate: NodeNameWithTemplate{
   816  						Name:     "",
   817  						Template: "template-c",
   818  					},
   819  					Expression: "c: whatever valid or not",
   820  				},
   821  			},
   822  		},
   823  		{
   824  			name: "part of the branch is selected",
   825  			args: args{
   826  				conditionalBranches: []v1alpha1.ConditionalBranch{
   827  					{
   828  						Target:     "template-a",
   829  						Expression: "a: whatever valid or not",
   830  					},
   831  					{
   832  						Target:     "template-b",
   833  						Expression: "b: whatever valid or not",
   834  					},
   835  					{
   836  						Target:     "template-c",
   837  						Expression: "c: whatever valid or not",
   838  					},
   839  				},
   840  				nodes: []string{
   841  					"template-a-0",
   842  				},
   843  			},
   844  			want: []ConditionalBranch{
   845  				{
   846  					NodeNameWithTemplate: NodeNameWithTemplate{
   847  						Name:     "template-a-0",
   848  						Template: "template-a",
   849  					},
   850  					Expression: "a: whatever valid or not",
   851  				},
   852  				{
   853  					NodeNameWithTemplate: NodeNameWithTemplate{
   854  						Name:     "",
   855  						Template: "template-b",
   856  					},
   857  					Expression: "b: whatever valid or not",
   858  				},
   859  				{
   860  					NodeNameWithTemplate: NodeNameWithTemplate{
   861  						Name:     "",
   862  						Template: "template-c",
   863  					},
   864  					Expression: "c: whatever valid or not",
   865  				},
   866  			},
   867  		},
   868  	}
   869  	for _, tt := range tests {
   870  		t.Run(tt.name, func(t *testing.T) {
   871  			if got := composeTaskConditionalBranches(tt.args.conditionalBranches, tt.args.nodes); !reflect.DeepEqual(got, tt.want) {
   872  				t.Errorf("composeTaskConditionalBranches() = %v, want %v", got, tt.want)
   873  			}
   874  		})
   875  	}
   876  }
   877