Skip to content

Commit 2cd4047

Browse files
authored
VCR tests for create, update and delete (#9)
Fixes #5
1 parent 312994e commit 2cd4047

17 files changed

Lines changed: 1338 additions & 0 deletions

client/client.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ func NewWithBaseURL(apiKey, baseURL string) *Client {
3939
}
4040
}
4141

42+
// NewWithHTTPClient creates a new client with a custom HTTP client.
43+
// This is useful for testing with tools like go-vcr.
44+
func NewWithHTTPClient(apiKey, baseURL string, httpClient *http.Client) *Client {
45+
return &Client{
46+
apiKey: apiKey,
47+
baseURL: baseURL,
48+
httpClient: httpClient,
49+
}
50+
}
51+
4252
func (c *Client) makeRequest(method, endpoint string, body any) ([]byte, error) {
4353
var reqBody io.Reader
4454
var contentType string
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package client
2+
3+
import (
4+
"net/http"
5+
"os"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
"gopkg.in/dnaeon/go-vcr.v2/cassette"
11+
"gopkg.in/dnaeon/go-vcr.v2/recorder"
12+
)
13+
14+
// TestListInstancesVCR tests listing all instances
15+
func TestListInstancesVCR(t *testing.T) {
16+
r, err := recorder.New("fixtures/list_instances")
17+
if err != nil {
18+
t.Fatal(err)
19+
}
20+
defer r.Stop()
21+
22+
// Add sanitization filters
23+
r.AddFilter(func(i *cassette.Interaction) error {
24+
delete(i.Request.Headers, "Authorization")
25+
i.Response.Body = sanitizeResponseBody(i.Response.Body)
26+
delete(i.Response.Headers, "Set-Cookie")
27+
return nil
28+
})
29+
30+
apiKey := os.Getenv("CLOUDAMQP_APIKEY")
31+
if apiKey == "" {
32+
apiKey = "vcr-replay-mode"
33+
}
34+
35+
httpClient := &http.Client{Transport: r}
36+
client := NewWithHTTPClient(apiKey, "https://customer.cloudamqp.com/api", httpClient)
37+
38+
instances, err := client.ListInstances()
39+
40+
require.NoError(t, err)
41+
require.NotNil(t, instances)
42+
// Should have at least some instances (or empty list is OK)
43+
t.Logf("✓ Listed %d instances", len(instances))
44+
}
45+
46+
// TestGetInstanceVCR tests getting a specific instance
47+
func TestGetInstanceVCR(t *testing.T) {
48+
r, err := recorder.New("fixtures/get_instance")
49+
if err != nil {
50+
t.Fatal(err)
51+
}
52+
defer r.Stop()
53+
54+
r.AddFilter(func(i *cassette.Interaction) error {
55+
delete(i.Request.Headers, "Authorization")
56+
i.Response.Body = sanitizeResponseBody(i.Response.Body)
57+
delete(i.Response.Headers, "Set-Cookie")
58+
return nil
59+
})
60+
61+
apiKey := os.Getenv("CLOUDAMQP_APIKEY")
62+
if apiKey == "" {
63+
apiKey = "vcr-replay-mode"
64+
}
65+
66+
httpClient := &http.Client{Transport: r}
67+
client := NewWithHTTPClient(apiKey, "https://customer.cloudamqp.com/api", httpClient)
68+
69+
// Use an existing instance ID (should match cassette)
70+
instanceID := 359563
71+
72+
instance, err := client.GetInstance(instanceID)
73+
74+
require.NoError(t, err)
75+
require.NotNil(t, instance)
76+
assert.Equal(t, instanceID, instance.ID)
77+
t.Logf("✓ Got instance %d: %s (plan: %s)", instance.ID, instance.Name, instance.Plan)
78+
}
79+
80+
// TestListRegionsVCR tests listing all regions
81+
func TestListRegionsVCR(t *testing.T) {
82+
r, err := recorder.New("fixtures/list_regions")
83+
if err != nil {
84+
t.Fatal(err)
85+
}
86+
defer r.Stop()
87+
88+
r.AddFilter(func(i *cassette.Interaction) error {
89+
delete(i.Request.Headers, "Authorization")
90+
i.Response.Body = sanitizeResponseBody(i.Response.Body)
91+
delete(i.Response.Headers, "Set-Cookie")
92+
return nil
93+
})
94+
95+
apiKey := os.Getenv("CLOUDAMQP_APIKEY")
96+
if apiKey == "" {
97+
apiKey = "vcr-replay-mode"
98+
}
99+
100+
httpClient := &http.Client{Transport: r}
101+
client := NewWithHTTPClient(apiKey, "https://customer.cloudamqp.com/api", httpClient)
102+
103+
regions, err := client.ListRegions("")
104+
105+
require.NoError(t, err)
106+
require.NotEmpty(t, regions)
107+
t.Logf("✓ Listed %d regions", len(regions))
108+
109+
// Verify some expected fields
110+
if len(regions) > 0 {
111+
assert.NotEmpty(t, regions[0].Name)
112+
assert.NotEmpty(t, regions[0].Provider)
113+
t.Logf(" Example: %s (%s)", regions[0].Name, regions[0].Provider)
114+
}
115+
}
116+
117+
// TestListPlansVCR tests listing all plans
118+
func TestListPlansVCR(t *testing.T) {
119+
r, err := recorder.New("fixtures/list_plans")
120+
if err != nil {
121+
t.Fatal(err)
122+
}
123+
defer r.Stop()
124+
125+
r.AddFilter(func(i *cassette.Interaction) error {
126+
delete(i.Request.Headers, "Authorization")
127+
i.Response.Body = sanitizeResponseBody(i.Response.Body)
128+
delete(i.Response.Headers, "Set-Cookie")
129+
return nil
130+
})
131+
132+
apiKey := os.Getenv("CLOUDAMQP_APIKEY")
133+
if apiKey == "" {
134+
apiKey = "vcr-replay-mode"
135+
}
136+
137+
httpClient := &http.Client{Transport: r}
138+
client := NewWithHTTPClient(apiKey, "https://customer.cloudamqp.com/api", httpClient)
139+
140+
plans, err := client.ListPlans("")
141+
142+
require.NoError(t, err)
143+
require.NotEmpty(t, plans)
144+
t.Logf("✓ Listed %d plans", len(plans))
145+
146+
// Verify some expected fields and find bunny-1
147+
bunnyFound := false
148+
for _, plan := range plans {
149+
assert.NotEmpty(t, plan.Name)
150+
if plan.Name == "bunny-1" {
151+
bunnyFound = true
152+
t.Logf(" Found bunny-1 plan")
153+
}
154+
}
155+
assert.True(t, bunnyFound, "Should find bunny-1 plan")
156+
}

client/fixtures/bunny1_create.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
version: 1
3+
interactions:
4+
- request:
5+
body: name=bunny1-test&plan=bunny-1&region=amazon-web-services%3A%3Aus-east-1&tags%5B%5D=test&tags%5B%5D=bunny1
6+
form:
7+
name:
8+
- bunny1-test
9+
plan:
10+
- bunny-1
11+
region:
12+
- amazon-web-services::us-east-1
13+
tags[]:
14+
- test
15+
- bunny1
16+
headers:
17+
Content-Type:
18+
- application/x-www-form-urlencoded
19+
url: https://customer.cloudamqp.com/api/instances
20+
method: POST
21+
response:
22+
body: '{"apikey":"REDACTED","id":359564,"message":"Your dedicated instance will be available within a couple of minutes","url":"amqps://REDACTED:REDACTED@test-fast-plum-quail.rmq7.cloudamqp.com/udhldnlq"}'
23+
headers:
24+
Cache-Control:
25+
- no-cache
26+
Content-Length:
27+
- "249"
28+
Content-Type:
29+
- application/json
30+
Date:
31+
- Tue, 25 Nov 2025 20:13:15 GMT
32+
Nel:
33+
- '{"report_to":"heroku-nel","response_headers":["Via"],"max_age":3600,"success_fraction":0.01,"failure_fraction":0.1}'
34+
- '{"report_to":"default","max_age":31536000,"include_subdomains":true}'
35+
Referrer-Policy:
36+
- strict-origin-when-cross-origin
37+
Report-To:
38+
- '{"group":"heroku-nel","endpoints":[{"url":"https://nel.heroku.com/reports?s=CtumpQyVXDpMYq2G1aI%2BN87AkNEWkQQRtNtC%2BZ%2BPmhA%3D\u0026sid=af571f24-03ee-46d1-9f90-ab9030c2c74c\u0026ts=1764101595"}],"max_age":3600}'
39+
- '{"group":"default","max_age":31536000,"endpoints":[{"url":"https://84codes.report-uri.com/a/t/g"}],"include_subdomains":true}'
40+
Reporting-Endpoints:
41+
- heroku-nel="https://nel.heroku.com/reports?s=CtumpQyVXDpMYq2G1aI%2BN87AkNEWkQQRtNtC%2BZ%2BPmhA%3D&sid=af571f24-03ee-46d1-9f90-ab9030c2c74c&ts=1764101595"
42+
Server:
43+
- Heroku
44+
Strict-Transport-Security:
45+
- max-age=31536000; includeSubDomains
46+
Via:
47+
- 2.0 heroku-router
48+
X-Content-Type-Options:
49+
- nosniff
50+
X-Request-Id:
51+
- 5118a2cd-9728-5577-61d6-6659e4cc769c
52+
status: 200 OK
53+
code: 200
54+
duration: 972.057374ms

client/fixtures/bunny1_delete.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
version: 1
3+
interactions:
4+
- request:
5+
body: ""
6+
form: {}
7+
headers: {}
8+
url: https://customer.cloudamqp.com/api/instances/359559
9+
method: DELETE
10+
response:
11+
body: ""
12+
headers:
13+
Cache-Control:
14+
- no-cache
15+
Date:
16+
- Tue, 25 Nov 2025 20:21:27 GMT
17+
Nel:
18+
- '{"report_to":"heroku-nel","response_headers":["Via"],"max_age":3600,"success_fraction":0.01,"failure_fraction":0.1}'
19+
- '{"report_to":"default","max_age":31536000,"include_subdomains":true}'
20+
Referrer-Policy:
21+
- strict-origin-when-cross-origin
22+
Report-To:
23+
- '{"group":"heroku-nel","endpoints":[{"url":"https://nel.heroku.com/reports?s=6CMwcmhwCQkJWao6kfw%2FQ%2B1uNyspXxu6IlEsbprvy8E%3D\u0026sid=af571f24-03ee-46d1-9f90-ab9030c2c74c\u0026ts=1764102087"}],"max_age":3600}'
24+
- '{"group":"default","max_age":31536000,"endpoints":[{"url":"https://84codes.report-uri.com/a/t/g"}],"include_subdomains":true}'
25+
Reporting-Endpoints:
26+
- heroku-nel="https://nel.heroku.com/reports?s=6CMwcmhwCQkJWao6kfw%2FQ%2B1uNyspXxu6IlEsbprvy8E%3D&sid=af571f24-03ee-46d1-9f90-ab9030c2c74c&ts=1764102087"
27+
Server:
28+
- Heroku
29+
Strict-Transport-Security:
30+
- max-age=31536000; includeSubDomains
31+
Via:
32+
- 2.0 heroku-router
33+
X-Content-Type-Options:
34+
- nosniff
35+
X-Request-Id:
36+
- 64b2640d-5c01-4d69-7e44-74f92825df43
37+
status: 204 No Content
38+
code: 204
39+
duration: 710.235286ms

0 commit comments

Comments
 (0)