203 lines
4.7 KiB
Go
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())
|
|
}
|