Skip to main content

Testing Strategy

Citadel employs a multi-layered testing strategy to ensure reliability across its microservices architecture, frontend applications, and critical user flows.

1. Unit Testing

Unit tests focus on individual functions, classes, and modules in isolation.

Backend Services (Go)

Each Go service includes a helper script scripts/test.sh that handles unit tests, integration tests, and coverage reporting.

  • Command: ./scripts/test.sh (within a service directory)
  • What it does: Runs go test with race detection, coverage profiles, and appropriate flags.

To run tests for a specific package from the root using Turbo:

# Test a specific package (e.g., iam-service)
pnpm -F iam-service test

# Lint a specific package
pnpm -F iam-service lint

Frontend Applications (React)

  • Framework: Jest / Vitest + React Testing Library.
  • Command: pnpm -F <app-name> test (e.g., pnpm -F admin-portal-ui test).

2. End-to-End (E2E) Testing

End-to-End tests verify the integrated system from the user's perspective. We use Playwright to simulate user interactions against a running environment.

Location

All E2E tests are located in scripts/tests:

  • scripts/tests/ui/: UI-driven flows (login, registration).
  • scripts/tests/api/: Direct API calls to verify backend logic.

Running E2E Tests

The scripts/e2e.sh script is the single entry point for running the test suite. It handles:

  1. Database Reset: Cleans the database to a known state.
  2. Service Startup: Boots up all services (IAM, Keycloak, Frontend, etc.).
  3. Test Execution: Runs Playwright.

Prerequisites

  • Docker & Docker Compose running.
  • Node.js installed (pnpm install).
  • Playwright browsers installed: pnpm exec playwright install --with-deps.

Command

./scripts/e2e.sh

Interactive Mode (UI)

To debug tests or watch them execute:

./scripts/e2e.sh --ui

This opens the Playwright Inspector, allowing you to step through tests and view traces.

Writing New E2E Tests

  1. Create a new .spec.ts file in scripts/tests/ui or scripts/tests/api.
  2. Use the test object from @playwright/test.
  3. Leverage helper functions in scripts/tests/utils for common tasks like generating random users or tokens.
import { test, expect } from '@playwright/test';

test('should allow user to login', async ({ page }) => {
await page.goto('http://localhost:5000');
await page.getByRole('button', { name: 'Login' }).click();
// ... interactions ...
await expect(page).toHaveURL(/dashboard/);
});