Files
MusicServer/internal/server/sync_handler_test.go
T
Sansan a446dad7b6
Build / build (push) Successful in 48s
Make pool and poolSong global variables
2026-05-26 20:54:12 +02:00

290 lines
8.0 KiB
Go

package server
import (
"encoding/json"
"net/http"
"os"
"testing"
"time"
"music-server/internal/backend"
"music-server/internal/db"
"music-server/internal/db/repository"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
)
// waitForSyncCompletion polls the sync progress endpoint until sync is complete
// Returns true if sync completed, false if timeout
func waitForSyncCompletion(t *testing.T, e *echo.Echo, maxAttempts int) bool {
for i := 0; i < maxAttempts; i++ {
progressResp := MakeTestRequest(t, e, "GET", "/sync/progress")
assert.Equal(t, http.StatusOK, progressResp.Code)
// Try to parse as ProgressResponse first (while syncing)
var progress backend.ProgressResponse
err := json.Unmarshal(progressResp.Body.Bytes(), &progress)
if err == nil && progress.Progress != "" {
// Successfully parsed as ProgressResponse with non-empty progress
t.Logf("Sync progress: %s%%", progress.Progress)
if progress.Progress == "100" {
t.Log("Sync completed!")
// Wait for Syncing flag to be updated
for j := 0; j < 50; j++ {
if !backend.Syncing {
return true
}
time.Sleep(50 * time.Millisecond)
}
return false
}
} else {
// If Progress is empty or parse failed, it might be a SyncResponse (sync already completed)
var result backend.SyncResponse
err2 := json.Unmarshal(progressResp.Body.Bytes(), &result)
if err2 == nil {
t.Log("Sync already completed")
// Wait for Syncing flag to be updated
for j := 0; j < 50; j++ {
if !backend.Syncing {
return true
}
time.Sleep(50 * time.Millisecond)
}
return false
}
}
time.Sleep(1 * time.Second)
}
return false
}
// TestSyncPopulatesDatabase verifies that sync populates the database with games
func TestSyncPopulatesDatabase(t *testing.T) {
db.TestSetupDB(t)
defer db.TestTearDownDB(t)
// Debug: Check MUSIC_PATH
t.Logf("MUSIC_PATH: %s", os.Getenv("MUSIC_PATH"))
e := StartTestServer(t)
// Clear any existing data first
db.TestClearDatabase(t)
// Before sync - should have no games
repo := repository.New(db.Dbpool)
gamesBefore, err := repo.FindAllGames(db.Ctx)
assert.NoError(t, err)
beforeCount := len(gamesBefore)
t.Logf("Games before sync: %d", beforeCount)
assert.Equal(t, 0, beforeCount, "Database should be empty after clear")
// Run sync
resp := MakeTestRequest(t, e, "GET", "/sync/full")
assert.Equal(t, http.StatusOK, resp.Code)
// Wait for sync to complete
if !waitForSyncCompletion(t, e, 60) {
t.Error("Sync did not complete within timeout")
}
// After sync - should have games
gamesAfter, err := repo.FindAllGames(db.Ctx)
assert.NoError(t, err)
afterCount := len(gamesAfter)
t.Logf("Games after sync: %d", afterCount)
// Should have more games than before (unless database was already populated)
assert.True(t, afterCount > 0, "Database should have games after sync")
}
// TestSyncMakesDifference verifies that sync actually changes the database state
func TestSyncMakesDifference(t *testing.T) {
db.TestSetupDB(t)
defer db.TestTearDownDB(t)
e := StartTestServer(t)
// Clear any existing data first
db.TestClearDatabase(t)
// Before sync - should have no games
repo := repository.New(db.Dbpool)
gamesBefore, err := repo.FindAllGames(db.Ctx)
assert.NoError(t, err)
assert.Equal(t, 0, len(gamesBefore), "Should have no games before sync")
// Run sync
resp := MakeTestRequest(t, e, "GET", "/sync/full")
assert.Equal(t, http.StatusOK, resp.Code)
// Wait for sync to complete
if !waitForSyncCompletion(t, e, 60) {
t.Error("Sync did not complete within timeout")
}
// After sync - should have games
gamesAfter, err := repo.FindAllGames(db.Ctx)
assert.NoError(t, err)
assert.True(t, len(gamesAfter) > 0, "Should have games after sync")
}
// TestSyncProgress verifies the sync progress endpoint
func TestSyncProgress(t *testing.T) {
db.TestSetupDB(t)
defer db.TestTearDownDB(t)
e := StartTestServer(t)
// Start sync in background
go MakeTestRequest(t, e, "GET", "/sync/full")
// Poll progress endpoint
maxAttempts := 30
foundComplete := false
for i := 0; i < maxAttempts; i++ {
resp := MakeTestRequest(t, e, "GET", "/sync/progress")
assert.Equal(t, http.StatusOK, resp.Code)
// Try ProgressResponse first
var progress backend.ProgressResponse
err := json.Unmarshal(resp.Body.Bytes(), &progress)
if err == nil && progress.Progress != "" {
// Successfully parsed as ProgressResponse with non-empty progress
t.Logf("Sync progress: %s%%", progress.Progress)
// Verify we get valid progress values
if progress.Progress != "0" {
// Sync is making progress
}
if progress.Progress == "100" {
foundComplete = true
break
}
} else {
// If Progress is empty or parse failed, it might be a SyncResponse (sync already completed)
var result backend.SyncResponse
err2 := json.Unmarshal(resp.Body.Bytes(), &result)
if err2 == nil {
foundComplete = true
break
}
}
time.Sleep(1 * time.Second)
}
// Note: foundNonZero might be false if sync completed too quickly
// So we only assert that sync completed
assert.True(t, foundComplete, "Should have seen completion")
}
// TestSyncGamesNewOnlyChanges verifies the incremental sync endpoint
func TestSyncGamesNewOnlyChanges(t *testing.T) {
db.TestSetupDB(t)
defer db.TestTearDownDB(t)
e := StartTestServer(t)
// Run full sync first
MakeTestRequest(t, e, "GET", "/sync/full")
// Wait for it to complete
if !waitForSyncCompletion(t, e, 60) {
t.Error("Initial sync did not complete within timeout")
return
}
// Get initial count
repo := repository.New(db.Dbpool)
gamesBefore, _ := repo.FindAllGames(db.Ctx)
beforeCount := len(gamesBefore)
// Run incremental sync (should not change count if nothing changed)
resp := MakeTestRequest(t, e, "GET", "/sync/new")
assert.Equal(t, http.StatusOK, resp.Code)
// Wait a bit
time.Sleep(2 * time.Second)
// Count should be the same
gamesAfter, _ := repo.FindAllGames(db.Ctx)
afterCount := len(gamesAfter)
// Note: This might not be exactly equal due to timing, but should be close
t.Logf("Games before incremental sync: %d, after: %d", beforeCount, afterCount)
}
// TestResetGames verifies the reset endpoint clears the database
// RUN THIS LAST
func TestResetGames(t *testing.T) {
db.TestSetupDB(t)
defer db.TestTearDownDB(t)
e := StartTestServer(t)
// First ensure we have data
repo := repository.New(db.Dbpool)
gamesBefore, _ := repo.FindAllGames(db.Ctx)
beforeCount := len(gamesBefore)
if beforeCount == 0 {
// Run sync to populate
MakeTestRequest(t, e, "GET", "/sync/full")
if !waitForSyncCompletion(t, e, 60) {
t.Error("Sync did not complete within timeout")
return
}
gamesBefore, _ = repo.FindAllGames(db.Ctx)
beforeCount = len(gamesBefore)
}
t.Logf("Games before reset: %d", beforeCount)
assert.True(t, beforeCount > 0, "Should have games to reset")
// Call reset
resp := MakeTestRequest(t, e, "GET", "/sync/reset")
assert.Equal(t, http.StatusOK, resp.Code)
// Verify database is cleared
// Note: reset might take a moment to propagate
time.Sleep(1 * time.Second)
gamesAfter, _ := repo.FindAllGames(db.Ctx)
afterCount := len(gamesAfter)
t.Logf("Games after reset: %d", afterCount)
assert.Equal(t, 0, afterCount, "Database should be empty after reset")
}
// TestSyncGamesNewFull verifies the full sync endpoint
// RUN THIS LAST (before TestResetGames)
func TestSyncGamesNewFull(t *testing.T) {
db.TestSetupDB(t)
defer db.TestTearDownDB(t)
e := StartTestServer(t)
// Clear database first
db.TestClearDatabase(t)
// Run full sync
resp := MakeTestRequest(t, e, "GET", "/sync/full")
assert.Equal(t, http.StatusOK, resp.Code)
// Wait for sync to complete
if !waitForSyncCompletion(t, e, 60) {
t.Error("Full sync did not complete within timeout")
}
// Verify database is populated
repo := repository.New(db.Dbpool)
games, err := repo.FindAllGames(db.Ctx)
assert.NoError(t, err)
assert.True(t, len(games) > 0, "Database should be populated after full sync")
t.Logf("Full sync populated %d games", len(games))
}