feat: initial implementation taller-wox.fitlabs.dev
Portal FastAPI + 5 endpoints REST para Bootcamp Agentic AI con watsonx Orchestrate (FactorIT). Single container, Coolify-ready. - Landing brandeado FIT con formulario de registro (honeypot anti-bot) - Tokens itsdangerous para descargas (24h expiry) - 5 endpoints API: historical/available procedures, member-insights, schedule, generate-report (Jinja2 + Plotly) - SQLite con upsert-on-email para leads + log de descargas - Admin endpoints (HTTP Basic): leads.json, leads.csv, stats - 23 tests pytest pasando - Dockerfile listo para Coolify con volúmenes persistentes (/app/leads.db, /app/app/data/reports_output, /app/material) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
122
tests/test_frontend.py
Normal file
122
tests/test_frontend.py
Normal file
@@ -0,0 +1,122 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_landing_renders(client):
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert "Bootcamp Agentic AI" in response.text
|
||||
assert 'name="email"' in response.text
|
||||
assert 'name="website"' in response.text
|
||||
|
||||
|
||||
def test_register_valid_form_redirects_with_token(client):
|
||||
response = client.post(
|
||||
"/register",
|
||||
data={
|
||||
"nombre": "Felipe Arentsen",
|
||||
"email": "felipe@factorit.com",
|
||||
"empresa": "FactorIT",
|
||||
"consentimiento": "on",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 303
|
||||
assert response.headers["location"].startswith("/descargas?token=")
|
||||
|
||||
|
||||
def test_register_honeypot_filled_silently_drops(client):
|
||||
response = client.post(
|
||||
"/register",
|
||||
data={
|
||||
"nombre": "Bot",
|
||||
"email": "bot@spam.com",
|
||||
"empresa": "Spam Inc",
|
||||
"consentimiento": "on",
|
||||
"website": "https://spam.com",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert response.status_code == 303
|
||||
from app.db import get_lead_by_email
|
||||
assert get_lead_by_email("bot@spam.com") is None
|
||||
|
||||
|
||||
def test_register_duplicate_email_reissues_token(client):
|
||||
for _ in range(2):
|
||||
client.post(
|
||||
"/register",
|
||||
data={
|
||||
"nombre": "Felipe",
|
||||
"email": "dup@factorit.com",
|
||||
"empresa": "FactorIT",
|
||||
"consentimiento": "on",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
from app.db import get_lead_by_email
|
||||
lead = get_lead_by_email("dup@factorit.com")
|
||||
assert lead["times_registered"] == 2
|
||||
|
||||
|
||||
def test_descargas_with_invalid_token_redirects_home(client):
|
||||
response = client.get("/descargas?token=invalid-junk", follow_redirects=False)
|
||||
assert response.status_code == 307
|
||||
assert response.headers["location"].startswith("/?error=")
|
||||
|
||||
|
||||
def test_descargas_with_valid_token_renders(client):
|
||||
reg = client.post(
|
||||
"/register",
|
||||
data={
|
||||
"nombre": "Maria",
|
||||
"email": "maria@test.com",
|
||||
"empresa": "Test Co",
|
||||
"consentimiento": "on",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
token = reg.headers["location"].split("token=")[1]
|
||||
response = client.get(f"/descargas?token={token}")
|
||||
assert response.status_code == 200
|
||||
assert "Hola Maria" in response.text
|
||||
assert "Material técnico" in response.text
|
||||
assert "Material funcional" in response.text
|
||||
|
||||
|
||||
def test_download_with_valid_token_serves_file(client):
|
||||
material_dir = Path(os.environ["MATERIAL_DIR"])
|
||||
material_dir.mkdir(parents=True, exist_ok=True)
|
||||
(material_dir / "taller-wox-tecnico.zip").write_bytes(b"PK\x03\x04 fake zip")
|
||||
|
||||
reg = client.post(
|
||||
"/register",
|
||||
data={
|
||||
"nombre": "Test",
|
||||
"email": "test-dl@test.com",
|
||||
"empresa": "TC",
|
||||
"consentimiento": "on",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
token = reg.headers["location"].split("token=")[1]
|
||||
response = client.get(f"/download/taller-wox-tecnico.zip?token={token}")
|
||||
assert response.status_code == 200
|
||||
assert response.content == b"PK\x03\x04 fake zip"
|
||||
assert "attachment" in response.headers["content-disposition"]
|
||||
|
||||
|
||||
def test_download_invalid_filename_returns_404(client):
|
||||
reg = client.post(
|
||||
"/register",
|
||||
data={
|
||||
"nombre": "Test",
|
||||
"email": "bad-fn@test.com",
|
||||
"empresa": "TC",
|
||||
"consentimiento": "on",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
token = reg.headers["location"].split("token=")[1]
|
||||
response = client.get(f"/download/etc-passwd?token={token}")
|
||||
assert response.status_code == 404
|
||||
Reference in New Issue
Block a user