github.com/kubevela/workflow@v0.6.0/pkg/tasks/custom/task_test.go (about) 1 /* 2 Copyright 2022 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package custom 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "testing" 24 25 "github.com/crossplane/crossplane-runtime/pkg/test" 26 monitorContext "github.com/kubevela/pkg/monitor/context" 27 "github.com/pkg/errors" 28 "github.com/stretchr/testify/require" 29 corev1 "k8s.io/api/core/v1" 30 "k8s.io/apimachinery/pkg/runtime" 31 "sigs.k8s.io/controller-runtime/pkg/client" 32 "sigs.k8s.io/yaml" 33 34 "github.com/kubevela/workflow/api/v1alpha1" 35 wfContext "github.com/kubevela/workflow/pkg/context" 36 "github.com/kubevela/workflow/pkg/cue/model/value" 37 "github.com/kubevela/workflow/pkg/cue/process" 38 "github.com/kubevela/workflow/pkg/providers" 39 "github.com/kubevela/workflow/pkg/types" 40 ) 41 42 func TestTaskLoader(t *testing.T) { 43 wfCtx := newWorkflowContextForTest(t) 44 r := require.New(t) 45 discover := providers.NewProviders() 46 discover.Register("test", map[string]types.Handler{ 47 "output": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 48 ip, _ := v.MakeValue(` 49 myIP: value: "1.1.1.1" 50 `) 51 return v.FillObject(ip) 52 }, 53 "input": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 54 val, err := v.LookupValue("set", "prefixIP") 55 r.NoError(err) 56 str, err := val.CueValue().String() 57 r.NoError(err) 58 r.Equal(str, "1.1.1.1") 59 return nil 60 }, 61 "wait": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 62 act.Wait("I am waiting") 63 return nil 64 }, 65 "terminate": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 66 act.Terminate("I am terminated") 67 return nil 68 }, 69 "suspend": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 70 act.Terminate("I am suspended") 71 return nil 72 }, 73 "resume": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 74 act.Terminate("I am resumed") 75 return nil 76 }, 77 "executeFailed": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 78 return errors.New("execute error") 79 }, 80 "ok": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 81 return nil 82 }, 83 }) 84 85 pCtx := process.NewContext(process.ContextData{ 86 Name: "app", 87 Namespace: "default", 88 }) 89 tasksLoader := NewTaskLoader(mockLoadTemplate, nil, discover, 0, pCtx) 90 91 steps := []v1alpha1.WorkflowStep{ 92 { 93 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 94 Name: "output", 95 Type: "output", 96 Outputs: v1alpha1.StepOutputs{{ 97 ValueFrom: "myIP.value", 98 Name: "podIP", 99 }}, 100 }, 101 }, 102 { 103 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 104 Name: "input", 105 Type: "input", 106 Inputs: v1alpha1.StepInputs{{ 107 From: "podIP", 108 ParameterKey: "set.prefixIP", 109 }}, 110 }, 111 }, 112 { 113 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 114 Name: "wait", 115 Type: "wait", 116 }, 117 }, 118 { 119 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 120 Name: "terminate", 121 Type: "terminate", 122 }, 123 }, 124 { 125 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 126 Name: "template", 127 Type: "templateError", 128 }, 129 }, 130 { 131 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 132 Name: "execute", 133 Type: "executeFailed", 134 }, 135 }, 136 { 137 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 138 Name: "steps", 139 Type: "steps", 140 }, 141 }, 142 } 143 144 for _, step := range steps { 145 gen, err := tasksLoader.GetTaskGenerator(context.Background(), step.Type) 146 r.NoError(err) 147 run, err := gen(step, &types.TaskGeneratorOptions{}) 148 r.NoError(err) 149 status, action, err := run.Run(wfCtx, &types.TaskRunOptions{}) 150 r.NoError(err) 151 if step.Name == "wait" { 152 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseRunning) 153 r.Equal(status.Reason, types.StatusReasonWait) 154 r.Equal(status.Message, "I am waiting") 155 continue 156 } 157 if step.Name == "terminate" { 158 r.Equal(action.Terminated, true) 159 r.Equal(status.Reason, types.StatusReasonTerminate) 160 r.Equal(status.Message, "I am terminated") 161 continue 162 } 163 if step.Name == "template" { 164 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseFailed) 165 r.Equal(status.Reason, types.StatusReasonExecute) 166 continue 167 } 168 if step.Name == "execute" { 169 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseFailed) 170 r.Equal(status.Reason, types.StatusReasonExecute) 171 continue 172 } 173 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseSucceeded) 174 } 175 176 } 177 178 func TestErrCases(t *testing.T) { 179 wfCtx := newWorkflowContextForTest(t) 180 r := require.New(t) 181 closeVar, err := value.NewValue(` 182 close({ 183 x: 100 184 }) 185 `, nil, "", value.TagFieldOrder) 186 r.NoError(err) 187 err = wfCtx.SetVar(closeVar, "score") 188 r.NoError(err) 189 discover := providers.NewProviders() 190 discover.Register("test", map[string]types.Handler{ 191 "input": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 192 val, err := v.LookupValue("prefixIP") 193 r.NoError(err) 194 str, err := val.CueValue().String() 195 r.NoError(err) 196 r.Equal(str, "1.1.1.1") 197 return nil 198 }, 199 "ok": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 200 return nil 201 }, 202 "error": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 203 return errors.New("mock error") 204 }, 205 }) 206 pCtx := process.NewContext(process.ContextData{ 207 Name: "app", 208 Namespace: "default", 209 }) 210 tasksLoader := NewTaskLoader(mockLoadTemplate, nil, discover, 0, pCtx) 211 212 steps := []v1alpha1.WorkflowStep{ 213 { 214 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 215 Name: "input-replace", 216 Type: "ok", 217 Properties: &runtime.RawExtension{Raw: []byte(` 218 {"score": {"x": 101}} 219 `)}, 220 Inputs: v1alpha1.StepInputs{{ 221 From: "score", 222 ParameterKey: "score", 223 }}, 224 }, 225 }, 226 { 227 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 228 Name: "input", 229 Type: "input", 230 Inputs: v1alpha1.StepInputs{{ 231 From: "podIP", 232 ParameterKey: "prefixIP", 233 }}, 234 }, 235 }, 236 { 237 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 238 Name: "output-var-conflict", 239 Type: "ok", 240 Outputs: v1alpha1.StepOutputs{{ 241 Name: "score", 242 ValueFrom: "name", 243 }}, 244 }, 245 }, 246 { 247 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 248 Name: "wait", 249 Type: "wait", 250 }, 251 }, 252 { 253 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 254 Name: "err", 255 Type: "error", 256 }, 257 }, 258 { 259 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 260 Name: "failed-after-retries", 261 Type: "error", 262 }, 263 }, 264 } 265 for _, step := range steps { 266 gen, err := tasksLoader.GetTaskGenerator(context.Background(), step.Type) 267 r.NoError(err) 268 run, err := gen(step, &types.TaskGeneratorOptions{}) 269 r.NoError(err) 270 status, operation, _ := run.Run(wfCtx, &types.TaskRunOptions{}) 271 switch step.Name { 272 case "input-replace": 273 r.Equal(status.Message, "") 274 r.Equal(operation.Waiting, false) 275 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseSucceeded) 276 r.Equal(status.Reason, "") 277 case "input": 278 r.Equal(status.Message, "get input from [podIP]: failed to lookup value: var(path=podIP) not exist") 279 r.Equal(operation.Waiting, false) 280 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseFailed) 281 r.Equal(status.Reason, types.StatusReasonInput) 282 case "output-var-conflict": 283 r.Contains(status.Message, "conflict") 284 r.Equal(operation.Waiting, false) 285 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseSucceeded) 286 case "failed-after-retries": 287 wfContext.CleanupMemoryStore("app-v1", "default") 288 newCtx := newWorkflowContextForTest(t) 289 for i := 0; i < types.MaxWorkflowStepErrorRetryTimes; i++ { 290 status, operation, err = run.Run(newCtx, &types.TaskRunOptions{}) 291 r.NoError(err) 292 r.Equal(operation.Waiting, true) 293 r.Equal(operation.FailedAfterRetries, false) 294 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseFailed) 295 } 296 status, operation, err = run.Run(newCtx, &types.TaskRunOptions{}) 297 r.NoError(err) 298 r.Equal(operation.Waiting, false) 299 r.Equal(operation.FailedAfterRetries, true) 300 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseFailed) 301 r.Equal(status.Reason, types.StatusReasonFailedAfterRetries) 302 default: 303 r.Equal(operation.Waiting, true) 304 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseFailed) 305 } 306 } 307 } 308 309 func TestSteps(t *testing.T) { 310 311 var ( 312 echo string 313 mockErr = errors.New("mock error") 314 ) 315 316 wfCtx := newWorkflowContextForTest(t) 317 r := require.New(t) 318 discover := providers.NewProviders() 319 discover.Register("test", map[string]types.Handler{ 320 "ok": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 321 echo = echo + "ok" 322 return nil 323 }, 324 "error": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 325 return mockErr 326 }, 327 }) 328 exec := &executor{ 329 handlers: discover, 330 } 331 332 testCases := []struct { 333 base string 334 expected string 335 hasErr bool 336 }{ 337 { 338 base: ` 339 process: { 340 #provider: "test" 341 #do: "ok" 342 } 343 344 #up: [process] 345 `, 346 expected: "okok", 347 }, 348 { 349 base: ` 350 process: { 351 #provider: "test" 352 #do: "ok" 353 } 354 355 #up: [process,{ 356 #do: "steps" 357 p1: process 358 #up: [process] 359 }] 360 `, 361 expected: "okokokok", 362 }, 363 { 364 base: ` 365 process: { 366 #provider: "test" 367 #do: "ok" 368 } 369 370 #up: [process,{ 371 p1: process 372 #up: [process] 373 }] 374 `, 375 expected: "okok", 376 }, 377 { 378 base: ` 379 process: { 380 #provider: "test" 381 #do: "ok" 382 } 383 384 #up: [process,{ 385 #do: "steps" 386 err: { 387 #provider: "test" 388 #do: "error" 389 } @step(1) 390 #up: [{},process] @step(2) 391 }] 392 `, 393 expected: "okok", 394 hasErr: true, 395 }, 396 397 { 398 base: ` 399 #provider: "test" 400 #do: "ok" 401 `, 402 expected: "ok", 403 }, 404 { 405 base: ` 406 process: { 407 #provider: "test" 408 #do: "ok" 409 err: true 410 } 411 412 if process.err { 413 err: { 414 #provider: "test" 415 #do: "error" 416 } 417 } 418 419 apply: { 420 #provider: "test" 421 #do: "ok" 422 } 423 424 #up: [process,{}] 425 `, 426 expected: "ok", 427 hasErr: true, 428 }, 429 } 430 431 for i, tc := range testCases { 432 echo = "" 433 v, err := value.NewValue(tc.base, nil, "", value.TagFieldOrder) 434 r.NoError(err) 435 err = exec.doSteps(nil, wfCtx, v) 436 r.Equal(err != nil, tc.hasErr) 437 r.Equal(tc.expected, echo, i) 438 } 439 440 } 441 442 func TestPendingInputCheck(t *testing.T) { 443 wfCtx := newWorkflowContextForTest(t) 444 r := require.New(t) 445 discover := providers.NewProviders() 446 discover.Register("test", map[string]types.Handler{ 447 "ok": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 448 return nil 449 }, 450 }) 451 step := v1alpha1.WorkflowStep{ 452 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 453 Name: "pending", 454 Type: "ok", 455 Inputs: v1alpha1.StepInputs{{ 456 From: "score", 457 ParameterKey: "score", 458 }}, 459 }, 460 } 461 pCtx := process.NewContext(process.ContextData{ 462 Name: "app", 463 Namespace: "default", 464 }) 465 tasksLoader := NewTaskLoader(mockLoadTemplate, nil, discover, 0, pCtx) 466 gen, err := tasksLoader.GetTaskGenerator(context.Background(), step.Type) 467 r.NoError(err) 468 run, err := gen(step, &types.TaskGeneratorOptions{}) 469 r.NoError(err) 470 logCtx := monitorContext.NewTraceContext(context.Background(), "test-app") 471 p, _ := run.Pending(logCtx, wfCtx, nil) 472 r.Equal(p, true) 473 score, err := value.NewValue(` 474 100 475 `, nil, "") 476 r.NoError(err) 477 err = wfCtx.SetVar(score, "score") 478 r.NoError(err) 479 p, _ = run.Pending(logCtx, wfCtx, nil) 480 r.Equal(p, false) 481 } 482 483 func TestPendingDependsOnCheck(t *testing.T) { 484 wfCtx := newWorkflowContextForTest(t) 485 r := require.New(t) 486 discover := providers.NewProviders() 487 discover.Register("test", map[string]types.Handler{ 488 "ok": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 489 return nil 490 }, 491 }) 492 step := v1alpha1.WorkflowStep{ 493 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 494 Name: "pending", 495 Type: "ok", 496 DependsOn: []string{"depend"}, 497 }, 498 } 499 pCtx := process.NewContext(process.ContextData{ 500 Name: "app", 501 Namespace: "default", 502 }) 503 tasksLoader := NewTaskLoader(mockLoadTemplate, nil, discover, 0, pCtx) 504 gen, err := tasksLoader.GetTaskGenerator(context.Background(), step.Type) 505 r.NoError(err) 506 run, err := gen(step, &types.TaskGeneratorOptions{}) 507 r.NoError(err) 508 logCtx := monitorContext.NewTraceContext(context.Background(), "test-app") 509 p, _ := run.Pending(logCtx, wfCtx, nil) 510 r.Equal(p, true) 511 ss := map[string]v1alpha1.StepStatus{ 512 "depend": { 513 Phase: v1alpha1.WorkflowStepPhaseSucceeded, 514 }, 515 } 516 p, _ = run.Pending(logCtx, wfCtx, ss) 517 r.Equal(p, false) 518 } 519 520 func TestSkip(t *testing.T) { 521 r := require.New(t) 522 discover := providers.NewProviders() 523 discover.Register("test", map[string]types.Handler{ 524 "ok": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 525 return nil 526 }, 527 }) 528 step := v1alpha1.WorkflowStep{ 529 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 530 Name: "skip", 531 Type: "ok", 532 }, 533 } 534 pCtx := process.NewContext(process.ContextData{ 535 Name: "app", 536 Namespace: "default", 537 }) 538 tasksLoader := NewTaskLoader(mockLoadTemplate, nil, discover, 0, pCtx) 539 gen, err := tasksLoader.GetTaskGenerator(context.Background(), step.Type) 540 r.NoError(err) 541 runner, err := gen(step, &types.TaskGeneratorOptions{}) 542 r.NoError(err) 543 wfCtx := newWorkflowContextForTest(t) 544 status, operations, err := runner.Run(wfCtx, &types.TaskRunOptions{ 545 PreCheckHooks: []types.TaskPreCheckHook{ 546 func(step v1alpha1.WorkflowStep, options *types.PreCheckOptions) (*types.PreCheckResult, error) { 547 return &types.PreCheckResult{Skip: true}, nil 548 }, 549 }, 550 }) 551 r.NoError(err) 552 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseSkipped) 553 r.Equal(status.Reason, types.StatusReasonSkip) 554 r.Equal(operations.Skip, true) 555 } 556 557 func TestTimeout(t *testing.T) { 558 r := require.New(t) 559 discover := providers.NewProviders() 560 discover.Register("test", map[string]types.Handler{ 561 "ok": func(mCtx monitorContext.Context, ctx wfContext.Context, v *value.Value, act types.Action) error { 562 return nil 563 }, 564 }) 565 step := v1alpha1.WorkflowStep{ 566 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 567 Name: "timeout", 568 Type: "ok", 569 }, 570 } 571 pCtx := process.NewContext(process.ContextData{ 572 Name: "app", 573 Namespace: "default", 574 }) 575 tasksLoader := NewTaskLoader(mockLoadTemplate, nil, discover, 0, pCtx) 576 gen, err := tasksLoader.GetTaskGenerator(context.Background(), step.Type) 577 r.NoError(err) 578 runner, err := gen(step, &types.TaskGeneratorOptions{}) 579 r.NoError(err) 580 ctx := newWorkflowContextForTest(t) 581 status, _, err := runner.Run(ctx, &types.TaskRunOptions{ 582 PreCheckHooks: []types.TaskPreCheckHook{ 583 func(step v1alpha1.WorkflowStep, options *types.PreCheckOptions) (*types.PreCheckResult, error) { 584 return &types.PreCheckResult{Timeout: true}, nil 585 }, 586 }, 587 }) 588 r.NoError(err) 589 r.Equal(status.Phase, v1alpha1.WorkflowStepPhaseFailed) 590 r.Equal(status.Reason, types.StatusReasonTimeout) 591 } 592 593 func TestValidateIfValue(t *testing.T) { 594 ctx := newWorkflowContextForTest(t) 595 pCtx := process.NewContext(process.ContextData{ 596 Name: "app", 597 Namespace: "default", 598 Data: map[string]interface{}{"arr": []string{"a", "b"}}, 599 }) 600 basicVal, basicTemplate, err := MakeBasicValue(ctx, `key: "value"`, pCtx) 601 r := require.New(t) 602 r.NoError(err) 603 604 testCases := []struct { 605 name string 606 step v1alpha1.WorkflowStep 607 status map[string]v1alpha1.StepStatus 608 expected bool 609 expectedErr string 610 }{ 611 { 612 name: "timeout true", 613 step: v1alpha1.WorkflowStep{ 614 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 615 If: "status.step1.timeout", 616 }, 617 }, 618 status: map[string]v1alpha1.StepStatus{ 619 "step1": { 620 Reason: "Timeout", 621 }, 622 }, 623 expected: true, 624 }, 625 { 626 name: "context true", 627 step: v1alpha1.WorkflowStep{ 628 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 629 If: `context.name == "app"`, 630 }, 631 }, 632 expected: true, 633 }, 634 { 635 name: "context arr true", 636 step: v1alpha1.WorkflowStep{ 637 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 638 If: `context.arr[0] == "a"`, 639 }, 640 }, 641 expected: true, 642 }, 643 { 644 name: "parameter true", 645 step: v1alpha1.WorkflowStep{ 646 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 647 If: `parameter.key == "value"`, 648 }, 649 }, 650 expected: true, 651 }, 652 { 653 name: "failed true", 654 step: v1alpha1.WorkflowStep{ 655 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 656 If: `status.step1.phase != "failed"`, 657 }, 658 }, 659 status: map[string]v1alpha1.StepStatus{ 660 "step1": { 661 Phase: v1alpha1.WorkflowStepPhaseSucceeded, 662 }, 663 }, 664 expected: true, 665 }, 666 { 667 name: "input true", 668 step: v1alpha1.WorkflowStep{ 669 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 670 If: `inputs.test == "yes"`, 671 Inputs: v1alpha1.StepInputs{ 672 { 673 From: "test", 674 }, 675 }, 676 }, 677 }, 678 expected: true, 679 }, 680 { 681 name: "input with arr in context", 682 step: v1alpha1.WorkflowStep{ 683 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 684 If: `inputs["context.arr[0]"] == "a"`, 685 Inputs: v1alpha1.StepInputs{ 686 { 687 From: "context.arr[0]", 688 }, 689 }, 690 }, 691 }, 692 expected: true, 693 }, 694 { 695 name: "input false with dash", 696 step: v1alpha1.WorkflowStep{ 697 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 698 If: `inputs["test-input"] == "yes"`, 699 Inputs: v1alpha1.StepInputs{ 700 { 701 From: "test-input", 702 }, 703 }, 704 }, 705 }, 706 expectedErr: "not found", 707 expected: false, 708 }, 709 { 710 name: "input value is struct", 711 step: v1alpha1.WorkflowStep{ 712 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 713 If: `inputs["test-struct"].hello == "world"`, 714 Inputs: v1alpha1.StepInputs{ 715 { 716 From: "test-struct", 717 }, 718 }, 719 }, 720 }, 721 expected: true, 722 }, 723 { 724 name: "dash in if", 725 step: v1alpha1.WorkflowStep{ 726 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 727 If: "status.step1-test.timeout", 728 }, 729 }, 730 expectedErr: "invalid if value", 731 expected: false, 732 }, 733 { 734 name: "dash in status", 735 step: v1alpha1.WorkflowStep{ 736 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 737 If: `status["step1-test"].timeout`, 738 }, 739 }, 740 status: map[string]v1alpha1.StepStatus{ 741 "step1-test": { 742 Reason: "Timeout", 743 }, 744 }, 745 expected: true, 746 }, 747 { 748 name: "error if", 749 step: v1alpha1.WorkflowStep{ 750 WorkflowStepBase: v1alpha1.WorkflowStepBase{ 751 If: `test == true`, 752 }, 753 }, 754 expectedErr: "invalid if value", 755 expected: false, 756 }, 757 } 758 759 for _, tc := range testCases { 760 t.Run(tc.name, func(t *testing.T) { 761 r := require.New(t) 762 v, err := ValidateIfValue(ctx, tc.step, tc.status, &types.PreCheckOptions{ 763 BasicTemplate: basicTemplate, 764 BasicValue: basicVal, 765 }) 766 if tc.expectedErr != "" { 767 r.Contains(err.Error(), tc.expectedErr) 768 r.Equal(v, false) 769 return 770 } 771 r.NoError(err) 772 r.Equal(v, tc.expected) 773 }) 774 } 775 } 776 777 func newWorkflowContextForTest(t *testing.T) wfContext.Context { 778 r := require.New(t) 779 cm := corev1.ConfigMap{} 780 testCaseJson, err := yaml.YAMLToJSON([]byte(testCaseYaml)) 781 r.NoError(err) 782 err = json.Unmarshal(testCaseJson, &cm) 783 r.NoError(err) 784 785 cli := &test.MockClient{ 786 MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { 787 o, ok := obj.(*corev1.ConfigMap) 788 if ok { 789 *o = cm 790 } 791 return nil 792 }, 793 MockPatch: func(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { 794 return nil 795 }, 796 } 797 wfCtx, err := wfContext.NewContext(context.Background(), cli, "default", "app-v1", nil) 798 r.NoError(err) 799 v, err := value.NewValue(`"yes"`, nil, "") 800 r.NoError(err) 801 r.NoError(wfCtx.SetVar(v, "test")) 802 v, err = value.NewValue(`{hello: "world"}`, nil, "") 803 r.NoError(err) 804 r.NoError(wfCtx.SetVar(v, "test-struct")) 805 return wfCtx 806 } 807 808 func mockLoadTemplate(_ context.Context, name string) (string, error) { 809 templ := ` 810 parameter: {} 811 process: { 812 #provider: "test" 813 #do: "%s" 814 parameter 815 } 816 // check injected context. 817 name: context.name 818 ` 819 switch name { 820 case "output": 821 return fmt.Sprintf(templ+`myIP: process.myIP`, "output"), nil 822 case "input": 823 return fmt.Sprintf(templ, "input"), nil 824 case "wait": 825 return fmt.Sprintf(templ, "wait"), nil 826 case "terminate": 827 return fmt.Sprintf(templ, "terminate"), nil 828 case "templateError": 829 return ` 830 output: xx 831 `, nil 832 case "executeFailed": 833 return fmt.Sprintf(templ, "executeFailed"), nil 834 case "ok": 835 return fmt.Sprintf(templ, "ok"), nil 836 case "error": 837 return fmt.Sprintf(templ, "error"), nil 838 case "steps": 839 return ` 840 #do: "steps" 841 ok: { 842 #provider: "test" 843 #do: "ok" 844 } 845 `, nil 846 } 847 848 return "", nil 849 } 850 851 var ( 852 testCaseYaml = `apiVersion: v1 853 data: 854 test: "" 855 kind: ConfigMap 856 metadata: 857 name: app-v1 858 ` 859 )