"""Tests for T03 — style listing across CLI, REST, MCP (FR-907).""" from __future__ import annotations import json import pytest from fastapi.testclient import TestClient import markidocx.mcp_server as mcp_module from markidocx.rest import create_app @pytest.fixture() def rest_client() -> TestClient: return TestClient(create_app()) class TestListStylesFunction: def test_returns_non_empty_list(self) -> None: from markidocx.templates import list_styles entries = list_styles() assert len(entries) > 0 def test_standard_heading_styles_present(self) -> None: from markidocx.templates import list_styles entries = list_styles(family="article") names = {e.name for e in entries} # Standard Word styles that python-docx always creates assert any("Heading" in n or "Normal" in n for n in names) def test_family_field_matches_requested(self) -> None: from markidocx.templates import list_styles for family in ("article", "book", "website"): entries = list_styles(family=family) assert all(e.family == family for e in entries) def test_sorted_by_type_then_name(self) -> None: from markidocx.templates import list_styles entries = list_styles() pairs = [(e.type, e.name) for e in entries] assert pairs == sorted(pairs) def test_style_entry_fields(self) -> None: from markidocx.templates import list_styles entries = list_styles() for e in entries: assert isinstance(e.name, str) and e.name assert isinstance(e.style_id, str) and e.style_id assert e.type in ("paragraph", "character", "table", "numbering") assert isinstance(e.built_in, bool) def test_each_built_in_family_has_styles(self) -> None: from markidocx.templates import list_styles for family in ("article", "book", "website"): entries = list_styles(family=family) assert len(entries) > 0, f"No styles for family {family!r}" class TestRestStylesEndpoint: def test_get_styles_returns_list(self, rest_client: TestClient) -> None: resp = rest_client.get("/styles") assert resp.status_code == 200 data = resp.json() assert data["status"] == "ok" assert isinstance(data["outputs"], list) assert len(data["outputs"]) > 0 def test_get_styles_with_family(self, rest_client: TestClient) -> None: resp = rest_client.get("/styles?family=book") assert resp.status_code == 200 data = resp.json() assert all(e["family"] == "book" for e in data["outputs"]) def test_get_styles_has_required_fields(self, rest_client: TestClient) -> None: resp = rest_client.get("/styles") data = resp.json() for entry in data["outputs"]: assert "name" in entry assert "style_id" in entry assert "type" in entry assert "family" in entry assert "built_in" in entry class TestMcpListStyles: def test_list_styles_returns_non_empty(self) -> None: result = mcp_module.list_styles() assert isinstance(result, list) assert len(result) > 0 def test_list_styles_with_family(self) -> None: result = mcp_module.list_styles(family="website") assert all(e["family"] == "website" for e in result) class TestCliTemplateStyles: def test_template_styles_command(self) -> None: from typer.testing import CliRunner from markidocx.cli import app runner = CliRunner() result = runner.invoke(app, ["template", "styles"]) assert result.exit_code == 0 def test_template_styles_json(self) -> None: from typer.testing import CliRunner from markidocx.cli import app runner = CliRunner() result = runner.invoke(app, ["template", "styles", "--json"]) assert result.exit_code == 0 data = json.loads(result.output.strip()) assert isinstance(data, list) assert len(data) > 0 assert "name" in data[0] def test_template_styles_family_filter(self) -> None: from typer.testing import CliRunner from markidocx.cli import app runner = CliRunner() result = runner.invoke(app, ["template", "styles", "--family", "book", "--json"]) assert result.exit_code == 0 data = json.loads(result.output.strip()) assert all(e["family"] == "book" for e in data) class TestStylesParityAcrossInterfaces: def test_rest_mcp_same_count_for_article(self, rest_client: TestClient) -> None: rest_entries = rest_client.get("/styles?family=article").json()["outputs"] mcp_entries = mcp_module.list_styles(family="article") assert len(rest_entries) == len(mcp_entries) def test_rest_mcp_same_names_for_article(self, rest_client: TestClient) -> None: rest_names = {e["name"] for e in rest_client.get("/styles?family=article").json()["outputs"]} mcp_names = {e["name"] for e in mcp_module.list_styles(family="article")} assert rest_names == mcp_names