"""Campaign engine integration tests: materialization, sequencing, uniqueness. Tests the async service layer directly (materialize_campaign / get_campaign_entities) against the REAL test database used by the app: per-recipient generated values, invoice-number uniqueness within a campaign, subject/template/phone/account assignment plans, preview-mode non-persistence, and exhaustion behavior. """ import secrets import uuid from datetime import datetime, timedelta, timezone import pytest from app.models import (Campaign, CampaignRecipient, CompanyPhoneNumber, EmailTemplate, GmailAccount, OAuthToken, Recipient, RecipientList, SubjectLine, User) from app.services.campaign_engine import (get_campaign_entities, materialize_campaign) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _make_user(db) -> User: u = User(email=f"eng{secrets.token_hex(4)}@x.com", password_hash="x" * 60, full_name="Eng") db.add(u) db.commit() return u def _make_list(db, user_id, n=3, prefix="r") -> RecipientList: rl = RecipientList(name=f"list-{prefix}-{uuid.uuid4().hex[:6]}", user_id=user_id) db.add(rl) db.commit() for i in range(n): db.add(Recipient(list_id=rl.id, email=f"{prefix}{i}-{uuid.uuid4().hex[:6]}@x.com", first_name=f"Eng{prefix}", last_name=str(i), unsub_token=secrets.token_urlsafe(24))) db.commit() return rl def _make_subjects(db, user_id, n=3) -> list: subs = [] for i in range(n): s = SubjectLine(user_id=user_id, subject=f"Subject {i} %RandomInvoiceNumber%") db.add(s) subs.append(s) db.commit() return subs def _make_templates(db, user_id, n=2, use_invoice=True) -> list: tpls = [] for i in range(n): body = f"

Hi %FirstName%, total %RandomTotal%." if use_invoice: body += " Invoice %RandomInvoiceNumber%." body += "

" t = EmailTemplate(user_id=user_id, name=f"tpl-{i}-{uuid.uuid4().hex[:6]}", subject_hint=f"hint {i}", body_html=body) db.add(t) tpls.append(t) db.commit() return tpls def _make_accounts(db, user_id, n=2, status="authorized") -> list: accs = [] for i in range(n): a = GmailAccount(user_id=user_id, email=f"eng-acc{i}-{uuid.uuid4().hex[:6]}@gmail.com", status=status, sending_method="api", is_active=(status == "authorized")) db.add(a) db.commit() db.add(OAuthToken(account_id=a.id, access_token=f"tok-{i}", refresh_token=f"ref-{i}", expires_at=datetime.now(timezone.utc) + timedelta(hours=1))) db.commit() accs.append(a) return accs def _make_phones(db, user_id, n=2) -> list: ps = [] for i in range(n): p = CompanyPhoneNumber(user_id=user_id, label=f"line {i}", phone=f"+1555000{i:02d}") db.add(p) ps.append(p) db.commit() return ps def _make_campaign(db, user_id, list_id, subject_ids, template_ids, account_ids, **over) -> Campaign: kw = dict( user_id=user_id, name=over.pop("name", f"engine-{uuid.uuid4().hex[:6]}"), recipient_list_id=list_id, subject_set_ids=subject_ids, template_ids=template_ids, account_ids=account_ids, ) kw.update(over) c = Campaign(**kw) db.add(c) db.commit()