Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yaml → .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
name: Test and Lint
name: Lint and Test

permissions:
contents: write
checks: write
pull-requests: write

on:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!-- Pytest Coverage Comment:Begin -->
<a href="https://github.com/constructorfleet/mcp-plex/blob/main/README.md"><img alt="Coverage" src="https://img.shields.io/badge/Coverage-73%25-yellow.svg" /></a><details><summary>Coverage Report </summary><table><tr><th>File</th><th>Stmts</th><th>Miss</th><th>Cover</th><th>Missing</th></tr><tbody><tr><td colspan="5"><b>mcp_plex</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py">loader.py</a></td><td>172</td><td>73</td><td>58%</td><td><a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L40-L44">40&ndash;44</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L50-L56">50&ndash;56</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L62-L68">62&ndash;68</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L76-L80">76&ndash;80</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L100-L118">100&ndash;118</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L139-L190">139&ndash;190</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L295-L302">295&ndash;302</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L337-L346">337&ndash;346</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L450-L463">450&ndash;463</a></td></tr><tr><td><b>TOTAL</b></td><td><b>274</b></td><td><b>73</b></td><td><b>73%</b></td><td>&nbsp;</td></tr></tbody></table></details>
<a href="https://github.com/constructorfleet/mcp-plex/blob/main/README.md"><img alt="Coverage" src="https://img.shields.io/badge/Coverage-82%25-green.svg" /></a><details><summary>Coverage Report </summary><table><tr><th>File</th><th>Stmts</th><th>Miss</th><th>Cover</th><th>Missing</th></tr><tbody><tr><td colspan="5"><b>mcp_plex</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py">loader.py</a></td><td>172</td><td>48</td><td>72%</td><td><a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L139-L190">139&ndash;190</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L295-L302">295&ndash;302</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L337-L346">337&ndash;346</a>, <a href="https://github.com/constructorfleet/mcp-plex/blob/main/mcp_plex/loader.py#L450-L463">450&ndash;463</a></td></tr><tr><td><b>TOTAL</b></td><td><b>274</b></td><td><b>48</b></td><td><b>82%</b></td><td>&nbsp;</td></tr></tbody></table></details>
<!-- Pytest Coverage Comment:End -->

# mcp-plex
Expand Down
84 changes: 83 additions & 1 deletion tests/test_loader_unit.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import types
import asyncio
import httpx

from pathlib import Path

from mcp_plex.loader import _extract_external_ids, _load_from_sample
from mcp_plex.loader import (
_extract_external_ids,
_load_from_sample,
_build_plex_item,
_fetch_imdb,
_fetch_tmdb_movie,
_fetch_tmdb_show,
_fetch_tmdb_episode,
)


def test_extract_external_ids():
Expand All @@ -17,3 +28,74 @@ def test_load_from_sample_returns_items():
items = _load_from_sample(sample_dir)
assert len(items) == 2
assert {i.plex.type for i in items} == {"movie", "episode"}


def test_build_plex_item_handles_full_metadata():
guid_objs = [types.SimpleNamespace(id="imdb://tt123"), types.SimpleNamespace(id="tmdb://456")]
raw = types.SimpleNamespace(
ratingKey="1",
guid="guid",
type="movie",
title="Title",
summary="Summary",
year=2024,
guids=guid_objs,
thumb="thumb.jpg",
art="art.jpg",
tagline="Tagline",
contentRating="PG",
directors=[types.SimpleNamespace(id=1, tag="Director", thumb="d.jpg")],
writers=[types.SimpleNamespace(id=2, tag="Writer", thumb="w.jpg")],
actors=[types.SimpleNamespace(id=3, tag="Actor", thumb="a.jpg", role="Role")],
)

item = _build_plex_item(raw)
assert item.rating_key == "1"
assert item.directors[0].tag == "Director"
assert item.actors[0].role == "Role"


def test_fetch_functions_success_and_failure():
async def imdb_mock(request):
if "good" in str(request.url):
return httpx.Response(200, json={"id": "tt1", "type": "movie", "primaryTitle": "T"})
return httpx.Response(404)

async def tmdb_movie_mock(request):
if "good" in str(request.url):
return httpx.Response(200, json={"id": 1, "title": "M"})
return httpx.Response(404)

async def tmdb_show_mock(request):
if "good" in str(request.url):
return httpx.Response(200, json={"id": 1, "name": "S"})
return httpx.Response(404)

async def tmdb_episode_mock(request):
if "good" in str(request.url):
return httpx.Response(200, json={"id": 1, "name": "E"})
return httpx.Response(404)

async def main():
imdb_transport = httpx.MockTransport(imdb_mock)
movie_transport = httpx.MockTransport(tmdb_movie_mock)
show_transport = httpx.MockTransport(tmdb_show_mock)
episode_transport = httpx.MockTransport(tmdb_episode_mock)

async with httpx.AsyncClient(transport=imdb_transport) as client:
assert (await _fetch_imdb(client, "good")) is not None
assert (await _fetch_imdb(client, "bad")) is None

async with httpx.AsyncClient(transport=movie_transport) as client:
assert (await _fetch_tmdb_movie(client, "good", "k")) is not None
assert (await _fetch_tmdb_movie(client, "bad", "k")) is None

async with httpx.AsyncClient(transport=show_transport) as client:
assert (await _fetch_tmdb_show(client, "good", "k")) is not None
assert (await _fetch_tmdb_show(client, "bad", "k")) is None

async with httpx.AsyncClient(transport=episode_transport) as client:
assert (await _fetch_tmdb_episode(client, "good", "k")) is not None
assert (await _fetch_tmdb_episode(client, "bad", "k")) is None

asyncio.run(main())