Expose session/last-used user and device IP on player rows. Filter retrieve_player_list by users, merge GET /Devices when include_offline is set, and resolve spoken names to HA media_player entities via player_links (device_id or IP). PlayNow still requires a live session.
156 lines
3.8 KiB
Go
156 lines
3.8 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// clearEnv empties every config variable so a developer/CI environment cannot
|
|
// leak into Load results (empty variables are treated as unset by Load).
|
|
func clearEnv(t *testing.T) {
|
|
t.Helper()
|
|
for _, k := range []string{
|
|
"EMBY_SERVER_URL", "EMBY_USERNAME", "EMBY_PASSWORD",
|
|
"EMBY_API_KEY", "EMBY_USER_ID", "EMBY_VERIFY_SSL", "LLM_MAX_ITEMS",
|
|
"MCP_TRANSPORT", "MCP_LISTEN_ADDR", "MCP_SESSION_TIMEOUT",
|
|
"PLAYER_USERS", "PLAYER_LINKS",
|
|
} {
|
|
t.Setenv(k, "")
|
|
}
|
|
}
|
|
|
|
func writeEnv(t *testing.T, content string) string {
|
|
t.Helper()
|
|
p := filepath.Join(t.TempDir(), ".env")
|
|
if err := os.WriteFile(p, []byte(content), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return p
|
|
}
|
|
|
|
func TestLoadBasic(t *testing.T) {
|
|
clearEnv(t)
|
|
p := writeEnv(t, `
|
|
# comment
|
|
EMBY_SERVER_URL = "http://emby.local:8096/"
|
|
EMBY_USERNAME = 'user'
|
|
EMBY_PASSWORD = pass
|
|
EMBY_VERIFY_SSL = False
|
|
LLM_MAX_ITEMS = 42
|
|
`)
|
|
cfg, err := Load(p)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cfg.ServerURL != "http://emby.local:8096" {
|
|
t.Errorf("ServerURL = %q", cfg.ServerURL)
|
|
}
|
|
if cfg.Username != "user" || cfg.Password != "pass" {
|
|
t.Errorf("creds = %q/%q", cfg.Username, cfg.Password)
|
|
}
|
|
if cfg.VerifySSL {
|
|
t.Error("VerifySSL should be false")
|
|
}
|
|
if cfg.MaxChunkSize != 42 {
|
|
t.Errorf("MaxChunkSize = %d", cfg.MaxChunkSize)
|
|
}
|
|
}
|
|
|
|
func TestLoadAPIKeyRequiresUserID(t *testing.T) {
|
|
clearEnv(t)
|
|
p := writeEnv(t, `EMBY_SERVER_URL="http://x"
|
|
EMBY_API_KEY="abc"`)
|
|
if _, err := Load(p); err == nil {
|
|
t.Fatal("expected error for missing EMBY_USER_ID")
|
|
}
|
|
p2 := writeEnv(t, `EMBY_SERVER_URL="http://x"
|
|
EMBY_API_KEY="abc"
|
|
EMBY_USER_ID="u1"`)
|
|
cfg, err := Load(p2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cfg.APIKey != "abc" || cfg.UserID != "u1" {
|
|
t.Errorf("cfg = %+v", cfg)
|
|
}
|
|
}
|
|
|
|
func TestLoadMissingFile(t *testing.T) {
|
|
clearEnv(t)
|
|
if _, err := Load(filepath.Join(t.TempDir(), "nope.env")); err == nil {
|
|
t.Fatal("expected error for missing file")
|
|
}
|
|
}
|
|
|
|
func TestLoadDefaults(t *testing.T) {
|
|
clearEnv(t)
|
|
p := writeEnv(t, `EMBY_SERVER_URL="http://x"
|
|
EMBY_USERNAME="u"
|
|
EMBY_PASSWORD="p"`)
|
|
cfg, err := Load(p)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !cfg.VerifySSL {
|
|
t.Error("VerifySSL should default to true")
|
|
}
|
|
if cfg.Transport != TransportStdio {
|
|
t.Errorf("Transport = %q", cfg.Transport)
|
|
}
|
|
if cfg.ListenAddr != "0.0.0.0:8085" {
|
|
t.Errorf("ListenAddr = %q", cfg.ListenAddr)
|
|
}
|
|
if cfg.SessionTimeout != 30*time.Minute {
|
|
t.Errorf("SessionTimeout = %v", cfg.SessionTimeout)
|
|
}
|
|
}
|
|
|
|
func TestLoadHTTPMode(t *testing.T) {
|
|
clearEnv(t)
|
|
// HTTP mode authenticates per request; no server-side creds needed.
|
|
p := writeEnv(t, `EMBY_SERVER_URL="http://x"
|
|
MCP_TRANSPORT=http
|
|
MCP_LISTEN_ADDR="0.0.0.0:9090"
|
|
MCP_SESSION_TIMEOUT="5m"`)
|
|
cfg, err := Load(p)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cfg.Transport != TransportHTTP || cfg.ListenAddr != "0.0.0.0:9090" || cfg.SessionTimeout != 5*time.Minute {
|
|
t.Errorf("cfg = %+v", cfg)
|
|
}
|
|
}
|
|
|
|
func TestLoadInvalidTransport(t *testing.T) {
|
|
clearEnv(t)
|
|
p := writeEnv(t, `EMBY_SERVER_URL="http://x"
|
|
MCP_TRANSPORT=grpc`)
|
|
if _, err := Load(p); err == nil {
|
|
t.Fatal("expected error for invalid transport")
|
|
}
|
|
}
|
|
|
|
func TestLoadPlayerPolicy(t *testing.T) {
|
|
clearEnv(t)
|
|
p := writeEnv(t, `EMBY_SERVER_URL="http://x"
|
|
EMBY_USERNAME=u
|
|
EMBY_PASSWORD=p
|
|
PLAYER_USERS=Gordon, Alice
|
|
PLAYER_LINKS=[{"names":"lounge,living room","ha_entity":"media_player.lounge","device_ip":"192.168.0.20","ha_source":"Emby"}]`)
|
|
cfg, err := Load(p)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(cfg.PlayerUsers) != 2 || cfg.PlayerUsers[0] != "Gordon" {
|
|
t.Fatalf("users = %#v", cfg.PlayerUsers)
|
|
}
|
|
if len(cfg.PlayerLinks) != 1 || cfg.PlayerLinks[0].HAEntity != "media_player.lounge" {
|
|
t.Fatalf("links = %#v", cfg.PlayerLinks)
|
|
}
|
|
if len(cfg.PlayerLinks[0].Names) != 2 {
|
|
t.Fatalf("names = %#v", cfg.PlayerLinks[0].Names)
|
|
}
|
|
}
|