From cb26cbba22737f7145559f5c36664818a9e4c9b6 Mon Sep 17 00:00:00 2001 From: Nick Fagerlund Date: Mon, 25 Oct 2021 19:32:46 -0700 Subject: [PATCH] Add tests for cloud.StateMgr() when TFC lacks local tf version Alas, there's not a very good way to test the message we're supposed to print to the console in this situation; we just don't appear to have a mock terminal that the test can read from. But we can at least test that the function returns without erroring under the exact conditions where it was erroring before. Note that the behaviors of mc.Workspaces.Update and UpdateByID were already starting to drift, so I consolidated their actual attribute update logic into a helper function before they drifted much further. --- internal/cloud/backend_test.go | 52 +++++++++++++++++++++++++++++++ internal/cloud/tfe_client_mock.go | 44 ++++++++++++++++---------- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/internal/cloud/backend_test.go b/internal/cloud/backend_test.go index d3b5d7c5f2..0b0bb316a2 100644 --- a/internal/cloud/backend_test.go +++ b/internal/cloud/backend_test.go @@ -305,6 +305,58 @@ func TestCloud_configVerifyMinimumTFEVersion(t *testing.T) { } } +func TestCloud_setUnavailableTerraformVersion(t *testing.T) { + // go-tfe returns an error IRL if you try to set a Terraform version that's + // not available in your TFC instance. To test this, tfe_client_mock errors if + // you try to set any Terraform version for this specific workspace name. + workspaceName := "unavailable-terraform-version" + + config := cty.ObjectVal(map[string]cty.Value{ + "hostname": cty.NullVal(cty.String), + "organization": cty.StringVal("hashicorp"), + "token": cty.NullVal(cty.String), + "workspaces": cty.ObjectVal(map[string]cty.Value{ + "name": cty.NullVal(cty.String), + "tags": cty.SetVal( + []cty.Value{ + cty.StringVal("sometag"), + }, + ), + }), + }) + + b, bCleanup := testBackend(t, config) + defer bCleanup() + + // Make sure the workspace doesn't exist yet -- otherwise, we can't test what + // happens when a workspace gets created. This is why we can't use "name" in + // the backend config above, btw: if you do, testBackend() creates the default + // workspace before we get a chance to do anything. + _, err := b.client.Workspaces.Read(context.Background(), b.organization, workspaceName) + if err != tfe.ErrResourceNotFound { + t.Fatalf("the workspace we were about to try and create (%s/%s) already exists in the mocks somehow, so this test isn't trustworthy anymore", b.organization, workspaceName) + } + + _, err = b.StateMgr(workspaceName) + if err != nil { + t.Fatalf("expected no error from StateMgr, despite not being able to set remote Terraform version: %#v", err) + } + // Make sure the workspace was created: + workspace, err := b.client.Workspaces.Read(context.Background(), b.organization, workspaceName) + if err != nil { + t.Fatalf("b.StateMgr() didn't actually create the desired workspace") + } + // Make sure our mocks still error as expected, using the same update function b.StateMgr() would call: + _, err = b.client.Workspaces.UpdateByID( + context.Background(), + workspace.ID, + tfe.WorkspaceUpdateOptions{TerraformVersion: tfe.String("1.1.0")}, + ) + if err == nil { + t.Fatalf("the mocks aren't emulating a nonexistent remote Terraform version correctly, so this test isn't trustworthy anymore") + } +} + func TestCloud_setConfigurationFields(t *testing.T) { originalForceBackendEnv := os.Getenv("TF_FORCE_LOCAL_BACKEND") diff --git a/internal/cloud/tfe_client_mock.go b/internal/cloud/tfe_client_mock.go index 518602c3c8..8ba6883c3a 100644 --- a/internal/cloud/tfe_client_mock.go +++ b/internal/cloud/tfe_client_mock.go @@ -1159,6 +1159,10 @@ func (m *MockWorkspaces) List(ctx context.Context, organization string, options } func (m *MockWorkspaces) Create(ctx context.Context, organization string, options tfe.WorkspaceCreateOptions) (*tfe.Workspace, error) { + // for TestCloud_setUnavailableTerraformVersion + if *options.Name == "unavailable-terraform-version" && options.TerraformVersion != nil { + return nil, fmt.Errorf("requested Terraform version not available in this TFC instance") + } if strings.HasSuffix(*options.Name, "no-operations") { options.Operations = tfe.Bool(false) } else if options.Operations == nil { @@ -1234,17 +1238,9 @@ func (m *MockWorkspaces) Update(ctx context.Context, organization, workspace str return nil, tfe.ErrResourceNotFound } - if options.Operations != nil { - w.Operations = *options.Operations - } - if options.Name != nil { - w.Name = *options.Name - } - if options.TerraformVersion != nil { - w.TerraformVersion = *options.TerraformVersion - } - if options.WorkingDirectory != nil { - w.WorkingDirectory = *options.WorkingDirectory + err := updateMockWorkspaceAttributes(w, options) + if err != nil { + return nil, err } delete(m.workspaceNames, workspace) @@ -1259,6 +1255,26 @@ func (m *MockWorkspaces) UpdateByID(ctx context.Context, workspaceID string, opt return nil, tfe.ErrResourceNotFound } + err := updateMockWorkspaceAttributes(w, options) + if err != nil { + return nil, err + } + + delete(m.workspaceNames, w.Name) + m.workspaceNames[w.Name] = w + + return w, nil +} + +func updateMockWorkspaceAttributes(w *tfe.Workspace, options tfe.WorkspaceUpdateOptions) error { + // for TestCloud_setUnavailableTerraformVersion + if w.Name == "unavailable-terraform-version" && options.TerraformVersion != nil { + return fmt.Errorf("requested Terraform version not available in this TFC instance") + } + + if options.Operations != nil { + w.Operations = *options.Operations + } if options.Name != nil { w.Name = *options.Name } @@ -1268,11 +1284,7 @@ func (m *MockWorkspaces) UpdateByID(ctx context.Context, workspaceID string, opt if options.WorkingDirectory != nil { w.WorkingDirectory = *options.WorkingDirectory } - - delete(m.workspaceNames, w.Name) - m.workspaceNames[w.Name] = w - - return w, nil + return nil } func (m *MockWorkspaces) Delete(ctx context.Context, organization, workspace string) error {