Skip to content

Commit 4aa70a9

Browse files
authored
feat: Introduce acceptance test sweepers for multiple resources and a sweeper make target. (#167)
1 parent 83d458c commit 4aa70a9

14 files changed

+464
-44
lines changed

GNUmakefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ test:
5050
testacc:
5151
TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m
5252

53+
sweeper:
54+
@echo "WARNING: This will destroy infrastructure. Use only in development accounts."
55+
go test ./internal/provider -v -tags=sweep -sweep=all -sweep-allow-failures -timeout 120m
56+
5357
codegen:
5458
curl $(SWAGGER_URL) -o schema/swagger.json
5559
node tools/clean-swagger.js schema/swagger.json
@@ -127,6 +131,7 @@ help:
127131
@echo " make docs - Generate documentation"
128132
@echo " make test - Run unit tests"
129133
@echo " make testacc - Run acceptance tests"
134+
@echo " make sweeper - Delete resources created by acceptance tests. They have names starting with tf-"
130135
@echo " make install - Install provider locally"
131136
@echo " make release - Create local snapshot build"
132137
@echo ""

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,14 @@ require (
5454
github.com/hashicorp/logutils v1.0.0 // indirect
5555
github.com/hashicorp/terraform-exec v0.23.1 // indirect
5656
github.com/hashicorp/terraform-json v0.27.0 // indirect
57-
github.com/hashicorp/terraform-plugin-framework v1.16.1 // indirect
57+
github.com/hashicorp/terraform-plugin-framework v1.16.1
5858
github.com/hashicorp/terraform-plugin-testing v1.13.3
5959
github.com/hashicorp/terraform-registry-address v0.4.0 // indirect
6060
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
6161
github.com/hashicorp/yamux v0.1.2 // indirect
6262
github.com/huandu/xstrings v1.5.0 // indirect
6363
github.com/invopop/yaml v0.3.1 // indirect
64+
github.com/jianyuan/go-utils v0.0.0-20250223213401-62c93a9e0b6c
6465
github.com/josharian/intern v1.0.0 // indirect
6566
github.com/mailru/easyjson v0.7.7 // indirect
6667
github.com/mattn/go-colorable v0.1.14 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
167167
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
168168
github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94=
169169
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
170+
github.com/jianyuan/go-utils v0.0.0-20250223213401-62c93a9e0b6c h1:VG+sd6t1wnle1Rc9Z52CmsYuwhBkXcW+aPIHqcw9e0I=
171+
github.com/jianyuan/go-utils v0.0.0-20250223213401-62c93a9e0b6c/go.mod h1:D1O+WiG+p0g3LExvWtPEn6+UDTKJoUxTfJxyM6Ya6+k=
170172
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
171173
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
172174
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=

internal/acctest/shared_client.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package acctest
2+
3+
import (
4+
"log"
5+
"os"
6+
7+
"github.com/rootlyhq/terraform-provider-rootly/v2/internal/apiclient"
8+
"github.com/rootlyhq/terraform-provider-rootly/v2/meta"
9+
rootly "github.com/rootlyhq/terraform-provider-rootly/v2/schema"
10+
)
11+
12+
var (
13+
TestApiHost string
14+
TestApiToken string
15+
16+
SharedClient *rootly.ClientWithResponses
17+
)
18+
19+
func init() {
20+
if v := os.Getenv("ROOTLY_API_URL"); v != "" {
21+
TestApiHost = v
22+
} else {
23+
TestApiHost = "https://api.rootly.com"
24+
}
25+
26+
if v := os.Getenv("ROOTLY_API_TOKEN"); v != "" {
27+
TestApiToken = v
28+
}
29+
30+
_, client, err := apiclient.New(TestApiHost, TestApiToken, meta.GetVersion())
31+
if err != nil {
32+
log.Fatalln(err.Error())
33+
}
34+
35+
SharedClient = client
36+
}

internal/apiclient/apiclient.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package apiclient
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/rootlyhq/terraform-provider-rootly/v2/client"
7+
sdkv2_provider "github.com/rootlyhq/terraform-provider-rootly/v2/provider"
8+
rootly "github.com/rootlyhq/terraform-provider-rootly/v2/schema"
9+
)
10+
11+
func New(apiHost string, apiToken string, version string) (*client.Client, *rootly.ClientWithResponses, error) {
12+
legacyClient, err := client.NewClient(apiHost, apiToken, sdkv2_provider.RootlyUserAgent(version))
13+
if err != nil {
14+
return nil, nil, fmt.Errorf("unable to create Rootly client: %v", err)
15+
}
16+
17+
client, err := rootly.NewClientWithResponses(
18+
apiHost,
19+
// Piggyback on the legacy client's HTTP client. Inherits the same headers, authentication, and retry logic.
20+
rootly.WithHTTPClient(legacyClient),
21+
)
22+
if err != nil {
23+
return nil, nil, fmt.Errorf("unable to create Rootly client: %v", err)
24+
}
25+
26+
return legacyClient, client, nil
27+
}

internal/provider/provider.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"github.com/hashicorp/terraform-plugin-framework/resource"
1111
"github.com/hashicorp/terraform-plugin-framework/types"
1212
"github.com/rootlyhq/terraform-provider-rootly/v2/client"
13-
sdkv2_provider "github.com/rootlyhq/terraform-provider-rootly/v2/provider"
13+
"github.com/rootlyhq/terraform-provider-rootly/v2/internal/apiclient"
1414
rootly "github.com/rootlyhq/terraform-provider-rootly/v2/schema"
1515
)
1616

