1b69bec12f
Update test passwords from 'password123' to 'Password123!' to meet security requirements (uppercase, lowercase, digit, special char). Fix migrations test to match actual habit_entries schema (removed non-existent deleted_at column).
161 lines
4.1 KiB
Go
161 lines
4.1 KiB
Go
package http
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
)
|
|
|
|
func TestHabitCRUDFlow(t *testing.T) {
|
|
ts := setupTestServer(t)
|
|
defer ts.Close()
|
|
|
|
registerBody := RegisterRequest{
|
|
Email: "habituser@example.com",
|
|
Password: "Password123!",
|
|
Timezone: "UTC",
|
|
}
|
|
rr := makeRequest(t, *ts.Router, "POST", "/api/v1/auth/register", registerBody, "")
|
|
var authResp AuthResponse
|
|
decodeResponse(t, rr, &authResp)
|
|
token := authResp.Token
|
|
|
|
var habitID string
|
|
|
|
t.Run("Create habit", func(t *testing.T) {
|
|
reqBody := CreateHabitRequest{
|
|
Name: "Exercise",
|
|
Description: "Daily workout",
|
|
Type: "BOOLEAN",
|
|
Frequency: "DAILY",
|
|
CarryOver: false,
|
|
}
|
|
|
|
rr := makeRequest(t, *ts.Router, "POST", "/api/v1/habits", reqBody, token)
|
|
|
|
if rr.Code != http.StatusCreated {
|
|
t.Fatalf("Expected status 201, got %d. Body: %s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
var resp map[string]string
|
|
decodeResponse(t, rr, &resp)
|
|
|
|
habitID = resp["id"]
|
|
if habitID == "" {
|
|
t.Fatal("Expected habit ID in response")
|
|
}
|
|
})
|
|
|
|
t.Run("Create habit without auth", func(t *testing.T) {
|
|
reqBody := CreateHabitRequest{
|
|
Name: "No Auth",
|
|
Type: "BOOLEAN",
|
|
Frequency: "DAILY",
|
|
}
|
|
|
|
rr := makeRequest(t, *ts.Router, "POST", "/api/v1/habits", reqBody, "")
|
|
|
|
if rr.Code != http.StatusUnauthorized {
|
|
t.Errorf("Expected status 401, got %d", rr.Code)
|
|
}
|
|
})
|
|
|
|
t.Run("Get all user habits", func(t *testing.T) {
|
|
rr := makeRequest(t, *ts.Router, "GET", "/api/v1/habits", nil, token)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("Expected status 200, got %d", rr.Code)
|
|
}
|
|
|
|
var habits []UserHabitResponse
|
|
decodeResponse(t, rr, &habits)
|
|
|
|
if len(habits) != 1 {
|
|
t.Errorf("Expected 1 habit, got %d", len(habits))
|
|
}
|
|
|
|
if habits[0].Name != "Exercise" {
|
|
t.Errorf("Expected habit name 'Exercise', got '%s'", habits[0].Name)
|
|
}
|
|
})
|
|
|
|
t.Run("Get habit by ID", func(t *testing.T) {
|
|
rr := makeRequest(t, *ts.Router, "GET", "/api/v1/habits/"+habitID, nil, token)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("Expected status 200, got %d", rr.Code)
|
|
}
|
|
|
|
var habit UserHabitResponse
|
|
decodeResponse(t, rr, &habit)
|
|
|
|
if habit.Name != "Exercise" {
|
|
t.Errorf("Expected habit name 'Exercise', got '%s'", habit.Name)
|
|
}
|
|
})
|
|
|
|
t.Run("Update habit", func(t *testing.T) {
|
|
reqBody := UpdateHabitRequest{
|
|
Name: "Morning Exercise",
|
|
Description: "Updated description",
|
|
}
|
|
|
|
rr := makeRequest(t, *ts.Router, "PUT", "/api/v1/habits/"+habitID, reqBody, token)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("Expected status 200, got %d. Body: %s", rr.Code, rr.Body.String())
|
|
}
|
|
|
|
rr = makeRequest(t, *ts.Router, "GET", "/api/v1/habits/"+habitID, nil, token)
|
|
var habit UserHabitResponse
|
|
decodeResponse(t, rr, &habit)
|
|
|
|
if habit.Name != "Morning Exercise" {
|
|
t.Errorf("Expected updated name 'Morning Exercise', got '%s'", habit.Name)
|
|
}
|
|
})
|
|
|
|
t.Run("Archive habit", func(t *testing.T) {
|
|
rr := makeRequest(t, *ts.Router, "DELETE", "/api/v1/habits/"+habitID, nil, token)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("Expected status 200, got %d", rr.Code)
|
|
}
|
|
|
|
rr = makeRequest(t, *ts.Router, "GET", "/api/v1/habits", nil, token)
|
|
var habits []UserHabitResponse
|
|
decodeResponse(t, rr, &habits)
|
|
|
|
if len(habits) != 0 {
|
|
t.Errorf("Expected 0 active habits after archive, got %d", len(habits))
|
|
}
|
|
})
|
|
|
|
t.Run("Access other user's habit", func(t *testing.T) {
|
|
registerBody := RegisterRequest{
|
|
Email: "otheruser@example.com",
|
|
Password: "Password123!",
|
|
Timezone: "UTC",
|
|
}
|
|
rr := makeRequest(t, *ts.Router, "POST", "/api/v1/auth/register", registerBody, "")
|
|
var authResp AuthResponse
|
|
decodeResponse(t, rr, &authResp)
|
|
otherToken := authResp.Token
|
|
|
|
reqBody := CreateHabitRequest{
|
|
Name: "Other User Habit",
|
|
Type: "BOOLEAN",
|
|
Frequency: "DAILY",
|
|
}
|
|
rr = makeRequest(t, *ts.Router, "POST", "/api/v1/habits", reqBody, otherToken)
|
|
var createResp map[string]string
|
|
decodeResponse(t, rr, &createResp)
|
|
otherHabitID := createResp["id"]
|
|
|
|
rr = makeRequest(t, *ts.Router, "GET", "/api/v1/habits/"+otherHabitID, nil, token)
|
|
|
|
if rr.Code != http.StatusForbidden {
|
|
t.Errorf("Expected status 403, got %d", rr.Code)
|
|
}
|
|
})
|
|
}
|