conftest.py 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import dataclasses
  2. import socket
  3. import subprocess
  4. import sys
  5. import time
  6. import httpx
  7. import pytest
  8. def _free_port() -> int:
  9. with socket.socket() as s:
  10. s.bind(('127.0.0.1', 0))
  11. return s.getsockname()[1]
  12. @pytest.fixture(scope='session')
  13. def radicale_server(tmp_path_factory):
  14. """Start a local Radicale CalDAV server for the test session.
  15. auth.type = none accepts any credentials; the username in the HTTP auth
  16. still determines the path prefix (http://host/{username}/), so we always
  17. use username='test'. The 'House' calendar is created on first use; any
  18. test that writes to it cleans up in its own finally block.
  19. """
  20. port = _free_port()
  21. data_dir = tmp_path_factory.mktemp('radicale')
  22. config_ini = data_dir / 'config.ini'
  23. config_ini.write_text('\n'.join([
  24. '[server]',
  25. f'hosts = 127.0.0.1:{port}',
  26. '[auth]',
  27. 'type = none',
  28. '[storage]',
  29. f'filesystem_folder = {data_dir / "collections"}',
  30. ]))
  31. proc = subprocess.Popen(
  32. [sys.executable, '-m', 'radicale', '--config', str(config_ini)],
  33. stdout=subprocess.DEVNULL,
  34. stderr=subprocess.DEVNULL,
  35. )
  36. url = f'http://127.0.0.1:{port}/'
  37. for _ in range(50):
  38. try:
  39. httpx.get(url, timeout=1.0)
  40. break
  41. except Exception:
  42. time.sleep(0.1)
  43. else:
  44. proc.kill()
  45. pytest.fail('Radicale did not start within 5 seconds')
  46. import caldav
  47. client = caldav.DAVClient(url=url, username='test', password='test')
  48. principal = client.principal()
  49. principal.make_calendar(name='House', cal_id='house')
  50. yield {'url': url, 'username': 'test', 'password': 'test', 'calendar_name': 'House'}
  51. proc.terminate()
  52. try:
  53. proc.wait(timeout=5)
  54. except subprocess.TimeoutExpired:
  55. proc.kill()
  56. @pytest.fixture
  57. def require_user_cal(radicale_server, monkeypatch):
  58. """Redirect user_cal to the local Radicale server instead of skipping.
  59. Patches get_config in the two modules that call it on the user_cal path:
  60. - room_cal.user_cal (UserCal.__init__)
  61. - room_cal.actions.fetch_user_cal (calendar_name lookup)
  62. """
  63. from room_cal._utils import get_config
  64. cfg = get_config()
  65. test_user_cal_cfg = dataclasses.replace(
  66. cfg.user_cal,
  67. url=radicale_server['url'],
  68. username=radicale_server['username'],
  69. password=radicale_server['password'],
  70. calendar_name=radicale_server['calendar_name'],
  71. )
  72. test_cfg = dataclasses.replace(cfg, user_cal=test_user_cal_cfg)
  73. mock = lambda: test_cfg
  74. monkeypatch.setattr('room_cal.user_cal.get_config', mock)
  75. monkeypatch.setattr('room_cal.actions.fetch_user_cal.get_config', mock)