"""In-memory fakes of a Plex Media Server and plex.tv, served over real HTTP. ``make_fake_plex(state)`` builds a FastAPI app speaking just enough of the PMS wire protocol (XML MediaContainer responses) for plexapi 4.x and the engine's raw ``/hubs`` calls to work unmodified. ``make_fake_plextv(state)`` builds the plex.tv surface (``/api/users`` XML, ``/api/v2/*`` JSON) the engine's ``PlexTvClient`` talks to. Both share one ``FakePlexState``, so tests can assert on server-side effects directly. Fidelity notes (mirrors of real-Plex behavior the engine depends on): - New labels are stored title-cased (``shortlist_x`` -> ``Shortlist_x``), like a real PMS. - ``/hubs`` respects the requesting token: ``server-`` tokens see only collections promoted to shared Home whose labels are NOT in that user's ``label!=`` share-filter excludes. - plex.tv Home-user switch mints ``switch-``; ``/api/v2/resources`` exchanges it for the server-scoped ``server-`` token (the T2 canary mechanism). """ from __future__ import annotations import base64 import io import os import re from collections import Counter from dataclasses import dataclass, field from functools import lru_cache from pathlib import Path from urllib.parse import unquote from xml.etree.ElementTree import Element, SubElement, tostring from fastapi import FastAPI, HTTPException, Request, Response from fastapi.responses import JSONResponse FILTER_FIELDS = ("filterAll", "filterMovies", "filterTelevision", "filterMusic", "filterPhotos") _LABEL_PARAM = re.compile(r"^label\[\d+\]\.tag\.tag$") _SORT_KEYS = { "addedAt": lambda m: m.added_at, "audienceRating": lambda m: m.audience_rating, "titleSort": lambda m: m.title, } @dataclass class FakeMovie: """A library item. `media_type` decides how the PMS serves it (