@@ -75,19 +75,9 @@ func (p *RootlyProvider) Configure(ctx context.Context, req provider.ConfigureRe
7575
apiToken = v
7676
}
7777

78-
legacyClient, err := client.NewClient(apiHost, apiToken, sdkv2_provider.RootlyUserAgent(p.version))
78+
legacyClient, client, err := apiclient.New(apiHost, apiToken, p.version)
7979
if err != nil {
80-
resp.Diagnostics.AddError("Unable to create Rootly client", "Unable to authenticate user for authenticated Rootly client")
81-
return
82-
}
83-
84-
client, err := rootly.NewClientWithResponses(
85-
apiHost,
86-
// Piggyback on the legacy client's HTTP client. Inherits the same headers, authentication, and retry logic.
87-
rootly.WithHTTPClient(legacyClient),
88-
)
89-
if err != nil {
90-
resp.Diagnostics.AddError("Unable to create Rootly client", "Unable to authenticate user for authenticated Rootly client")
80+
resp.Diagnostics.AddError("Unable to create Rootly client", err.Error())
9181
return
9282
}
9383

internal/provider/provider_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@ import (
88
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
99
"github.com/hashicorp/terraform-plugin-mux/tf5to6server"
1010
"github.com/hashicorp/terraform-plugin-mux/tf6muxserver"
11+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
1112
"github.com/rootlyhq/terraform-provider-rootly/v2/meta"
1213
sdkv2_provider "github.com/rootlyhq/terraform-provider-rootly/v2/provider"
1314
)
1415

