| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- import pytest
- import httpx
- from unittest.mock import patch, AsyncMock
- from datetime import date
- from room_cal.api import create_app
- from room_cal.api._routes import _serialize
- # ── _serialize ─────────────────────────────────────────────────────────────────
- def test_serialize_date():
- assert _serialize(date(2026, 1, 15)) == '2026-01-15'
- def test_serialize_nested_dict():
- result = _serialize({'a': date(2026, 1, 1), 'b': 42})
- assert result == {'a': '2026-01-01', 'b': 42}
- def test_serialize_list():
- result = _serialize([date(2026, 2, 1), 'x', 7])
- assert result == ['2026-02-01', 'x', 7]
- def test_serialize_passthrough():
- assert _serialize(42) == 42
- assert _serialize('hello') == 'hello'
- assert _serialize(None) is None
- # ── app factory ────────────────────────────────────────────────────────────────
- def test_create_app():
- from fastapi import FastAPI
- assert isinstance(create_app(), FastAPI)
- # ── HTTP tests ─────────────────────────────────────────────────────────────────
- @pytest.fixture
- def app():
- return create_app()
- @pytest.fixture
- async def client(app):
- async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url='http://test') as c:
- yield c
- _AUTHED = {'sub': 'u', 'email': 'u@test.com', 'name': 'User'}
- @pytest.mark.asyncio
- async def test_calc_net_to_list(client):
- r = await client.get('/calc/net_to_list?service=airbnb&amount=100')
- assert r.status_code == 200
- data = r.json()
- assert data['net'] == 100
- assert data['list'] > data['net']
- assert 'retail' in data
- assert 'details' in data
- @pytest.mark.asyncio
- async def test_calc_unknown_transformation(client):
- r = await client.get('/calc/bad_transform?service=airbnb&amount=100')
- assert r.status_code == 404
- @pytest.mark.asyncio
- async def test_calc_bad_service(client):
- r = await client.get('/calc/net_to_list?service=nonexistent&amount=100')
- assert r.status_code == 400
- @pytest.mark.asyncio
- async def test_auth_middleware_calc_is_public(client):
- # /calc/ prefix is public — no auth cookie needed
- r = await client.get('/calc/net_to_list?service=airbnb&amount=50')
- assert r.status_code == 200
- @pytest.mark.asyncio
- async def test_auth_middleware_private_api_returns_401(client):
- r = await client.get('/prices/2026-01-01')
- assert r.status_code == 401
- @pytest.mark.asyncio
- async def test_auth_middleware_html_path_redirects_to_login(client):
- r = await client.get('/calendar', follow_redirects=False)
- assert r.status_code in (302, 307)
- assert '/auth/login' in r.headers['location']
- @pytest.mark.asyncio
- async def test_workflow_discount_invalid_type(client):
- with patch('room_cal.api.get_user', return_value=_AUTHED):
- r = await client.post('/workflow/discount/2026-01-15', json={'type': 'bad', 'value': 10})
- assert r.status_code == 422
- @pytest.mark.asyncio
- async def test_action_dispatch_unknown_action(client):
- with patch('room_cal.api.get_user', return_value=_AUTHED):
- r = await client.post('/action/nonexistent-action/2026-01-15')
- assert r.status_code == 404
- @pytest.mark.asyncio
- async def test_reload_clears_caches(client):
- import room_cal.cal as _cal
- cal = _cal.get_calendar()
- cal._event_cache[('fake_key',)] = []
- cal._cached_url = 'http://example.com/cal'
- with patch('room_cal.api.get_user', return_value=_AUTHED):
- r = await client.post('/reload')
- assert r.status_code == 200
- data = r.json()
- assert 'cleared_calendars' in data
- assert data['cleared_calendars'] >= 1
- assert cal._event_cache == {}
- assert cal._cached_url is None
|