1. phases
Itinera API
  • ✈ Itinera API Documentation
  • Docs
    • legacy
      • Itinera — System Overview
      • Technology Stack & Architecture
      • Getting Started Guide
      • Infrastructure
      • Frontend Application
      • Architecture Overview
      • API Reference
      • Backend Services
      • Development Guidelines
    • phases
      • 01. Architecture Overview & Checkout Idempotency
      • Phase 2: Commerce & Checkout Engine
      • Phase 3: Webhooks & Asynchronous Fulfillment
      • Phase 4: Security Perimeter & Authentication
      • Phase 5: Database Schema & Entity Relationships
      • Phase 6: AI Quota & Telemetry Subsystems
      • Phase 7: API Route Matrix & FormRequests
      • Phase 8: Global Exception & Error Handling
      • Phase 9: Frontend Ecosystem & State Management
      • Phase 10: Design System & Component Library
      • Phase 11: Interactive GSAP Animations
      • Phase 12: Deployment & CI/CD Pipeline
      • Phase 13: Testing Strategies
      • Phase 14: Performance & Optimization
      • Phase 15: Developer Onboarding & Runbooks
      • 15-Phase Comprehensive Wiki & Documentation Plan
  • APIs
    • Auth
      • Register a new user
      • Log in a user
      • Log out user
      • Refresh JWT token
      • Forgot password request
      • Reset password verification
      • Get current user profile
      • Update user profile
      • Redirect to Google OAuth
      • Google OAuth Callback
      • Verify email via signed URL
    • Catalog
      • List all countries
      • Get country details
      • List all cities
      • List all regions
      • List all destinations
      • Get destination details
      • Get hotels by destination
      • List all hotels
      • Get hotel details
      • Get reviews for a hotel
      • List all flights
      • Get flight details
      • List all restaurants
      • Get restaurant details
      • List all attractions
      • Get current weather
      • Submit review for an entity
      • Delete review
      • Toggle favourite status for entity
      • List my submitted reviews
    • Bookings
      • Book a tour destination
    • V1 Aliases
      • V1 List all countries
      • V1 Get country details
      • V1 List all cities
      • V1 List all destinations
      • V1 Get destination details
      • V1 Get hotels by destination
      • V1 List all hotels
      • V1 Get hotel details
      • V1 Get reviews for a hotel
      • V1 List all flights
      • V1 Get flight details
      • V1 List all restaurants
      • V1 Get restaurant details
      • V1 List all attractions
      • V1 Get attraction details
      • V1 List all regions
      • V1 Get weather details
    • Trips
      • List user trips
      • Create a new trip
      • Get trip details
      • Update trip details
      • Delete a trip
      • Get creation metadata
      • Attach items to a trip
      • Update trip item
      • Detach items from a trip
      • Fork a trip
    • Conversations
      • List user conversations
      • Start a new conversation
      • Get conversation details
      • List messages in conversation
      • Send message to conversation
      • Mark conversation as read
    • Commerce Plans
      • List public plans
      • Get public plan details
    • Commerce Subscriptions
      • Subscribe to a plan
      • Upgrade active plan
      • Get active subscription info
      • Cancel active subscription
    • Commerce Checkout
      • Initiate Paymob payment checkout
    • Integrations
      • Paymob status webhook callback
      • Paymob redirect return callback
    • System Settings & Support
      • Submit public contact message
      • Subscribe to system newsletter
      • Get list of my reports
      • List all notifications
      • Mark single notification as read
      • List available surveys
      • Submit answers for survey
      • Get survey details
      • Update survey details
      • Delete survey response
    • AI Tools
      • Enhance itinerary details using AI
      • Request AI review of itinerary
      • Plan route using AI assistance
      • Get AI quota remaining details
      • Chat with AI Concierge assistant
      • Get AI Review progress by ID
    • Agency Integration
      • Request agency assignment
      • List agency active tasks
      • List agency managed trips
      • Get agency total earnings
      • Get agency profile details
      • Update agency profile details
    • Admin User Management
      • List users inside admin dashboard
      • Get user profile
      • Set user active status
      • Block user profile
    • Admin Catalog Moderation
      • Create new catalog category
      • Create new catalog destination
      • Create new hotel catalog record
      • Create new flight catalog record
      • Create new restaurant catalog record
      • Create new attraction catalog record
  • Schemas
    • User
    • ErrorResponse
    • Trip
    • Destination
    • Hotel
    • Flight
    • Restaurant
    • Attraction
    • Booking
    • Review
    • Agency
    • Survey
  1. phases

Phase 13: Testing Strategies

This document maps how the monorepo guarantees zero-drift releases: backend RefreshDatabase + sqlite :memory: feature suites, frontend vitest + happy-dom scrub mocks, and CI gates that run pint before artisan test.

1. Backend: phpunit.xml Harness#

