package database import ( "database/sql" "path/filepath" "testing" "git.noroutes.net/NoRoutes/vibetunes/internal/audiofeatures" ) func openAudioFeaturesTestDB(t *testing.T) (*sql.DB, string) { t.Helper() db, err := Open(filepath.Join(t.TempDir(), "test.db")) if err != nil { t.Fatalf("Open: %v", err) } t.Cleanup(func() { db.Close() }) folder, err := CreateMusicFolder(db, "Music", "/music", "music") if err != nil { t.Fatal(err) } mf, err := CreateMediaFile(db, folder.ID, "song.mp3", 100, 0, "h", "mp3", "song", 10, 128, 44100, 2) if err != nil { t.Fatal(err) } song, err := UpsertSong(db, UpsertSongInput{MediaFileID: mf.ID, Title: "Song"}) if err != nil { t.Fatal(err) } return db, song.ID } func TestUpsertAndGetAudioFeatures(t *testing.T) { db, songID := openAudioFeaturesTestDB(t) f := &audiofeatures.Features{ TempoBPM: 120, TempoValid: true, Key: "C major", LoudnessLUFS: -14, SpectralCentroidHz: 1500, SpectralRolloffHz: 4000, ZeroCrossingRate: 0.05, Chroma: [12]float64{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, } if err := UpsertAudioFeatures(db, songID, f); err != nil { t.Fatalf("UpsertAudioFeatures: %v", err) } got, err := GetAudioFeatures(db, songID) if err != nil { t.Fatalf("GetAudioFeatures: %v", err) } if got == nil { t.Fatal("expected a row, got nil") } if !got.TempoValid || got.TempoBPM != 120 { t.Errorf("tempo = %v/%v, want 120/valid", got.TempoBPM, got.TempoValid) } if got.MusicalKey != "C major" { t.Errorf("key = %q", got.MusicalKey) } if got.LoudnessLUFS != -14 { t.Errorf("loudness = %v", got.LoudnessLUFS) } if got.Extra.SpectralCentroidHz != 1500 || got.Extra.Chroma[0] != 1 { t.Errorf("extra = %+v", got.Extra) } } func TestUpsertAudioFeaturesOverwrites(t *testing.T) { db, songID := openAudioFeaturesTestDB(t) if err := UpsertAudioFeatures(db, songID, &audiofeatures.Features{Key: "C major"}); err != nil { t.Fatal(err) } if err := UpsertAudioFeatures(db, songID, &audiofeatures.Features{Key: "D minor"}); err != nil { t.Fatal(err) } got, err := GetAudioFeatures(db, songID) if err != nil { t.Fatal(err) } if got.MusicalKey != "D minor" { t.Errorf("key = %q, want overwrite to D minor", got.MusicalKey) } } func TestGetAudioFeaturesNotAnalyzed(t *testing.T) { db, songID := openAudioFeaturesTestDB(t) got, err := GetAudioFeatures(db, songID) if err != nil { t.Fatal(err) } if got != nil { t.Errorf("expected nil for an unanalyzed song, got %+v", got) } } func TestListAllAudioFeatures(t *testing.T) { db, songID := openAudioFeaturesTestDB(t) if err := UpsertAudioFeatures(db, songID, &audiofeatures.Features{TempoValid: true, TempoBPM: 90}); err != nil { t.Fatal(err) } all, err := ListAllAudioFeatures(db) if err != nil { t.Fatal(err) } if len(all) != 1 || all[0].SongID != songID { t.Fatalf("all = %+v", all) } if len(all[0].Vector()) != 17 { t.Fatalf("Vector length = %d, want 17", len(all[0].Vector())) } }