131 lines
3.3 KiB
Go
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
|
|
}
|