Files
MusicServer/internal/server/helpers_test.go
T
Sansan 7b144ae419 Rewrite tests to run without a database
- Unit tests use an in-memory fake database (internal/testutil/fakedb),
  temp folders and httptest; `go test ./...` needs no Docker or env vars
- Integration tests (build tag "integration") start PostgreSQL with
  testcontainers: migrations step by step, SQL dumps (skipped if
  missing) and a full sync against a real database
- Removed tests that only checked copies of the logic or had empty
  bodies, the broken root integration_test.go, compose.test.yaml and
  .env.test with hardcoded paths
- testMusic MP3s replaced with tiny placeholders (sync never decodes
  audio), 194 MB -> 168 KB
2026-10-06 15:20:31 +02:00

121 lines
3.3 KiB
Go

package server
import (
"context"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"sync/atomic"
"testing"
"music-server/internal/config"
"music-server/internal/db/repository"
"music-server/internal/logging"
"music-server/internal/testutil/fakedb"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
logging.Init("fatal", false)
os.Exit(m.Run())
}
type fakeDatabase struct {
closed atomic.Bool
onClose func()
}
func (f *fakeDatabase) Health(context.Context) map[string]string {
return map[string]string{"status": "up"}
}
func (f *fakeDatabase) Close() {
if f.onClose != nil {
f.onClose()
}
f.closed.Store(true)
}
// testEnv is a Server backed by an in-memory database and temporary folders.
type testEnv struct {
t *testing.T
cfg config.Config
store *fakedb.Store
db *fakeDatabase
server *Server
handler http.Handler
}
func writeFile(t *testing.T, path, content string) {
t.Helper()
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
require.NoError(t, os.WriteFile(path, []byte(content), 0o644))
}
func newTestEnv(t *testing.T, configure ...func(*config.Config)) *testEnv {
t.Helper()
base := t.TempDir()
cfg := config.Config{
Port: 0,
MusicPath: filepath.Join(base, "music"),
CharactersPath: filepath.Join(base, "characters"),
SoundCheckPath: filepath.Join(base, "songs"),
FrontendPath: filepath.Join(base, "frontend"),
DisableSessionAuth: true,
ReleasesURL: "http://127.0.0.1:1",
}
for _, c := range configure {
c(&cfg)
}
writeFile(t, filepath.Join(cfg.CharactersPath, "Adol.png"), "png")
writeFile(t, filepath.Join(cfg.CharactersPath, "Aloy.jpg"), "jpg")
writeFile(t, filepath.Join(base, "secret.png"), "secret")
writeFile(t, filepath.Join(cfg.SoundCheckPath, "check.mp3"), "sound check")
writeFile(t, filepath.Join(cfg.FrontendPath, "index.html"), "<html>frontend</html>")
store := fakedb.New()
database := &fakeDatabase{}
s, err := New(cfg, store, database)
require.NoError(t, err)
t.Cleanup(func() { s.sync.Wait(); s.characters.Close() })
return &testEnv{t: t, cfg: cfg, store: store, db: database, server: s, handler: s.httpServer.Handler}
}
// addSong creates a song file and its database rows.
func (e *testEnv) addSong(soundtrack, song, content string) repository.Song {
e.t.Helper()
dir := e.cfg.MusicPath + "/" + soundtrack + "/"
fileName := song + ".mp3"
writeFile(e.t, dir+fileName, content)
st, ok := e.store.Soundtrack(soundtrack)
if !ok {
st = e.store.AddSoundtrack(repository.Soundtrack{SoundtrackName: soundtrack, Path: dir})
}
return e.store.AddTestSong(repository.Song{SoundtrackID: st.ID, SongName: song, Path: dir + fileName, FileName: &fileName})
}
func (e *testEnv) do(method, path string, headers ...string) *httptest.ResponseRecorder {
return e.doBody(method, path, "", headers...)
}
func (e *testEnv) doBody(method, path, body string, headers ...string) *httptest.ResponseRecorder {
var reader io.Reader
if body != "" {
reader = strings.NewReader(body)
}
req := httptest.NewRequest(method, path, reader)
if body != "" {
req.Header.Set("Content-Type", "application/json")
}
for i := 0; i+1 < len(headers); i += 2 {
req.Header.Set(headers[i], headers[i+1])
}
rec := httptest.NewRecorder()
e.handler.ServeHTTP(rec, req)
return rec
}