//go:build integration package integrationtest import ( "bytes" "encoding/json" "fmt" "io" "net/http" "strings" ) // Client performs authenticated HTTP requests against the test server. type Client struct { baseURL string token string http *http.Client } func NewClient(baseURL, token string) *Client { return &Client{ baseURL: strings.TrimSuffix(baseURL, "/"), token: token, http: &http.Client{}, } } type Response struct { Status int Body []byte Header http.Header } func (c *Client) Do(method, path string, body any) (*Response, error) { var reader io.Reader if body != nil { data, err := json.Marshal(body) if err != nil { return nil, err } reader = bytes.NewReader(data) } req, err := http.NewRequest(method, c.baseURL+path, reader) if err != nil { return nil, err } if body != nil { req.Header.Set("Content-Type", "application/json") } if c.token != "" { req.Header.Set("Authorization", "Bearer "+c.token) } resp, err := c.http.Do(req) if err != nil { return nil, err } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { return nil, err } return &Response{Status: resp.StatusCode, Body: data, Header: resp.Header}, nil } func (c *Client) Get(path string) (*Response, error) { return c.Do(http.MethodGet, path, nil) } func (c *Client) Post(path string, body any) (*Response, error) { return c.Do(http.MethodPost, path, body) } func (c *Client) Put(path string, body any) (*Response, error) { return c.Do(http.MethodPut, path, body) } func (c *Client) Patch(path string, body any) (*Response, error) { return c.Do(http.MethodPatch, path, body) } func (c *Client) Delete(path string) (*Response, error) { return c.Do(http.MethodDelete, path, nil) } func (r *Response) JSON(v any) error { return json.Unmarshal(r.Body, v) } func (r *Response) MustStatus(want int) error { if r.Status != want { return fmt.Errorf("status = %d, want %d; body = %s", r.Status, want, string(r.Body)) } return nil } // WithToken returns a copy using a different bearer token (e.g. API token). func (c *Client) WithToken(token string) *Client { return &Client{baseURL: c.baseURL, token: token, http: c.http} }