Skip to content

Local Development ​

Prerequisites ​

Step 1 — Clone and Install ​

bash
git clone https://github.com/haflidif/chior-tickets.git
cd chior-tickets

# Install API dependencies
cd api
npm install

# Install frontend dependencies
cd ../frontend
npm install

Step 2 — Start Cosmos DB Emulator ​

Start the Azure Cosmos DB Emulator on your machine. The default connection settings are already configured in api/local.settings.json.

Step 3 — Run Locally ​

bash
# Terminal 1: Start API
cd api
npm start

# Terminal 2: Start Frontend
cd frontend
npm run dev

Open http://localhost:5173 in your browser.

Step 4 — Setup Database and Create Initial Super Admin ​

Run the setup scripts to create database containers and seed the first super admin:

bash
cd api

# Create all containers with proper indexes and TTL
npx ts-node scripts/setup-database.ts

# Create super admin user
npx ts-node scripts/seed-admin.ts your@email.com "Your Name"

This creates:

  • All required containers (users, organizations, organizationMemberships, events, registrations, etc.)
  • A super admin user who can create organizations and manage the entire system

Step 5 — First-Time Setup Checklist ​

After installation and database setup, complete these steps:

  1. Login as Super Admin:

    • Navigate to http://localhost:5173/login
    • Enter the email you used in seed-admin.ts
    • Check console for magic link (if RESEND_API_KEY not set)
  2. Create Your First Organization:

    • Navigate to Organizations → Create Organization
    • Enter organization name and details
    • You'll be automatically added as admin
  3. Add Members:

    • Option A: Create invitation code → Share link with members
    • Option B: Import members via CSV (email, name, role columns)
    • Option C: Add members individually in Members view
  4. Create Your First Event:

    • Navigate to Events → Create Event
    • Set event name, date, location, venue capacity
    • Add showtimes (multiple dates/times)
    • Add ticket types with prices
  5. Configure Settings (Optional):

    • Lock sales if needed
    • Set up allocations for members
    • Configure external sales tracking

Testing ​

API Tests ​

bash
cd api
npm test

# Run specific test pattern
npm test -- orders

Isolated Cosmos emulator CI ​

The squad-ci workflow runs the index-policy and printed-ticket suites in separate Linux Docker emulators. The sales-correction foundation suite uses the preinstalled native emulator on the standard windows-2022 runner. Do not combine the suites in one emulator run. Database deletion does not reset the emulator's process or partition state.

The printed and correction jobs each provision six emulator partitions. This covers the printed suite's five containers or the correction suite's four containers, plus the readiness probe allocation. The index-policy job retains its separate twelve-partition budget. Windows starts with -PartitionCount 6 and -DefaultPartitionCount 1; the per-container setting is necessary to keep the four fixture containers and readiness allocation within that total. Partition count does not limit request concurrency: all correction races still launch 100 contenders and require one winner and 99 conflicts. Receipt tests require one create, one HTTP 409, and a matching persisted receipt.

The foundation suite also calls the production correction operation against the same real ledger container for pool and quota counters: committed/reserved 9/2 becomes 11/2, stays 11/2 on replay, then becomes 9/2 after correcting the second sale. It checks immutable receipts and original evidence, and injects real counter/fence writes before the SDK batch to verify CAS rollback.

Each job waits for HTTPS and then uses the SDK to create a database and container, write and point-read a document. Most jobs then delete the probe database. The partner-quota job instead passes --retain-database and leaves that allocation for final disposal of its job-owned Docker container. Its six-partition budget covers five suite containers (ticketOrders, registrations, events, freeTickets, externalSales) and the retained probe. Do not use probe retention with a shared or persistent emulator.

The partner-quota image is pinned by digest to the image observed in PR #539. This makes diagnosis reproducible; it is not an emulator version upgrade. The suite runs once per fresh emulator, without the previous whole-command retry. Its afterAll deletes the suite database, so retrying in that same emulator would reintroduce partition teardown before the next attempt. Setup-only bounded retries and every business assertion remain unchanged. An ambiguous batch response, conflict, assertion failure, or test timeout still fails the job. A new job attempt must provision a fresh emulator.

Database and container provisioning share a setup-only conflict reconciliation helper. A create conflict is not success: the helper reads the exact same named resource and verifies its ID, container partition key, requested index policy and throughput. Only Cosmos's automatically added system _etag exclusion is normalized when comparing an explicitly empty exclusion list. Other configuration mismatches fail.

After a provisioning 409, recovery permits at most three read-back attempts with 1s/2s delays for recognized setup transport errors or the observed 404/substatus 1013 visibility response. It does not issue another create during recovery. Ordinary 404s, authorization errors, conflicts on the read itself and configuration mismatches stop immediately. Logs identify the resource, activity ID, status, substatus and read attempt; failures retain the original conflict and subsequent read errors. This does not apply to business writes or batches. Readiness reports actual attempts completed, separately from its attempt limit. CLI safety checks run before loading the Cosmos SDK, so workflow policy checks can exercise them before API dependencies are installed.

Passing readiness is not proof that the engine remains healthy under contention. The Linux jobs emit bounded native logs, container state and image ID, a resource snapshot, and available cgroup peak-memory, OOM-event, and CPU counters. Windows reports its installed emulator version, process resource usage, job-owned data file metadata, and bounded relevant Application event logs.

