"""Unit tests for ProcessService component.
Tests process retrieval and listing functionality.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pathlib import Path
class TestProcessServiceGetProcess:
"""Tests for ProcessService.get_process method (User Story 1)."""
def test_get_process_by_exact_name(self, temp_dir: Path) -> None:
"""Test retrieving a process by exact name from frontmatter."""
from sso_mcp_server.processes.service import ProcessService
# Create test file with frontmatter
(temp_dir / "code-review.md").write_text(
"""---
name: Code Review Process
description: Standard procedure for code reviews
---
# Code Review Process
Follow these steps.
"""
)
service = ProcessService(temp_dir)
result = service.get_process("Code Review Process")
assert result is not None
assert result["name"] == "Code Review Process"
assert result["description"] == "Standard procedure for code reviews"
assert "# Code Review Process" in result["content"]
def test_get_process_case_insensitive(self, temp_dir: Path) -> None:
"""Test that process retrieval is case-insensitive (FR-004)."""
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "deployment.md").write_text(
"""---
name: Deployment Process
---
Content.
"""
)
service = ProcessService(temp_dir)
# Various case combinations should all work
assert service.get_process("deployment process") is not None
assert service.get_process("DEPLOYMENT PROCESS") is not None
assert service.get_process("Deployment Process") is not None
assert service.get_process("DeployMENT ProCESS") is not None
def test_get_process_by_filename(self, temp_dir: Path) -> None:
"""Test retrieving a process by filename (without extension)."""
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "incident-response.md").write_text(
"""---
name: Incident Response Guide
---
Handle incidents.
"""
)
service = ProcessService(temp_dir)
# Should match by filename
result = service.get_process("incident-response")
assert result is not None
assert result["name"] == "Incident Response Guide"
def test_get_process_not_found_returns_none(self, temp_dir: Path) -> None:
"""Test that get_process returns None for non-existent process."""
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "existing.md").write_text("# Existing")
service = ProcessService(temp_dir)
result = service.get_process("nonexistent")
assert result is None
def test_get_process_empty_directory(self, temp_dir: Path) -> None:
"""Test get_process with empty directory."""
from sso_mcp_server.processes.service import ProcessService
empty_dir = temp_dir / "empty"
empty_dir.mkdir()
service = ProcessService(empty_dir)
result = service.get_process("any")
assert result is None
def test_get_process_missing_directory(self, temp_dir: Path) -> None:
"""Test get_process when directory doesn't exist (edge case)."""
from sso_mcp_server.processes.service import ProcessService
missing_dir = temp_dir / "missing"
service = ProcessService(missing_dir)
result = service.get_process("any")
assert result is None
class TestProcessServiceGetAvailableNames:
"""Tests for ProcessService.get_available_names method (T011)."""
def test_get_available_names(self, temp_dir: Path) -> None:
"""Test getting list of available process names."""
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "code-review.md").write_text(
"""---
name: Code Review
---
Content.
"""
)
(temp_dir / "deployment.md").write_text(
"""---
name: Deployment
---
Content.
"""
)
service = ProcessService(temp_dir)
names = service.get_available_names()
assert len(names) == 2
assert "Code Review" in names
assert "Deployment" in names
def test_get_available_names_empty_directory(self, temp_dir: Path) -> None:
"""Test get_available_names with empty directory."""
from sso_mcp_server.processes.service import ProcessService
empty_dir = temp_dir / "empty"
empty_dir.mkdir()
service = ProcessService(empty_dir)
names = service.get_available_names()
assert names == []
class TestProcessServiceListProcesses:
"""Tests for ProcessService.list_processes method (User Story 2)."""
def test_list_processes_returns_metadata(self, temp_dir: Path) -> None:
"""Test that list_processes returns name and description only."""
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "code-review.md").write_text(
"""---
name: Code Review
description: Code review procedure
---
# Code Review
Full content here.
"""
)
service = ProcessService(temp_dir)
result = service.list_processes()
assert len(result) == 1
assert result[0]["name"] == "Code Review"
assert result[0]["description"] == "Code review procedure"
# Should NOT include content
assert "content" not in result[0]
def test_list_processes_multiple_files(self, temp_dir: Path) -> None:
"""Test listing multiple process files."""
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "code-review.md").write_text(
"""---
name: Code Review
description: Review code
---
Content.
"""
)
(temp_dir / "deployment.md").write_text(
"""---
name: Deployment
description: Deploy to prod
---
Content.
"""
)
(temp_dir / "incident-response.md").write_text(
"""---
name: Incident Response
description: Handle incidents
---
Content.
"""
)
service = ProcessService(temp_dir)
result = service.list_processes()
assert len(result) == 3
names = {p["name"] for p in result}
assert "Code Review" in names
assert "Deployment" in names
assert "Incident Response" in names
def test_list_processes_empty_directory(self, temp_dir: Path) -> None:
"""Test list_processes with empty directory (US2 Scenario 3)."""
from sso_mcp_server.processes.service import ProcessService
empty_dir = temp_dir / "empty"
empty_dir.mkdir()
service = ProcessService(empty_dir)
result = service.list_processes()
assert result == []
def test_list_processes_dynamic_discovery(self, temp_dir: Path) -> None:
"""Test that new files are discovered without restart (FR-007)."""
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "first.md").write_text(
"""---
name: First Process
---
Content.
"""
)
service = ProcessService(temp_dir)
result1 = service.list_processes()
assert len(result1) == 1
# Add new file at runtime
(temp_dir / "second.md").write_text(
"""---
name: Second Process
---
Content.
"""
)
# Should see new file without restart
result2 = service.list_processes()
assert len(result2) == 2
def test_list_processes_handles_missing_description(self, temp_dir: Path) -> None:
"""Test listing processes without description in frontmatter."""
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "no-desc.md").write_text(
"""---
name: No Description Process
---
Content.
"""
)
service = ProcessService(temp_dir)
result = service.list_processes()
assert len(result) == 1
assert result[0]["name"] == "No Description Process"
assert result[0]["description"] == ""
class TestProcessServiceSearchProcesses:
"""Tests for ProcessService.search_processes method (User Story 3)."""
def test_search_processes_delegates_to_engine(self, temp_dir: Path) -> None:
"""Test that search_processes uses SearchEngine correctly."""
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "deploy.md").write_text(
"""---
name: Deployment
description: Deploy process
---
Deploy content.
"""
)
service = ProcessService(temp_dir)
results = service.search_processes("deploy")
assert len(results) == 1
assert results[0]["name"] == "Deployment"
def test_search_processes_returns_structured_results(self, temp_dir: Path) -> None:
"""Test that search results have required fields."""
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "test.md").write_text(
"""---
name: Test Process
description: A test
---
Test content.
"""
)
service = ProcessService(temp_dir)
results = service.search_processes("test")
assert len(results) == 1
assert "name" in results[0]
assert "relevance_score" in results[0]
assert "snippet" in results[0]