party/cmd/api/testutils_test.go
2026-04-28 19:46:06 +02:00

203 lines
4.7 KiB
Go

package main
import (
"io"
"net/http"
"net/http/httptest"
"testing"
"os"
"encoding/json"
"bytes"
"fmt"
"time"
"party.at/party/internal/data"
"party.at/party/internal/jsonlog"
)
func newTestApplication(t *testing.T) *application {
cfg := config{}
cfg.db.dsn = "postgres://party:password@localhost:5432/party?sslmode=disable"
cfg.db.maxOpenConns = 25
cfg.db.maxIdleConns = 25
cfg.db.maxIdleTime = "15m"
cfg.limiter.enabled = false
cfg.env = "development"
logger := jsonlog.New(os.Stdout, jsonlog.LevelInfo)
db, err := openDB(cfg)
if err != nil {
logger.PrintFatal(err, nil)
}
t.Cleanup(func() {db.Close()})
return &application{
logger: logger,
models: data.NewModels(db),
config: cfg,
}
}
type testServer struct {
*httptest.Server
app *application
}
func newTestServer(t *testing.T, app *application, h http.Handler) *testServer {
ts := httptest.NewTLSServer(h)
return &testServer{ts, app}
}
func (ts *testServer) postJSON(t *testing.T, path string, body any) (int, http.Header, []byte) {
t.Helper()
b, err := json.Marshal(body)
if err != nil {
t.Fatal(err)
}
req, err := http.NewRequest(http.MethodPost, ts.URL+path, bytes.NewReader(b))
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
resp, err := ts.Client().Do(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
return resp.StatusCode, resp.Header, respBody
}
func (ts *testServer) get(t *testing.T, path string) (int, http.Header, []byte) {
t.Helper()
rs, err := ts.Client().Get(ts.URL + path)
if err != nil {
t.Fatal(err)
}
defer rs.Body.Close()
body, err := io.ReadAll(rs.Body)
if err != nil {
t.Fatal(err)
}
return rs.StatusCode, rs.Header, body
}
// registers a user, activates them, logs in, returns the bearer token
func (ts *testServer) registerAndLogin(t *testing.T, email, password string) string {
t.Helper()
// 1. Register
registerBody := map[string]any{
"email": email,
"password": password,
"username": email,
"name": "Test User",
"alt_name" : "",
"provider_id": 1,
}
code, _, body := ts.postJSON(t, "/v1/users", registerBody)
if code != http.StatusCreated {
t.Fatalf("register: want 201 got %d: %s", code, body)
}
// 2. Activate — if your flow requires it, either hit the endpoint
// or directly flip the activated flag in the test DB
// ts.activateUser(t, email)
// 3. Login
loginBody := map[string]string{"email": email, "password": password}
code, _, body = ts.postJSON(t, "/v1/tokens/authentication", loginBody)
if code != http.StatusCreated {
t.Fatalf("login: want 201 got %d: %s", code, body)
}
// 4. Parse token out of response
var resp struct {
AuthenticationToken struct {
Token string `json:"token"`
} `json:"authentication_token"`
}
if err := json.Unmarshal(body, &resp); err != nil {
t.Fatalf("parse token: %v", err)
}
return resp.AuthenticationToken.Token
}
// activateUser directly updates the DB — avoids needing a real email flow
// func (ts *testServer) activateUser(t *testing.T, email string) {
// t.Helper()
// _, err := ts.app.db.Exec("UPDATE users SET activated = true WHERE email = $1", email)
// if err != nil {
// t.Fatalf("activate user: %v", err)
// }
// }
// like ts.get but adds Authorization header
func (ts *testServer) getWithToken(t *testing.T, path, token string) (int, http.Header, []byte) {
t.Helper()
req, err := http.NewRequest(http.MethodGet, ts.URL+path, nil)
if err != nil {
t.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
rs, err := ts.Client().Do(req)
if err != nil {
t.Fatal(err)
}
defer rs.Body.Close()
body, err := io.ReadAll(rs.Body)
if err != nil {
t.Fatal(err)
}
return rs.StatusCode, rs.Header, body
}
func (ts *testServer) postJSONWithToken(t *testing.T, path, token string, body any) (int, http.Header, []byte) {
t.Helper()
b, err := json.Marshal(body)
if err != nil {
t.Fatal(err)
}
req, err := http.NewRequest(http.MethodPost, ts.URL+path, bytes.NewReader(b))
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
rs, err := ts.Client().Do(req)
if err != nil {
t.Fatal(err)
}
defer rs.Body.Close()
respBody, err := io.ReadAll(rs.Body)
if err != nil {
t.Fatal(err)
}
return rs.StatusCode, rs.Header, respBody
}
func uniqueEmail() string {
return fmt.Sprintf("test_%d@example.com", time.Now().UnixNano())
}