package server import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "music-server/internal/backend" "music-server/internal/db" "music-server/internal/db/repository" "github.com/labstack/echo/v5" "github.com/stretchr/testify/require" ) // TestStatisticsEndpoints tests the statistics API endpoints func TestStatisticsEndpoints(t *testing.T) { // Skip if test database not configured e := StartTestServer(t) if e == nil { t.Skip("Test database not configured") } // Get token first token := getTestToken(t, e) if token == "" { t.Skip("Could not get test token") } // Test /api/v1/statistics/summary req := httptest.NewRequest(http.MethodGet, "/api/v1/statistics/summary", nil) req.Header.Set("Authorization", "Bearer "+token) rec := httptest.NewRecorder() e.ServeHTTP(rec, req) require.Equal(t, http.StatusOK, rec.Code) var summary backend.StatisticsSummary err := json.Unmarshal(rec.Body.Bytes(), &summary) require.NoError(t, err) require.NotNil(t, summary) } // TestPartialMigrationThenSyncThenComplete tests migration workflow // Note: This test requires the database to be in a specific state // It tests: partial migration → data insert → sync → complete migration func TestPartialMigrationThenSyncThenComplete(t *testing.T) { // This test is complex and requires careful setup // For now, we test the final state: all migrations + sync e := StartTestServer(t) if e == nil { t.Skip("Test database not configured") } // Get token token := getTestToken(t, e) if token == "" { t.Skip("Could not get test token") } // Insert test data manually (5 soundtracks with songs) insertTestData(t) // Run sync to ensure data is properly loaded req := httptest.NewRequest(http.MethodGet, "/sync/new", nil) req.Header.Set("Authorization", "Bearer "+token) rec := httptest.NewRecorder() e.ServeHTTP(rec, req) require.Equal(t, http.StatusOK, rec.Code) // Wait for sync to complete if !waitForSyncCompletion(t, e, 60) { t.Error("Sync did not complete within timeout") } // Verify data via statistics endpoint req = httptest.NewRequest(http.MethodGet, "/api/v1/statistics/summary", nil) req.Header.Set("Authorization", "Bearer "+token) rec = httptest.NewRecorder() e.ServeHTTP(rec, req) require.Equal(t, http.StatusOK, rec.Code) var summary backend.StatisticsSummary err := json.Unmarshal(rec.Body.Bytes(), &summary) require.NoError(t, err) // After sync with /sync/new, only soundtracks matching filesystem remain // testMusic has 3 games require.Equal(t, int64(3), summary.TotalGames) } // insertTestData inserts 5 test soundtracks with songs into the database func insertTestData(t *testing.T) { if db.TestDatabase == nil || db.TestDatabase.Pool == nil { t.Skip("Test database not initialized") return } ctx := context.Background() queries := repository.New(db.TestDatabase.Pool) // Insert 5 soundtracks soundtracks := []struct { name string path string }{ {"Test Soundtrack 1", "/path/to/soundtrack1"}, {"Test Soundtrack 2", "/path/to/soundtrack2"}, {"Test Soundtrack 3", "/path/to/soundtrack3"}, {"Test Soundtrack 4", "/path/to/soundtrack4"}, {"Test Soundtrack 5", "/path/to/soundtrack5"}, } for _, st := range soundtracks { _, err := queries.InsertSoundtrack(ctx, repository.InsertSoundtrackParams{ SoundtrackName: st.name, Path: st.path, Hash: "test-hash-" + st.name, }) require.NoError(t, err, "Failed to insert soundtrack: %s", st.name) } // Get soundtrack IDs soundtrackIDs, err := queries.FindAllSoundtracks(ctx) require.NoError(t, err) require.GreaterOrEqual(t, len(soundtrackIDs), 5) // Insert songs for each soundtrack songData := []struct { soundtrackID int32 songs []string }{ {soundtrackIDs[0].ID, []string{"Song A", "Song B"}}, {soundtrackIDs[1].ID, []string{"Song C", "Song D"}}, {soundtrackIDs[2].ID, []string{"Song E"}}, {soundtrackIDs[3].ID, []string{"Song F", "Song G", "Song H"}}, {soundtrackIDs[4].ID, []string{"Song I"}}, } for _, sd := range songData { for _, songName := range sd.songs { err := queries.AddSong(ctx, repository.AddSongParams{ SoundtrackID: sd.soundtrackID, SongName: songName, Path: "/path/to/" + songName + ".mp3", }) require.NoError(t, err, "Failed to insert song: %s", songName) } } t.Logf("Inserted %d soundtracks with %d total songs", len(soundtracks), 8) } // getTestToken gets a valid token for testing func getTestToken(t *testing.T, e *echo.Echo) string { reqBody := `{"client_type": "test"}` req := httptest.NewRequest(http.MethodPost, "/api/v1/token", strings.NewReader(reqBody)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() e.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Logf("Failed to get token: %s", rec.Body.String()) return "" } var resp struct { Token string `json:"token"` } err := json.Unmarshal(rec.Body.Bytes(), &resp) require.NoError(t, err) return resp.Token }