# Testing

```bash
cp .env.testing.example .env.testing   # then point it at a throwaway MySQL/MariaDB
php artisan test
```

- Tests run against a **real** MySQL/MariaDB server because multi-database isolation cannot be faked with SQLite.
- The master test DB (`social_saas_master_test`) is wrapped in `RefreshDatabase`.
- Tenant databases are real. Before every feature test, `Tests\Support\TenantDatabases::reset()` drops every schema/account matching the *test* patterns (`social_test_tenant_%`, `tu_test_%`). It refuses to run if those patterns don't contain `test`.
- The credentials in `.env.testing` need `CREATE/DROP DATABASE`, `CREATE USER` and `GRANT`.
- `tests/TestCase.php` fails any test that finishes with a tenant still initialised (leak detection).

Helpers (`tests/Pest.php`): `createAgency('acme')` (real registration + provisioning; owner verified with 2FA), `addMember($tenant, 'creator')`, `inTenant($tenant, $user, fn () => …)`, `tenantUrl($tenant, '/team')` (also makes `$tenant` the session's current workspace for the next request), `centralUrl('/signup')`.

| Suite | Covers |
|---|---|
| `Feature/Tenancy/ProvisioningTest` | Dedicated DB + account, secret never in master, cross-tenant/master access denied, failure injected at every step then resumed |
| `Feature/Tenancy/AccessTest` | Workspace from the session, auto-select / picker, enter + `next` safety, foreign members, forged tenant id, stale Livewire component after a switch (419), suspension, revocation |
| `Feature/Tenancy/ContextIsolationTest` | Jobs for A then B on one worker, exceptions, leaked context reset, cache/storage scoping |
| `Feature/Auth/*` | Login audit, rate limits, MFA enforcement, password confirmation, workspace choice and switching |
| `Feature/Team/*` | Invitations, seat limits, role-change rules, removal |
| `Feature/Authorization/MatrixTest` | Every role × permission against `config/rbac.php` |
| `Feature/Clients/ClientAccessTest` | Plan limits, hidden unassigned clients (404), per-client permissions, client roles narrow but never widen |
| `Feature/Social/OAuthConnectionTest` | Full flow (callback → workspace enter link → select), state replay/expiry/mismatch, PKCE, token encryption, result bound to user, duplicate/reconnect, account limit |
| `Feature/Social/TokenHealthTest` | Refresh success/failure → needs_reconnect + e-mail, revoked scope → capability downgrade, scheduling, disconnect/revoke |
| `Feature/Social/ProviderAdaptersTest` | Each real adapter against provider-shaped HTTP fakes: auth URL, exchange, discovery, refresh, error mapping, introspection |
| `Feature/Social/AvailabilityTest` / `MetaWebhookTest` | Never simulate when fakes are off, boot guard, integration gating, flags; signed deauth/data-deletion callbacks |
| `Feature/Publishing/DeliveryTest` | 10 targets / 2 failures / no duplicates on double replay, backoff + give-up, rate limits, auth → reconnect, timeout-after-send reconciliation, lost-job + killed-worker recovery, cancel, edit-after-approval, manual retry, suspended tenant, outbox atomicity, usage roll-up |
| `Feature/Publishing/ComposerTest` | Immutable versions, per-target validation and overrides, non-material edits, approval gate, workflow auto-approval, creator vs approver, cross-client confirmation, Livewire editor and post page |
| `Feature/Media/MediaLibraryTest` | Tenant-prefixed storage, EICAR quarantine, MIME sniffing, dedupe, quota, signed/expiring/tenant-bound URLs, ffprobe metadata |
| `Feature/Social/PublishAdaptersTest` | Each adapter's publish calls, unknown-outcome mapping, TikTok privacy rules, reconciliation lookups, LinkedIn escaping |
| `Feature/Collaboration/ApprovalWorkflowTest` | Internal → client stages, distinct approvals, per-client steps, changes requested, no self-approval, named approvers, delegation, reminder/escalation once, crafted Livewire calls, supersede on edit, client-side sign-off |
| `Feature/Collaboration/PortalTest` | Portal/agency separation, own-client posts only, no drafts, client-visible comments only, portal approvals, shared assets, feedback routing |
| `Feature/Collaboration/CalendarTest` | Drag across DST recomputes UTC + dispatch, blackouts, planned drafts, visibility, queue slots, pause/resume, series across DST, Livewire calendar |
| `Feature/Collaboration/NotificationsAndTasksTest` | Tenant-DB notifications + preferences, members only, read state, Slack events, campaign paise + client scoping, task assignment rules |
| `Unit/*` | Redaction, SQL identifier safety, privilege inspection, RBAC config, TokenSet/PKCE, DST schedule times, content hash, architecture rules |
