Use UUID models and soft deletion, persist file relationships with deduplicated monthly MinIO objects, and add admin file management. Update mini-program media handling and add local dev configuration.
72 lines
3.2 KiB
Go
72 lines
3.2 KiB
Go
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)
|
|
}
|
|
}
|