"""Flask test client / real-DB integration tests for the public "contact the owner" API (contact_api.py) -- the customer-to-owner path, deliberately separate from the agency's leads/Telegram pipeline. See contact_mail.py. """ import pytest import booking_db as bdb from app import app as flask_app CLIENT_A = "C-TEST-CONTACT-A" @pytest.fixture def client(): flask_app.config["TESTING"] = True return flask_app.test_client() def _insert_booking_client(client_id, business_name="Café Test"): with bdb.db.connect() as conn, conn.cursor() as cur: cur.execute( "INSERT INTO clients (client_id, business_name) VALUES (%s, %s) " "ON CONFLICT (client_id) DO NOTHING", (client_id, business_name)) conn.commit() def test_contact_request_emails_the_owner(client, monkeypatch): sent = [] monkeypatch.setattr( "contact_mail.mailer.send_email", lambda to, subject, html, from_addr=None: sent.append( {"to": to, "subject": subject, "html": html})) _insert_booking_client(CLIENT_A) bdb.create_user(CLIENT_A, "inhaber@contact-test.example", "irrelevant-pw") resp = client.post("/api/contact", json={ "client_id": CLIENT_A, "name": "Jamie", "contact": "jamie@example.com", "service_interest": "Herrenschnitt", "message": "Gibt es heute noch einen Termin?"}) assert resp.status_code == 201 assert resp.get_json() == {"sent": True} assert resp.headers["Access-Control-Allow-Origin"] == "*" assert len(sent) == 1 assert sent[0]["to"] == "inhaber@contact-test.example" assert "Jamie" in sent[0]["html"] assert "jamie@example.com" in sent[0]["html"] assert "Herrenschnitt" in sent[0]["html"] def test_contact_request_falls_back_to_service_field(client, monkeypatch): """The Rückruf widget sends service_interest; the plain contact form sends the same concept under the field name "service" -- both work.""" sent = [] monkeypatch.setattr( "contact_mail.mailer.send_email", lambda to, subject, html, from_addr=None: sent.append(html)) client_id = "C-TEST-CONTACT-SVCFALLBACK" _insert_booking_client(client_id) bdb.create_user(client_id, "inhaber@svcfallback.example", "irrelevant-pw") resp = client.post("/api/contact", json={ "client_id": client_id, "name": "Robin", "contact": "robin@example.com", "service": "Bartpflege"}) assert resp.status_code == 201 assert "Bartpflege" in sent[0] def test_honeypot_fakes_success_without_sending(client, monkeypatch): sent = [] monkeypatch.setattr( "contact_mail.mailer.send_email", lambda to, subject, html, from_addr=None: sent.append(to)) client_id = "C-TEST-CONTACT-HONEYPOT" _insert_booking_client(client_id) bdb.create_user(client_id, "inhaber@honeypot.example", "irrelevant-pw") resp = client.post("/api/contact", json={ "client_id": client_id, "name": "Bot", "contact": "bot@example.com", "website": "http://spam.example"}) assert resp.status_code == 201 assert resp.get_json() == {"sent": True} assert sent == [] def test_missing_required_fields_is_rejected(client): resp = client.post("/api/contact", json={"client_id": CLIENT_A, "name": "NoContact"}) assert resp.status_code == 400 def test_unknown_client_is_rejected(client): resp = client.post("/api/contact", json={ "client_id": "C-DOES-NOT-EXIST", "name": "Alex", "contact": "a@example.com"}) assert resp.status_code == 404 def test_client_without_owner_account_is_rejected(client): """A client that exists in the CRM but hasn't been provisioned into the booking schema yet (no owner account) -- nothing to notify, so this is a 404 rather than a silent success the visitor would wrongly trust.""" client_id = "C-TEST-CONTACT-NOOWNER" _insert_booking_client(client_id) resp = client.post("/api/contact", json={ "client_id": client_id, "name": "Alex", "contact": "a@example.com"}) assert resp.status_code == 404 def test_preflight_options_returns_cors_headers(client): resp = client.options("/api/contact") assert resp.status_code < 300 assert resp.headers["Access-Control-Allow-Origin"] == "*" assert "POST" in resp.headers["Access-Control-Allow-Methods"]