168 lines
4.9 KiB
Go
168 lines
4.9 KiB
Go
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 participant + ticket with code
|
|
p, _ := app.createParticipant(Participant{Email: "titania@test.com", PreferredName: "Titania"})
|
|
token, _ := app.generateUniqueToken()
|
|
tk, _ := app.createTicket(Ticket{ParticipantID: &p.ID, Name: "Titania", Source: "manual", Code: &token})
|
|
_ = tk
|
|
|
|
// Create linked volunteer via participant_id
|
|
app.createVolunteer(Volunteer{Name: "Titania", ParticipantID: &p.ID, DepartmentID: &deptID})
|
|
|
|
// 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
|
|
other, _ := app.createVolunteer(Volunteer{Name: "Other", 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))
|
|
}
|
|
}
|