Three self-contained features, verified complete and cross-wired end-to-end (audited: backend 54/54 tests, frontend 110/110 tests, tsc --noEmit clean, i18n coverage script clean): - EMF mode: DeviceMotion/DeviceOrientation-based field-meter sensing, a fifth séance channel alongside Wire/EVP/Radio/Ouija, with its own fragment prompt persona and full frontend gauge UI. - Armory (shop/waitlist): pre-order capture page for the future Ultimate Quantum Box hardware line, rate-limited public endpoint, explicitly no payment collection. - Haunting layer: ambient possession effects (dread-bed audio, title glitching, idle-paced whispers/manifests), respects prefers-reduced-motion, mounted once at the app root. Plus WebUSB robustness fixes in lib/sdr.ts (Terratec vendor ID, explicit selectConfiguration, isSecureContext gate). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013PphXq1s43DNRj1uWKGXof
30 lines
978 B
Python
30 lines
978 B
Python
import pytest
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_waitlist_join_and_idempotent_rejoin(client):
|
|
response = await client.post(
|
|
"/api/shop/waitlist", json={"email": "hunter@example.com", "interest": "quantum-box"}
|
|
)
|
|
assert response.status_code == 201
|
|
assert response.json()["status"] == "listed"
|
|
|
|
again = await client.post(
|
|
"/api/shop/waitlist", json={"email": "hunter@example.com"}
|
|
)
|
|
assert again.status_code == 201
|
|
assert again.json()["status"] == "already_listed"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_waitlist_rejects_bad_email(client):
|
|
response = await client.post("/api/shop/waitlist", json={"email": "not-an-email"})
|
|
assert response.status_code == 422
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_waitlist_is_public(client):
|
|
# No auth cookie — the shop is open to every passing seeker.
|
|
response = await client.post("/api/shop/waitlist", json={"email": "ghost@example.org"})
|
|
assert response.status_code == 201
|