package main import ( "net/http" "net/http/httptest" "testing" ) func TestShiftsCRUDHandler(t *testing.T) { app := testApp(t) admin := testAdminUser(t, app) token := testToken(t, app, admin) mux := testMux(app) dept, _ := app.createDepartment(Department{Name: "Gate"}) // Create req := testAuthRequest("POST", "/api/shifts", map[string]any{ "department_id": dept.ID, "name": "Morning", "day": "2026-03-15", "start_time": "08:00", "end_time": "12:00", "capacity": 5, }, token) w := httptest.NewRecorder() mux.ServeHTTP(w, req) if w.Code != http.StatusCreated { t.Fatalf("create: status = %d\nbody: %s", w.Code, w.Body.String()) } // List req = testAuthRequest("GET", "/api/shifts", nil, token) w = httptest.NewRecorder() mux.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("list: status = %d", w.Code) } // Delete req = testAuthRequest("DELETE", "/api/shifts/1", nil, token) w = httptest.NewRecorder() mux.ServeHTTP(w, req) if w.Code != http.StatusNoContent { t.Errorf("delete: status = %d", w.Code) } } func TestShiftAssignVolunteer(t *testing.T) { app := testApp(t) admin := testAdminUser(t, app) token := testToken(t, app, admin) mux := testMux(app) dept, _ := app.createDepartment(Department{Name: "Gate"}) deptID := dept.ID app.createShift(Shift{DepartmentID: deptID, Name: "AM", Day: "2026-03-15", StartTime: "08:00", EndTime: "12:00"}) app.createVolunteer(Volunteer{Name: "Alice", DepartmentID: &deptID}) // Assign req := testAuthRequest("POST", "/api/shifts/1/volunteers", map[string]any{ "volunteer_id": 1, }, token) w := httptest.NewRecorder() mux.ServeHTTP(w, req) if w.Code != http.StatusNoContent { t.Fatalf("assign: status = %d\nbody: %s", w.Code, w.Body.String()) } // Unassign req = testAuthRequest("DELETE", "/api/shifts/1/volunteers/1", nil, token) w = httptest.NewRecorder() mux.ServeHTTP(w, req) if w.Code != http.StatusNoContent { t.Errorf("unassign: status = %d", w.Code) } } func TestShiftAssignConflict(t *testing.T) { app := testApp(t) admin := testAdminUser(t, app) token := testToken(t, app, admin) mux := testMux(app) dept, _ := app.createDepartment(Department{Name: "Gate"}) deptID := dept.ID app.createShift(Shift{DepartmentID: deptID, Name: "AM", Day: "2026-03-15", StartTime: "08:00", EndTime: "12:00"}) app.createShift(Shift{DepartmentID: deptID, Name: "Overlap", Day: "2026-03-15", StartTime: "10:00", EndTime: "14:00"}) app.createVolunteer(Volunteer{Name: "Alice", DepartmentID: &deptID}) // Assign to first shift app.assignShift(1, 1) // Try to assign to overlapping shift — should get 409 req := testAuthRequest("POST", "/api/shifts/2/volunteers", map[string]any{ "volunteer_id": 1, }, token) w := httptest.NewRecorder() mux.ServeHTTP(w, req) if w.Code != http.StatusConflict { t.Fatalf("conflict: status = %d, want 409", w.Code) } } func TestShiftReorder(t *testing.T) { app := testApp(t) admin := testAdminUser(t, app) token := testToken(t, app, admin) mux := testMux(app) dept, _ := app.createDepartment(Department{Name: "Gate"}) deptID := dept.ID app.createShift(Shift{DepartmentID: deptID, Name: "A", Day: "2026-03-15", StartTime: "08:00", EndTime: "12:00"}) app.createShift(Shift{DepartmentID: deptID, Name: "B", Day: "2026-03-15", StartTime: "12:00", EndTime: "16:00"}) req := testAuthRequest("POST", "/api/shifts/reorder", []map[string]int{ {"id": 1, "position": 2}, {"id": 2, "position": 1}, }, token) w := httptest.NewRecorder() mux.ServeHTTP(w, req) if w.Code != http.StatusNoContent { t.Errorf("reorder: status = %d\nbody: %s", w.Code, w.Body.String()) } s1, _ := app.getShift(1) s2, _ := app.getShift(2) if s1.Position != 2 || s2.Position != 1 { t.Errorf("positions: s1=%d, s2=%d, want 2,1", s1.Position, s2.Position) } }