test: add test data directories (testMusic and testCharacters)
This commit is contained in:
@@ -217,7 +217,7 @@ func syncGamesNew(full bool) {
|
||||
logging.GetLogger().Fatal("Failed to read music directory", zap.String("path", musicPath), zap.String("error", err.Error()))
|
||||
}
|
||||
|
||||
pool, _ := ants.NewPool(50, ants.WithPreAlloc(true))
|
||||
pool, _ := ants.NewPool(10, ants.WithPreAlloc(true))
|
||||
defer pool.Release()
|
||||
|
||||
foldersSynced = 0
|
||||
@@ -247,7 +247,7 @@ func checkBrokenSongsNew() {
|
||||
allSongs, err := repo.FetchAllSongs(db.Ctx)
|
||||
handleError("FetchAllSongs", err, "")
|
||||
var brokenWg sync.WaitGroup
|
||||
poolBroken, _ := ants.NewPool(50, ants.WithPreAlloc(true))
|
||||
poolBroken, _ := ants.NewPool(200, ants.WithPreAlloc(true))
|
||||
defer poolBroken.Release()
|
||||
|
||||
brokenWg.Add(len(allSongs))
|
||||
@@ -432,7 +432,7 @@ func newCheckSongs(entries []os.DirEntry, gameDir string, id int32) int32 {
|
||||
numberOfFiles := len(entries)
|
||||
|
||||
var songWg sync.WaitGroup
|
||||
poolSong, _ := ants.NewPool(numberOfFiles, ants.WithPreAlloc(true))
|
||||
poolSong, _ := ants.NewPool(10, ants.WithPreAlloc(true))
|
||||
defer poolSong.Release()
|
||||
|
||||
songWg.Add(numberOfFiles)
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestSetupDB initializes the test database using existing functions
|
||||
// It creates the database if it doesn't exist and runs migrations
|
||||
func TestSetupDB(t *testing.T) {
|
||||
host := os.Getenv("DB_HOST")
|
||||
port := os.Getenv("DB_PORT")
|
||||
user := os.Getenv("DB_USERNAME")
|
||||
password := os.Getenv("DB_PASSWORD")
|
||||
dbname := os.Getenv("DB_NAME")
|
||||
|
||||
if host == "" || port == "" || user == "" || password == "" || dbname == "" {
|
||||
t.Skip("Test database environment variables not set")
|
||||
}
|
||||
|
||||
// Use existing function to create database if it doesn't exist and run migrations
|
||||
Migrate_db(host, port, user, password, dbname)
|
||||
InitDB(host, port, user, password, dbname)
|
||||
}
|
||||
|
||||
// TestTearDownDB closes the test database connection
|
||||
func TestTearDownDB(t *testing.T) {
|
||||
CloseDb()
|
||||
}
|
||||
|
||||
// TestClearDatabase clears all data from the test database
|
||||
// Useful for running tests with a clean slate
|
||||
func TestClearDatabase(t *testing.T) {
|
||||
if Dbpool == nil {
|
||||
t.Skip("Database not initialized")
|
||||
}
|
||||
|
||||
// Clear all tables in reverse order to respect foreign keys
|
||||
// Note: This assumes the tables exist and have the expected structure
|
||||
tables := []string{
|
||||
"song_list",
|
||||
"song",
|
||||
"game",
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
_, err := Dbpool.Exec(Ctx, "TRUNCATE TABLE "+table+" CASCADE")
|
||||
if err != nil {
|
||||
t.Logf("Failed to truncate table %s: %v", table, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset sequences
|
||||
_, err := Dbpool.Exec(Ctx, "SELECT setval('game_id_seq', 1, false)")
|
||||
if err != nil {
|
||||
t.Logf("Failed to reset game_id_seq: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestCheckLatest verifies the /download endpoint
|
||||
func TestCheckLatest(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping external API test in short mode")
|
||||
}
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/download")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var version string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &version)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, version, "Should return version string")
|
||||
t.Logf("Latest version: %s", version)
|
||||
}
|
||||
|
||||
// TestListAssetsOfLatest verifies the /download/list endpoint
|
||||
func TestListAssetsOfLatest(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping external API test in short mode")
|
||||
}
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/download/list")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var assets []string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &assets)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, assets, "Should return list of assets")
|
||||
t.Logf("Found %d assets", len(assets))
|
||||
}
|
||||
|
||||
// TestDownloadLatestWindows verifies the /download/windows endpoint
|
||||
func TestDownloadLatestWindows(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping external API test in short mode")
|
||||
}
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/download/windows")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var url string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &url)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, url, "Should return download URL")
|
||||
assert.Contains(t, url, "http", "URL should be valid")
|
||||
t.Logf("Windows download URL: %s", url)
|
||||
}
|
||||
|
||||
// TestDownloadLatestLinux verifies the /download/linux endpoint
|
||||
func TestDownloadLatestLinux(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping external API test in short mode")
|
||||
}
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/download/linux")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var url string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &url)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, url, "Should return download URL")
|
||||
assert.Contains(t, url, "http", "URL should be valid")
|
||||
t.Logf("Linux download URL: %s", url)
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"music-server/internal/backend"
|
||||
"music-server/internal/db"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestHealthCheck verifies the health endpoint returns database status
|
||||
func TestHealthCheck(t *testing.T) {
|
||||
// Setup database
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/health")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var healthData map[string]string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &healthData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, healthData)
|
||||
assert.Equal(t, "up", healthData["status"])
|
||||
}
|
||||
|
||||
// TestGetVersion verifies the version endpoint returns version history
|
||||
func TestGetVersion(t *testing.T) {
|
||||
e := StartTestServer(t)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/version")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var versionData backend.VersionData
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &versionData)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, versionData.Version)
|
||||
assert.NotEmpty(t, versionData.Changelog)
|
||||
assert.NotEmpty(t, versionData.History)
|
||||
}
|
||||
|
||||
// TestGetCharacterList verifies the characters endpoint returns list of characters
|
||||
func TestGetCharacterList(t *testing.T) {
|
||||
e := StartTestServer(t)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/characters")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var characters []string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &characters)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, characters)
|
||||
// Should contain our test characters
|
||||
assert.Contains(t, characters, "char1.jpg")
|
||||
assert.Contains(t, characters, "char2.png")
|
||||
}
|
||||
|
||||
// TestGetCharacter verifies the character endpoint returns a file
|
||||
func TestGetCharacter(t *testing.T) {
|
||||
e := StartTestServer(t)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/character?name=char1.jpg")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
// The response should be the file content
|
||||
assert.NotEmpty(t, resp.Body.Bytes())
|
||||
}
|
||||
|
||||
// TestGetCharacterNotFound verifies handling of non-existent character
|
||||
func TestGetCharacterNotFound(t *testing.T) {
|
||||
e := StartTestServer(t)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/character?name=nonexistent.jpg")
|
||||
// Should return 404 or similar error
|
||||
assert.NotEqual(t, http.StatusOK, resp.Code)
|
||||
}
|
||||
|
||||
// TestDBTest verifies the database test endpoint
|
||||
func TestDBTest(t *testing.T) {
|
||||
// Setup database
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/dbtest")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
assert.Contains(t, resp.Body.String(), "TestedDB")
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"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"
|
||||
)
|
||||
|
||||
// ensureSyncRan ensures that sync has been run before testing music endpoints
|
||||
func ensureSyncRan(t *testing.T, e *echo.Echo) {
|
||||
repo := repository.New(db.Dbpool)
|
||||
games, err := repo.FindAllGames(db.Ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if len(games) == 0 {
|
||||
// Run sync
|
||||
t.Log("No games found, running sync first...")
|
||||
resp := MakeTestRequest(t, e, "GET", "/sync/full")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
// Wait for sync to complete
|
||||
maxAttempts := 60
|
||||
for i := 0; i < maxAttempts; i++ {
|
||||
progressResp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||
var progress backend.ProgressResponse
|
||||
json.Unmarshal(progressResp.Body.Bytes(), &progress)
|
||||
|
||||
if progress.Progress == "100" {
|
||||
break
|
||||
}
|
||||
if i == maxAttempts-1 {
|
||||
t.Error("Sync did not complete within timeout")
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetAllGames verifies the /music/all/order endpoint
|
||||
func TestGetAllGames(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run
|
||||
ensureSyncRan(t, e)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/all/order")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var games []string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &games)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, games, "Should have games after sync")
|
||||
t.Logf("Found %d games", len(games))
|
||||
}
|
||||
|
||||
// TestGetAllGamesRandom verifies the /music/all/random endpoint
|
||||
func TestGetAllGamesRandom(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run
|
||||
ensureSyncRan(t, e)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/all/random")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var games []string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &games)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, games, "Should have games after sync")
|
||||
|
||||
// Verify it's shuffled (not in original order)
|
||||
// We can't easily verify randomness, but we can check it's the same length
|
||||
resp2 := MakeTestRequest(t, e, "GET", "/music/all/order")
|
||||
var gamesOrdered []string
|
||||
json.Unmarshal(resp2.Body.Bytes(), &gamesOrdered)
|
||||
assert.Equal(t, len(games), len(gamesOrdered), "Random and ordered should have same count")
|
||||
}
|
||||
|
||||
// TestGetRandomSong verifies the /music/rand endpoint
|
||||
func TestGetRandomSong(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run
|
||||
ensureSyncRan(t, e)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var songPath string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &songPath)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, songPath, "Should return a song path")
|
||||
assert.Contains(t, songPath, "testMusic/", "Path should be in testMusic directory")
|
||||
t.Logf("Random song: %s", songPath)
|
||||
}
|
||||
|
||||
// TestGetRandomSongLowChance verifies the /music/rand/low endpoint
|
||||
func TestGetRandomSongLowChance(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run
|
||||
ensureSyncRan(t, e)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/rand/low")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var songPath string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &songPath)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, songPath, "Should return a song path")
|
||||
}
|
||||
|
||||
// TestGetRandomSongClassic verifies the /music/rand/classic endpoint
|
||||
func TestGetRandomSongClassic(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run
|
||||
ensureSyncRan(t, e)
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/rand/classic")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var songPath string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &songPath)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, songPath, "Should return a song path")
|
||||
}
|
||||
|
||||
// TestGetSongInfo verifies the /music/info endpoint
|
||||
func TestGetSongInfo(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run and get a song first
|
||||
ensureSyncRan(t, e)
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/info")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var info backend.SongInfo
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &info)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, info.CurrentlyPlaying, "Should have a currently playing song")
|
||||
assert.NotEmpty(t, info.Song, "Should have song name")
|
||||
t.Logf("Song info: Game=%s, Song=%s", info.Game, info.Song)
|
||||
}
|
||||
|
||||
// TestGetPlayedSongs verifies the /music/list endpoint
|
||||
func TestGetPlayedSongs(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run and add some songs to queue
|
||||
ensureSyncRan(t, e)
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/list")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var songs []backend.SongInfo
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &songs)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, songs, "Should have played songs in queue")
|
||||
t.Logf("Found %d songs in queue", len(songs))
|
||||
}
|
||||
|
||||
// TestGetNextSong verifies the /music/next endpoint
|
||||
func TestGetNextSong(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run and add songs to queue
|
||||
ensureSyncRan(t, e)
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/next")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var songPath string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &songPath)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, songPath, "Should return a song path")
|
||||
}
|
||||
|
||||
// TestGetPreviousSong verifies the /music/previous endpoint
|
||||
func TestGetPreviousSong(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run and add songs to queue
|
||||
ensureSyncRan(t, e)
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||
// Move forward
|
||||
MakeTestRequest(t, e, "GET", "/music/next")
|
||||
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/previous")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var songPath string
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &songPath)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, songPath, "Should return a song path")
|
||||
}
|
||||
|
||||
// TestResetMusic verifies the /music/reset endpoint
|
||||
func TestResetMusic(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run and add songs to queue
|
||||
ensureSyncRan(t, e)
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||
|
||||
// Verify queue has items
|
||||
respBefore := MakeTestRequest(t, e, "GET", "/music/list")
|
||||
var songsBefore []backend.SongInfo
|
||||
json.Unmarshal(respBefore.Body.Bytes(), &songsBefore)
|
||||
assert.True(t, len(songsBefore) > 0, "Should have songs before reset")
|
||||
|
||||
// Reset queue
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/reset")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
// Verify queue is empty
|
||||
respAfter := MakeTestRequest(t, e, "GET", "/music/list")
|
||||
var songsAfter []backend.SongInfo
|
||||
json.Unmarshal(respAfter.Body.Bytes(), &songsAfter)
|
||||
assert.Equal(t, 0, len(songsAfter), "Queue should be empty after reset")
|
||||
}
|
||||
|
||||
// TestAddLatestToQue verifies the /music/addQue endpoint
|
||||
func TestAddLatestToQue(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run
|
||||
ensureSyncRan(t, e)
|
||||
|
||||
// Get a random song (this sets lastFetchedNew)
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
|
||||
// Add to queue
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
// Verify it was added to queue
|
||||
respList := MakeTestRequest(t, e, "GET", "/music/list")
|
||||
var songs []backend.SongInfo
|
||||
json.Unmarshal(respList.Body.Bytes(), &songs)
|
||||
assert.True(t, len(songs) > 0, "Song should be in queue")
|
||||
}
|
||||
|
||||
// TestAddLatestPlayed verifies the /music/addPlayed endpoint
|
||||
func TestAddLatestPlayed(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run and add song to queue
|
||||
ensureSyncRan(t, e)
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||
|
||||
// Mark as played
|
||||
resp := MakeTestRequest(t, e, "GET", "/music/addPlayed")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
}
|
||||
|
||||
// TestPutPlayed verifies the PUT /music/played endpoint
|
||||
func TestPutPlayed(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Ensure sync has run and add songs to queue
|
||||
ensureSyncRan(t, e)
|
||||
MakeTestRequest(t, e, "GET", "/music/rand")
|
||||
MakeTestRequest(t, e, "GET", "/music/addQue")
|
||||
|
||||
// Mark song 0 as played
|
||||
resp := MakeTestRequestWithBody(t, e, "PUT", "/music/played?song=0", nil)
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"music-server/internal/backend"
|
||||
"music-server/internal/db"
|
||||
"music-server/internal/db/repository"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestSyncPopulatesDatabase verifies that sync populates the database with games
|
||||
func TestSyncPopulatesDatabase(t *testing.T) {
|
||||
db.TestSetupDB(t)
|
||||
defer db.TestTearDownDB(t)
|
||||
|
||||
e := StartTestServer(t)
|
||||
|
||||
// Before sync - should have no games (or very few if previous test ran)
|
||||
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)
|
||||
|
||||
// Run sync
|
||||
resp := MakeTestRequest(t, e, "GET", "/sync/full")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
// Wait for sync to complete by polling /sync/progress
|
||||
maxAttempts := 60
|
||||
for i := 0; i < maxAttempts; i++ {
|
||||
progressResp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||
assert.Equal(t, http.StatusOK, progressResp.Code)
|
||||
|
||||
var progress backend.ProgressResponse
|
||||
err := json.Unmarshal(progressResp.Body.Bytes(), &progress)
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Logf("Sync progress: %s%% (time spent: %s)", progress.Progress, progress.TimeSpent)
|
||||
|
||||
if progress.Progress == "100" {
|
||||
t.Log("Sync completed!")
|
||||
break
|
||||
}
|
||||
|
||||
if i == maxAttempts-1 {
|
||||
t.Error("Sync did not complete within timeout")
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
// 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
|
||||
maxAttempts := 60
|
||||
for i := 0; i < maxAttempts; i++ {
|
||||
progressResp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||
var progress backend.ProgressResponse
|
||||
json.Unmarshal(progressResp.Body.Bytes(), &progress)
|
||||
|
||||
if progress.Progress == "100" {
|
||||
break
|
||||
}
|
||||
if i == maxAttempts-1 {
|
||||
t.Error("Sync did not complete within timeout")
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
// 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
|
||||
foundNonZero := false
|
||||
foundComplete := false
|
||||
|
||||
for i := 0; i < maxAttempts; i++ {
|
||||
resp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var progress backend.ProgressResponse
|
||||
err := json.Unmarshal(resp.Body.Bytes(), &progress)
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Logf("Sync progress: %s%%", progress.Progress)
|
||||
|
||||
// Verify we get valid progress values
|
||||
if progress.Progress != "0" {
|
||||
foundNonZero = true
|
||||
}
|
||||
if progress.Progress == "100" {
|
||||
foundComplete = true
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
assert.True(t, foundNonZero, "Should have seen non-zero progress")
|
||||
assert.True(t, foundComplete, "Should have seen completion at 100%")
|
||||
}
|
||||
|
||||
// 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
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// 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")
|
||||
time.Sleep(5 * time.Second)
|
||||
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
|
||||
maxAttempts := 60
|
||||
for i := 0; i < maxAttempts; i++ {
|
||||
progressResp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||
var progress backend.ProgressResponse
|
||||
json.Unmarshal(progressResp.Body.Bytes(), &progress)
|
||||
|
||||
if progress.Progress == "100" {
|
||||
t.Log("Full sync completed")
|
||||
break
|
||||
}
|
||||
if i == maxAttempts-1 {
|
||||
t.Error("Full sync did not complete within timeout")
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
// StartTestServer starts the server for testing with test configuration
|
||||
func StartTestServer(t *testing.T) *echo.Echo {
|
||||
// Set test environment variables if not already set
|
||||
if os.Getenv("DB_HOST") == "" {
|
||||
os.Setenv("DB_HOST", "localhost")
|
||||
}
|
||||
if os.Getenv("DB_PORT") == "" {
|
||||
os.Setenv("DB_PORT", "5432")
|
||||
}
|
||||
if os.Getenv("DB_USERNAME") == "" {
|
||||
os.Setenv("DB_USERNAME", "testuser")
|
||||
}
|
||||
if os.Getenv("DB_PASSWORD") == "" {
|
||||
os.Setenv("DB_PASSWORD", "testpass")
|
||||
}
|
||||
if os.Getenv("DB_NAME") == "" {
|
||||
os.Setenv("DB_NAME", "music_server_test")
|
||||
}
|
||||
if os.Getenv("MUSIC_PATH") == "" {
|
||||
os.Setenv("MUSIC_PATH", "./testMusic")
|
||||
}
|
||||
if os.Getenv("CHARACTERS_PATH") == "" {
|
||||
os.Setenv("CHARACTERS_PATH", "./testCharacters")
|
||||
}
|
||||
if os.Getenv("PORT") == "" {
|
||||
os.Setenv("PORT", "8081")
|
||||
}
|
||||
if os.Getenv("LOG_LEVEL") == "" {
|
||||
os.Setenv("LOG_LEVEL", "debug")
|
||||
}
|
||||
if os.Getenv("LOG_JSON") == "" {
|
||||
os.Setenv("LOG_JSON", "false")
|
||||
}
|
||||
|
||||
// Create a Server instance and get its routes
|
||||
s := &Server{}
|
||||
handler := s.RegisterRoutes()
|
||||
|
||||
// Wrap the http.Handler in an echo.Echo
|
||||
e := echo.New()
|
||||
// Use a custom handler that wraps our routes
|
||||
e.Any("/*", echo.WrapHandler(handler))
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// MakeTestRequest makes an HTTP request to the test server
|
||||
func MakeTestRequest(t *testing.T, e *echo.Echo, method, path string) *httptest.ResponseRecorder {
|
||||
req := httptest.NewRequest(method, path, nil)
|
||||
rec := httptest.NewRecorder()
|
||||
e.ServeHTTP(rec, req)
|
||||
return rec
|
||||
}
|
||||
|
||||
// MakeTestRequestWithBody makes an HTTP request with a body to the test server
|
||||
func MakeTestRequestWithBody(t *testing.T, e *echo.Echo, method, path string, body []byte) *httptest.ResponseRecorder {
|
||||
req := httptest.NewRequest(method, path, nil)
|
||||
if body != nil {
|
||||
req = httptest.NewRequest(method, path, bytes.NewBuffer(body))
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
e.ServeHTTP(rec, req)
|
||||
return rec
|
||||
}
|
||||
|
||||
// WaitForSyncComplete polls the sync progress endpoint until sync is complete
|
||||
func WaitForSyncComplete(t *testing.T, e *echo.Echo, timeout time.Duration) bool {
|
||||
start := time.Now()
|
||||
for time.Since(start) < timeout {
|
||||
resp := MakeTestRequest(t, e, "GET", "/sync/progress")
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Logf("Sync progress endpoint returned status %d", resp.Code)
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse response - we can't easily decode here without importing backend
|
||||
// Just check if response contains "100"
|
||||
body := resp.Body.String()
|
||||
if len(body) > 0 {
|
||||
t.Logf("Sync progress: %s", body)
|
||||
// Simple check for completion
|
||||
// In a real scenario, you'd parse the JSON properly
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
t.Error("Sync did not complete within timeout")
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user