"""FlowDeck — v6.2.0 Web Clipper tests.""" from __future__ import annotations import json from conftest import anon from app.auth.session import SessionManager def _token(user_id, login="user", is_admin=0): return SessionManager.create_session({"id": user_id, "login": login, "full_name": "User", "is_admin": is_admin}) def _as(client, user_id, login="user", is_admin=0): client.cookies.set("flowdeck_session", _token(user_id, login, is_admin)) def _insert_user(login="clipper_user", email="clip@example.com"): from app.db import get_conn with get_conn() as conn: cur = conn.execute( "INSERT INTO users (login, email, full_name, is_active) VALUES (?,?,?,1)", (login, email, "Clipper User"), ) conn.commit() return cur.lastrowid def _insert_workspace(owner_id, name="ClipWS"): from app.db import get_conn with get_conn() as conn: cur = conn.execute("INSERT INTO workspaces (name, owner_id) VALUES (?,?)", (name, owner_id)) conn.execute("INSERT INTO workspace_members (workspace_id, user_id, role) VALUES (?,?, 'admin')", (cur.lastrowid, owner_id)) conn.commit() return cur.lastrowid # ── Service unit tests ── def test_sanitize_removes_script(client): from app.services.web_clipper import sanitize_html html = '
Hello
hi
", "device_id": "rate-dev"}, ) assert r.status_code == 200, r.text r = client.post( "/api/v2/web-clipper/clip", json={"url": "https://example.com/overflow", "title": "overflow", "content": "hi
", "device_id": "rate-dev"}, ) assert r.status_code == 429 _rate_store.clear() def test_clip_bearer_token_auth(client): import hashlib from app.db import get_conn uid = _insert_user("clip_bearer") _insert_workspace(uid) # Create api_token token = "fd_test_clipper_bearer_123" th = hashlib.sha256(token.encode()).hexdigest() with get_conn() as conn: conn.execute("INSERT INTO api_tokens (user_id, name, token_hash, token_prefix) VALUES (?,?,?,?)", (uid, "test", th, token[:12])) conn.commit() # No session cookie anon(client) r = client.post( "/api/v2/web-clipper/clip", json={"url": "https://example.com/bearer", "title": "BearerClip", "content": "via bearer
", "device_id": "bearer-dev"}, headers={"Authorization": f"Bearer {token}"}, ) assert r.status_code == 200, r.text with get_conn() as conn: row = conn.execute("SELECT title FROM pages WHERE id=?", (r.json()["page_id"],)).fetchone() assert row["title"] == "BearerClip" def test_status_and_devices_flow(client): uid = _insert_user("clip_status") _insert_workspace(uid) # unauth status anon(client) r = client.get("/api/v2/web-clipper/status") assert r.status_code == 200 assert r.json()["authenticated"] is False # auth status _as(client, uid, "clip_status") r = client.get("/api/v2/web-clipper/status") assert r.json()["authenticated"] is True # register device via verify r = client.post("/api/v2/web-clipper/auth/verify", json={"device_id": "dev-xyz-123", "device_name": "Chrome — Test", "extension_name": "chrome"}) assert r.status_code == 200 data = r.json() assert data["device_id"] == "dev-xyz-123" # second verify same device reports existing r2 = client.post("/api/v2/web-clipper/auth/verify", json={"device_id": "dev-xyz-123", "device_name": "Chrome — Test"}) assert r2.json().get("existing") is True # list devices r = client.get("/api/v2/web-clipper/devices") assert r.status_code == 200 devs = r.json()["devices"] assert any(d["device_id"] == "dev-xyz-123" for d in devs) did = [d for d in devs if d["device_id"] == "dev-xyz-123"][0]["id"] # revoke r = client.delete(f"/api/v2/web-clipper/devices/{did}") assert r.status_code == 200 r = client.get("/api/v2/web-clipper/devices") remaining = [d for d in r.json()["devices"] if d["id"] == did] assert remaining[0]["revoked"] == 1 def test_extensions_page(client): r = client.get("/extensions") assert r.status_code == 200 assert "Web Clipper" in r.text assert "Chrome" in r.text def test_clip_invalid_url_rejected(client): uid = _insert_user("clip_badurl") _insert_workspace(uid) _as(client, uid, "clip_badurl") r = client.post("/api/v2/web-clipper/clip", json={"url": "javascript:alert(1)", "title": "x", "device_id": "d"}) assert r.status_code == 400 def test_clip_payload_too_large(client): uid = _insert_user("clip_big") _insert_workspace(uid) _as(client, uid, "clip_big") big = "x" * (11 * 1024 * 1024) # 11 MB string r = client.post("/api/v2/web-clipper/clip", json={"url": "https://example.com", "title": "big", "content": big, "device_id": "big-dev"}) # Expect 413 or 400 due to body size / content-length assert r.status_code in (413, 400, 422)