Skip to main content

Testing Guide

Drop Testing Guide

Last updated: 2026-03-03 Source: src/drop-app/vitest.config.ts, playwright.config.ts, package.json, tests/


Test Frameworks

Framework Version Type Config
Vitest ^4.0.18 Unit, Integration, Performance, Regression vitest.config.ts
Playwright ^1.58.2 E2E (browser) playwright.config.ts

Prerequisites

Docker is required. Tests run against a real PostgreSQL 16 instance (port 5433). There is no in-memory SQLite fallback.

# One-time setup (first time or after fresh clone)
make db-test-setup
# This: starts Docker, creates drop_test DB, enables pgcrypto, pushes Drizzle schema

The DATABASE_URL for tests is set automatically by tests/setup.ts:

postgresql://drop:dev_only_not_a_secret@localhost:5433/drop_test

Running Tests

Unit + Integration Tests (Vitest)

# Recommended: via Makefile (handles Docker + DATABASE_URL automatically)
make test

# Direct run (Docker must already be up)
cd src/drop-app && DATABASE_URL=postgresql://drop:dev_only_not_a_secret@localhost:5433/drop_test npx vitest run

# Watch mode (development)
cd src/drop-app && DATABASE_URL=postgresql://drop:dev_only_not_a_secret@localhost:5433/drop_test npx vitest

# Run specific test file
cd src/drop-app && DATABASE_URL=postgresql://drop:dev_only_not_a_secret@localhost:5433/drop_test npx vitest run tests/integration/api-endpoints.test.ts

# Run with coverage
cd src/drop-app && DATABASE_URL=postgresql://drop:dev_only_not_a_secret@localhost:5433/drop_test npx vitest run --coverage

Configuration (vitest.config.ts):

  • Environment: node
  • Test pattern: tests/**/*.test.ts
  • Setup file: tests/setup.ts (sets NODE_ENV=test, sets DATABASE_URL)
  • Path alias: @ -> ./src
  • fileParallelism: false — tests run sequentially to avoid PostgreSQL race conditions

E2E Tests (Playwright)

# Run all E2E tests
npx playwright test

# Run specific project
npx playwright test --project=user-flows
npx playwright test --project=full-flows
npx playwright test --project=input-chaos

# Run with UI mode
npx playwright test --ui

# View HTML report
npx playwright show-report

Configuration (playwright.config.ts:3-38):

  • Serial execution (1 worker) to avoid rate limit conflicts
  • Auto-starts dev server (npm run dev) if not running
  • HTML reporter
  • Trace on first retry (CI mode: 2 retries)

Test Architecture

Setup

File: tests/setup.ts

Sets NODE_ENV=test and DATABASE_URL=postgresql://drop:dev_only_not_a_secret@localhost:5433/drop_test for all Vitest tests.

Database Strategy (PostgreSQL — ADR-014)

All integration tests use a shared PostgreSQL test helper (tests/helpers/pg-test-db.ts):

  • Connects to the real drop_test database on port 5433
  • Runs Drizzle schema push once before each test suite
  • Truncates all tables between tests for isolation
  • No in-memory SQLite — full test/prod parity
// Integration test pattern (post-migration)
import { getTestDb, cleanupTestDb } from "../helpers/pg-test-db";
import { db } from "@/lib/db";

beforeAll(async () => { await getTestDb(); });
afterEach(async () => { await cleanupTestDb(); });

Unit tests that test pure logic (validation, auth utilities) mock @/lib/db directly without a real DB connection.

Note: better-sqlite3 has been removed. There is no new Database(":memory:") pattern anymore.

E2E Test Helpers

File: tests/e2e/input-chaos.spec.ts:21-42

loginAsDemo(page) -- Mocks the /api/auth/me endpoint to bypass rate limiting in chaos tests. Returns a pre-authenticated user object.

CHAOS_STRINGS -- Dictionary of malicious/edge-case inputs used across E2E tests:

  • Empty, spaces, XSS, SQL injection, HTML injection
  • Unicode, RTL override, null bytes
  • Very long strings (10K chars)
  • Norwegian (aeoa), Bosnian (sdccz), Japanese characters
  • Zalgo text, special characters

Writing Tests

Integration Test Pattern (PostgreSQL)

import { describe, it, expect, beforeAll, afterEach } from "vitest";
import { getTestDb, cleanupTestDb } from "../helpers/pg-test-db";

// DATABASE_URL is set by tests/setup.ts — no manual config needed

describe("Feature", () => {
  beforeAll(async () => {
    await getTestDb(); // Ensure schema is pushed to drop_test
  });

  afterEach(async () => {
    await cleanupTestDb(); // Truncate all tables between tests
  });

  it("does something with DB", async () => {
    // Arrange, Act, Assert against real PostgreSQL
  });
});

Unit Test Pattern (pure logic, no DB)

import { describe, it, expect, vi } from "vitest";

// Mock @/lib/db entirely for pure unit tests
vi.mock("@/lib/db", () => ({
  db: { select: vi.fn(), insert: vi.fn() },
}));

describe("Feature", () => {
  it("validates input", () => {
    // Arrange, Act, Assert — no DB needed
  });
});

E2E Test Pattern

import { test, expect } from "@playwright/test";

test.describe.configure({ mode: "serial" }); // Avoid rate limits

test.describe("Feature", () => {
  test("user flow", async ({ page }) => {
    await page.goto("http://localhost:3000/login");
    await page.locator('input[type="email"]').fill("[email protected]");
    await page.locator('input[type="password"]').fill("demo1234");
    await page.locator('button[type="submit"]').click();
    await page.waitForURL("**/dashboard", { timeout: 15000 });
  });
});

Test Results

Current status (2026-03-03): Migrated to PostgreSQL. All tests run against drop_test on port 5433.

Vitest

# Run: make test
# All test files use PostgreSQL — no SQLite, no better-sqlite3

Playwright

  • 3 test projects configured: user-flows, full-flows, input-chaos
  • input-chaos depends on user-flows

Test Coverage Areas

Area Unit Integration E2E Performance
Password hashing (bcrypt) auth.test.ts api-routes.test.ts -- api-benchmarks.test.ts
JWT sign/verify auth.test.ts -- -- --
Rate limiting middleware.test.ts api-routes.test.ts user-flows.spec.ts api-benchmarks.test.ts
Input validation validation.test.ts -- input-chaos.spec.ts --
Database schema db.test.ts api-endpoints.test.ts -- --
Feature flags feature-flags.test.ts -- -- --
Utility functions utils.test.ts -- -- --
Registration flow -- api-endpoints.test.ts user-flows.spec.ts --
Login flow -- api-endpoints.test.ts user-flows.spec.ts --
Remittance -- api-endpoints.test.ts full-flows.spec.ts --
QR Payment -- api-endpoints.test.ts full-flows.spec.ts --
XSS/SQLi prevention validation.test.ts -- input-chaos.spec.ts --
Session management -- api-routes.test.ts full-flows.spec.ts --
Known bug regressions -- known-bugs.test.ts -- --