Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ func NewWithBaseURL(apiKey, baseURL string) *Client {
}
}

// NewWithHTTPClient creates a new client with a custom HTTP client.
// This is useful for testing with tools like go-vcr.
func NewWithHTTPClient(apiKey, baseURL string, httpClient *http.Client) *Client {
return &Client{
apiKey: apiKey,
baseURL: baseURL,
httpClient: httpClient,
}
}

func (c *Client) makeRequest(method, endpoint string, body any) ([]byte, error) {
var reqBody io.Reader
var contentType string
Expand Down
156 changes: 156 additions & 0 deletions client/common_operations_vcr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package client

import (
"net/http"
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/dnaeon/go-vcr.v2/cassette"
"gopkg.in/dnaeon/go-vcr.v2/recorder"
)

// TestListInstancesVCR tests listing all instances
func TestListInstancesVCR(t *testing.T) {
r, err := recorder.New("fixtures/list_instances")
if err != nil {
t.Fatal(err)
}
defer r.Stop()

// Add sanitization filters
r.AddFilter(func(i *cassette.Interaction) error {
delete(i.Request.Headers, "Authorization")
i.Response.Body = sanitizeResponseBody(i.Response.Body)
delete(i.Response.Headers, "Set-Cookie")
return nil
})

apiKey := os.Getenv("CLOUDAMQP_APIKEY")
if apiKey == "" {
apiKey = "vcr-replay-mode"
}

httpClient := &http.Client{Transport: r}
client := NewWithHTTPClient(apiKey, "https://customer.cloudamqp.com/api", httpClient)

instances, err := client.ListInstances()

require.NoError(t, err)
require.NotNil(t, instances)
// Should have at least some instances (or empty list is OK)
t.Logf("✓ Listed %d instances", len(instances))
}

// TestGetInstanceVCR tests getting a specific instance
func TestGetInstanceVCR(t *testing.T) {
r, err := recorder.New("fixtures/get_instance")
if err != nil {
t.Fatal(err)
}
defer r.Stop()

r.AddFilter(func(i *cassette.Interaction) error {
delete(i.Request.Headers, "Authorization")
i.Response.Body = sanitizeResponseBody(i.Response.Body)
delete(i.Response.Headers, "Set-Cookie")
return nil
})

apiKey := os.Getenv("CLOUDAMQP_APIKEY")
if apiKey == "" {
apiKey = "vcr-replay-mode"
}

httpClient := &http.Client{Transport: r}
client := NewWithHTTPClient(apiKey, "https://customer.cloudamqp.com/api", httpClient)

// Use an existing instance ID (should match cassette)
instanceID := 359563

instance, err := client.GetInstance(instanceID)

require.NoError(t, err)
require.NotNil(t, instance)
assert.Equal(t, instanceID, instance.ID)
t.Logf("✓ Got instance %d: %s (plan: %s)", instance.ID, instance.Name, instance.Plan)
}

// TestListRegionsVCR tests listing all regions
func TestListRegionsVCR(t *testing.T) {
r, err := recorder.New("fixtures/list_regions")
if err != nil {
t.Fatal(err)
}
defer r.Stop()

r.AddFilter(func(i *cassette.Interaction) error {
delete(i.Request.Headers, "Authorization")
i.Response.Body = sanitizeResponseBody(i.Response.Body)
delete(i.Response.Headers, "Set-Cookie")
return nil
})

apiKey := os.Getenv("CLOUDAMQP_APIKEY")
if apiKey == "" {
apiKey = "vcr-replay-mode"
}

httpClient := &http.Client{Transport: r}
client := NewWithHTTPClient(apiKey, "https://customer.cloudamqp.com/api", httpClient)

regions, err := client.ListRegions("")

require.NoError(t, err)
require.NotEmpty(t, regions)
t.Logf("✓ Listed %d regions", len(regions))

// Verify some expected fields
if len(regions) > 0 {
assert.NotEmpty(t, regions[0].Name)
assert.NotEmpty(t, regions[0].Provider)
t.Logf(" Example: %s (%s)", regions[0].Name, regions[0].Provider)
}
}

// TestListPlansVCR tests listing all plans
func TestListPlansVCR(t *testing.T) {
r, err := recorder.New("fixtures/list_plans")
if err != nil {
t.Fatal(err)
}
defer r.Stop()

r.AddFilter(func(i *cassette.Interaction) error {
delete(i.Request.Headers, "Authorization")
i.Response.Body = sanitizeResponseBody(i.Response.Body)
delete(i.Response.Headers, "Set-Cookie")
return nil
})

apiKey := os.Getenv("CLOUDAMQP_APIKEY")
if apiKey == "" {
apiKey = "vcr-replay-mode"
}

httpClient := &http.Client{Transport: r}
client := NewWithHTTPClient(apiKey, "https://customer.cloudamqp.com/api", httpClient)

plans, err := client.ListPlans("")

require.NoError(t, err)
require.NotEmpty(t, plans)
t.Logf("✓ Listed %d plans", len(plans))

// Verify some expected fields and find bunny-1
bunnyFound := false
for _, plan := range plans {
assert.NotEmpty(t, plan.Name)
if plan.Name == "bunny-1" {
bunnyFound = true
t.Logf(" Found bunny-1 plan")
}
}
assert.True(t, bunnyFound, "Should find bunny-1 plan")
}
54 changes: 54 additions & 0 deletions client/fixtures/bunny1_create.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
version: 1
interactions:
- request:
body: name=bunny1-test&plan=bunny-1&region=amazon-web-services%3A%3Aus-east-1&tags%5B%5D=test&tags%5B%5D=bunny1
form:
name:
- bunny1-test
plan:
- bunny-1
region:
- amazon-web-services::us-east-1
tags[]:
- test
- bunny1
headers:
Content-Type:
- application/x-www-form-urlencoded
url: https://customer.cloudamqp.com/api/instances
method: POST
response:
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"}'
headers:
Cache-Control:
- no-cache
Content-Length:
- "249"
Content-Type:
- application/json
Date:
- Tue, 25 Nov 2025 20:13:15 GMT
Nel:
- '{"report_to":"heroku-nel","response_headers":["Via"],"max_age":3600,"success_fraction":0.01,"failure_fraction":0.1}'
- '{"report_to":"default","max_age":31536000,"include_subdomains":true}'
Referrer-Policy:
- strict-origin-when-cross-origin
Report-To:
- '{"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}'
- '{"group":"default","max_age":31536000,"endpoints":[{"url":"https://84codes.report-uri.com/a/t/g"}],"include_subdomains":true}'
Reporting-Endpoints:
- heroku-nel="https://nel.heroku.com/reports?s=CtumpQyVXDpMYq2G1aI%2BN87AkNEWkQQRtNtC%2BZ%2BPmhA%3D&sid=af571f24-03ee-46d1-9f90-ab9030c2c74c&ts=1764101595"
Server:
- Heroku
Strict-Transport-Security:
- max-age=31536000; includeSubDomains
Via:
- 2.0 heroku-router
X-Content-Type-Options:
- nosniff
X-Request-Id:
- 5118a2cd-9728-5577-61d6-6659e4cc769c
status: 200 OK
code: 200
duration: 972.057374ms
39 changes: 39 additions & 0 deletions client/fixtures/bunny1_delete.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
version: 1
interactions:
- request:
body: ""
form: {}
headers: {}
url: https://customer.cloudamqp.com/api/instances/359559
method: DELETE
response:
body: ""
headers:
Cache-Control:
- no-cache
Date:
- Tue, 25 Nov 2025 20:21:27 GMT
Nel:
- '{"report_to":"heroku-nel","response_headers":["Via"],"max_age":3600,"success_fraction":0.01,"failure_fraction":0.1}'
- '{"report_to":"default","max_age":31536000,"include_subdomains":true}'
Referrer-Policy:
- strict-origin-when-cross-origin
Report-To:
- '{"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}'
- '{"group":"default","max_age":31536000,"endpoints":[{"url":"https://84codes.report-uri.com/a/t/g"}],"include_subdomains":true}'
Reporting-Endpoints:
- heroku-nel="https://nel.heroku.com/reports?s=6CMwcmhwCQkJWao6kfw%2FQ%2B1uNyspXxu6IlEsbprvy8E%3D&sid=af571f24-03ee-46d1-9f90-ab9030c2c74c&ts=1764102087"
Server:
- Heroku
Strict-Transport-Security:
- max-age=31536000; includeSubDomains
Via:
- 2.0 heroku-router
X-Content-Type-Options:
- nosniff
X-Request-Id:
- 64b2640d-5c01-4d69-7e44-74f92825df43
status: 204 No Content
code: 204
duration: 710.235286ms
Loading
Loading