Files

159 lines
3.6 KiB
Go

//go:build integration
package testinfra
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"testing"
"time"
)
type APIResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data json.RawMessage `json:"data"`
RequestID string `json:"request_id"`
HTTPStatus int
}
type APIClient struct {
BaseURL string
HTTPClient *http.Client
Token string
}
func NewAPIClient(baseURL string) *APIClient {
return &APIClient{
BaseURL: baseURL,
HTTPClient: &http.Client{Timeout: 10 * time.Second},
}
}
func (c *APIClient) WithToken(token string) *APIClient {
clone := *c
clone.Token = token
return &clone
}
func (c *APIClient) Get(path string, params map[string]string) *APIResponse {
return c.doRequest("GET", path, params, nil)
}
func (c *APIClient) Post(path string, body any) *APIResponse {
return c.doRequest("POST", path, nil, body)
}
func (c *APIClient) Put(path string, body any) *APIResponse {
return c.doRequest("PUT", path, nil, body)
}
func (c *APIClient) Delete(path string) *APIResponse {
return c.doRequest("DELETE", path, nil, nil)
}
func (c *APIClient) doRequest(method, path string, params map[string]string, body any) *APIResponse {
var bodyReader io.Reader
if body != nil {
b, err := json.Marshal(body)
if err != nil {
return &APIResponse{HTTPStatus: 0, Message: fmt.Sprintf("marshal error: %v", err)}
}
bodyReader = bytes.NewReader(b)
}
fullURL := c.BaseURL + path
if len(params) > 0 {
q := url.Values{}
for k, v := range params {
q.Set(k, v)
}
if strings.Contains(fullURL, "?") {
fullURL += "&" + q.Encode()
} else {
fullURL += "?" + q.Encode()
}
}
req, err := http.NewRequest(method, fullURL, bodyReader)
if err != nil {
return &APIResponse{HTTPStatus: 0, Message: fmt.Sprintf("create request error: %v", err)}
}
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
if c.Token != "" {
req.Header.Set("Authorization", "Bearer "+c.Token)
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return &APIResponse{HTTPStatus: 0, Message: fmt.Sprintf("request error: %v", err)}
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return &APIResponse{HTTPStatus: resp.StatusCode, Message: fmt.Sprintf("read body error: %v", err)}
}
apiResp := &APIResponse{HTTPStatus: resp.StatusCode}
_ = json.Unmarshal(data, apiResp)
return apiResp
}
func (r *APIResponse) AssertSuccess(t *testing.T) {
t.Helper()
if r.HTTPStatus == 0 {
t.Fatalf("request failed: %s", r.Message)
}
if r.Code != 0 {
t.Fatalf("expected code=0, got code=%d message=%q request_id=%s", r.Code, r.Message, r.RequestID)
}
}
func (r *APIResponse) AssertError(t *testing.T, expectedCode int) {
t.Helper()
if r.Code != expectedCode {
t.Fatalf("expected code=%d, got code=%d message=%q", expectedCode, r.Code, r.Message)
}
}
func (r *APIResponse) AssertHTTPStatus(t *testing.T, expected int) {
t.Helper()
if r.HTTPStatus != expected {
t.Fatalf("expected HTTP %d, got %d", expected, r.HTTPStatus)
}
}
func (r *APIResponse) DecodeData(t *testing.T, target any) {
t.Helper()
if err := json.Unmarshal(r.Data, target); err != nil {
t.Fatalf("decode data error: %v, raw: %s", err, string(r.Data))
}
}
func (r *APIResponse) GetID(t *testing.T) int64 {
t.Helper()
var result struct {
ID int64 `json:"id"`
}
r.DecodeData(t, &result)
return result.ID
}
func (r *APIResponse) GetTotal(t *testing.T) int {
t.Helper()
var result struct {
Total int `json:"total"`
}
r.DecodeData(t, &result)
return result.Total
}