The Windows lifecycle uses the preinstalled Microsoft.Azure.CosmosDB.Emulator PowerShell module with Windows PowerShell 5.1. Other steps use pwsh, Node.js 22, and the pinned Cosmos SDK 4.10.0. Startup fails if the binary/module is missing, an emulator is already running, or the intended data directory already exists. There is no download fallback. Start-CosmosDbEmulator uses -NoUI -NoWait -NoFirewall -NoTelemetry, without network-access enablement. Only https://localhost:8081 is used; inherited connection strings are cleared for this job. Native Data Explorer HTTPS readiness uses /_explorer/index.html, Windows certificate trust, a 240-second budget, five-second requests, and no redirects. It does not require the Linux container's /_explorer/emulator.pem download endpoint.

The job selects exactly one currently valid, self-signed certificate named DocumentDbEmulatorCertificate from Cert:\LocalMachine\My, with localhost in its DNS names. Missing or ambiguous candidates fail explicitly. Only its public DER bytes are encoded as PEM for NODE_EXTRA_CA_CERTS; private keys and PFX files are never exported. A bounded Node.js HTTPS request verifies normal TLS trust and that the served certificate matches the selected certificate before the same SDK document-write/read/cleanup probe used on Linux runs. Neither check disables TLS validation. See Microsoft's native certificate export instructions and platform-specific certificate guidance.

Data lives in a unique cosmos-correction-<run>-<attempt> directory under the runner's temporary directory. Cleanup runs even after failure, stops only the job-owned emulator with Stop-CosmosDbEmulator -NoWait, and verifies shutdown within 120 seconds before deleting that exact directory and the job-exported public certificate. A failed shutdown fails cleanup and retains data; it never resets the emulator's default data directory or kills processes by name. See the official native emulator parameter reference.

For PR #504, run 35217398718, job 105189084469, the printed suite passed 31 tests before the native engine crashed during the correction capacity race at 2026-09-17T11:50:23Z. The log reports Last Chance, reason 0x00000006, followed by socket hang up and connection refusals. The zero-winner receipt assertion is not valid evidence of a transaction defect while the engine is unavailable. The log alone does not establish OOM or regional Azure capacity.

Run 35226143322, correction job 105218196109, subsequently passed 11 of 15 tests, including the real multi-sale/receipt/CAS cases and capacity/activation 100-way races, but the Linux engine crashed again at the source-lease boundary. Docker reported OOMKilled=false; peak-memory counters were unavailable after exit. Printed passed 31 of 31 and failed only its correction dependency guard. The approved native Windows migration removes the Linux compatibility layer, not any test requirement. Linux vNext is not a substitute because it lacks stored procedures required by this suite.

Run 35230955519 passed all 15 correction tests on Windows, including the source-lease race and real CAS rollback cases. Its separate Linux printed job 105235860676 passed 30 of 31 tests but timed out on the first impersonationAudit.items.create fixture write, before the audit assertions. Subsequent duplicate-rejection and ETag tests passed. The container remained running, with OOMKilled=false, zero cgroup OOM events, and no native crash in the collected logs. This is distinct from the earlier correction crashes.

Only that initial audit fixture create uses withEmulatorRetry with three attempts and 1s/2s backoff. Its ID, partition key, timestamp, and payload stay fixed across attempts. Assertions, duplicate creates, CAS operations, and production capacity calls are not wrapped. The 10-second SDK request timeout and existing test timeout remain unchanged. Empty gateway statistics do not prove the request was never committed: the SDK records the gateway response only after receiving it. A 409 therefore still fails, including after a lost create response; there is no upsert or conflict-to-success recovery.

Require all 15 foundation tests and all three emulator jobs to pass before merging. The existing printed-ticket check also requires successful completion of the isolated correction job, preserving its previous coverage without changing branch-protection settings. If the isolated engine crashes, retain the resource diagnostics and native failure signature instead of weakening assertions or blindly rerunning.

For local reproduction, use only an isolated local emulator with the public emulator key and a unique test database. Never load production connection settings or use the PR preview database, which is shared with production. Keep the full test-file commands from the corresponding CI job.

E2E Tests ​

bash
cd frontend
npx playwright test

# Run specific test file
npx playwright test login.spec.ts

Development Notes ​

Grace Period for Registrations ​

Registrations have a 1-hour grace period for edits/deletions:

  • Within 1 hour: Admin can edit or delete registration freely
  • After 1 hour: Registration is locked to prevent accidental data loss
  • This protects historical data while allowing quick corrections

Database Cleanup (Development Only) ​

DANGER

A database cleanup endpoint exists for development purposes only.

  • Endpoint: DELETE /api/db-cleanup
  • Requires: Super Admin authentication
  • Action: Deletes ALL data across ALL choirs
  • NEVER run in production — no undo available

Production Safety Recommendations:

  • Disable this endpoint in production via environment variables
  • Use Azure Functions' deployment slots to exclude it from production code
  • Implement additional safeguards (e.g., require specific development-only header)
  • Monitor audit logs for any attempts to access this endpoint in production

To safely reset development data:

bash
cd api
npx ts-node scripts/setup-database.ts
npx ts-node scripts/seed-admin.ts your@email.com "Your Name"

Impersonation Best Practices ​

When using impersonation:

  • Always stop impersonation when done (ends audit session)
  • Review audit logs regularly
  • Impersonation is for debugging user issues, not data manipulation
  • All impersonation sessions are permanently logged

Next: Troubleshooting · See also: Environment Variables · Azure Deployment

Built with VitePress