Added tests, shift 'delete'. Fixed overnight shifts, sync, error handling.
This commit is contained in:
parent
7fb5716a35
commit
10037add99
21 changed files with 2522 additions and 40 deletions
110
handle_sync_test.go
Normal file
110
handle_sync_test.go
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSyncPullFull(t *testing.T) {
|
||||
app := testApp(t)
|
||||
admin := testAdminUser(t, app)
|
||||
token := testToken(t, app, admin)
|
||||
mux := testMux(app)
|
||||
|
||||
app.createAttendee(Attendee{Name: "Alice"})
|
||||
dept, _ := app.createDepartment(Department{Name: "Gate"})
|
||||
deptID := dept.ID
|
||||
app.createVolunteer(Volunteer{Name: "Alice", DepartmentID: &deptID})
|
||||
app.createShift(Shift{DepartmentID: deptID, Name: "AM", Day: "2026-03-15", StartTime: "08:00", EndTime: "12:00"})
|
||||
|
||||
req := testAuthRequest("GET", "/api/sync/pull", nil, token)
|
||||
w := httptest.NewRecorder()
|
||||
mux.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("status = %d", w.Code)
|
||||
}
|
||||
result := parseJSON(t, w)
|
||||
|
||||
if result["server_time"] == nil {
|
||||
t.Error("missing server_time")
|
||||
}
|
||||
attendees := result["attendees"].([]any)
|
||||
if len(attendees) != 1 {
|
||||
t.Errorf("attendees = %d, want 1", len(attendees))
|
||||
}
|
||||
depts := result["departments"].([]any)
|
||||
if len(depts) != 1 {
|
||||
t.Errorf("departments = %d, want 1", len(depts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncPullIncremental(t *testing.T) {
|
||||
app := testApp(t)
|
||||
admin := testAdminUser(t, app)
|
||||
token := testToken(t, app, admin)
|
||||
mux := testMux(app)
|
||||
|
||||
app.createAttendee(Attendee{Name: "Alice"})
|
||||
// Backdate Alice so she falls before the "since" cutoff
|
||||
app.db.Exec(`UPDATE attendees SET updated_at = '2026-01-01T00:00:00Z' WHERE name = 'Alice'`)
|
||||
|
||||
since := "2026-01-01T12:00:00Z"
|
||||
|
||||
// Bob created with default updated_at (now), which is after our since
|
||||
app.createAttendee(Attendee{Name: "Bob"})
|
||||
|
||||
req := testAuthRequest("GET", "/api/sync/pull?since="+since, nil, token)
|
||||
w := httptest.NewRecorder()
|
||||
mux.ServeHTTP(w, req)
|
||||
|
||||
result := parseJSON(t, w)
|
||||
attendees := result["attendees"].([]any)
|
||||
// Should only include Bob (created after `since`)
|
||||
if len(attendees) != 1 {
|
||||
t.Errorf("incremental: got %d attendees, want 1", len(attendees))
|
||||
}
|
||||
if len(attendees) == 1 {
|
||||
a := attendees[0].(map[string]any)
|
||||
if a["name"] != "Bob" {
|
||||
t.Errorf("name = %v, want Bob", a["name"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncPullIncludesSoftDeleted(t *testing.T) {
|
||||
app := testApp(t)
|
||||
admin := testAdminUser(t, app)
|
||||
token := testToken(t, app, admin)
|
||||
mux := testMux(app)
|
||||
|
||||
a, _ := app.createAttendee(Attendee{Name: "Alice"})
|
||||
// Backdate Alice's creation so the since cutoff is between creation and deletion
|
||||
app.db.Exec(`UPDATE attendees SET updated_at = '2026-01-01T00:00:00Z' WHERE id = ?`, a.ID)
|
||||
|
||||
since := "2026-01-01T12:00:00Z"
|
||||
|
||||
// Delete updates updated_at to now(), which is after our since
|
||||
app.deleteAttendee(a.ID)
|
||||
|
||||
req := testAuthRequest("GET", "/api/sync/pull?since="+since, nil, token)
|
||||
w := httptest.NewRecorder()
|
||||
mux.ServeHTTP(w, req)
|
||||
|
||||
var result struct {
|
||||
Attendees []struct {
|
||||
ID int `json:"id"`
|
||||
DeletedAt *string `json:"deleted_at"`
|
||||
} `json:"attendees"`
|
||||
}
|
||||
json.Unmarshal(w.Body.Bytes(), &result)
|
||||
|
||||
if len(result.Attendees) != 1 {
|
||||
t.Fatalf("got %d attendees, want 1", len(result.Attendees))
|
||||
}
|
||||
if result.Attendees[0].DeletedAt == nil {
|
||||
t.Error("deleted_at should be set for soft-deleted record")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue