|
|
@@ -0,0 +1,177 @@
|
|
|
+package repository
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "testing"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/models"
|
|
|
+)
|
|
|
+
|
|
|
+func TestPlaceRepo_List_WithStatusFilter(t *testing.T) {
|
|
|
+ if testPool == nil {
|
|
|
+ t.Skip("DATABASE_URL_TEST not set")
|
|
|
+ }
|
|
|
+ cleanTables(t, "places", "users")
|
|
|
+
|
|
|
+ user := insertTestUser(t)
|
|
|
+ ctx := context.Background()
|
|
|
+ repo := NewPlaceRepo(testPool)
|
|
|
+
|
|
|
+ insertTestPlace(t, user.ID, nil)
|
|
|
+ insertTestPlace(t, user.ID, nil)
|
|
|
+
|
|
|
+ pending := insertTestPlace(t, user.ID, nil)
|
|
|
+ if _, err := testPool.Exec(ctx,
|
|
|
+ `UPDATE places SET status = $1 WHERE id = $2`, "pending_moderation", pending.ID); err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ t.Run("filter by status published", func(t *testing.T) {
|
|
|
+ results, err := repo.List(ctx, models.PlaceFilter{Status: "published"})
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("List: %v", err)
|
|
|
+ }
|
|
|
+ if len(results) < 2 {
|
|
|
+ t.Errorf("expected at least 2 published places, got %d", len(results))
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ t.Run("filter by status pending_moderation", func(t *testing.T) {
|
|
|
+ results, err := repo.List(ctx, models.PlaceFilter{Status: "pending_moderation"})
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("List: %v", err)
|
|
|
+ }
|
|
|
+ if len(results) != 1 {
|
|
|
+ t.Errorf("expected 1 pending_moderation place, got %d", len(results))
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ t.Run("filter by both status and type", func(t *testing.T) {
|
|
|
+ results, err := repo.List(ctx, models.PlaceFilter{Type: "studio", Status: "published"})
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("List: %v", err)
|
|
|
+ }
|
|
|
+ if len(results) < 2 {
|
|
|
+ t.Errorf("expected at least 2 published studios, got %d", len(results))
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+func TestPlaceRepo_List_CursorPagination(t *testing.T) {
|
|
|
+ if testPool == nil {
|
|
|
+ t.Skip("DATABASE_URL_TEST not set")
|
|
|
+ }
|
|
|
+ cleanTables(t, "places", "users")
|
|
|
+
|
|
|
+ user := insertTestUser(t)
|
|
|
+ repo := NewPlaceRepo(testPool)
|
|
|
+
|
|
|
+ // Create 5 places with slightly different created_at times
|
|
|
+ for i := 0; i < 5; i++ {
|
|
|
+ insertTestPlace(t, user.ID, nil)
|
|
|
+ time.Sleep(5 * time.Millisecond) // ensure different created_at
|
|
|
+ }
|
|
|
+
|
|
|
+ t.Run("first page returns limit items with hasMore", func(t *testing.T) {
|
|
|
+ results, err := repo.List(context.Background(), models.PlaceFilter{
|
|
|
+ Limit_: 2,
|
|
|
+ Status: "published",
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("List: %v", err)
|
|
|
+ }
|
|
|
+ if len(results) != 3 { // LIMIT 2 + 1 (extra for hasMore detection)
|
|
|
+ t.Errorf("expected 3 results (2 + 1 extra), got %d", len(results))
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ t.Run("cursor returns correct page", func(t *testing.T) {
|
|
|
+ // Get first page
|
|
|
+ page1, err := repo.List(context.Background(), models.PlaceFilter{
|
|
|
+ Limit_: 2,
|
|
|
+ Status: "published",
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("List page1: %v", err)
|
|
|
+ }
|
|
|
+ if len(page1) < 2 {
|
|
|
+ t.Skip("not enough places for cursor test")
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get second page using cursor from last item of page1
|
|
|
+ last := page1[1]
|
|
|
+ cursorCreatedAt := last.CreatedAt
|
|
|
+ page2, err := repo.List(context.Background(), models.PlaceFilter{
|
|
|
+ Cursor: last.ID,
|
|
|
+ CursorCreatedAt: &cursorCreatedAt,
|
|
|
+ Limit_: 2,
|
|
|
+ Status: "published",
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("List page2 with cursor: %v", err)
|
|
|
+ }
|
|
|
+ if len(page2) == 0 {
|
|
|
+ t.Error("expected at least 1 result after cursor, got 0")
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+func TestPlaceRepo_GetTagsBatch(t *testing.T) {
|
|
|
+ if testPool == nil {
|
|
|
+ t.Skip("DATABASE_URL_TEST not set")
|
|
|
+ }
|
|
|
+ cleanTables(t, "place_tags", "tags", "places", "users")
|
|
|
+
|
|
|
+ user := insertTestUser(t)
|
|
|
+ ctx := context.Background()
|
|
|
+ repo := NewPlaceRepo(testPool)
|
|
|
+
|
|
|
+ place1 := insertTestPlace(t, user.ID, nil)
|
|
|
+ place2 := insertTestPlace(t, user.ID, nil)
|
|
|
+
|
|
|
+ tag1 := insertTestTag(t)
|
|
|
+ tag2 := insertTestTag(t)
|
|
|
+
|
|
|
+ insertTestPlaceTag(t, place1.ID, tag1.ID)
|
|
|
+ insertTestPlaceTag(t, place1.ID, tag2.ID)
|
|
|
+ insertTestPlaceTag(t, place2.ID, tag1.ID)
|
|
|
+
|
|
|
+ tagsMap, err := repo.GetTagsBatch(ctx, []string{place1.ID, place2.ID})
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("GetTagsBatch: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(tagsMap[place1.ID]) != 2 {
|
|
|
+ t.Errorf("place1 expected 2 tags, got %d", len(tagsMap[place1.ID]))
|
|
|
+ }
|
|
|
+ if len(tagsMap[place2.ID]) != 1 {
|
|
|
+ t.Errorf("place2 expected 1 tag, got %d", len(tagsMap[place2.ID]))
|
|
|
+ }
|
|
|
+ if len(tagsMap) != 2 {
|
|
|
+ t.Errorf("expected 2 place entries in map, got %d", len(tagsMap))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestPlaceRepo_GetTagsBatch_EmptyInput(t *testing.T) {
|
|
|
+ if testPool == nil {
|
|
|
+ t.Skip("DATABASE_URL_TEST not set")
|
|
|
+ }
|
|
|
+
|
|
|
+ repo := NewPlaceRepo(testPool)
|
|
|
+ result, err := repo.GetTagsBatch(context.Background(), nil)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("GetTagsBatch with nil: %v", err)
|
|
|
+ }
|
|
|
+ if len(result) != 0 {
|
|
|
+ t.Errorf("expected empty map, got %d entries", len(result))
|
|
|
+ }
|
|
|
+
|
|
|
+ result, err = repo.GetTagsBatch(context.Background(), []string{})
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("GetTagsBatch with empty: %v", err)
|
|
|
+ }
|
|
|
+ if len(result) != 0 {
|
|
|
+ t.Errorf("expected empty map, got %d entries", len(result))
|
|
|
+ }
|
|
|
+}
|