test_mcp.py 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import pytest
  2. from unittest.mock import AsyncMock, MagicMock, patch
  3. from datetime import date
  4. from room_cal.mcp import mcp, convert_price, run_action
  5. def test_mcp_is_fastmcp():
  6. from fastmcp import FastMCP
  7. assert isinstance(mcp, FastMCP)
  8. @pytest.mark.asyncio
  9. async def test_convert_price_from_net():
  10. result = await convert_price('net_to_list', 'airbnb', 100)
  11. assert set(result.keys()) == {'net', 'list', 'retail'}
  12. assert result['net'] == 100
  13. assert result['list'] > result['net']
  14. @pytest.mark.asyncio
  15. async def test_convert_price_from_list():
  16. result = await convert_price('list_to_net', 'airbnb', 150)
  17. assert set(result.keys()) == {'net', 'list', 'retail'}
  18. assert result['list'] == 150
  19. assert result['net'] < result['list']
  20. @pytest.mark.asyncio
  21. async def test_convert_price_from_retail():
  22. result = await convert_price('retail_to_list', 'airbnb', 200)
  23. assert set(result.keys()) == {'net', 'list', 'retail'}
  24. assert result['retail'] == 200
  25. @pytest.mark.asyncio
  26. async def test_convert_price_unknown_transformation():
  27. with pytest.raises(ValueError, match='Unknown transformation'):
  28. await convert_price('bad_transform', 'airbnb', 100)
  29. @pytest.mark.asyncio
  30. async def test_run_action_unknown_raises():
  31. with pytest.raises(ValueError, match='Unknown action'):
  32. await run_action('bad-action', '2026-01-01')
  33. @pytest.mark.asyncio
  34. async def test_run_action_end_date_defaults_to_start():
  35. mock_fn = AsyncMock(return_value=[])
  36. mock_mod = MagicMock()
  37. mock_mod.fetch_prices = mock_fn
  38. with patch('importlib.import_module', return_value=mock_mod):
  39. await run_action('fetch-prices', '2026-01-15')
  40. mock_fn.assert_called_once_with(date(2026, 1, 15), date(2026, 1, 15))
  41. @pytest.mark.asyncio
  42. async def test_run_action_kwargs_filtering_allows_allowed_keys():
  43. mock_fn = AsyncMock(return_value=[])
  44. mock_mod = MagicMock()
  45. mock_mod.fetch_feed = mock_fn
  46. with patch('importlib.import_module', return_value=mock_mod):
  47. # fetch-feed allows feed_name and title, but not service
  48. await run_action('fetch-feed', '2026-01-15', end_date='2026-01-20', feed_name='Fire', title='T', service='airbnb')
  49. mock_fn.assert_called_once_with(date(2026, 1, 15), date(2026, 1, 20), feed_name='Fire', title='T')
  50. @pytest.mark.asyncio
  51. async def test_run_action_kwargs_filtering_excludes_none():
  52. mock_fn = AsyncMock(return_value=[])
  53. mock_mod = MagicMock()
  54. mock_mod.fetch_feed = mock_fn
  55. with patch('importlib.import_module', return_value=mock_mod):
  56. # feed_name=None should be excluded even though it's in allowed_keys
  57. await run_action('fetch-feed', '2026-01-15', feed_name=None)
  58. mock_fn.assert_called_once_with(date(2026, 1, 15), date(2026, 1, 15))