"""Unit tests for Process SearchEngine component.
Tests search functionality with relevance scoring (FR-009 to FR-012a).
"""
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pathlib import Path
class TestSearchEngine:
"""Tests for SearchEngine class (T021)."""
def test_search_finds_keyword_in_name(self, temp_dir: Path) -> None:
"""Test that search finds keyword in process name."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "deployment.md").write_text(
"""---
name: Deployment Process
description: Deploy to production
---
Content about deploying.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
results = engine.search("deployment")
assert len(results) == 1
assert results[0]["name"] == "Deployment Process"
def test_search_finds_keyword_in_description(self, temp_dir: Path) -> None:
"""Test that search finds keyword in process description."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "process.md").write_text(
"""---
name: Some Process
description: This handles deployment tasks
---
Other content.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
results = engine.search("deployment")
assert len(results) == 1
assert results[0]["name"] == "Some Process"
def test_search_finds_keyword_in_content(self, temp_dir: Path) -> None:
"""Test that search finds keyword in process content (FR-010)."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "guide.md").write_text(
"""---
name: General Guide
description: A general guide
---
This document explains the deployment pipeline.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
results = engine.search("deployment")
assert len(results) == 1
assert results[0]["name"] == "General Guide"
def test_search_is_case_insensitive(self, temp_dir: Path) -> None:
"""Test that search is case-insensitive."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "deploy.md").write_text(
"""---
name: DEPLOYMENT Guide
---
Content.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
# Should find regardless of case
assert len(engine.search("deployment")) == 1
assert len(engine.search("DEPLOYMENT")) == 1
assert len(engine.search("Deployment")) == 1
def test_search_partial_match(self, temp_dir: Path) -> None:
"""Test that search finds partial keyword matches (FR-012)."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "deploy.md").write_text(
"""---
name: Deployment Process
---
Content.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
# Partial match should work
results = engine.search("deploy")
assert len(results) == 1
def test_search_returns_empty_for_no_matches(self, temp_dir: Path) -> None:
"""Test that search returns empty list when no matches found."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "process.md").write_text(
"""---
name: Code Review
---
Review code.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
results = engine.search("nonexistent")
assert results == []
def test_search_relevance_name_ranked_higher(self, temp_dir: Path) -> None:
"""Test that name matches rank higher than content matches (FR-011)."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
# Process with "deploy" in name
(temp_dir / "deploy.md").write_text(
"""---
name: Deploy Guide
description: How to deploy
---
Content about deployment.
"""
)
# Process with "deploy" only in content
(temp_dir / "other.md").write_text(
"""---
name: Other Process
description: Another process
---
You should deploy regularly.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
results = engine.search("deploy")
assert len(results) == 2
# Name match should be first
assert results[0]["name"] == "Deploy Guide"
assert results[0]["relevance_score"] > results[1]["relevance_score"]
def test_search_max_50_results(self, temp_dir: Path) -> None:
"""Test that search returns maximum 50 results (FR-012a)."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
# Create 60 files with matching keyword
for i in range(60):
(temp_dir / f"process-{i}.md").write_text(
f"""---
name: Process {i}
description: Contains keyword
---
Content with keyword.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
results = engine.search("keyword")
assert len(results) <= 50
class TestSearchEngineRelevance:
"""Tests for relevance scoring."""
def test_calculate_relevance_name_match(self, temp_dir: Path) -> None:
"""Test that name matches get highest score (100 pts)."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "test.md").write_text(
"""---
name: Test Process
---
Content.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
results = engine.search("test")
# Name match should have high relevance
assert results[0]["relevance_score"] >= 0.5
def test_calculate_relevance_description_match(self, temp_dir: Path) -> None:
"""Test that description matches get medium score (50 pts)."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "process.md").write_text(
"""---
name: Some Process
description: Test description here
---
Other content.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
results = engine.search("test")
# Should find with moderate relevance
assert len(results) == 1
assert 0.2 <= results[0]["relevance_score"] <= 0.8
class TestSearchEngineSnippet:
"""Tests for snippet extraction."""
def test_extract_snippet_shows_context(self, temp_dir: Path) -> None:
"""Test that snippet shows match context."""
from sso_mcp_server.processes.search import SearchEngine
from sso_mcp_server.processes.service import ProcessService
(temp_dir / "process.md").write_text(
"""---
name: Process Guide
---
This document explains how deployment works in our system.
"""
)
service = ProcessService(temp_dir)
engine = SearchEngine(service)
results = engine.search("deployment")
assert len(results) == 1
assert "snippet" in results[0]
assert "deployment" in results[0]["snippet"].lower()