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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dev = [
"mypy>=1.0",
"ruff>=0.6",
"types-protobuf",
"types-jsonschema",
]

[project.scripts]
Expand Down
17 changes: 10 additions & 7 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from collections.abc import Callable
from collections.abc import Callable, Iterator
from pathlib import Path
import sys
from typing import Any
Expand All @@ -11,7 +11,7 @@

from vesper.config import Settings
from vesper.rpc import CiderRpcClient
from vesper.resolver import ResolvedAction, SessionQueryPlan, SessionSearchSource
from vesper.resolver import FallbackResolver, ResolvedAction, SessionQueryPlan, SessionSearchSource
from vesper.service import CiderAgentService
from vesper.storage import PreferenceStore, close_connections, close_lifecycle_locks

Expand Down Expand Up @@ -48,7 +48,7 @@ class StubRpcClient:
def __init__(self) -> None:
self.is_playing = True
self.volume = 0.5
self.current_track = self._track(
self.current_track: dict[str, Any] | None = self._track(
"track-1",
"Track",
"Artist",
Expand Down Expand Up @@ -84,6 +84,9 @@ def _track(
def close(self) -> None:
return None

def set_failure_callback(self, callback) -> None:
return None

def playback_get(self, path: str):
self.playback_get_calls.append(path)
if path == "/now-playing":
Expand Down Expand Up @@ -132,7 +135,7 @@ def _catalog_track_for_id(self, item_id: str) -> dict[str, Any]:
}
return catalog_map.get(item_id, self._track(item_id or "unknown-track", item_id or "Unknown"))

def search_catalog(self, query: str, *, limit: int, storefront: str, offset: int = 0):
def search_catalog(self, query: str, *, limit: int = 10, storefront: str = "us", offset: int = 0):
self.search_catalog_calls.append({"query": query, "limit": limit, "storefront": storefront, "offset": offset})
if query == "Favorite Artist Liked Song":
return {
Expand Down Expand Up @@ -238,7 +241,7 @@ def search_catalog(self, query: str, *, limit: int, storefront: str, offset: int
}
}

def search_library(self, query: str, *, limit: int, types: list[str] | None = None):
def search_library(self, query: str, *, limit: int = 10, types: list[str] | None = None):
return {
"data": {
"results": {
Expand Down Expand Up @@ -312,7 +315,7 @@ def run_amapi_v3(self, path: str, *, method: str = "GET", body: dict[str, Any] |
return {"data": {"data": [{"id": "playlist-1", "type": "library-playlists", "attributes": {"name": "Mix"}}]}}


class StubResolver:
class StubResolver(FallbackResolver):
def __init__(self) -> None:
self.session_plan_calls = 0

Expand Down Expand Up @@ -395,7 +398,7 @@ def settings(tmp_path: Path) -> Settings:


@pytest.fixture
def service(settings: Settings) -> CiderAgentService:
def service(settings: Settings) -> Iterator[CiderAgentService]:
svc = CiderAgentService(
settings,
rpc_client=StubRpcClient(),
Expand Down
23 changes: 16 additions & 7 deletions tests/test_mcp.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from pathlib import Path
from typing import Any, cast

import anyio
import httpx
Expand Down Expand Up @@ -75,8 +76,12 @@ async def _exercise() -> None:

assert play.structuredContent == {"status": "ok", "result": {"path": "/play", "body": None}}
assert pause.structuredContent == {"status": "ok", "result": {"path": "/pause", "body": None}}
assert next_track.structuredContent["result"]["path"] == "/next"
assert previous.structuredContent["result"]["path"] == "/previous"
next_content = next_track.structuredContent
assert next_content is not None
assert next_content["result"]["path"] == "/next"
prev_content = previous.structuredContent
assert prev_content is not None
assert prev_content["result"]["path"] == "/previous"

anyio.run(_exercise)

Expand All @@ -90,9 +95,11 @@ async def _exercise() -> None:
async with create_connected_server_and_client_session(mcp_server.create_mcp_server()) as session:
result = await session.call_tool("ask", {"text": "play some kep1er"})
assert result.isError is False
assert result.structuredContent["status"] == "ok"
assert result.structuredContent["input"] == "play some kep1er"
assert result.structuredContent["execution"]["action"] == "search"
content = result.structuredContent
assert content is not None
assert content["status"] == "ok"
assert content["input"] == "play some kep1er"
assert content["execution"]["action"] == "search"

anyio.run(_exercise)

Expand All @@ -106,7 +113,7 @@ async def _exercise() -> None:
async with create_connected_server_and_client_session(mcp_server.create_mcp_server()) as session:
result = await session.call_tool("ask", {"text": ""})
assert result.isError is True
assert "text cannot be empty" in result.content[0].text
assert "text cannot be empty" in cast(Any, result.content[0]).text

anyio.run(_exercise)

Expand Down Expand Up @@ -184,7 +191,9 @@ async def _exercise() -> None:
tools = await session.list_tools()
result = await session.call_tool("play", {})
assert [tool.name for tool in tools.tools] == ["play", "pause", "next", "previous", "ask"]
assert result.structuredContent["result"]["path"] == "/play"
play_content = result.structuredContent
assert play_content is not None
assert play_content["result"]["path"] == "/play"

anyio.run(_exercise)

Expand Down
3 changes: 2 additions & 1 deletion tests/test_resolver.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import json
from typing import Any

import httpx
import pytest
Expand Down Expand Up @@ -1396,7 +1397,7 @@ def handle_request(self, request: httpx.Request) -> httpx.Response:


def test_session_plan_prompt_omits_recent_track_history(settings: Settings, service) -> None:
captured_payload: dict[str, object] = {}
captured_payload: dict[str, Any] = {}

resolver_settings = Settings(
http_host=settings.http_host,
Expand Down
Loading
Loading