Files

131 lines
3.3 KiB
Go

package acceptance
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"testing"
)
type Client struct {
baseURL string
httpClient *http.Client
token string
}
type Envelope struct {
Code uint32 `json:"code"`
Msg string `json:"msg"`
Data json.RawMessage `json:"data"`
}
func newClient(c Config) *Client {
return &Client{
baseURL: strings.TrimRight(c.StagingURL, "/"),
httpClient: &http.Client{
Timeout: c.HTTPTimeout,
},
}
}
func (c *Client) WithToken(token string) *Client {
next := *c
next.token = token
return &next
}
func (c *Client) Get(ctx context.Context, path string, query url.Values) (*Envelope, []byte, int, error) {
return c.Do(ctx, http.MethodGet, path, query, nil)
}
func (c *Client) PostJSON(ctx context.Context, path string, payload any) (*Envelope, []byte, int, error) {
return c.Do(ctx, http.MethodPost, path, nil, payload)
}
func (c *Client) Do(ctx context.Context, method string, path string, query url.Values, payload any) (*Envelope, []byte, int, error) {
target, err := url.Parse(c.baseURL + "/" + strings.TrimLeft(path, "/"))
if err != nil {
return nil, nil, 0, fmt.Errorf("build request URL: %w", err)
}
if query != nil {
target.RawQuery = query.Encode()
}
var body io.Reader
if payload != nil {
encoded, err := json.Marshal(payload)
if err != nil {
return nil, nil, 0, fmt.Errorf("marshal request body: %w", err)
}
body = bytes.NewReader(encoded)
}
req, err := http.NewRequestWithContext(ctx, method, target.String(), body)
if err != nil {
return nil, nil, 0, fmt.Errorf("create request: %w", err)
}
req.Header.Set("Accept", "application/json")
req.Header.Set("User-Agent", "hifast-acceptance/1.0")
req.Header.Set("Login-Type", "email")
if payload != nil {
req.Header.Set("Content-Type", "application/json")
}
if c.token != "" {
req.Header.Set("Authorization", c.token)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, nil, 0, fmt.Errorf("%s %s: %w", method, target.Redacted(), err)
}
defer func() {
_ = resp.Body.Close()
}()
raw, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20))
if err != nil {
return nil, nil, resp.StatusCode, fmt.Errorf("read response: %w", err)
}
envelope := &Envelope{}
if strings.Contains(resp.Header.Get("Content-Type"), "application/json") && len(raw) > 0 {
if err := json.Unmarshal(raw, envelope); err != nil {
return nil, raw, resp.StatusCode, fmt.Errorf("decode response envelope: %w", err)
}
}
return envelope, raw, resp.StatusCode, nil
}
func assertOK(t *testing.T, envelope *Envelope, raw []byte, status int) {
t.Helper()
if status != http.StatusOK {
t.Fatalf("http status = %d, want 200; body=%s", status, sanitizeBody(raw))
}
if envelope == nil || envelope.Code != 200 {
t.Fatalf("api code = %d, want 200; msg=%q body=%s", envelope.Code, envelope.Msg, sanitizeBody(raw))
}
}
func assertAPIError(t *testing.T, envelope *Envelope, raw []byte, status int) {
t.Helper()
if status != http.StatusOK {
t.Fatalf("http status = %d, want 200 API envelope; body=%s", status, sanitizeBody(raw))
}
if envelope == nil || envelope.Code == 200 {
t.Fatalf("api code = %d, want non-200; body=%s", envelope.Code, sanitizeBody(raw))
}
}
func sanitizeBody(raw []byte) string {
value := string(raw)
if len(value) > 512 {
value = value[:512] + "...<truncated>"
}
return value
}