Testing Quick Start for New Developers
Run all tests before pushing code. No exceptions.
TL;DR The Commands You Must Run Before Pushing
bash
# Backend (client_backend)
cd backend/client_backend
npm test # Vitest all unit, property, and integration tests
npm run build # TypeScript compile check
# Frontend (app)
cd ../../app
npm test # Vitest all component, hook, and utility tests
npm run build # TypeScript + Vite production buildBoth must pass with zero failures. If any test or build fails, do not push. Fix the issue first.
What Gets Tested
Backend (backend/client_backend/)
| Category | Count | What It Validates |
|---|---|---|
| Property-based tests (fast-check) | 27 | Mathematical correctness of billing, rates, cascading, service pricing |
| Integration tests | 2 suites | Full API endpoint CRUD + end-to-end billing flow |
| Unit tests | ~14 suites | Controllers, middleware, utilities, services |
Total runtime: ~60-90 seconds (property tests spin up in-memory MongoDB replicas).
Frontend (app/)
| Category | Count | What It Validates |
|---|---|---|
| Property-based tests (fast-check) | 1 suite | Client-side floor validation (service pricing) |
| Hook tests | 3 suites | useOnboardingState, useFormPersistence, useExportCsv |
| Component tests | 3 suites | CallFlowComponents, PresetSelector, OnCallConnectedPanel |
| Page tests | 2 suites | AgentEditorPage (enhance), AddCreditsPage |
Total runtime: ~5-10 seconds (uses happy-dom, no real browser).
Before You Commit Full Checklist
bash
# Step 1: Backend tests
cd backend/client_backend
npm test
# Step 2: Backend TypeScript compilation
npm run build
# Step 3: Frontend tests
cd ../../app
npm test
# Step 4: Frontend build (catches TypeScript + import errors)
npm run buildQuick one-liner (Windows CMD):
cmd
cd /d E:\Oktivo\Oktivo\oktivo_voice\backend\client_backend & npm test & npm run build & cd /d E:\Oktivo\Oktivo\oktivo_voice\app & npm test & npm run buildIf any step fails, stop and fix before pushing.
Test File Locations
backend/client_backend/src/
├── __tests__/ ← Main test directory (property + integration)
│ ├── billing-split-correctness.test.ts
│ ├── cumulative-profit-tracking.test.ts
│ ├── end-to-end-billing-flow.integration.test.ts
│ ├── global-cascade-isolation.test.ts
│ ├── orgConfigReplica-api.integration.test.ts
│ ├── orgConfigReplica-effective-superadmin-rate.test.ts
│ ├── orgConfigReplica-schema-constraint.test.ts
│ ├── rate-snapshot-effective-superadmin-rate.test.ts
│ ├── rate-snapshot-immutability.test.ts
│ ├── superadmin-rate-update-persistence.test.ts
│ ├── transaction-metadata-completeness.test.ts
│ ├── service-pricing-data-model.test.ts ← NEW: Properties 6, 8
│ ├── service-pricing-floor-constraint.test.ts ← NEW: Property 1
│ ├── service-pricing-cascade-floor.test.ts ← NEW: Property 3
│ ├── service-pricing-visibility-cascade.test.ts ← NEW: Property 4
│ ├── service-pricing-visibility-filtering.test.ts← NEW: Property 5
│ ├── service-pricing-org-boundary.test.ts ← NEW: Property 7
│ ├── rate-resolution-hierarchy.test.ts ← NEW: Property 2
│ ├── invisible-preset-rate-resolution.test.ts ← NEW: Property 9
│ ├── (+ bugfix-related tests)
│ └── ...
├── controllers/__tests__/ ← Controller tests
│ ├── servicePricing.controller.test.ts ← NEW: 20 unit tests for CRUD + validation
│ └── ...
├── middleware/__tests__/ ← Middleware tests
├── utils/__tests__/ ← Utility tests
├── services/*.test.ts ← Colocated service tests
└── tests/ ← EXCLUDED (legacy manual/E2E see TESTING-GUIDE.md)Vitest config (vitest.config.ts):
- Includes:
src/**/*.test.ts - Excludes:
src/tests/**(legacy scripts that need a running server) - Timeout: 60 seconds per test
Understanding the Property-Based Tests (Credit System + Service Pricing)
These tests use fast-check to generate random inputs and verify mathematical invariants hold for ALL possible values, not just handpicked examples.
Credit System Properties
| Property # | File | What It Proves |
|---|---|---|
| 1 | services/orgConfigReplica.service.rateResolution.test.ts | Rate resolution picks the correct rate based on overrides |
| 2 | __tests__/superadmin-rate-update-persistence.test.ts | Config updates persist correctly with version increment |
| 3 | __tests__/orgConfigReplica-schema-constraint.test.ts | Replica schema only contains expected fields |
| 4 | __tests__/global-cascade-isolation.test.ts | Floor enforcement + per-org isolation during cascade |
| 8 | __tests__/billing-split-correctness.test.ts | customerCharge = ceil(units * org_rate), orgCharge = ceil(units * superadmin_rate) |
| 9 | __tests__/rate-snapshot-immutability.test.ts | Mid-call rate changes don't affect billing |
| 11 | __tests__/transaction-metadata-completeness.test.ts | All required metadata fields present in transactions |
| 12 | __tests__/cumulative-profit-tracking.test.ts | totalProfitRetained = Σ(customerCharge - orgCharge) |
| 13 | __tests__/orgConfigReplica-effective-superadmin-rate.test.ts | Effective rate = per-org when set, else global |
| 14 | services/orgConfigReplica.service.superadminOrgRatePersistence.test.ts | Set/read round-trip for per-org rates |
| 17 | __tests__/rate-snapshot-effective-superadmin-rate.test.ts | Snapshot captures correct source (per_org vs global) |
Service Pricing Properties
| Property # | File | What It Proves |
|---|---|---|
| SP-1 | __tests__/service-pricing-floor-constraint.test.ts | Upsert succeeds ⟺ customer_rate >= org_rate |
| SP-2 | __tests__/rate-resolution-hierarchy.test.ts | Resolved rate = customer_override → org_override → default |
| SP-3 | __tests__/service-pricing-cascade-floor.test.ts | After cascade, ALL customer rates >= new org_rate |
| SP-4 | __tests__/service-pricing-visibility-cascade.test.ts | Disabled preset → zero overrides remain |
| SP-5 | __tests__/service-pricing-visibility-filtering.test.ts | Only visible presets returned; invisible presets reject upserts |
| SP-6 | __tests__/service-pricing-data-model.test.ts | Repeated upserts → exactly one document (idempotent) |
| SP-7 | __tests__/service-pricing-org-boundary.test.ts | Cross-org operations → 403 Forbidden |
| SP-8 | __tests__/service-pricing-data-model.test.ts | Schema rejects customer_rate <= 0 |
| SP-9 | __tests__/invisible-preset-rate-resolution.test.ts | Invisible presets never resolve to customer_override |
| SP-10 | app/pages/admin/__tests__/ServicePricingPage.floorValidation.property.test.ts | Client-side: submit disabled ⟺ input < org_rate |
Each test runs 30-100 random iterations. If any single iteration fails, fast-check reports the minimal counterexample.
If a Test Fails
- Read the error message Vitest shows exactly which assertion failed and which file/line.
- For property tests fast-check shows the "counterexample" (the random input that broke the invariant). This is your debugging starting point.
- For integration tests check if your code change affected an API response shape or database schema.
- Common causes:
- Changed a field name in a model without updating tests
- Modified a service method signature
- Broke rate validation logic (org_rate < superadmin_rate)
- Changed transaction metadata structure
Adding New Tests
Follow existing patterns:
typescript
// Property test template (fast-check)
import { describe, it, expect } from "vitest";
import fc from "fast-check";
describe("Property N: My Property", () => {
it("property: description of invariant", async () => {
await fc.assert(
fc.asyncProperty(myGenerator, async (input) => {
// Arrange + Act
const result = await myService.doThing(input);
// Assert the invariant
expect(result).toBe(expectedValue);
}),
{ numRuns: 50 }
);
});
});typescript
// Integration test template (supertest)
import request from "supertest";
import express from "express";
describe("My API endpoint", () => {
it("should return 200 with correct data", async () => {
const res = await request(app).get("/api/my-endpoint");
expect(res.status).toBe(200);
expect(res.body.success).toBe(true);
});
});Related Docs
- TESTING-GUIDE.md Step-by-step guide for E2E and load tests (real calls)
- integration-tests.md Legacy webhook pipeline tests
- load-test-pipeline.md Synthetic load testing
- e2e-tests.md Real phone call tests
- property-tests.md Deep dive into property-based testing approach