test(09-02): add etape management tests

This commit is contained in:
Arthur Belleville 2026-05-15 22:42:39 +02:00
parent c1c20c4158
commit 9b89282692
No known key found for this signature in database

View file

@ -223,3 +223,283 @@ func TestEtapeFilterRendersExistingKanbanColumns(t *testing.T) {
t.Errorf("filtered body includes unassigned task; body: %.500s", body)
}
}
func TestEtapeUpdateChangesTitleAndDescription(t *testing.T) {
pool, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
q := sqlc.New(pool)
store := auth.NewStore(q)
router := newEtapeTestRouter(q, store)
user := preInsertUser(t, ctx, q, "etapeupdate@example.com", "correct-horse-12")
tablo, err := q.InsertTablo(ctx, sqlc.InsertTabloParams{
UserID: user.ID,
Title: "Etape Update Tablo",
Description: pgtype.Text{Valid: false},
Color: pgtype.Text{Valid: false},
})
if err != nil {
t.Fatalf("InsertTablo: %v", err)
}
etape, err := q.InsertEtape(ctx, sqlc.InsertEtapeParams{
TabloID: tablo.ID,
Title: "Draft",
Description: pgtype.Text{String: "Old", Valid: true},
Position: 100,
})
if err != nil {
t.Fatalf("InsertEtape: %v", err)
}
cookieVal, _, err := store.Create(ctx, user.ID)
if err != nil {
t.Fatalf("store.Create: %v", err)
}
sessionCookie := &http.Cookie{Name: auth.SessionCookieName, Value: cookieVal}
csrfToken, csrfCookies := getCSRFToken(t, router, "/tablos/"+tablo.ID.String()+"/tasks", []*http.Cookie{sessionCookie})
form := url.Values{
"title": {"Design"},
"description": {"Ready for build"},
"_csrf": {csrfToken},
}
req := httptest.NewRequest(http.MethodPost, "/tablos/"+tablo.ID.String()+"/etapes/"+etape.ID.String(), strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("HX-Request", "true")
for _, c := range csrfCookies {
req.AddCookie(c)
}
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("POST /tablos/{id}/etapes/{etape_id} status = %d; want 200", rec.Code)
}
body := rec.Body.String()
if !strings.Contains(body, "Design") {
t.Fatalf("response body missing updated title; body: %.500s", body)
}
updated, err := q.GetEtapeByID(ctx, sqlc.GetEtapeByIDParams{ID: etape.ID, TabloID: tablo.ID})
if err != nil {
t.Fatalf("GetEtapeByID: %v", err)
}
if updated.Title != "Design" || !updated.Description.Valid || updated.Description.String != "Ready for build" {
t.Fatalf("updated etape = %+v; want new title and description", updated)
}
}
func TestEtapeDeleteUnassignsTasks(t *testing.T) {
pool, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
q := sqlc.New(pool)
store := auth.NewStore(q)
router := newEtapeTestRouter(q, store)
user := preInsertUser(t, ctx, q, "etapedelete@example.com", "correct-horse-12")
tablo, err := q.InsertTablo(ctx, sqlc.InsertTabloParams{
UserID: user.ID,
Title: "Etape Delete Tablo",
Description: pgtype.Text{Valid: false},
Color: pgtype.Text{Valid: false},
})
if err != nil {
t.Fatalf("InsertTablo: %v", err)
}
etape, err := q.InsertEtape(ctx, sqlc.InsertEtapeParams{
TabloID: tablo.ID,
Title: "Design",
Description: pgtype.Text{Valid: false},
Position: 100,
})
if err != nil {
t.Fatalf("InsertEtape: %v", err)
}
task, err := q.InsertTask(ctx, sqlc.InsertTaskParams{
TabloID: tablo.ID,
Title: "Survives",
Description: pgtype.Text{Valid: false},
Status: sqlc.TaskStatusTodo,
Position: 100,
EtapeID: pgtype.UUID{Bytes: etape.ID, Valid: true},
})
if err != nil {
t.Fatalf("InsertTask: %v", err)
}
cookieVal, _, err := store.Create(ctx, user.ID)
if err != nil {
t.Fatalf("store.Create: %v", err)
}
sessionCookie := &http.Cookie{Name: auth.SessionCookieName, Value: cookieVal}
csrfToken, csrfCookies := getCSRFToken(t, router, "/tablos/"+tablo.ID.String()+"/tasks", []*http.Cookie{sessionCookie})
form := url.Values{"_csrf": {csrfToken}}
req := httptest.NewRequest(http.MethodPost, "/tablos/"+tablo.ID.String()+"/etapes/"+etape.ID.String()+"/delete", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("HX-Request", "true")
for _, c := range csrfCookies {
req.AddCookie(c)
}
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("POST delete etape status = %d; want 200", rec.Code)
}
remaining, err := q.GetTaskByID(ctx, sqlc.GetTaskByIDParams{ID: task.ID, TabloID: tablo.ID})
if err != nil {
t.Fatalf("GetTaskByID after etape delete: %v", err)
}
if remaining.EtapeID.Valid {
t.Fatalf("task etape_id valid after etape delete; want null")
}
}
func TestEtapeReorderPersistsPosition(t *testing.T) {
pool, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
q := sqlc.New(pool)
store := auth.NewStore(q)
router := newEtapeTestRouter(q, store)
user := preInsertUser(t, ctx, q, "etapereorder@example.com", "correct-horse-12")
tablo, err := q.InsertTablo(ctx, sqlc.InsertTabloParams{
UserID: user.ID,
Title: "Etape Reorder Tablo",
Description: pgtype.Text{Valid: false},
Color: pgtype.Text{Valid: false},
})
if err != nil {
t.Fatalf("InsertTablo: %v", err)
}
first, err := q.InsertEtape(ctx, sqlc.InsertEtapeParams{
TabloID: tablo.ID,
Title: "First",
Description: pgtype.Text{Valid: false},
Position: 100,
})
if err != nil {
t.Fatalf("InsertEtape first: %v", err)
}
second, err := q.InsertEtape(ctx, sqlc.InsertEtapeParams{
TabloID: tablo.ID,
Title: "Second",
Description: pgtype.Text{Valid: false},
Position: 200,
})
if err != nil {
t.Fatalf("InsertEtape second: %v", err)
}
cookieVal, _, err := store.Create(ctx, user.ID)
if err != nil {
t.Fatalf("store.Create: %v", err)
}
sessionCookie := &http.Cookie{Name: auth.SessionCookieName, Value: cookieVal}
csrfToken, csrfCookies := getCSRFToken(t, router, "/tablos/"+tablo.ID.String()+"/tasks", []*http.Cookie{sessionCookie})
form := url.Values{
"etape_id": {second.ID.String(), first.ID.String()},
"_csrf": {csrfToken},
}
req := httptest.NewRequest(http.MethodPost, "/tablos/"+tablo.ID.String()+"/etapes/reorder", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("HX-Request", "true")
for _, c := range csrfCookies {
req.AddCookie(c)
}
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("POST reorder etapes status = %d; want 200", rec.Code)
}
etapes, err := q.ListEtapesByTablo(ctx, tablo.ID)
if err != nil {
t.Fatalf("ListEtapesByTablo: %v", err)
}
if len(etapes) != 2 || etapes[0].ID != second.ID || etapes[1].ID != first.ID {
t.Fatalf("etape order = %+v; want second then first", etapes)
}
}
func TestEtapeOwnershipReturns404(t *testing.T) {
pool, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
q := sqlc.New(pool)
store := auth.NewStore(q)
router := newEtapeTestRouter(q, store)
owner := preInsertUser(t, ctx, q, "etapeowner@example.com", "correct-horse-12")
nonOwner := preInsertUser(t, ctx, q, "etapenonowner@example.com", "correct-horse-12")
tablo, err := q.InsertTablo(ctx, sqlc.InsertTabloParams{
UserID: owner.ID,
Title: "Owned Elsewhere",
Description: pgtype.Text{Valid: false},
Color: pgtype.Text{Valid: false},
})
if err != nil {
t.Fatalf("InsertTablo: %v", err)
}
etape, err := q.InsertEtape(ctx, sqlc.InsertEtapeParams{
TabloID: tablo.ID,
Title: "Private",
Description: pgtype.Text{Valid: false},
Position: 100,
})
if err != nil {
t.Fatalf("InsertEtape: %v", err)
}
cookieVal, _, err := store.Create(ctx, nonOwner.ID)
if err != nil {
t.Fatalf("store.Create: %v", err)
}
sessionCookie := &http.Cookie{Name: auth.SessionCookieName, Value: cookieVal}
csrfToken, csrfCookies := getCSRFToken(t, router, "/", []*http.Cookie{sessionCookie})
cases := []struct {
name string
path string
form url.Values
}{
{
name: "update",
path: "/tablos/" + tablo.ID.String() + "/etapes/" + etape.ID.String(),
form: url.Values{"title": {"Nope"}, "_csrf": {csrfToken}},
},
{
name: "delete",
path: "/tablos/" + tablo.ID.String() + "/etapes/" + etape.ID.String() + "/delete",
form: url.Values{"_csrf": {csrfToken}},
},
{
name: "reorder",
path: "/tablos/" + tablo.ID.String() + "/etapes/reorder",
form: url.Values{"etape_id": {etape.ID.String()}, "_csrf": {csrfToken}},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, tc.path, strings.NewReader(tc.form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("HX-Request", "true")
for _, c := range csrfCookies {
req.AddCookie(c)
}
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusNotFound {
t.Fatalf("%s status = %d; want 404", tc.name, rec.Code)
}
})
}
}