Files
gronod d40646211f
Build and publish / Test and build (darwin) (push) Failing after 1m42s
Build and publish / Test and build (linux) (push) Failing after 1m53s
Build and publish / Test and build (windows) (push) Failing after 2m50s
Build and publish / Build and publish Docker image (push) Skipped
Initial Go implementation of Emby.MCP
Go rewrite of the Python Emby.MCP server with tool parity (20 tools),
plus a streamable-HTTP transport with per-request Emby authentication
(Basic / Bearer token passthrough), a multi-arch Docker build, and a
Gitea CI workflow.
2026-09-20 22:52:46 +01:00

132 lines
3.6 KiB
Go

package emby
import (
"context"
"encoding/json"
"net/http"
"testing"
)
// itemsHandler captures the query params of GET /Users/{uid}/Items and returns
// a canned item list.
func itemsHandler(t *testing.T, items []map[string]any, captured *map[string][]string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if captured != nil {
*captured = map[string][]string(r.URL.Query())
}
json.NewEncoder(w).Encode(map[string]any{
"Items": items,
"TotalRecordCount": len(items),
})
}
}
func TestGetLibraryListFilters(t *testing.T) {
c, _ := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{
"TotalRecordCount": 3,
"Items": []map[string]any{
{"Name": "Music", "Type": "CollectionFolder", "CollectionType": "music", "Id": "1"},
{"Name": "BoxSets", "Type": "BoxSet", "CollectionType": "boxsets", "Id": "2"},
{"Name": "Movies", "Type": "CollectionFolder", "CollectionType": "movies", "Id": "3"},
},
})
})
libs, err := c.GetLibraryList(context.Background())
if err != nil {
t.Fatal(err)
}
if len(libs) != 2 || libs[0].Name != "Music" || libs[1].ID != "3" {
t.Fatalf("libs = %+v", libs)
}
if libs[0].Type != "music" {
t.Errorf("type = %q", libs[0].Type)
}
}
func TestGetItemsQueryTranslation(t *testing.T) {
var q map[string][]string
c, srv := newTestClient(t, itemsHandler(t, nil, &q))
defer srv.Close()
_, err := c.GetItems(context.Background(), "u1", "lib9", ItemQuery{
SearchTerm: "track", Artist: "bob", Genre: "Jazz",
Years: "1999,2001", FirstDate: "1990-01-01", LastDate: "2005-12-31",
IsUnplayed: true, IsFavorite: true, Limit: "5",
})
if err != nil {
t.Fatal(err)
}
want := map[string]string{
"SearchTerm": "track",
"Artists": "bob",
"Genres": "Jazz",
"Years": "1999,2001",
"MinStartDate": "1990-01-01",
"MaxEndDate": "2005-12-31",
"Filters": "IsUnplayed,IsFavorite",
"Limit": "5",
"ParentId": "lib9",
"MediaTypes": "Audio,Video",
"Recursive": "true",
}
for k, v := range want {
if got := q[k]; len(got) != 1 || got[0] != v {
t.Errorf("query[%s] = %v, want %q", k, got, v)
}
}
}
func itemWithLyrics(name, lyrics, overview string) map[string]any {
var sources []any
if lyrics != "" {
sources = []any{map[string]any{
"MediaStreams": []any{map[string]any{
"IsTextSubtitleStream": true,
"Title": "Lyrics",
"ExtraData": lyrics,
}},
}}
}
return map[string]any{
"Name": name,
"Id": "id-" + name,
"MediaType": "Audio",
"Overview": overview,
"MediaSources": sources,
"RunTimeTicks": 3661 * 10_000_000,
}
}
func TestGetItemsLyricsExtractionAndFilter(t *testing.T) {
items := []map[string]any{
itemWithLyrics("song1", "We are the champions", ""),
itemWithLyrics("song2", "Another one bites", ""),
itemWithLyrics("song3", "", "A cäst of thousands"),
}
c, srv := newTestClient(t, itemsHandler(t, items, nil))
defer srv.Close()
got, err := c.GetItems(context.Background(), "u1", "lib", ItemQuery{Lyrics: "champions"})
if err != nil {
t.Fatal(err)
}
if len(got) != 1 || got[0].Title != "song1" {
t.Fatalf("items = %+v", got)
}
if got[0].Lyrics != "We are the champions" {
t.Errorf("lyrics = %q", got[0].Lyrics)
}
if got[0].RunTime != "01:01:01" {
t.Errorf("run_time = %q", got[0].RunTime)
}
// Folded match against overview.
got, err = c.GetItems(context.Background(), "u1", "lib", ItemQuery{Lyrics: "cast"})
if err != nil {
t.Fatal(err)
}
if len(got) != 1 || got[0].Title != "song3" {
t.Fatalf("items = %+v", got)
}
}