Files
xer-mcp/tests/contract/test_list_activities.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

139 lines
5.0 KiB
Python

"""Contract tests for list_activities 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 TestListActivitiesContract:
"""Contract tests verifying list_activities tool interface matches spec."""
async def test_list_activities_returns_paginated_results(
self, sample_xer_single_project: Path
) -> None:
"""list_activities returns activities with pagination metadata."""
from xer_mcp.tools.list_activities import list_activities
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
result = await list_activities()
assert "activities" in result
assert "pagination" in result
assert len(result["activities"]) == 5
assert result["pagination"]["total_count"] == 5
assert result["pagination"]["offset"] == 0
assert result["pagination"]["limit"] == 100
assert result["pagination"]["has_more"] is False
async def test_list_activities_with_limit(self, sample_xer_single_project: Path) -> None:
"""list_activities respects limit parameter."""
from xer_mcp.tools.list_activities import list_activities
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
result = await list_activities(limit=2)
assert len(result["activities"]) == 2
assert result["pagination"]["limit"] == 2
assert result["pagination"]["has_more"] is True
async def test_list_activities_with_offset(self, sample_xer_single_project: Path) -> None:
"""list_activities respects offset parameter."""
from xer_mcp.tools.list_activities import list_activities
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
result = await list_activities(offset=2, limit=2)
assert len(result["activities"]) == 2
assert result["pagination"]["offset"] == 2
async def test_list_activities_filter_by_date_range(
self, sample_xer_single_project: Path
) -> None:
"""list_activities filters by date range."""
from xer_mcp.tools.list_activities import list_activities
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
# Filter to January only
result = await list_activities(start_date="2026-01-01", end_date="2026-01-31")
# Should include activities in January
for activity in result["activities"]:
assert "2026-01" in activity["target_start_date"]
async def test_list_activities_filter_by_activity_type(
self, sample_xer_single_project: Path
) -> None:
"""list_activities filters by activity type."""
from xer_mcp.tools.list_activities import list_activities
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
result = await list_activities(activity_type="TT_Mile")
assert len(result["activities"]) == 2
for activity in result["activities"]:
assert activity["task_type"] == "TT_Mile"
async def test_list_activities_filter_by_wbs(self, sample_xer_single_project: Path) -> None:
"""list_activities filters by WBS ID."""
from xer_mcp.tools.list_activities import list_activities
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
result = await list_activities(wbs_id="101")
# WBS 101 has 3 activities in the fixture
assert len(result["activities"]) == 3
async def test_list_activities_no_file_loaded_returns_error(self) -> None:
"""list_activities without loaded file returns NO_FILE_LOADED error."""
from xer_mcp.server import set_file_loaded
from xer_mcp.tools.list_activities import list_activities
set_file_loaded(False)
result = await list_activities()
assert "error" in result
assert result["error"]["code"] == "NO_FILE_LOADED"
async def test_list_activities_returns_expected_fields(
self, sample_xer_single_project: Path
) -> None:
"""list_activities returns activities with all expected fields."""
from xer_mcp.tools.list_activities import list_activities
from xer_mcp.tools.load_xer import load_xer
await load_xer(file_path=str(sample_xer_single_project))
result = await list_activities()
activity = result["activities"][0]
assert "task_id" in activity
assert "task_code" in activity
assert "task_name" in activity
assert "task_type" in activity
assert "target_start_date" in activity
assert "target_end_date" in activity
assert "status_code" in activity
assert "driving_path_flag" in activity