package api import ( "testing" "time" "github.com/jackc/pgx/v5/pgtype" "git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq" ) func dupUUID(b byte) pgtype.UUID { var u pgtype.UUID u.Bytes[15] = b u.Valid = true return u } func dupTS(t time.Time) pgtype.Timestamptz { return pgtype.Timestamptz{Time: t, Valid: true} } // Rows arrive one per member, members of a group together. The fold must keep // groups apart, keep the query's order, and propose each group's survivor from // its own members only. func TestFoldDuplicateGroups(t *testing.T) { older := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) newer := older.Add(48 * time.Hour) ber := float32(0.04) rows := []dbq.ListPendingDuplicateGroupMembersRow{ // Group 1: identical audio, sizes tie, the older copy should be kept. {GroupID: dupUUID(1), Tier: "exact", DetectedAt: dupTS(newer), TrackID: dupUUID(10), Title: "WWW", FileFormat: "mp3", FileSize: 6_900_000, DurationMs: 215_400, AddedAt: dupTS(newer), PlayCount: 3}, {GroupID: dupUUID(1), Tier: "exact", DetectedAt: dupTS(newer), TrackID: dupUUID(11), Title: "WWW", FileFormat: "mp3", FileSize: 6_900_000, DurationMs: 215_400, AddedAt: dupTS(older), LikeCount: 1}, // Group 2: the same recording, FLAC against MP3. {GroupID: dupUUID(2), Tier: "acoustic", WorstBitErrorRate: &ber, DetectedAt: dupTS(older), TrackID: dupUUID(20), Title: "Lovesick", FileFormat: "mp3", FileSize: 9_000_000, DurationMs: 198_000, AddedAt: dupTS(older)}, {GroupID: dupUUID(2), Tier: "acoustic", WorstBitErrorRate: &ber, DetectedAt: dupTS(older), TrackID: dupUUID(21), Title: "Lovesick", FileFormat: "flac", FileSize: 30_000_000, DurationMs: 198_000, AddedAt: dupTS(newer)}, } got := foldDuplicateGroups(rows) if len(got) != 2 { t.Fatalf("folded %d groups, want 2", len(got)) } g1, g2 := got[0], got[1] if g1.ID != uuidToString(dupUUID(1)) || len(g1.Members) != 2 || g1.WorstBitErrorRate != nil { t.Fatalf("group 1 = %+v, want the exact pair with no score", g1) } if g1.SurvivorTrackID != uuidToString(dupUUID(11)) || g1.SurvivorReason != "in the library longest" { t.Errorf("group 1 survivor = (%s, %q), want the older copy", g1.SurvivorTrackID, g1.SurvivorReason) } if g1.Members[0].DurationSec != 215 || g1.Members[0].PlayCount != 3 || g1.Members[1].LikeCount != 1 { t.Errorf("group 1 members lost their facts: %+v", g1.Members) } if g2.Tier != "acoustic" || g2.WorstBitErrorRate == nil || *g2.WorstBitErrorRate != ber { t.Fatalf("group 2 = %+v, want the acoustic pair with its score", g2) } // Chosen from group 2's own members: a survivor leaking across groups is // exactly what a wrong fold boundary would produce. if g2.SurvivorTrackID != uuidToString(dupUUID(21)) || g2.SurvivorReason != "lossless (flac)" { t.Errorf("group 2 survivor = (%s, %q), want the FLAC copy", g2.SurvivorTrackID, g2.SurvivorReason) } }