test_refresh_cookies.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import asyncio
  2. import os
  3. import subprocess
  4. import warnings
  5. from datetime import date, timedelta
  6. from importlib.resources import files
  7. from pathlib import Path
  8. import pytest
  9. from room_cal._utils import data_home as _data_home
  10. from room_cal.service_api.types import ServiceAuthError
  11. def _ts_dir() -> Path:
  12. return Path(str(files('room_cal.service_api').joinpath('typescript')))
  13. def _browsers_path() -> Path:
  14. return _data_home() / '.playwright-browsers'
  15. def _npm_env() -> dict:
  16. env = os.environ.copy()
  17. env.setdefault('ROOM_CAL_DATA_HOME', str(_data_home()))
  18. env['PLAYWRIGHT_BROWSERS_PATH'] = str(_browsers_path())
  19. return env
  20. def _npm_run(script: str, timeout: int, extra_env: dict | None = None) -> subprocess.CompletedProcess:
  21. ts_dir = _ts_dir()
  22. if not (ts_dir / 'node_modules').exists():
  23. subprocess.run(['npm', 'install'], cwd=ts_dir, check=True, capture_output=True)
  24. env = _npm_env()
  25. if extra_env:
  26. env.update(extra_env)
  27. try:
  28. return subprocess.run(
  29. ['npm', 'run', script],
  30. cwd=ts_dir,
  31. env=env,
  32. capture_output=True,
  33. text=True,
  34. timeout=timeout,
  35. )
  36. except subprocess.TimeoutExpired as exc:
  37. pytest.fail(f'npm run {script} timed out after {exc.timeout}s')
  38. except Exception as exc:
  39. pytest.fail(f'Failed to launch npm run {script}: {exc}')
  40. @pytest.fixture(scope='session')
  41. def chrome_installed():
  42. result = _npm_run('install-browsers', timeout=30)
  43. if result.returncode != 0:
  44. pytest.fail(
  45. f'npm run install-browsers failed (exit {result.returncode})\n'
  46. f'stdout:\n{result.stdout}\nstderr:\n{result.stderr}'
  47. )
  48. browsers = _browsers_path()
  49. chromium_dirs = [p for p in browsers.iterdir() if p.is_dir() and 'chromium' in p.name] if browsers.exists() else []
  50. if not chromium_dirs:
  51. pytest.fail(f'install-browsers exited 0 but no chromium dirs found in {browsers}')
  52. def test_install_chrome(chrome_installed):
  53. """npm run install-browsers puts Chromium binaries in the expected location."""
  54. def test_refresh_cookies_headless(chrome_installed):
  55. """Cookie-refresh machinery: Chromium must launch and write cookies.
  56. Pass — cookies updated and API call succeeds.
  57. Pass + warn — script ran correctly but session is stale (exit 2), cookies
  58. unchanged, or API auth failed after writing cookies.
  59. Fail — unexpected error or non-zero exit.
  60. """
  61. data = _data_home()
  62. airbnb_env = data / 'airbnb.env'
  63. booking_env = data / 'booking.env'
  64. before = {p: p.read_text() if p.exists() else None for p in (airbnb_env, booking_env)}
  65. result = _npm_run('refresh-all', timeout=60, extra_env={'HEADLESS': '1'})
  66. output = f'stdout:\n{result.stdout}\nstderr:\n{result.stderr}'
  67. if result.returncode == 2:
  68. warnings.warn(
  69. 'Session is stale — run `refresh-creds` to re-authenticate.\n' + output,
  70. UserWarning,
  71. stacklevel=1,
  72. )
  73. return
  74. if result.returncode != 0:
  75. pytest.fail(f'npm run refresh-all exited {result.returncode}\n' + output)
  76. after = {p: p.read_text() if p.exists() else None for p in (airbnb_env, booking_env)}
  77. if not any(after[p] != before[p] for p in (airbnb_env, booking_env)):
  78. warnings.warn(
  79. 'refresh-all exited 0 but neither airbnb.env nor booking.env changed.',
  80. UserWarning,
  81. stacklevel=1,
  82. )
  83. return
  84. try:
  85. from room_cal.service_api import ServiceAPI
  86. api = ServiceAPI()
  87. asyncio.run(api.get_prices(date.today() + timedelta(days=1)))
  88. except ServiceAuthError as exc:
  89. warnings.warn(
  90. f'Cookies were updated but API auth failed ({exc}); session may be invalid.',
  91. UserWarning,
  92. stacklevel=1,
  93. )