# FieldOps Cloud Commercial Test Plan

Date: 2026-06-14

## Purpose

This plan defines the commercial-readiness test coverage for FieldOps Cloud. It follows the attached `FieldOps Testing.docx` programme and the local codebase inspection performed against the XAMPP application at `http://localhost/FieldOps-Cloud/public`.

## Scope

In scope:

- Repository setup and Composer health.
- PHP syntax and existing automated service checks.
- MySQL schema and seed import.
- XAMPP route, base-path, CSRF, redirect, and form-flow behavior.
- Jobs, schedule, quotes, invoices, clients, import/export, SaaS pricing, platform admin, integrations, and LLM settings foundations.
- Security, data safety, browser smoke, accessibility/responsive smoke, and commercial release gating.

Out of scope for live execution in this pass:

- Full production-mode business workflows beyond the current auth smoke coverage.
- Live accounting OAuth/sync.
- Live Google/Outlook OAuth, webhooks, and event mutation.
- Live LLM provider calls.
- Live billing capture, Checkout/Customer Portal flows, tax calculation, and subscription-state mutation from provider events. Gate 5 signed webhook and idempotency foundations are covered.
- Actual hosted deployment with owner-provided infrastructure values. Gate 7 deployment-readiness contract tests are covered locally.
- Full destructive or high-volume performance testing.

## Test Strategy

Priority order:

1. Build and install viability.
2. Data and financial correctness.
3. Route, CSRF, RBAC, and XAMPP base-path behavior.
4. Core workflows: jobs, clients, schedule, quotes, invoices, imports, exports, and pricing.
5. Security posture: secrets, session hardening, upload/export safety, and tenant-risk areas.
6. Browser and responsive smoke.
7. Commercial release gate and unresolved risk assessment.

## Test Matrix