fullstack/Backend/phpunit.xml:5-45 defines two suites (Unit tests/Unit, Feature tests/Feature) and forces a hermetic env:
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="CACHE_STORE" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="MAIL_MAILER" value="array"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="GROQ_API_KEY" value="gsk-test-mock-key"/>
sqlite :memory: + RefreshDatabase trait (tests/Feature/Commerce/PaymentFlowTest.php:11, AuthThrottleTest.php:7, etc.) migrates fresh per test class — no MySQL container, sub-second boot.
CACHE_STORE array makes Cache::lock and Cache::remember (AI quota, webhook concurrency) synchronous.
QUEUE_CONNECTION sync forces ShouldQueue listeners (FulfillOrderListener, queue:work in prod) to run inline — PaymentSucceeded fulfillment is testable without a worker.
GROQ_API_KEY gsk-test-mock-key satisfies GroqService without external calls; AiQuotaCacheHitTest, AiRateLimitTest mock the provider.
File census: 54 test files (tests/Feature/** 52 + tests/Unit 2). By domain: Account (auth throttle, blocked user, verification gate), Catalog (destinations/hotels etc.), Commerce (CheckoutAbuse, PaymentFlow, Paymob cycle/timeout, Concurrency, Plans, Subscription), Trips (AI quota/rate, trip attach/fork), System (contacts, survey, reports, weather). grep shows every feature test uses RefreshDatabase.

2. Backend: What Is Tested#

Throttle matrix (Phase 4): AuthThrottleTest hits RateLimiter::for('login') per IP+email, WeatherAbuseTest, CheckoutAbuseTest (SEC-08) assert 429 after 5/min.
Commerce idempotency (Phase 2): PaymobTimeoutTest, ConcurrencyTest assert findReusableCheckout + idempotency_key unique: double POST with same key returns one order.
Webhooks (Phase 3): PaymobPaymentCycleTest, PaymentFlowTest assert verifyWebhook HMAC, Cache::lock, grace-period, and FulfillOrderListener idempotency provider_ref exists → abort.
AI quota (Phase 6): AiQuotaCacheHitTest asserts Cache::remember closure not called on hit (no quota burn), AiQuotaIntegrationTest asserts atomic where ai_generations_count < limit update.
Validation (Phase 7/8): SurveyValidationTest, PlansTest assert 422 validation_errors flat array {field,message} (Phase 8 shape).

3. Frontend: itinera-showcase-react Vitest#

vite.config.ts:7-11 — test.environment happy-dom, globals true. No MSW, no network: showcase is static (Phase 9).
Suites: 2 files, 18 tests (all green in local npm.cmd test):
src/pages/LifecyclePage.test.tsx — scrub-engine behavior (not DOM snapshot).
src/components/lifecycle/ChapterScene.test.tsx — SVG scene prop rendering.

LifecyclePage scrub mocks (LifecyclePage.test.tsx:14-120)#

Real ScrollTrigger needs a scroll container; tests replace ../lib/gsap with a synthetic engine:
vi.mock("../lib/gsap", () => {
  const makeTl = (vars) => {
    const tl = { from: vi.fn(()=>tl), to: vi.fn(()=>tl), fromTo: vi.fn(()=>tl) }
    const stv = vars?.scrollTrigger
    if (stv) {
      const start = 4000 + tlVars.length*7
      tl.scrollTrigger = { ...stv, start, end:start+480, kill: vi.fn() }
      if (stv.pin===true) fakePinStarts.push(start) // pin start synthetic
    }
    return tl
  }
  return { gsap:{ timeline:vi.fn(...), to:vi.fn((t,vars)=>{ if("scrollTop" in vars) (t as HTMLElement).scrollTop = vars.scrollTop }), set:vi.fn(), context:vi.fn(cb=>{cb();return{revert:vi.fn()}})}, ScrollTrigger:{ defaults:vi.fn(), create:vi.fn((v)=>{stConfigs.push(v); return{kill:vi.fn(),start:4000}}), refresh:vi.fn()} }
})
Assertions:
12 sections (hero + 10 stages + outro) built, 10 timelines with pin:true, scrub:0.45.
refreshStarts priority: fakePinStarts sort b.animation!=null wins over plain top center.
goTo uses startOffset.get(id) vs fallback rect delta.
Edge wrap: wrapAnimate is gsap.to(scroller,{scrollTop}) synchronous in mock.
Reduced-motion: ?motion=reduced → zero pins, top center triggers only.
Quality gate: npm run build (tsc -b && vite build) + oxlint + vitest run must pass. tasks/todo.md marks all R-tasks green before merge.

4. CI Gates — .github/workflows/ci.yml:9-47#

Two jobs in fullstack/Backend working dir, setup-php@2 php 8.2:
lint: pint --test (fails on style drift).
test: cp .env.example .env && artisan key:generate && artisan jwt:secret --force && artisan test — boots sqlite memory, runs all 54 suites with array cache/queue, GROQ_API_KEY mock.
Gate is push: [main, develop, feat/community-hub] + pull_request: [main, develop] — no direct-to-main merge without both jobs green.

5. Test Execution Flow#

6. Principles#

1.
Memory is faster than MySQL. sqlite :memory: + RefreshDatabase gives per-test isolation without containers; BCRYPT_ROUNDS 4 keeps factory users cheap.
2.
Mock the edge, not the core. GROQ_API_KEY and Cache STORE array are mocked; CheckoutService, WebhookService, AiUsageService atomic UPDATE WHERE are exercised real.
3.
Frontend mocks the scroll, not the timeline. gsap.context + ScrollTrigger.create are replaced with start synthetics so refreshStarts priority and goTo math are testable without a layout engine.
4.
Lint is a test. pint --test fails the pipeline — style drift blocks merge same as a failing assertion.
5.
One artisan test is the contract. 106 route:list operations (Phase 7) are reachable only through RefreshDatabase suites; if a FormRequest rule changes, the 422 flat bag assertion fails.
Modified at 2026-08-25 22:41:45
Previous
Phase 12: Deployment &amp; CI/CD Pipeline
Next
Phase 14: Performance &amp; Optimization
Built with