Turnpike/handle_kiosk_test.go

167 lines
4.9 KiB
Go
Raw Normal View History

package main
import (
"net/http"
"net/http/httptest"
"testing"
)
func setupKiosk(t *testing.T) (*App, *http.ServeMux, string) {
t.Helper()
app := testApp(t)
mux := testMux(app)
dept, _ := app.createDepartment(Department{Name: "Gate"})
deptID := dept.ID
// Create volunteer with a kiosk_code directly on the volunteer record
p, _ := app.createParticipant(Participant{Email: "titania@test.com", PreferredName: "Titania"})
v, _ := app.createVolunteer(Volunteer{ParticipantID: p.ID, DepartmentID: &deptID})
token, _ := app.generateVolunteerKioskCode()
app.assignKioskCode(v.ID, token)
// Create shifts
app.createShift(Shift{DepartmentID: deptID, Name: "Morning", Day: "2026-03-15", StartTime: "08:00", EndTime: "12:00", Capacity: 2})
app.createShift(Shift{DepartmentID: deptID, Name: "Afternoon", Day: "2026-03-15", StartTime: "14:00", EndTime: "18:00", Capacity: 1})
return app, mux, token
}
func TestKioskGetValid(t *testing.T) {
_, mux, token := setupKiosk(t)
req := httptest.NewRequest("GET", "/api/v/"+token, nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status = %d\nbody: %s", w.Code, w.Body.String())
}
result := parseJSON(t, w)
if result["volunteer"] == nil {
t.Error("missing volunteer")
}
available := result["available"].([]any)
if len(available) != 2 {
t.Errorf("available = %d, want 2", len(available))
}
}
func TestKioskGetInvalidToken(t *testing.T) {
_, mux, _ := setupKiosk(t)
req := httptest.NewRequest("GET", "/api/v/BADTOKEN", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusNotFound {
t.Errorf("status = %d, want 404", w.Code)
}
}
func TestKioskClaimShift(t *testing.T) {
_, mux, token := setupKiosk(t)
req := httptest.NewRequest("POST", "/api/v/"+token+"/shifts/1", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("claim: status = %d\nbody: %s", w.Code, w.Body.String())
}
result := parseJSON(t, w)
shifts := result["shifts"].([]any)
if len(shifts) != 1 {
t.Errorf("assigned shifts = %d, want 1", len(shifts))
}
}
func TestKioskClaimConflict(t *testing.T) {
app, mux, token := setupKiosk(t)
dept, _ := app.createDepartment(Department{Name: "Build"})
deptID := dept.ID
// Create overlapping shift
app.createShift(Shift{DepartmentID: deptID, Name: "Overlap", Day: "2026-03-15", StartTime: "10:00", EndTime: "14:00"})
// Claim morning (08:00-12:00)
req := httptest.NewRequest("POST", "/api/v/"+token+"/shifts/1", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("first claim: status = %d", w.Code)
}
// Claim overlapping shift (10:00-14:00) — should get 409
req = httptest.NewRequest("POST", "/api/v/"+token+"/shifts/3", nil)
w = httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusConflict {
t.Fatalf("conflict: status = %d, want 409\nbody: %s", w.Code, w.Body.String())
}
result := parseJSON(t, w)
if result["conflict"] != true {
t.Error("missing conflict flag")
}
}
func TestKioskClaimForce(t *testing.T) {
app, mux, token := setupKiosk(t)
dept, _ := app.createDepartment(Department{Name: "Build"})
deptID := dept.ID
app.createShift(Shift{DepartmentID: deptID, Name: "Overlap", Day: "2026-03-15", StartTime: "10:00", EndTime: "14:00"})
// Claim morning
req := httptest.NewRequest("POST", "/api/v/"+token+"/shifts/1", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
// Force-claim overlapping shift
req = httptest.NewRequest("POST", "/api/v/"+token+"/shifts/3?force=true", nil)
w = httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("force: status = %d, want 200", w.Code)
}
}
func TestKioskClaimFull(t *testing.T) {
app, mux, token := setupKiosk(t)
// Shift 2 has capacity 1. Fill it with another volunteer.
dept, _ := app.createDepartment(Department{Name: "Build"})
deptID := dept.ID
otherP, _ := app.createParticipant(Participant{PreferredName: "Other", Email: "other@test.com"})
other, _ := app.createVolunteer(Volunteer{ParticipantID: otherP.ID, DepartmentID: &deptID})
app.assignShift(other.ID, 2) // fills the capacity-1 shift
req := httptest.NewRequest("POST", "/api/v/"+token+"/shifts/2", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusConflict {
t.Errorf("full: status = %d, want 409\nbody: %s", w.Code, w.Body.String())
}
}
func TestKioskUnclaim(t *testing.T) {
_, mux, token := setupKiosk(t)
// Claim then unclaim
req := httptest.NewRequest("POST", "/api/v/"+token+"/shifts/1", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
req = httptest.NewRequest("DELETE", "/api/v/"+token+"/shifts/1", nil)
w = httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("unclaim: status = %d", w.Code)
}
result := parseJSON(t, w)
shifts := result["shifts"].([]any)
if len(shifts) != 0 {
t.Errorf("after unclaim: shifts = %d, want 0", len(shifts))
}
}