| Test ID | Area | Requirement | Execution status | Result | Evidence |
| --- | --- | --- | --- | --- | --- |
| BUILD-001 | Build | `composer.json` validates | Run | Pass | `composer validate --no-check-publish` |
| BUILD-002 | Build | Dependencies install from clone | Run | Pass after dependency fix | `composer install --no-interaction --no-progress`; lock file generated |
| BUILD-003 | Build | Composer test script runs | Run | Pass | `composer test` |
| BUILD-004 | Build | Composer check runs | Run | Pass | `composer check` |
| BUILD-005 | Build | PHP entrypoint lints | Run | Pass | `php -l public/index.php` |
| BUILD-006 | Build | PHP source/templates/config lint | Run | Pass | `composer check` lint output |
| BUILD-007 | Build | Composer audit can run | Run | Pass | `composer audit` reported no known vulnerability advisories |
| BUILD-008 | Config | `.env.example` exists | Run | Pass | File inspection |
| BUILD-009 | Config | `.env` and `vendor/` are ignored | Run | Pass | `.gitignore` inspection |
| BUILD-010 | Config | No obvious real secrets committed | Run | Pass | Secret scan only found fake test LLM keys |
| DB-001 | Database | `database/schema.sql` exists | Run | Pass | File inspection |
| DB-002 | Database | `database/seed.sql` exists | Run | Pass | File inspection |
| DB-003 | Database | Schema imports into clean MySQL DB | Run | Pass | Test DB import exit 0 |
| DB-004 | Database | Seed imports into clean MySQL DB | Run | Pass | Seed import exit 0 |
| DB-005 | Database | Required tables exist | Run | Pass | 40 tables found after import |
| DB-006 | Database | Seed tenants/users exist | Run | Pass | 2 tenants, 2 users found |
| DB-007 | Database/Security | MySQL tenant isolation across tenant-owned repositories | Run | Pass | `tools/mysql-isolation-smoke.php`: 16 checks against disposable MySQL DB |
| ROUTE-001 | Routing | Every documented GET route loads | Run | Pass after fix | Route smoke pass count 30/30 |
| ROUTE-002 | Routing | Unknown route returns 404 | Run | Pass | `/not-a-real-route` returned 404 |
| ROUTE-003 | Routing | Unknown job ID returns 404 | Run | Pass | `/jobs/view?id=999` returned 404 |
| ROUTE-004 | Routing | No PHP warnings/notices on GET routes | Run | Pass after fix | `/export-data` fatal fixed |
| XAMPP-001 | XAMPP | Base-path links/actions work | Run | Pass after fix | Job/client redirects fixed |
| CSRF-001 | CSRF | POST `/clients` rejects missing CSRF | Run | Pass | 403 |
| CSRF-002 | CSRF | POST `/jobs` rejects missing CSRF | Run | Pass | 403 |
| CSRF-003 | CSRF | POST `/schedule/appointments` rejects missing CSRF | Run | Pass | 403 |
| CSRF-004 | CSRF | POST `/quotes` rejects missing CSRF | Run | Pass | 403 |
| CSRF-005 | CSRF | POST `/invoices` rejects missing CSRF | Run | Pass | 403 |
| CSRF-006 | CSRF | POST `/logout` rejects missing CSRF | Run | Pass | 403 |
| CSRF-007 | CSRF | Pricing POST routes reject missing CSRF | Run | Pass | 403 |
| CLIENT-001 | Clients | Client list loads | Run | Pass | Route smoke |
| CLIENT-002 | Clients | Invalid client validation works | Run | Pass | 422 |
| CLIENT-003 | Clients | Valid client form redirects under XAMPP base path | Run | Pass after fix | Final URL `/FieldOps-Cloud/public/clients` |
| CLIENT-004 | Clients | Created client persists to list | Run | Pass owner workflow / partial broad production | Production workflow smoke verifies DB-backed owner create/search/detail/edit persistence |
| JOB-001 | Jobs | Jobs list loads | Run | Pass | Route smoke |
| JOB-002 | Jobs | Job detail drill-in loads | Run | Pass | `/jobs/view?id=1` |
| JOB-003 | Jobs | Invalid job validation works | Run | Pass | 422 |
| JOB-004 | Jobs | Valid job form redirects under XAMPP base path | Run | Pass after fix | Final URL `/FieldOps-Cloud/public/jobs` |
| JOB-005 | Jobs | Created job persists to list | Run | Pass owner workflow / partial broad production | Production workflow smoke verifies DB-backed owner create/search/detail/edit persistence |
| SCHED-001 | Schedule | Schedule calendar page loads | Run | Pass | Route/browser smoke |
| SCHED-002 | Schedule | Valid appointment can be booked | Run | Pass | Appointment appears after POST |
| SCHED-003 | Schedule | Same-technician conflict is rejected | Run | Pass | 422 conflict validation |
| QUOTE-001 | Quotes | Quote list/create pages load | Run | Pass | Route smoke |
| QUOTE-002 | Quotes | Valid quote persists in session | Run | Pass | `Q-CODEX-001` appears in list |
| QUOTE-003 | Quotes | Quote totals validated by service tests | Run | Pass | `composer test` |
| INV-001 | Invoices | Invoice list/create pages load | Run | Pass | Route smoke |
| INV-002 | Invoices | Valid invoice persists in session | Run | Pass | `I-CODEX-001` appears in list |
| INV-003 | Invoices | Invoice totals validated by service tests | Run | Pass | `composer test` |
| IMPORT-001 | Import | Import page loads | Run | Pass | Route/browser smoke |
| IMPORT-002 | Import | Sample CSV downloads | Run | Pass | `/import-data/sample?type=customers` |
| IMPORT-003 | Import | CSV upload/import commit exists | Run | Pass Gate 4 | File/paste preview, validation, upload extension/MIME/size checks, malicious CSV rejection, commit, duplicate-commit blocking, rollback, undo, and browser multipart upload smoke pass |
| EXPORT-001 | Export | Export page loads | Run | Pass after fix | `/export-data` |
| EXPORT-002 | Export | CSV download works | Run | Pass | `/export-data/download?dataset=clients` |
| EXPORT-003 | Export | CSV formula escaping works | Run | Pass after fix | `=1+1` exported as `'=1+1` |
| PRICE-001 | Pricing | Invalid pricing settings rejected | Run | Pass | 422 |
| PRICE-002 | Pricing | Valid pricing settings save | Run | Pass | Redirect to `?saved=1` |
| PRICE-003 | Pricing | Public pricing reflects saved settings | Run | Pass | GBP/Codex offer visible |
| PRICE-004 | Pricing | Pricing settings reset | Run | Pass | Redirect to `?reset=1` |
| ADMIN-001 | Platform admin | Platform admin page loads | Run | Pass | Route/browser smoke |
| ACC-001 | Accounting | Provider registry covers requested providers | Run | Pass | `composer test` |
| ACC-002 | Accounting | Live OAuth/sync exists | Inspected | Not implemented | Contract-tested foundation only; live credentials disabled |
| ACC-003 | Accounting | Sandbox provider contracts cover requested providers and keep live sync disabled | Run | Pass | Gate 6 provider contract tests |
| CAL-001 | Calendar | Google/Outlook registry and conflict logic | Run | Pass | `composer test` |
| CAL-002 | Calendar | Live OAuth/webhook sync exists | Inspected | Not implemented | Contract-tested foundation only; live credentials disabled |
| CAL-003 | Calendar | Google/Outlook contract tests cover scopes and webhook verification rules | Run | Pass | Gate 6 provider contract tests |
| AI-001 | LLM | Provider validation and secret masking | Run | Pass | `composer test` |
| AI-002 | LLM | Live LLM API calls exist | Inspected | Not implemented | Local-only contract-tested foundation; live model calls disabled |
| AI-003 | LLM | Redacted import sample removes PII/secrets and limits rows | Run | Pass | Gate 6 redaction tests |
| API-001 | API | API response helper exists | Run | Pass | `JsonResponder` test |
| API-002 | API | Active API documentation matches runtime | Inspected | Pass for active docs / planned REST not live | `docs/API_REFERENCE.md` now marks public REST routes as planned and lists only active API-style endpoints |
| SEC-001 | Security | Security headers present | Run | Pass after fix | XFO, XCTO, Referrer-Policy, Permissions-Policy, CSP, and X-Powered-By removal verified |
| SEC-002 | Security | Production demo auto-login disabled | Run | Pass after fix | `tools/auth-production-smoke.cjs` |
| SEC-003 | Security | Session cookies hardened | Run | Pass after fix | HttpOnly/SameSite verified by `tools/auth-production-smoke.cjs`; Secure is policy-tested for HTTPS production |
| SEC-004 | Security | Secrets not committed | Run | Pass | Only fake test keys found |
| SEC-005 | Security | Exports formula-safe | Run | Pass after fix | CSV formula escape verified |
| SEC-006 | Security | Cross-tenant relationship writes are rejected | Run | Pass | MySQL isolation smoke rejects foreign client/job/user/crew/quote/invoice/billing point/inventory/portal token links |
| BILL-001 | Billing | Manual platform billing is the default state | Run | Pass | `BillingProviderStatusService` tests and platform-admin UI labels |
| BILL-002 | Billing | Stripe sandbox readiness is separate from live payment capture | Run | Pass | Provider readiness tests cover sandbox vs live keys and required price mapping |
| BILL-003 | Billing/Security | Billing webhook rejects invalid, stale, or malformed signatures | Run | Pass | `BillingWebhookService` tests |
| BILL-004 | Billing/Security | Billing webhook requires tenant metadata and enforces idempotency | Run | Pass | Duplicate replay, payload-hash conflict, and tenant metadata tests |
| BILL-005 | Billing | Production billing overrides and provider event records persist through database repository | Run | Pass | `DatabasePlatformBillingRepository` tests |
| DEPLOY-001 | Deployment | Unsafe production config is blocked | Run | Pass Gate 7 | Debug enabled, weak secret, missing DB DSN, demo enabled, non-HTTPS public URL, insecure cookies, and non-public document root all block |
| DEPLOY-002 | Deployment | Complete production release evidence can pass | Run | Pass Gate 7 | `DeploymentReadinessService` ready-state test covers HTTPS URL, secure cookies, proxy policy, log path/retention, backup/restore, monitoring, rollback, and CI gate |
| DEPLOY-003 | Deployment | Platform admin exposes deployment hardening | Run | Pass Gate 7 | `/platform-admin/health` shows Deployment hardening table |
| DEPLOY-004 | Deployment | Rollback plan exists | Run | Pass Gate 7 | `docs/ROLLBACK_RUNBOOK.md` |
| A11Y-001 | Browser | Key pages load on mobile/tablet/desktop | Run | Pass | `tools/browser-smoke.cjs` |
| A11Y-002 | Browser | No body horizontal overflow on key pages | Run | Pass | Browser smoke |
| A11Y-003 | Browser | No console/page errors on key pages | Run | Pass | Browser smoke |
| PERF-001 | Performance | Demo-sized pages respond locally | Run | Pass | Route/browser smoke |
| PERF-002 | Performance | Large dataset/load plan exists | Designed | Not run | See gap analysis |
| RELEASE-001 | Release | Commercial gate satisfied | Assessed | Fail | See release gate |

## Automation Added

- `tools/browser-smoke.cjs`: Playwright/Chrome smoke check across mobile, tablet, and desktop for high-risk pages.
- `tools/auth-production-smoke.cjs`: temporary production-mode auth smoke with demo disabled, security headers, session cookie policy, invalid login, signup, DB-backed role logins, and platform-admin isolation.
- `tools/mysql-isolation-smoke.php`: disposable MySQL tenant-isolation smoke using the production schema.

## Release Position

The current application is suitable for continued local demo testing after the fixes in this pass. It is not commercially deployable because live provider activation, live payment capture, owner-provided hosted deployment evidence, performance, accessibility, and mobile/app-store readiness remain incomplete.
