marki-docx/tests/test_styles.py

148 lines
5.1 KiB
Python
Raw Normal View History

"""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