16+
func TestMain(m *testing.M) {
17+
resource.TestMain(m)
18+
}
19+
1520
var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
1621
"rootly": func() (tfprotov6.ProviderServer, error) {
1722
ctx := context.Background()
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"net/http"
8+
"strings"
9+
10+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
11+
"github.com/jianyuan/go-utils/ptr"
12+
"github.com/rootlyhq/terraform-provider-rootly/v2/internal/acctest"
13+
rootly "github.com/rootlyhq/terraform-provider-rootly/v2/schema"
14+
)
15+
16+
func init() {
17+
resource.AddTestSweepers("rootly_alert_route", &resource.Sweeper{
18+
Name: "rootly_alert_route",
19+
F: func(region string) error {
20+
ctx := context.Background()
21+
22+
params := &rootly.ListAlertRoutesParams{
23+
PageNumber: ptr.Ptr(1),
24+
}
25+
26+
for {
27+
httpResp, err := acctest.SharedClient.ListAlertRoutesWithResponse(ctx, params)
28+
if err != nil {
29+
return fmt.Errorf("Error getting alert routes, got error: %s", err)
30+
} else if httpResp.StatusCode() != http.StatusOK {
31+
return fmt.Errorf("Error getting alert routes, got status code: %d", httpResp.StatusCode())
32+
} else if httpResp.ApplicationvndApiJSON200 == nil {
33+
return fmt.Errorf("Error getting alert routes, got empty response")
34+
}
35+
36+
for _, alertRoute := range httpResp.ApplicationvndApiJSON200.Data {
37+
if strings.HasPrefix(alertRoute.Attributes.Name, "tf-") {
38+
httpResp, err := acctest.SharedClient.DeleteAlertRouteWithResponse(ctx, alertRoute.Id)
39+
if err != nil {
40+
return fmt.Errorf("Error deleting alert route: %s", err)
41+
} else if httpResp.StatusCode() != http.StatusOK {
42+
return fmt.Errorf("Error deleting alert route, got status code: %d", httpResp.StatusCode())
43+
}
44+
45+
log.Printf("[INFO] Deleted alert route %s", alertRoute.Attributes.Name)
46+
}
47+
}
48+
49+
if httpResp.ApplicationvndApiJSON200.Links.Next == nil {
50+
break
51+
}
52+
53+
params.PageNumber = ptr.Ptr(ptr.Value(params.PageNumber) + 1)
54+
}
55+
56+
return nil
57+
},
58+
})
59+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"net/http"
8+
"strings"
9+
"testing"
10+
11+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
12+
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
13+
"github.com/hashicorp/terraform-plugin-testing/statecheck"
14+
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
15+
"github.com/jianyuan/go-utils/ptr"
16+
"github.com/rootlyhq/terraform-provider-rootly/v2/internal/acctest"
17+
rootly "github.com/rootlyhq/terraform-provider-rootly/v2/schema"
18+
)
19+
20+
func init() {
21+
resource.AddTestSweepers("rootly_alert_urgency", &resource.Sweeper{
22+
Name: "rootly_alert_urgency",
23+
F: func(region string) error {
24+
ctx := context.Background()
25+
26+
params := &rootly.ListAlertUrgenciesParams{
27+
PageNumber: ptr.Ptr(1),
28+
}
29+
30+
for {
31+
httpResp, err := acctest.SharedClient.ListAlertUrgenciesWithResponse(ctx, params)
32+
if err != nil {
33+
return fmt.Errorf("Error getting alert urgencies, got error: %s", err)
34+
} else if httpResp.StatusCode() != http.StatusOK {
35+
return fmt.Errorf("Error getting alert urgencies, got status code: %d", httpResp.StatusCode())
36+
} else if httpResp.ApplicationvndApiJSON200 == nil {
37+
return fmt.Errorf("Error getting alert urgencies, got empty response")
38+
}
39+
40+
for _, alertUrgency := range httpResp.ApplicationvndApiJSON200.Data {
41+
if strings.HasPrefix(alertUrgency.Attributes.Name, "tf-") {
42+
httpResp, err := acctest.SharedClient.DeleteAlertUrgencyWithResponse(ctx, alertUrgency.Id)
43+
if err != nil {
44+
return fmt.Errorf("Error deleting alert urgency: %s", err)
45+
} else if httpResp.StatusCode() != http.StatusOK {
46+
return fmt.Errorf("Error deleting alert urgency, got status code: %d", httpResp.StatusCode())
47+
}
48+
49+
log.Printf("[INFO] Deleted alert urgency %s", alertUrgency.Attributes.Name)
50+
}
51+
}
52+
53+
if httpResp.ApplicationvndApiJSON200.Links.Next == nil {
54+
break
55+
}
56+
57+
params.PageNumber = ptr.Ptr(ptr.Value(params.PageNumber) + 1)
58+
}
59+
60+
return nil
61+
},
62+
})
63+
}
64+
65+
func TestAccResourceAlertUrgency(t *testing.T) {
66+
resName := "rootly_alert_urgency.test"
67+
alertUrgencyName := acctest.RandomWithPrefix("tf-alert-urgency")
68+
69+
configStateChecks := []statecheck.StateCheck{
70+
statecheck.ExpectKnownValue(resName, tfjsonpath.New("id"), knownvalue.NotNull()),
71+
statecheck.ExpectKnownValue(resName, tfjsonpath.New("position"), knownvalue.NotNull()),
72+
}
73+
74+
resource.UnitTest(t, resource.TestCase{
75+
PreCheck: func() { testAccPreCheck(t) },
76+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
77+
Steps: []resource.TestStep{
78+
{
79+
Config: testAccResourceAlertUrgency(alertUrgencyName, "description"),
80+
ConfigStateChecks: append(
81+
configStateChecks,
82+
statecheck.ExpectKnownValue(resName, tfjsonpath.New("name"), knownvalue.StringExact(alertUrgencyName)),
83+
statecheck.ExpectKnownValue(resName, tfjsonpath.New("description"), knownvalue.StringExact("description")),
84+
),
85+
},
86+
{
87+
Config: testAccResourceAlertUrgency(alertUrgencyName+"-updated", "updated description"),
88+
ConfigStateChecks: append(
89+
configStateChecks,
90+
statecheck.ExpectKnownValue(resName, tfjsonpath.New("name"), knownvalue.StringExact(alertUrgencyName+"-updated")),
91+
statecheck.ExpectKnownValue(resName, tfjsonpath.New("description"), knownvalue.StringExact("updated description")),
92+
),
93+
},
94+
},
95+
})
96+
}
97+
98+
func testAccResourceAlertUrgency(alertUrgencyName, alertUrgencyDescription string) string {
99+
return fmt.Sprintf(`
100+
resource "rootly_alert_urgency" "test" {
101+
name = "%s"
102+
description = "%s"
103+
}
104+
`, alertUrgencyName, alertUrgencyDescription)
105+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"net/http"
8+
"strings"
9+
10+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
11+
"github.com/jianyuan/go-utils/ptr"
12+
"github.com/rootlyhq/terraform-provider-rootly/v2/internal/acctest"
13+
rootly "github.com/rootlyhq/terraform-provider-rootly/v2/schema"
14+
)
15+
16+
func init() {
17+
resource.AddTestSweepers("rootly_alerts_source", &resource.Sweeper{
18+
Name: "rootly_alerts_source",
19+
F: func(region string) error {
20+
ctx := context.Background()
21+
22+
params := &rootly.ListAlertsSourcesParams{
23+
PageNumber: ptr.Ptr(1),
24+
}
25+
26+
for {
27+
httpResp, err := acctest.SharedClient.ListAlertsSourcesWithResponse(ctx, params)
28+
if err != nil {
29+
return fmt.Errorf("Error getting alerts sources, got error: %s", err)
30+
} else if httpResp.StatusCode() != http.StatusOK {
31+
return fmt.Errorf("Error getting alerts sources, got status code: %d", httpResp.StatusCode())
32+
} else if httpResp.ApplicationvndApiJSON200 == nil {
33+
return fmt.Errorf("Error getting alerts sources, got empty response")
34+
}
35+
36+
for _, alertSource := range httpResp.ApplicationvndApiJSON200.Data {
37+
if strings.HasPrefix(alertSource.Attributes.Name, "tf-") {
38+
httpResp, err := acctest.SharedClient.DeleteAlertsSourceWithResponse(ctx, alertSource.Id)
39+
if err != nil {
40+
return fmt.Errorf("Error deleting alerts source: %s", err)
41+
} else if httpResp.StatusCode() != http.StatusOK {
42+
return fmt.Errorf("Error deleting alerts source, got status code: %d", httpResp.StatusCode())
43+
}
44+
45+
log.Printf("[INFO] Deleted alerts source %s", alertSource.Attributes.Name)
46+
}
47+
}
48+
49+
if httpResp.ApplicationvndApiJSON200.Links.Next == nil {
50+
break
51+
}
52+
53+
params.PageNumber = ptr.Ptr(ptr.Value(params.PageNumber) + 1)
54+
}
55+
56+
return nil
57+
},
58+
})
59+
}

0 commit comments

Comments
 (0)