Files
MusicServer/internal/config/config_test.go
T
SansanandClaude Opus 5.5 bb8de5548d
CI / docker (push) Failing after 22s
CI / integration (push) Successful in 26s
CI / test (push) Successful in 26s
CI / publish (push) Skipped
Add alternative names and cover art for soundtracks
Alternative names (#32):
- PUT /api/v1/soundtracks/{uuid}/alternative-names replaces a soundtrack's
  alternative names (trimmed, deduplicated, without the soundtrack's own name).
- alternative_names is in the soundtrack and statistics responses.
- /find matches them too and shows which name matched.

Covers (#12), stored in COVERS_PATH and off when it is not set:
- GET, PUT (upload) and DELETE /api/v1/soundtracks/{uuid}/cover, and
  PUT .../cover/source to pick a SteamGridDB game or AniList anime by ID.
  Uploaded and picked covers are never replaced automatically.
- With STEAMGRIDDB_API_KEY, a background job searches SteamGridDB, then
  AniList, by name, alternative names and the name without bracketed parts,
  at startup, after a sync and after names change. Search results are ranked
  by matching words, so "Civilization IV" gets "Sid Meier's Civilization IV".
- cover_modified (Unix seconds) is in the soundtrack and statistics responses,
  so clients can cache covers.

The new columns are added to migration 000006, which is not released yet.

Closes #12, closes #32

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:57:18 +02:00

113 lines
3.3 KiB
Go

package config
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func env(values map[string]string) func(string) string {
return func(key string) string { return values[key] }
}
func validEnv() map[string]string {
return map[string]string{
"DB_HOST": "localhost",
"DB_PORT": "5432",
"DB_USERNAME": "user",
"DB_PASSWORD": "pass",
"DB_NAME": "music",
"MUSIC_PATH": "/music",
"CHARACTERS_PATH": "/characters",
}
}
func TestLoadDefaults(t *testing.T) {
cfg, err := load(env(validEnv()))
require.NoError(t, err)
assert.Equal(t, 8080, cfg.Port)
assert.Equal(t, "info", cfg.LogLevel)
assert.Equal(t, "songs", cfg.SoundCheckPath)
assert.Equal(t, DefaultReleasesURL, cfg.ReleasesURL)
assert.Empty(t, cfg.TrustedProxies)
assert.Empty(t, cfg.CORSAllowedOrigins)
assert.Equal(t, DefaultMetricsPort, cfg.MetricsPort)
assert.Empty(t, cfg.CoversPath, "covers are off by default")
assert.Empty(t, cfg.SteamGridDBAPIKey)
}
func TestLoadCovers(t *testing.T) {
values := validEnv()
values["COVERS_PATH"] = "/covers"
values["STEAMGRIDDB_API_KEY"] = "key"
cfg, err := load(env(values))
require.NoError(t, err)
assert.Equal(t, "/covers", cfg.CoversPath)
assert.Equal(t, "key", cfg.SteamGridDBAPIKey)
}
func TestLoadMetricsPort(t *testing.T) {
values := validEnv()
values["METRICS_PORT"] = "9100"
cfg, err := load(env(values))
require.NoError(t, err)
assert.Equal(t, 9100, cfg.MetricsPort)
values["METRICS_PORT"] = "off"
cfg, err = load(env(values))
require.NoError(t, err)
assert.Zero(t, cfg.MetricsPort)
for _, invalid := range []string{"abc", "0", "70000", "-1", "OFF"} {
values["METRICS_PORT"] = invalid
_, err = load(env(values))
assert.ErrorContains(t, err, "METRICS_PORT", invalid)
}
}
func TestLoadMissingRequired(t *testing.T) {
values := validEnv()
delete(values, "MUSIC_PATH")
delete(values, "DB_HOST")
_, err := load(env(values))
require.Error(t, err)
assert.Contains(t, err.Error(), "MUSIC_PATH is required")
assert.Contains(t, err.Error(), "DB_HOST is required")
}
func TestLoadTrustedProxies(t *testing.T) {
values := validEnv()
values["TRUSTED_PROXIES"] = "10.0.0.0/8, 172.16.0.0/12"
cfg, err := load(env(values))
require.NoError(t, err)
require.Len(t, cfg.TrustedProxies, 2)
assert.Equal(t, "10.0.0.0/8", cfg.TrustedProxies[0].String())
values["TRUSTED_PROXIES"] = "not-a-cidr"
_, err = load(env(values))
assert.ErrorContains(t, err, "TRUSTED_PROXIES")
}
func TestLoadCORSAllowedOrigins(t *testing.T) {
values := validEnv()
values["CORS_ALLOWED_ORIGINS"] = "https://music.example.com, http://localhost:5173,"
cfg, err := load(env(values))
require.NoError(t, err)
assert.Equal(t, []string{"https://music.example.com", "http://localhost:5173"}, cfg.CORSAllowedOrigins)
for _, invalid := range []string{"music.example.com", "ftp://example.com", "https://example.com/app",
"https://*.example.com", "https://*", "*", "https://example.com?x=1", "https://user@example.com"} {
values["CORS_ALLOWED_ORIGINS"] = invalid
_, err = load(env(values))
assert.ErrorContains(t, err, "CORS_ALLOWED_ORIGINS", invalid)
}
}
func TestLoadInvalidPort(t *testing.T) {
values := validEnv()
values["PORT"] = "abc"
_, err := load(env(values))
assert.ErrorContains(t, err, "PORT")
}