Bake the former Python bridge.py into emby-mcp as internal/bridge.
/mcp and /call/{tool} share one process. Session init errors are
returned instead of swallowed, and 401/400/404 trigger a fresh
initialize plus retry with the caller's Authorization header.
128 lines
3.7 KiB
Go
128 lines
3.7 KiB
Go
package emby
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"testing"
|
|
)
|
|
|
|
var playlistLibs = []Library{{Name: "Playlists", Type: "playlists", ID: "plib"}}
|
|
|
|
func TestNewPlaylistDuplicateName(t *testing.T) {
|
|
c, srv := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(map[string]any{
|
|
"Items": []map[string]any{
|
|
{"Name": "My Mix", "Type": "Playlist", "Id": "p1"},
|
|
},
|
|
"TotalRecordCount": 1,
|
|
})
|
|
})
|
|
defer srv.Close()
|
|
_, err := c.NewPlaylist(context.Background(), "u1", playlistLibs, "my mix", "Audio", "")
|
|
if err == nil {
|
|
t.Fatal("expected duplicate name error")
|
|
}
|
|
}
|
|
|
|
func TestNewPlaylistCreatesAndSetsOverview(t *testing.T) {
|
|
var paths []string
|
|
var updated map[string]any
|
|
c, srv := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
|
|
paths = append(paths, r.Method+" "+r.URL.Path)
|
|
switch {
|
|
case r.URL.Path == "/Users/u1/Items": // GetPlaylists (empty) then get item
|
|
json.NewEncoder(w).Encode(map[string]any{"Items": []any{}, "TotalRecordCount": 0})
|
|
case r.URL.Path == "/Playlists":
|
|
json.NewEncoder(w).Encode(map[string]any{"Id": "newpl"})
|
|
case r.URL.Path == "/Users/u1/Items/newpl":
|
|
json.NewEncoder(w).Encode(map[string]any{"Id": "newpl", "Name": "Mix", "Type": "Playlist"})
|
|
case r.URL.Path == "/Items/newpl":
|
|
json.NewDecoder(r.Body).Decode(&updated)
|
|
w.WriteHeader(200)
|
|
default:
|
|
t.Errorf("unexpected %s %s", r.Method, r.URL.Path)
|
|
}
|
|
})
|
|
defer srv.Close()
|
|
id, err := c.NewPlaylist(context.Background(), "u1", playlistLibs, "Mix", "Audio", "my desc")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if id != "newpl" {
|
|
t.Fatalf("id = %q", id)
|
|
}
|
|
if updated["Overview"] != "my desc" || updated["Name"] != "Mix" {
|
|
t.Fatalf("updated = %v", updated)
|
|
}
|
|
}
|
|
|
|
func TestPlaylistSharingEndpoints(t *testing.T) {
|
|
var hits []string
|
|
c, srv := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
|
|
hits = append(hits, r.Method+" "+r.URL.Path)
|
|
if r.URL.Path == "/Items/Access" {
|
|
var b map[string]any
|
|
json.NewDecoder(r.Body).Decode(&b)
|
|
hits = append(hits, "body:"+b["ItemAccess"].(string))
|
|
}
|
|
w.WriteHeader(200)
|
|
})
|
|
defer srv.Close()
|
|
ctx := context.Background()
|
|
c.SetPlaylistSharing(ctx, "p1", "Public", nil, "")
|
|
c.SetPlaylistSharing(ctx, "p1", "Private", nil, "")
|
|
c.SetPlaylistSharing(ctx, "p1", "Shared", []string{"u2", "u3"}, "Read")
|
|
want := []string{
|
|
"POST /Items/p1/MakePublic",
|
|
"POST /Items/p1/MakePrivate",
|
|
"POST /Items/Access",
|
|
"body:Read",
|
|
}
|
|
if len(hits) != len(want) {
|
|
t.Fatalf("hits = %v", hits)
|
|
}
|
|
for i := range want {
|
|
if hits[i] != want[i] {
|
|
t.Fatalf("hits = %v", hits)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPlaylistItemEndpoints(t *testing.T) {
|
|
var hits []string
|
|
c, srv := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
|
|
hit := r.Method + " " + r.URL.Path
|
|
if r.URL.RawQuery != "" {
|
|
hit += "?" + r.URL.RawQuery
|
|
}
|
|
hits = append(hits, hit)
|
|
if r.URL.Path == "/Playlists/p1/Items" && r.Method == "POST" {
|
|
json.NewEncoder(w).Encode(map[string]any{"ItemAddedCount": 2})
|
|
return
|
|
}
|
|
w.WriteHeader(200)
|
|
})
|
|
defer srv.Close()
|
|
ctx := context.Background()
|
|
n, err := c.AddPlaylistItems(ctx, "u1", "p1", "i1,i2")
|
|
if err != nil || n != 2 {
|
|
t.Fatalf("n=%d err=%v", n, err)
|
|
}
|
|
c.DeletePlaylistItems(ctx, "p1", "10,11")
|
|
c.MovePlaylistItem(ctx, "p1", "10", "3")
|
|
for _, h := range hits {
|
|
t.Log(h)
|
|
}
|
|
if hits[0] != "POST /Playlists/p1/Items?Ids=i1%2Ci2&UserId=u1" &&
|
|
hits[0] != "POST /Playlists/p1/Items?UserId=u1&Ids=i1%2Ci2" {
|
|
t.Errorf("add hit = %s", hits[0])
|
|
}
|
|
if hits[1] != "POST /Playlists/p1/Items/Delete?EntryIds=10%2C11" {
|
|
t.Errorf("delete hit = %s", hits[1])
|
|
}
|
|
if hits[2] != "POST /Playlists/p1/Items/10/Move/3" {
|
|
t.Errorf("move hit = %s", hits[2])
|
|
}
|
|
}
|