github.com/opentofu/opentofu@v1.7.1/internal/cloud/backend_taskStage_taskResults_test.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package cloud
     7  
     8  import (
     9  	"context"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/hashicorp/go-tfe"
    14  )
    15  
    16  type testIntegrationOutput struct {
    17  	ctx    *IntegrationContext
    18  	output *strings.Builder
    19  	t      *testing.T
    20  }
    21  
    22  var _ IntegrationOutputWriter = (*testIntegrationOutput)(nil) // Compile time check
    23  
    24  func (s *testIntegrationOutput) End() {
    25  	s.output.WriteString("END\n")
    26  }
    27  
    28  func (s *testIntegrationOutput) SubOutput(str string) {
    29  	s.output.WriteString(s.ctx.B.Colorize().Color("[reset]│ "+str) + "\n")
    30  }
    31  
    32  func (s *testIntegrationOutput) Output(str string) {
    33  	s.output.WriteString(s.ctx.B.Colorize().Color("[reset]│ ") + str + "\n")
    34  }
    35  
    36  func (s *testIntegrationOutput) OutputElapsed(message string, maxMessage int) {
    37  	s.output.WriteString("PENDING MESSAGE: " + message)
    38  }
    39  
    40  func newMockIntegrationContext(b *Cloud, t *testing.T) (*IntegrationContext, *testIntegrationOutput) {
    41  	ctx := context.Background()
    42  
    43  	// Retrieve the workspace used to run this operation in.
    44  	w, err := b.client.Workspaces.Read(ctx, b.organization, b.WorkspaceMapping.Name)
    45  	if err != nil {
    46  		t.Fatalf("error retrieving workspace: %v", err)
    47  	}
    48  
    49  	// Create a new configuration version.
    50  	c, err := b.client.ConfigurationVersions.Create(ctx, w.ID, tfe.ConfigurationVersionCreateOptions{})
    51  	if err != nil {
    52  		t.Fatalf("error creating configuration version: %v", err)
    53  	}
    54  
    55  	// Create a pending run to block this run.
    56  	r, err := b.client.Runs.Create(ctx, tfe.RunCreateOptions{
    57  		ConfigurationVersion: c,
    58  		Workspace:            w,
    59  	})
    60  	if err != nil {
    61  		t.Fatalf("error creating pending run: %v", err)
    62  	}
    63  
    64  	op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
    65  	defer configCleanup()
    66  	defer done(t)
    67  
    68  	integrationContext := &IntegrationContext{
    69  		B:             b,
    70  		StopContext:   ctx,
    71  		CancelContext: ctx,
    72  		Op:            op,
    73  		Run:           r,
    74  	}
    75  
    76  	return integrationContext, &testIntegrationOutput{
    77  		ctx:    integrationContext,
    78  		output: &strings.Builder{},
    79  		t:      t,
    80  	}
    81  }
    82  
    83  func TestCloud_runTasksWithTaskResults(t *testing.T) {
    84  	b, bCleanup := testBackendWithName(t)
    85  	defer bCleanup()
    86  
    87  	integrationContext, writer := newMockIntegrationContext(b, t)
    88  
    89  	cases := map[string]struct {
    90  		taskStage       func() *tfe.TaskStage
    91  		context         *IntegrationContext
    92  		writer          *testIntegrationOutput
    93  		expectedOutputs []string
    94  		isError         bool
    95  	}{
    96  		"all-succeeded": {
    97  			taskStage: func() *tfe.TaskStage {
    98  				ts := &tfe.TaskStage{}
    99  				ts.TaskResults = []*tfe.TaskResult{
   100  					{ID: "1", TaskName: "Mandatory", Message: "A-OK", Status: "passed", WorkspaceTaskEnforcementLevel: "mandatory"},
   101  					{ID: "2", TaskName: "Advisory", Message: "A-OK", Status: "passed", WorkspaceTaskEnforcementLevel: "advisory"},
   102  				}
   103  				return ts
   104  			},
   105  			writer:          writer,
   106  			context:         integrationContext,
   107  			expectedOutputs: []string{"Overall Result: Passed\n"},
   108  			isError:         false,
   109  		},
   110  		"mandatory-failed": {
   111  			taskStage: func() *tfe.TaskStage {
   112  				ts := &tfe.TaskStage{}
   113  				ts.TaskResults = []*tfe.TaskResult{
   114  					{ID: "1", TaskName: "Mandatory", Message: "500 Error", Status: "failed", WorkspaceTaskEnforcementLevel: "mandatory"},
   115  					{ID: "2", TaskName: "Advisory", Message: "A-OK", Status: "passed", WorkspaceTaskEnforcementLevel: "advisory"},
   116  				}
   117  				return ts
   118  			},
   119  			writer:          writer,
   120  			context:         integrationContext,
   121  			expectedOutputs: []string{"Passed\n", "A-OK\n", "Overall Result: Failed\n"},
   122  			isError:         true,
   123  		},
   124  		"advisory-failed": {
   125  			taskStage: func() *tfe.TaskStage {
   126  				ts := &tfe.TaskStage{}
   127  				ts.TaskResults = []*tfe.TaskResult{
   128  					{ID: "1", TaskName: "Mandatory", Message: "A-OK", Status: "passed", WorkspaceTaskEnforcementLevel: "mandatory"},
   129  					{ID: "2", TaskName: "Advisory", Message: "500 Error", Status: "failed", WorkspaceTaskEnforcementLevel: "advisory"},
   130  				}
   131  				return ts
   132  			},
   133  			writer:          writer,
   134  			context:         integrationContext,
   135  			expectedOutputs: []string{"Failed (Advisory)", "Overall Result: Passed with advisory failure"},
   136  			isError:         false,
   137  		},
   138  		"unreachable": {
   139  			taskStage: func() *tfe.TaskStage {
   140  				ts := &tfe.TaskStage{}
   141  				ts.TaskResults = []*tfe.TaskResult{
   142  					{ID: "1", TaskName: "Mandatory", Message: "", Status: "unreachable", WorkspaceTaskEnforcementLevel: "mandatory"},
   143  					{ID: "2", TaskName: "Advisory", Message: "", Status: "unreachable", WorkspaceTaskEnforcementLevel: "advisory"},
   144  				}
   145  				return ts
   146  			},
   147  			writer:          writer,
   148  			context:         integrationContext,
   149  			expectedOutputs: []string{"Skipping"},
   150  			isError:         false,
   151  		},
   152  	}
   153  
   154  	for _, c := range cases {
   155  		c.writer.output.Reset()
   156  		trs := taskResultSummarizer{
   157  			cloud: b,
   158  		}
   159  		c.context.Poll(0, 0, func(i int) (bool, error) {
   160  			cont, _, _ := trs.Summarize(c.context, c.writer, c.taskStage())
   161  			if cont {
   162  				return true, nil
   163  			}
   164  
   165  			output := c.writer.output.String()
   166  			for _, expected := range c.expectedOutputs {
   167  				if !strings.Contains(output, expected) {
   168  					t.Fatalf("Expected output to contain '%s' but it was:\n\n%s", expected, output)
   169  				}
   170  			}
   171  			return false, nil
   172  		})
   173  	}
   174  }