#!/usr/bin/env python3 """Exercise the actual Rust/static HTTP service; keep all service work foreground.""" import http.cookiejar import json import os from pathlib import Path import socket import subprocess import tempfile import time import urllib.error import urllib.request root = Path(__file__).resolve().parents[1] with socket.socket() as sock: sock.bind(('127.0.0.1', 0)) port = sock.getsockname()[1] origin = f'http://127.0.0.1:{port}' with tempfile.TemporaryDirectory(prefix='webka-smoke-') as data: env = dict(os.environ, WEBKA_DATA=data, WEBKA_BIND=f'127.0.0.1:{port}', WEBKA_ORIGIN=origin, WEBKA_MDNS='0') proc = subprocess.Popen([str(root / 'backend/target/debug/webka')], cwd=root, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: for _ in range(100): try: with urllib.request.urlopen(origin + '/api/bootstrap', timeout=1) as response: assert json.load(response)['needs_setup'] is True break except (urllib.error.URLError, ConnectionError): if proc.poll() is not None: raise RuntimeError(proc.stderr.read().decode()) time.sleep(0.1) else: raise RuntimeError('Service did not become ready') for route in ['/', '/login', '/host', '/watch', '/settings/accounts', '/settings/general']: with urllib.request.urlopen(origin + route) as response: assert response.status == 200, route assert 'text/html' in response.headers['Content-Type'], route assert response.headers['X-Content-Type-Options'] == 'nosniff' cookies = http.cookiejar.CookieJar() client = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookies)) def request(path, body=None): return client.open(urllib.request.Request(origin + path, data=json.dumps(body).encode() if body is not None else None, headers={'Origin': origin, 'Content-Type': 'application/json'})) password = 'smoke-test-password-123' with request('/api/bootstrap', {'username': 'owner', 'password': password, 'setup_token': Path(data, 'setup-token').read_text()}) as response: assert response.status == 201 with request('/api/login', {'username': 'owner', 'password': password}) as response: assert response.status == 200 with request('/api/session') as response: assert json.load(response)['role'] == 'admin' with request('/api/status') as response: status = json.load(response) assert status['local_url'] == origin + '/watch' and status['live'] is False with request('/api/logout', {}) as response: assert response.status == 204 print('PASS: all six static routes, first-run setup, login, cookie/session, local share URL, logout, security headers.') finally: proc.terminate() try: proc.communicate(timeout=5) except subprocess.TimeoutExpired: proc.kill() proc.communicate()