From 1791b711962a60e31233a979ca34dcef8843a182 Mon Sep 17 00:00:00 2001 From: Chris Arcand Date: Wed, 15 Sep 2021 00:04:15 -0500 Subject: [PATCH] cloud: TestCloud_backendWithTags Implementing this test was quite a rabbithole, as in order to satisfy backendTestBackendStates() the workspaces returned from backend.Workspaces() must match exactly, and the shortcut taken to test pagination in 3cc58813f088fac3b9d008372052694ae1f75914 created an impossible circumstance that got plastered over with the fact that prefix filtering is done clientside, not by the API as it should be. Tagging does not rely on clientside filtering, and expects that the request made to the TFC API returns exactly those workspaces with the given tags. These changes include a better way to test pagination, wherein we actually create over a page worth of valid workspaces in the mock client and implement a simplified pagination behavior to match how the TFC API actually works. --- internal/backend/remote/backend_apply_test.go | 8 +- internal/cloud/backend_apply_test.go | 8 +- internal/cloud/backend_test.go | 24 ++++++ internal/cloud/testing.go | 18 +++++ internal/cloud/tfe_client_mock.go | 73 ++++++++++++------- 5 files changed, 91 insertions(+), 40 deletions(-) diff --git a/internal/backend/remote/backend_apply_test.go b/internal/backend/remote/backend_apply_test.go index 9b4286010..1f0d319bf 100644 --- a/internal/backend/remote/backend_apply_test.go +++ b/internal/backend/remote/backend_apply_test.go @@ -777,9 +777,7 @@ func TestRemote_applyApprovedExternally(t *testing.T) { wl, err := b.client.Workspaces.List( ctx, b.organization, - tfe.WorkspaceListOptions{ - ListOptions: tfe.ListOptions{PageNumber: 2, PageSize: 10}, - }, + tfe.WorkspaceListOptions{}, ) if err != nil { t.Fatalf("unexpected error listing workspaces: %v", err) @@ -853,9 +851,7 @@ func TestRemote_applyDiscardedExternally(t *testing.T) { wl, err := b.client.Workspaces.List( ctx, b.organization, - tfe.WorkspaceListOptions{ - ListOptions: tfe.ListOptions{PageNumber: 2, PageSize: 10}, - }, + tfe.WorkspaceListOptions{}, ) if err != nil { t.Fatalf("unexpected error listing workspaces: %v", err) diff --git a/internal/cloud/backend_apply_test.go b/internal/cloud/backend_apply_test.go index 090f767cc..99d74c1cc 100644 --- a/internal/cloud/backend_apply_test.go +++ b/internal/cloud/backend_apply_test.go @@ -767,9 +767,7 @@ func TestCloud_applyApprovedExternally(t *testing.T) { wl, err := b.client.Workspaces.List( ctx, b.organization, - tfe.WorkspaceListOptions{ - ListOptions: tfe.ListOptions{PageNumber: 2, PageSize: 10}, - }, + tfe.WorkspaceListOptions{}, ) if err != nil { t.Fatalf("unexpected error listing workspaces: %v", err) @@ -843,9 +841,7 @@ func TestCloud_applyDiscardedExternally(t *testing.T) { wl, err := b.client.Workspaces.List( ctx, b.organization, - tfe.WorkspaceListOptions{ - ListOptions: tfe.ListOptions{PageNumber: 2, PageSize: 10}, - }, + tfe.WorkspaceListOptions{}, ) if err != nil { t.Fatalf("unexpected error listing workspaces: %v", err) diff --git a/internal/cloud/backend_test.go b/internal/cloud/backend_test.go index 5580cc925..928182b53 100644 --- a/internal/cloud/backend_test.go +++ b/internal/cloud/backend_test.go @@ -40,6 +40,30 @@ func TestCloud_backendWithPrefix(t *testing.T) { backend.TestBackendStates(t, b) } +func TestCloud_backendWithTags(t *testing.T) { + b, bCleanup := testBackendWithTags(t) + defer bCleanup() + + backend.TestBackendStates(t, b) + + // Test pagination works + for i := 0; i < 25; i++ { + _, err := b.StateMgr(fmt.Sprintf("foo-%d", i+1)) + if err != nil { + t.Fatalf("error: %s", err) + } + } + + workspaces, err := b.Workspaces() + if err != nil { + t.Fatalf("error: %s", err) + } + actual := len(workspaces) + if actual != 26 { + t.Errorf("expected 26 workspaces (over one standard paginated response), got %d", actual) + } +} + func TestCloud_PrepareConfig(t *testing.T) { cases := map[string]struct { config cty.Value diff --git a/internal/cloud/testing.go b/internal/cloud/testing.go index c5bee1c02..d289a42c2 100644 --- a/internal/cloud/testing.go +++ b/internal/cloud/testing.go @@ -92,6 +92,24 @@ func testBackendWithPrefix(t *testing.T) (*Cloud, func()) { return testBackend(t, obj) } +func testBackendWithTags(t *testing.T) (*Cloud, func()) { + obj := 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), + "prefix": cty.NullVal(cty.String), + "tags": cty.SetVal( + []cty.Value{ + cty.StringVal("billing"), + }, + ), + }), + }) + return testBackend(t, obj) +} + func testBackendNoOperations(t *testing.T) (*Cloud, func()) { obj := cty.ObjectVal(map[string]cty.Value{ "hostname": cty.NullVal(cty.String), diff --git a/internal/cloud/tfe_client_mock.go b/internal/cloud/tfe_client_mock.go index 2dea3096d..fd93c48d9 100644 --- a/internal/cloud/tfe_client_mock.go +++ b/internal/cloud/tfe_client_mock.go @@ -1091,20 +1091,36 @@ func newMockWorkspaces(client *MockClient) *MockWorkspaces { } func (m *MockWorkspaces) List(ctx context.Context, organization string, options tfe.WorkspaceListOptions) (*tfe.WorkspaceList, error) { - dummyWorkspaces := 10 wl := &tfe.WorkspaceList{} - // Get the prefix from the search options. - prefix := "" + // Get all the workspaces that match the Search value + searchValue := "" if options.Search != nil { - prefix = *options.Search + searchValue = *options.Search } - // Get all the workspaces that match the prefix. var ws []*tfe.Workspace + var tags []string + + if options.Tags != nil { + tags = strings.Split(*options.Tags, ",") + } for _, w := range m.workspaceIDs { - if strings.Contains(w.Name, prefix) { - ws = append(ws, w) + wTags := make(map[string]struct{}) + for _, wTag := range w.Tags { + wTags[wTag.Name] = struct{}{} + } + + if strings.Contains(w.Name, searchValue) { + tagsSatisfied := true + for _, tag := range tags { + if _, ok := wTags[tag]; !ok { + tagsSatisfied = false + } + } + if tagsSatisfied { + ws = append(ws, w) + } } } @@ -1116,32 +1132,27 @@ func (m *MockWorkspaces) List(ctx context.Context, organization string, options return wl, nil } - // Return dummy workspaces for the first page to test pagination. - if options.PageNumber <= 1 { - for i := 0; i < dummyWorkspaces; i++ { - wl.Items = append(wl.Items, &tfe.Workspace{ - ID: GenerateID("ws-"), - Name: fmt.Sprintf("dummy-workspace-%d", i), - }) - } + numPages := (len(ws) / 20) + 1 + currentPage := 1 + if options.PageNumber != 0 { + currentPage = options.PageNumber + } + previousPage := currentPage - 1 + nextPage := currentPage + 1 - wl.Pagination = &tfe.Pagination{ - CurrentPage: 1, - NextPage: 2, - TotalPages: 2, - TotalCount: len(wl.Items) + len(ws), + for i := ((currentPage - 1) * 20); i < ((currentPage-1)*20)+20; i++ { + if i > (len(ws) - 1) { + break } - - return wl, nil + wl.Items = append(wl.Items, ws[i]) } - // Return the actual workspaces that matched as the second page. - wl.Items = ws wl.Pagination = &tfe.Pagination{ - CurrentPage: 2, - PreviousPage: 1, - TotalPages: 2, - TotalCount: len(wl.Items) + dummyWorkspaces, + CurrentPage: currentPage, + NextPage: nextPage, + PreviousPage: previousPage, + TotalPages: numPages, + TotalCount: len(wl.Items), } return wl, nil @@ -1173,6 +1184,12 @@ func (m *MockWorkspaces) Create(ctx context.Context, organization string, option } else { w.TerraformVersion = tfversion.String() } + var tags []*tfe.Tag + for _, tag := range options.Tags { + tags = append(tags, tag) + w.TagNames = append(w.TagNames, tag.Name) + } + w.Tags = tags m.workspaceIDs[w.ID] = w m.workspaceNames[w.Name] = w return w, nil