Files
xer-mcp/tests/contract/test_get_activity.py
Bill Ballou ccc8296418 feat: implement XER MCP Server with 9 schedule query tools
Implement complete MCP server for parsing Primavera P6 XER files and
exposing schedule data through MCP tools. All 4 user stories complete.

Tools implemented:
- load_xer: Parse XER files into SQLite database
- list_activities: Query activities with pagination and filtering
- get_activity: Get activity details by ID
- list_relationships: Query activity dependencies
- get_predecessors/get_successors: Query activity relationships
- get_project_summary: Project overview with counts
- list_milestones: Query milestone activities
- get_critical_path: Query driving path activities

Features:
- Tab-delimited XER format parsing with pluggable table handlers
- In-memory SQLite database for fast queries
- Pagination with 100-item default limit
- Multi-project file support with project selection
- ISO8601 date formatting
- NO_FILE_LOADED error handling for all query tools

Test coverage: 81 tests (contract, integration, unit)
2026-01-06 21:27:35 -05:00

90 lines
3.1 KiB
Python

"""Contract tests for get_activity MCP tool."""
from pathlib import Path
import pytest
from xer_mcp.db import db
@pytest.fixture(autouse=True)
def setup_db():
"""Initialize and clear database for each test."""
db.initialize()
yield
db.clear()
class TestGetActivityContract:
"""Contract tests verifying get_activity tool interface matches spec."""
async def test_get_activity_returns_details(self, sample_xer_single_project: Path) -> None:
"""get_activity returns complete activity details."""
from xer_mcp.tools.get_activity import get_activity
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
result = await get_activity(activity_id="2002")
assert result["task_id"] == "2002"
assert result["task_code"] == "A1010"
assert result["task_name"] == "Site Preparation"
assert result["task_type"] == "TT_Task"
assert "target_start_date" in result
assert "target_end_date" in result
assert "wbs_id" in result
assert "predecessor_count" in result
assert "successor_count" in result
async def test_get_activity_includes_wbs_name(self, sample_xer_single_project: Path) -> None:
"""get_activity includes WBS name from lookup."""
from xer_mcp.tools.get_activity import get_activity
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
result = await get_activity(activity_id="2002")
assert "wbs_name" in result
async def test_get_activity_includes_relationship_counts(
self, sample_xer_single_project: Path
) -> None:
"""get_activity includes predecessor and successor counts."""
from xer_mcp.tools.get_activity import get_activity
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
# A1010 (2002) has 1 predecessor (A1000) and 2 successors (A1020, A1030)
result = await get_activity(activity_id="2002")
assert result["predecessor_count"] == 1
assert result["successor_count"] == 2
async def test_get_activity_not_found_returns_error(
self, sample_xer_single_project: Path
) -> None:
"""get_activity with invalid ID returns ACTIVITY_NOT_FOUND error."""
from xer_mcp.tools.get_activity import get_activity
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
result = await get_activity(activity_id="nonexistent")
assert "error" in result
assert result["error"]["code"] == "ACTIVITY_NOT_FOUND"
async def test_get_activity_no_file_loaded_returns_error(self) -> None:
"""get_activity without loaded file returns NO_FILE_LOADED error."""
from xer_mcp.server import set_file_loaded
from xer_mcp.tools.get_activity import get_activity
set_file_loaded(False)
result = await get_activity(activity_id="2002")
assert "error" in result
assert result["error"]["code"] == "NO_FILE_LOADED"