Loading...
No commits yet
Not committed History
Blame
test_scitex_writer_mcp.py • 8.1 KB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Timestamp: 2026-01-27
# File: tests/python/test_scitex_writer_mcp.py

"""Tests for scitex_writer MCP module.

The MCP server exposes comprehensive tools for manuscript operations.
"""

import asyncio
from pathlib import Path


def _get_tool_names(mcp) -> list:
    """Get registered tool names across FastMCP versions."""
    try:
        tools = asyncio.run(mcp.get_tools())  # FastMCP >= 2.3
        return list(tools.keys())
    except AttributeError:
        tools = asyncio.run(mcp._list_tools())  # FastMCP 2.0–2.2
        return [t.name for t in tools]


class TestMcpModule:
    """Test MCP module functionality."""

    def test_mcp_import(self):
        """Test that MCP module can be imported."""
        from scitex_writer._mcp import mcp

        assert mcp is not None
        # Server name uses branding
        from scitex_writer._branding import get_mcp_server_name

        assert mcp.name == get_mcp_server_name()

    def test_instructions_via_branding(self):
        """Test that instructions are available via branding module."""
        from scitex_writer._branding import get_mcp_instructions

        instructions = get_mcp_instructions()
        assert instructions is not None
        assert isinstance(instructions, str)
        assert "manuscript" in instructions

    def test_run_server_function_exists(self):
        """Test that run_server function exists."""
        from scitex_writer._mcp import run_server

        assert callable(run_server)


class TestToolRegistration:
    """Test that the usage tool is registered."""

    def test_usage_tool_registered(self):
        """Test that usage tool is registered with MCP."""
        from scitex_writer._mcp import mcp

        assert "usage" in _get_tool_names(mcp)

    def test_tool_count(self):
        """Test that expected number of tools are registered."""
        from scitex_writer._mcp import mcp

        # 30+ tools: usage(1), project(4), compile(4), tables(5), figures(5),
        # bib(6), guidelines(3), prompts(1), export(1), claim(6), update(1)
        assert len(_get_tool_names(mcp)) >= 28


class TestUsageTool:
    """Test usage tool functionality."""

    def test_usage_returns_instructions(self):
        """Test usage tool returns instructions."""
        from scitex_writer._mcp import mcp

        assert "usage" in _get_tool_names(mcp)


class TestInstructionsContent:
    """Test instructions content."""

    def test_instructions_has_setup(self):
        """Test that instructions contains setup/clone info."""
        from scitex_writer._branding import get_mcp_instructions

        instructions = get_mcp_instructions()
        assert "git clone" in instructions
        assert "scitex-writer" in instructions

    def test_instructions_has_structure(self):
        """Test that instructions contains project structure info."""
        from scitex_writer._branding import get_mcp_instructions

        instructions = get_mcp_instructions()
        assert "00_shared/" in instructions
        assert "01_manuscript/" in instructions
        assert "02_supplementary/" in instructions
        assert "03_revision/" in instructions

    def test_instructions_has_editable_files(self):
        """Test that instructions lists editable files."""
        from scitex_writer._branding import get_mcp_instructions

        instructions = get_mcp_instructions()
        assert "abstract.tex" in instructions
        assert "introduction.tex" in instructions
        assert "methods.tex" in instructions
        assert "results.tex" in instructions
        assert "discussion.tex" in instructions

    def test_instructions_has_compile_options(self):
        """Test that instructions lists compile options."""
        from scitex_writer._branding import get_mcp_instructions

        instructions = get_mcp_instructions()
        assert "--draft" in instructions
        assert "--no_figs" in instructions
        assert "--no_tables" in instructions
        assert "--no_diff" in instructions

    def test_instructions_has_output_info(self):
        """Test that instructions lists output files."""
        from scitex_writer._branding import get_mcp_instructions

        instructions = get_mcp_instructions()
        assert "manuscript.pdf" in instructions

    def test_instructions_has_scitex_writer_root(self):
        """Test that instructions explains SCITEX_WRITER_ROOT."""
        from scitex_writer._branding import get_mcp_instructions

        instructions = get_mcp_instructions()
        assert "SCITEX_WRITER_ROOT" in instructions
        assert "compile.sh" in instructions

    def test_instructions_has_revision_info(self):
        """Test that instructions mentions revision directory."""
        from scitex_writer._branding import get_mcp_instructions

        instructions = get_mcp_instructions()
        assert "03_revision" in instructions

    def test_instructions_has_bib_merge(self):
        """Test that instructions explains bib auto-merge."""
        from scitex_writer._branding import get_mcp_instructions

        instructions = get_mcp_instructions()
        assert "bib_files/" in instructions
        assert "auto-merge" in instructions.lower() or "merge" in instructions.lower()


class TestHandlersModule:
    """Test handlers module can be imported."""

    def test_handlers_import(self):
        """Test that handlers module can be imported."""
        from scitex_writer._mcp import handlers

        assert handlers is not None

    def test_handlers_functions_exist(self):
        """Test that all handler functions exist (not registered, but available)."""
        from scitex_writer._mcp import handlers

        assert callable(handlers.clone_project)
        assert callable(handlers.compile_manuscript)
        assert callable(handlers.compile_supplementary)
        assert callable(handlers.compile_revision)
        assert callable(handlers.get_project_info)
        assert callable(handlers.get_pdf)
        assert callable(handlers.list_document_types)
        assert callable(handlers.csv_to_latex)
        assert callable(handlers.latex_to_csv)
        assert callable(handlers.pdf_to_images)
        assert callable(handlers.list_figures)
        assert callable(handlers.convert_figure)


class TestUtilsModule:
    """Test utils module."""

    def test_utils_import(self):
        """Test that utils module can be imported."""
        from scitex_writer._mcp.utils import resolve_project_path, run_compile_script

        assert callable(resolve_project_path)
        assert callable(run_compile_script)

    def test_resolve_project_path_relative(self):
        """Test resolve_project_path with relative path."""
        from scitex_writer._mcp.utils import resolve_project_path

        result = resolve_project_path(".")
        assert result.is_absolute()

    def test_resolve_project_path_absolute(self):
        """Test resolve_project_path with absolute path."""
        from scitex_writer._mcp.utils import resolve_project_path

        result = resolve_project_path("/tmp")
        assert result == Path("/tmp")


class TestBranding:
    """Test branding module functionality."""

    def test_default_brand_values(self):
        """Test default branding values."""
        from scitex_writer._branding import BRAND_ALIAS, BRAND_NAME

        # Default values when env vars not set
        assert BRAND_NAME == "scitex-writer"
        assert BRAND_ALIAS == "sw"

    def test_get_mcp_server_name(self):
        """Test MCP server name generation."""
        from scitex_writer._branding import get_mcp_server_name

        # Should replace dots with hyphens
        name = get_mcp_server_name()
        assert "." not in name

    def test_rebrand_text_noop(self):
        """Test rebrand_text returns original when no branding change."""
        from scitex_writer._branding import rebrand_text

        text = "import scitex_writer as sw"
        result = rebrand_text(text)
        assert result == text

    def test_rebrand_text_none(self):
        """Test rebrand_text handles None."""
        from scitex_writer._branding import rebrand_text

        result = rebrand_text(None)
        assert result is None


# EOF