@@ -0,0 +1,130 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user