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") } }