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()) }