package handler import ( "database/sql/driver" "encoding/json" "past/internal/httpapi" "past/internal/testdb" "strings" "testing" ) func TestPagedListsBoundQueriesAndKeepMemberScope(t *testing.T) { for _, tt := range []struct { name, uri, table string handler httpapi.HandlerFunc args []any extra []testdb.Step }{ {"songs", "/api/songsList", "songs", songsList, []any{int64(1), int64(3), int64(20), int64(20)}, nil}, {"albums", "/api/albumList", "albums", albumList, []any{int64(1), int64(3), int64(20), int64(20)}, []testdb.Step{{Match: "FROM photos p JOIN albums a", Columns: []string{"c"}, Rows: [][]driver.Value{{int64(83)}}}}}, {"photos", "/api/photos?album_id=9", "photos", photos, []any{int64(1), int64(3), "9", int64(20), int64(20)}, nil}, {"timeline", "/api/timelineList", "timeline", timelineList, []any{int64(1), int64(3), int64(20), int64(20)}, []testdb.Step{{Match: "event_date >= TO_CHAR(CURRENT_DATE,'YYYY-MM-DD')", Columns: []string{"id", "event_date"}, Rows: [][]driver.Value{{int64(50), "2026-12-01"}}}}}, {"checkins", "/api/checkinList", "checkins", checkinList, []any{int64(1), int64(3), int64(20), int64(20)}, []testdb.Step{{Match: "FROM checkin_logs WHERE couple_id = ?", Args: []any{int64(22)}, Columns: []string{"c"}, Rows: [][]driver.Value{{int64(90)}}}}}, {"comments", "/api/diaryCommentList?diary_id=9", "diary_comments", diaryCommentList, []any{int64(9), int64(1), int64(3), int64(20), int64(20)}, nil}, } { t.Run(tt.name, func(t *testing.T) { uri := tt.uri if strings.Contains(uri, "?") { uri += "&" } else { uri += "?" } uri += "page=2&page_size=20" steps := []testdb.Step{uidStep(1), cidStep(22), {Match: "SELECT user1_id, user2_id FROM couples", Columns: []string{"user1_id", "user2_id"}, Rows: [][]driver.Value{{int64(1), int64(3)}}}, {Match: "LIMIT ? OFFSET ?", Args: tt.args, Check: func(q string, _ []any) error { if !strings.Contains(q, "IN (?, ?)") { t.Errorf("missing member predicate: %s", q) } return nil }, Columns: []string{"id"}, Rows: [][]driver.Value{{int64(21)}}}, {Match: "SELECT COUNT(*) as c FROM " + tt.table, Columns: []string{"c"}, Rows: [][]driver.Value{{int64(41)}}}} steps = append(steps, tt.extra...) c, out, _ := permissionCtx(t, "GET", uri, "", steps) tt.handler(c) if out.Code != 200 { t.Fatalf("status %d: %s", out.Code, out.Body.String()) } var result map[string]any if err := json.Unmarshal(out.Body.Bytes(), &result); err != nil { t.Fatal(err) } if result["total"] != float64(41) || result["page"] != float64(2) || result["has_more"] != true { t.Fatalf("wrong pagination: %s", out.Body.String()) } if len(result["items"].([]any)) != 1 { t.Fatal("wrong items") } if tt.name == "albums" && result["photo_total"] != float64(83) { t.Fatal("photo total must cover all albums") } }) } } func TestListPaginationCapsPageSizeAndClampsPage(t *testing.T) { c, _, _ := permissionCtx(t, "GET", "/api/list?page=-1&page_size=9999", "", nil) suffix, args := listWindow(c) if suffix != " LIMIT ? OFFSET ?" || args[0] != int64(100) || args[1] != int64(0) { t.Fatalf("unsafe window %s %v", suffix, args) } }