import asyncio import os import subprocess import warnings from datetime import date, timedelta from importlib.resources import files from pathlib import Path import pytest from room_cal._utils import data_home as _data_home from room_cal.service_api.types import ServiceAuthError def _ts_dir() -> Path: return Path(str(files('room_cal.service_api').joinpath('typescript'))) def _browsers_path() -> Path: return _data_home() / '.playwright-browsers' def _npm_env() -> dict: env = os.environ.copy() env.setdefault('ROOM_CAL_DATA_HOME', str(_data_home())) env['PLAYWRIGHT_BROWSERS_PATH'] = str(_browsers_path()) return env def _npm_run(script: str, timeout: int, extra_env: dict | None = None) -> subprocess.CompletedProcess: ts_dir = _ts_dir() if not (ts_dir / 'node_modules').exists(): subprocess.run(['npm', 'install'], cwd=ts_dir, check=True, capture_output=True) env = _npm_env() if extra_env: env.update(extra_env) try: return subprocess.run( ['npm', 'run', script], cwd=ts_dir, env=env, capture_output=True, text=True, timeout=timeout, ) except subprocess.TimeoutExpired as exc: pytest.fail(f'npm run {script} timed out after {exc.timeout}s') except Exception as exc: pytest.fail(f'Failed to launch npm run {script}: {exc}') @pytest.fixture(scope='session') def chrome_installed(): result = _npm_run('install-browsers', timeout=30) if result.returncode != 0: pytest.fail( f'npm run install-browsers failed (exit {result.returncode})\n' f'stdout:\n{result.stdout}\nstderr:\n{result.stderr}' ) browsers = _browsers_path() chromium_dirs = [p for p in browsers.iterdir() if p.is_dir() and 'chromium' in p.name] if browsers.exists() else [] if not chromium_dirs: pytest.fail(f'install-browsers exited 0 but no chromium dirs found in {browsers}') def test_install_chrome(chrome_installed): """npm run install-browsers puts Chromium binaries in the expected location.""" def test_refresh_cookies_headless(chrome_installed): """Cookie-refresh machinery: Chromium must launch and write cookies. Pass — cookies updated and API call succeeds. Pass + warn — script ran correctly but session is stale (exit 2), cookies unchanged, or API auth failed after writing cookies. Fail — unexpected error or non-zero exit. """ data = _data_home() airbnb_env = data / 'airbnb.env' booking_env = data / 'booking.env' before = {p: p.read_text() if p.exists() else None for p in (airbnb_env, booking_env)} result = _npm_run('refresh-all', timeout=60, extra_env={'HEADLESS': '1'}) output = f'stdout:\n{result.stdout}\nstderr:\n{result.stderr}' if result.returncode == 2: warnings.warn( 'Session is stale — run `refresh-creds` to re-authenticate.\n' + output, UserWarning, stacklevel=1, ) return if result.returncode != 0: pytest.fail(f'npm run refresh-all exited {result.returncode}\n' + output) after = {p: p.read_text() if p.exists() else None for p in (airbnb_env, booking_env)} if not any(after[p] != before[p] for p in (airbnb_env, booking_env)): warnings.warn( 'refresh-all exited 0 but neither airbnb.env nor booking.env changed.', UserWarning, stacklevel=1, ) return try: from room_cal.service_api import ServiceAPI api = ServiceAPI() asyncio.run(api.get_prices(date.today() + timedelta(days=1))) except ServiceAuthError as exc: warnings.warn( f'Cookies were updated but API auth failed ({exc}); session may be invalid.', UserWarning, stacklevel=1, )