import os
import sys
import pytest
from unittest.mock import patch, MagicMock
# --- Setup Environment BEFORE Import ---
os.environ["AGILEDAY_TENANT_ID"] = "test-tenant"
os.environ["AGILEDAY_API_TOKEN"] = "test-token"
# Add src to path so we can import the server
sys.path.append(os.path.join(os.path.dirname(__file__), '../src'))
# Add list_skills_logic to imports
from agileday_server import find_experts_logic, profile_logic, list_skills_logic, check_availability_logic, BASE_URL
# --- Fixtures ---
@pytest.fixture
def mock_api_response():
"""Mocks the requests.get call to avoid real network requests."""
with patch('requests.get') as mock_get:
# Default successful response mock
mock_response = MagicMock()
mock_response.status_code = 200
mock_get.return_value = mock_response
yield mock_get
# --- Tests ---
def test_base_url_configuration():
"""Ensure the URL is constructed correctly from the tenant ID."""
expected_url = "https://test-tenant.agileday.io/api"
assert BASE_URL == expected_url
def test_find_experts_case_insensitive(mock_api_response):
"""Test that 'python' finds 'Python'."""
mock_data = [{"firstName": "Alice", "skills": [{"name": "Python", "proficiency": 5}]}]
mock_api_response.return_value.json.return_value = mock_data
# UPDATED CALL
result = find_experts_logic("python")
assert "Python" in result
def test_find_experts_found(mock_api_response):
"""Test searching for a skill that exists."""
mock_data = [
{
"firstName": "Alice",
"lastName": "Engineer",
"title": "Senior Dev",
"skills": [
{"name": "Python", "proficiency": 5},
{"name": "Java", "proficiency": 2}
]
},
{
"firstName": "Bob",
"lastName": "Manager",
"title": "PM",
"skills": []
}
]
mock_api_response.return_value.json.return_value = mock_data
result = find_experts_logic("Python")
assert "Alice Engineer" in result
assert "Senior Dev" in result
# Note: Older logic didn't have Exp, so this asserts basic format
assert "Python" in result
assert "Bob" not in result
def test_find_experts_includes_experience(mock_api_response):
"""
New Test: Ensure that the 'experience' field from the API
is correctly formatted into the output string.
"""
mock_data = [
{
"firstName": "Senior",
"lastName": "Dev",
"title": "Architect",
"skills": [
# API returns experience as a string (e.g. "2 years" or ISO "P2Y")
{"name": "Java", "proficiency": 5, "experience": "5 years"},
{"name": "Python", "proficiency": 4, "experience": "6 months"}
]
}
]
mock_api_response.return_value.json.return_value = mock_data
# Search for Java
result = find_experts_logic("Java")
# Assert that the experience string is present in the output
assert "Senior Dev" in result
assert "Java (Lvl 5/5, Exp: 5 years)" in result
def test_find_experts_no_matches(mock_api_response):
"""Test response when no one has the skill."""
mock_data = [{"firstName": "Alice", "skills": [{"name": "Java"}]}]
mock_api_response.return_value.json.return_value = mock_data
# UPDATED CALL
result = find_experts_logic("Rust")
assert "No experts found" in result
def test_get_employee_profile(mock_api_response):
"""Test fetching a specific employee profile."""
mock_data = [
{
"firstName": "Jane",
"lastName": "Doe",
"skills": [
{"name": "Rust", "proficiency": 4, "motivation": 9},
{"name": "C++", "proficiency": 5, "motivation": 5}
]
}
]
mock_api_response.return_value.json.return_value = mock_data
# UPDATED CALL: profile_logic
result = profile_logic("Jane")
assert "Jane Doe" in result
assert "Rust: Level 4 🔥" in result
assert "C++: Level 5" in result
def test_api_failure_handling(mock_api_response):
"""Test graceful handling of API errors."""
mock_api_response.side_effect = Exception("API Connection Failed")
# UPDATED CALL
result = find_experts_logic("Python")
# Note: In the new implementation we return "Error: ..."
assert "Error" in result
assert "API Connection Failed" in result
def test_list_skills_grouping(mock_api_response):
"""Test that skills are fetched and grouped by category."""
mock_data = [
{"name": "Python", "category": "Backend"},
{"name": "Java", "category": "Backend"},
{"name": "React", "category": "Frontend"},
{"name": "Figma", "category": None} # Should be 'Uncategorized'
]
mock_api_response.return_value.json.return_value = mock_data
result = list_skills_logic()
# Verify Output Formatting
assert "### Backend" in result
assert "Python, Java" in result or "Java, Python" in result
assert "### Frontend" in result
assert "React" in result
assert "### Uncategorized" in result
assert "Figma" in result
def test_check_availability(mock_api_response):
"""Test the availability calculation logic."""
# 1. Setup Mock for Employees (First call)
employees_data = [
{"id": "user1", "firstName": "Alice", "lastName": "Free", "title": "Dev"},
{"id": "user2", "firstName": "Bob", "lastName": "Busy", "title": "Lead"}
]
# 2. Setup Mock for Allocations (Second call)
allocations_data = [
# Bob is busy with 100% allocation
{"employeeId": "user2", "projectName": "Project X", "allocation": 100}
]
# Configure side_effect to return different data for different URLs
def side_effect(*args, **kwargs):
if "employee" in args[0]:
mock = MagicMock()
mock.status_code = 200
mock.json.return_value = employees_data
return mock
if "allocation_reporting" in args[0]:
mock = MagicMock()
mock.status_code = 200
mock.json.return_value = allocations_data
return mock
return MagicMock()
mock_api_response.side_effect = side_effect
# 3. Run Logic
result = check_availability_logic("2024-01-01", "2024-01-31")
# 4. Assertions
# Alice should be green (100% free)
assert "🟢 **Alice Free**" in result
assert "Availability: **100%**" in result
# Bob should be red (0% free)
assert "🔴 **Bob Busy**" in result
assert "Allocated: 100%" in result
assert "Project X" in result