Files
devplacepy/tests/e2e/auth/terms.py
T
retoorandClaude Opus 5 91fac7fd67 Gate blocked actions behind an in-place terms acceptance dialog
A member whose account has not accepted the terms in force now gets one
dialog on the action they attempted instead of a dead-end refusal. The
client handler is the single TermsGate, wired into every Http POST helper
so the four optimistic controllers cannot swallow the gate into an error
flash, and the original request is replayed once the acceptance is
recorded. Reading the site and deleting an account stay unblocked.

apple.md is the source brief the compliance research documents reference.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 11:25:57 +02:00

116 lines
3.8 KiB
Python

# retoor <retoor@molodetz.nl>
import time
import requests
from playwright.sync_api import expect
from devplacepy.database import get_setting, get_table, refresh_snapshot, set_setting
from devplacepy.utils import clear_user_cache
from tests.conftest import BASE_URL
JSON = {"Accept": "application/json"}
def _seed_post(user):
session = requests.Session()
session.post(
f"{BASE_URL}/auth/login",
data={"email": user["email"], "password": user["password"]},
headers=JSON,
)
session.post(
f"{BASE_URL}/posts/create",
data={
"title": "Terms gate fixture post",
"content": "A post that exists so the vote control is present on the feed.",
},
headers=JSON,
)
def _accepted_version(username):
refresh_snapshot()
row = get_table("users").find_one(username=username)
return (row or {}).get("terms_version") or ""
def _restore_accepted_version(username, version):
refresh_snapshot()
row = get_table("users").find_one(username=username)
if not row:
return
get_table("users").update({"uid": row["uid"], "terms_version": version}, ["uid"])
clear_user_cache(row["uid"])
def _open_gate_dialog(page):
for _ in range(40):
page.locator(".post-card .vote-up").first.click()
dialog = page.locator(".dialog-overlay.visible")
try:
dialog.wait_for(state="visible", timeout=1500)
return dialog
except Exception:
time.sleep(0.5)
return None
def test_the_gate_dialog_accepts_the_terms_and_replays_the_action(alice, seeded_db):
page, _ = alice
username = seeded_db["alice"]["username"]
_seed_post(seeded_db["alice"])
original = get_setting("terms_version", "1") or "1"
accepted_before = _accepted_version(username)
bumped = f"{original}-e2eaccept"
set_setting("terms_version", bumped)
try:
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".post-card").first.wait_for(state="visible", timeout=10000)
dialog = _open_gate_dialog(page)
assert dialog is not None, "the terms gate dialog never appeared"
expect(page.locator(".dialog-title")).to_have_text("Terms of Service")
expect(page.locator(".dialog-links a")).to_have_count(3)
expect(page.locator(".dialog-confirm")).to_have_text("Accept and continue")
assert _accepted_version(username) != bumped
page.locator(".dialog-confirm").click()
page.locator(".dialog-overlay.visible").wait_for(state="hidden", timeout=10000)
recorded = ""
for _ in range(40):
recorded = _accepted_version(username)
if recorded == bumped:
break
time.sleep(0.5)
assert recorded == bumped
finally:
set_setting("terms_version", original)
_restore_accepted_version(username, accepted_before)
def test_declining_the_gate_dialog_records_no_acceptance(alice, seeded_db):
page, _ = alice
username = seeded_db["alice"]["username"]
_seed_post(seeded_db["alice"])
original = get_setting("terms_version", "1") or "1"
accepted_before = _accepted_version(username)
bumped = f"{original}-e2edecline"
set_setting("terms_version", bumped)
try:
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".post-card").first.wait_for(state="visible", timeout=10000)
dialog = _open_gate_dialog(page)
assert dialog is not None, "the terms gate dialog never appeared"
page.locator(".dialog-cancel").click()
page.locator(".dialog-overlay.visible").wait_for(state="hidden", timeout=10000)
assert _accepted_version(username) != bumped
finally:
set_setting("terms_version", original)
_restore_accepted_version(username, accepted_before)