Every comment naming a path in flutter_client/ now resolves to nothing, which is the failure mode this project has already been bitten by twice -- drift #572 came from delete.go describing behaviour it no longer had, and that same docstring was still wrong when it was fixed last week. A pointer to a deleted directory is the same thing in slower motion: the reader follows it, finds nothing, and cannot tell whether the comment is stale or they are looking in the wrong place. Three treatments, per what each comment was actually doing: - Naming a concept ("mirrors db.dart's CachedTracks Drift table"): keep the concept, drop the path. The Drift table is why the entity looks as it does; the file it lived in is not. - Pure port bookkeeping ("Mirrors <path>." and nothing else): deleted. Git history records the port; the comment only restated it. - Substance introduced by a pointer (a lifecycle list, a 200 px/s threshold, an inverted control-row placement): keep the substance, drop the lead-in. The two Go comments were the valuable ones and got more than a trim. They stated a live contract -- "field names match the client's FromJson helpers exactly, or fields are silently dropped" -- against a client that no longer exists. They now name the real consumer, SyncResponseWire.kt, and say why the failure is silent there too: kotlinx.serialization skips unknown keys, so a renamed field arrives as a default value rather than an error. The ticket counted 64 files by grepping flutter_client/. A second tier turned up during the sweep: 15 more references naming bare Dart files (player_bar.dart, now_playing_screen.dart:464, auth_provider.dart) with no directory prefix. Same dead tree, same treatment, folded in here. Comments only -- verified no non-comment line is touched in the diff.
104 lines
3.0 KiB
Go
104 lines
3.0 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
|
|
)
|
|
|
|
// These tests pin the wire-format keys for /api/library/sync upserts.
|
|
// Without them, sqlc model field-name drift or accidental
|
|
// `json.Marshal(rawStruct)` regressions would silently break the Android
|
|
// client, which reads these snake_case keys via @SerialName in
|
|
// models/wire/SyncResponseWire.kt. "Silently" is the operative word:
|
|
// kotlinx.serialization skips unknown keys, so a renamed field arrives as
|
|
// a default value rather than an error.
|
|
|
|
// validUUID is a deterministic test UUID — not a real value, just
|
|
// something that pgtype.UUID.Valid will accept.
|
|
var validUUID = pgtype.UUID{
|
|
Bytes: [16]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10},
|
|
Valid: true,
|
|
}
|
|
|
|
func assertJSONKeys(t *testing.T, label string, b []byte, want []string) {
|
|
t.Helper()
|
|
var m map[string]json.RawMessage
|
|
if err := json.Unmarshal(b, &m); err != nil {
|
|
t.Fatalf("%s: invalid json: %v\nbody: %s", label, err, b)
|
|
}
|
|
for _, k := range want {
|
|
if _, ok := m[k]; !ok {
|
|
t.Errorf("%s: missing key %q in %s", label, k, b)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestArtistSyncView_WireKeys(t *testing.T) {
|
|
a := dbq.Artist{ID: validUUID, Name: "Aphex Twin", SortName: "Aphex Twin"}
|
|
b, err := json.Marshal(toArtistSyncView(a))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assertJSONKeys(t, "artist", b, []string{
|
|
"id", "name", "sort_name", "mbid",
|
|
"artist_thumb_path", "artist_fanart_path",
|
|
})
|
|
}
|
|
|
|
func TestAlbumSyncView_WireKeys(t *testing.T) {
|
|
a := dbq.Album{ID: validUUID, ArtistID: validUUID, Title: "Drukqs", SortTitle: "Drukqs"}
|
|
b, err := json.Marshal(toAlbumSyncView(a))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assertJSONKeys(t, "album", b, []string{
|
|
"id", "artist_id", "title", "sort_title",
|
|
"release_date", "cover_art_path", "mbid",
|
|
})
|
|
}
|
|
|
|
func TestTrackSyncView_WireKeys(t *testing.T) {
|
|
tr := dbq.Track{
|
|
ID: validUUID, AlbumID: validUUID, ArtistID: validUUID,
|
|
Title: "Avril 14th", DurationMs: 121_000,
|
|
FilePath: "x", FileFormat: "flac",
|
|
}
|
|
b, err := json.Marshal(toTrackSyncView(tr))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assertJSONKeys(t, "track", b, []string{
|
|
"id", "album_id", "artist_id", "title", "duration_ms",
|
|
"track_number", "disc_number", "file_path", "file_format", "genre",
|
|
})
|
|
}
|
|
|
|
func TestPlaylistSyncView_WireKeys(t *testing.T) {
|
|
variant := "discover"
|
|
p := dbq.Playlist{
|
|
ID: validUUID, UserID: validUUID, Name: "Test",
|
|
IsPublic: true, TrackCount: 5, DurationSec: 600,
|
|
SystemVariant: &variant,
|
|
}
|
|
b, err := json.Marshal(toPlaylistSyncView(p))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assertJSONKeys(t, "playlist", b, []string{
|
|
"id", "user_id", "name", "description",
|
|
"is_public", "cover_path", "track_count",
|
|
"duration_sec", "system_variant",
|
|
})
|
|
// Sanity: system_variant carries the value, not just present.
|
|
var m map[string]any
|
|
_ = json.Unmarshal(b, &m)
|
|
if got := m["system_variant"]; got != "discover" {
|
|
t.Errorf("system_variant: want 'discover', got %v", got)
|
|
}
|
|
}
|