import asyncio import importlib.util import sys import types from pathlib import Path from urllib.parse import quote from quart import Quart, g, session sys.path.insert(0, str(Path(__file__).resolve().parents[1])) class _NoOpLimiter: def limit(self, *_args, **_kwargs): return lambda fn: fn def _load_module(module_name, module_path): spec = importlib.util.spec_from_file_location(module_name, module_path) module = importlib.util.module_from_spec(spec) sys.modules[module_name] = module spec.loader.exec_module(module) return module def _install_stubs(monkeypatch): setup_stub = types.ModuleType("my_modules.app.setup") setup_stub.LIMITER = _NoOpLimiter() monkeypatch.setitem(sys.modules, "my_modules.app.setup", setup_stub) header_stub = types.ModuleType("my_modules.decoratory.header") def login_required(func): async def wrapper(*args, **kwargs): user = session.get("user") if user is None: return "unauthorized", 401 return await func(user=user, *args, **kwargs) wrapper.__name__ = func.__name__ return wrapper header_stub.login_required = login_required monkeypatch.setitem(sys.modules, "my_modules.decoratory.header", header_stub) def _client(monkeypatch): _install_stubs(monkeypatch) root = Path(__file__).resolve().parents[1] module = _load_module("link_composer_route_under_test", root / "routes" / "side" / "link_composer.py") app = Quart(__name__, template_folder=str(root / "templates" / "side")) app.secret_key = "test-secret" app.register_blueprint(module.link_composer_bp) @app.before_request async def wide_event_context(): g.wide_event = {} return app, app.test_client() def test_decode_api_is_public(monkeypatch): async def run_test(): _app, client = _client(monkeypatch) from link_composer.codec import encode_url code = encode_url("https://example.com/target").code response = await client.post("/api/links/decode", json={"code": code}) payload = await response.get_json() assert response.status_code == 200 assert payload["ok"] is True assert payload["url"] == "https://example.com/target" asyncio.run(run_test()) def test_decode_api_accepts_redirect_url(monkeypatch): async def run_test(): _app, client = _client(monkeypatch) from link_composer.codec import encode_url code = encode_url("https://example.com/target").code response = await client.post("/api/links/decode", json={"code": f"http://localhost/l/{code}"}) payload = await response.get_json() assert response.status_code == 200 assert payload["ok"] is True assert payload["url"] == "https://example.com/target" asyncio.run(run_test()) def test_encode_api_requires_login(monkeypatch): async def run_test(): _app, client = _client(monkeypatch) response = await client.post("/api/links/encode", json={"url": "https://example.com/target"}) assert response.status_code == 401 asyncio.run(run_test()) def test_encode_api_returns_qr_for_logged_in_user(monkeypatch): async def run_test(): _app, client = _client(monkeypatch) async with client.session_transaction() as sess: sess["user"] = {"sub": "user_123", "name": "Demo"} response = await client.post("/api/links/encode", json={"url": "https://example.com/target"}) payload = await response.get_json() assert response.status_code == 200 assert payload["ok"] is True assert payload["code"].startswith("u") encoded_code = quote(payload["code"], safe="") assert payload["redirect_url"].endswith(f"/l/{encoded_code}") assert f"/links/qr/{encoded_code}" in payload["qr_url"] asyncio.run(run_test()) def test_qr_endpoint_returns_png_after_decoding(monkeypatch): async def run_test(): _app, client = _client(monkeypatch) from link_composer.codec import encode_url code = encode_url("https://example.com/target").code response = await client.get(f"/links/qr/{code}") assert response.status_code == 200 assert response.headers["Content-Type"] == "image/png" assert (await response.get_data()).startswith(b"\x89PNG") asyncio.run(run_test())