Testing Guide
This document covers the full testing strategy for BlockbotX, including unit tests, API integration tests, E2E browser tests, and load tests.
Overview
BlockbotX uses three test layers to ensure correctness at every level:
| Layer | Framework | Scope |
|---|---|---|
| Unit + API integration | Jest (ts-jest) | Business logic, utilities, API endpoints |
| End-to-end | Playwright | Full browser-based user flows |
| Load | k6 | Stress, soak, and spike testing |
The test suite includes:
- 380+ Jest tests across unit and API integration test files
- 10 Playwright E2E spec files covering critical user journeys
- 19 k6 load test scripts with 6 helper modules
All test types require PostgreSQL 16 and Redis 7 to be running.
Running Tests
pnpm test # All Jest tests (unit + API)
pnpm test:unit # Unit tests only (__tests__/ directory)
pnpm test:api # API integration tests (requires running server on localhost:3000)
pnpm test:e2e # Playwright E2E tests (requires running server on localhost:3000)
Prerequisites
For unit tests, you need PostgreSQL and Redis running. The development Docker Compose file provides both:
docker compose up -d postgres redis
For API and E2E tests, you also need the Next.js application running:
pnpm build && pnpm start
Playwright will auto-start a dev server locally if one is not already running (configured via webServer in playwright.config.ts).
Unit Tests (tests/)
Unit tests live in the __tests__/ directory and mirror the lib/ source structure. There are 43 unit test files covering the following domains:
Test Domains
| Domain | Files | What is tested |
|---|---|---|
auth/ | password.test.ts, jwt.test.ts | Password hashing, JWT generation and verification |
bot/engine/ | dca-bot.test.ts, position-manager.test.ts, scheduler.test.ts, execution-context.test.ts, bot-executor.test.ts | Bot execution lifecycle, position management, scheduling |
bot/backtest/ | backtest-executor.test.ts, backtest-reporter.test.ts, backtester.test.ts | Backtesting engine and report generation |
bot/performance/ | metrics-calculator.test.ts, performance-tracker.test.ts, trade-analyzer.test.ts, portfolio-correlation.test.ts | Performance metrics, trade analysis, portfolio correlation |
bot/risk/ | stop-loss-manager.test.ts, position-sizer.test.ts, risk-validator.test.ts, take-profit-manager.test.ts, drawdown-tracker.test.ts | Risk management, position sizing, stop-loss/take-profit |
bot/strategies/ | base-strategy.test.ts, strategy-factory.test.ts, breakout.test.ts | Trading strategy implementations |
bot/indicators/ | indicators.test.ts, macd.test.ts, cache.test.ts | Technical indicators and indicator caching |
defi/ | il-calculator.test.ts, farming-calculations.test.ts, staking-calculations.test.ts | DeFi calculations (impermanent loss, farming, staking) |
models/ | decimal-precision.test.ts | Decimal(65,30) precision for all financial fields |
notifications/ | notification-service.test.ts | Notification delivery |
portfolio/ | calculator.test.ts | Portfolio value calculations |
referrals/ | reward-calculator.test.ts | Referral reward calculations |
security/ | csrf.test.ts, sanitize.test.ts, security-logger.test.ts, env-validation.test.ts, auth-middleware.test.ts, cors.test.ts, encryption.test.ts | CSRF protection, input sanitization, encryption, CORS |
trading/ | validator.test.ts, operations.test.ts, balance.test.ts | Trade validation, operations, balance calculations |
webhooks/ | stripe.test.ts | Stripe webhook signature verification and event handling |
Decimal Precision Tests
The decimal-precision.test.ts file is critical for financial accuracy. It verifies that all financial fields (prices, quantities, P&L, percentages, APY, TVL) use Decimal(65,30) precision and that calculations do not suffer from floating-point rounding errors. This test is run as a separate explicit step in CI in addition to the standard unit test suite.
Configuration
Unit tests use the ts-jest preset with the following key settings from jest.config.js:
- Test roots:
__tests__/andtests/ - Module aliases:
@/maps to the project root (matchestsconfig.json) - Test timeout: 10,000ms (10 seconds)
- Coverage collection: Focuses on
lib/modules with business logic - Coverage thresholds: branches 55%, functions 65%, lines 68%, statements 68%
- Mocks:
clearMocks: true,resetMocks: false,restoreMocks: false(preserves manual mocks)
Mocking Strategy
- Logger and Redis: Mocked globally in
tests/jest-setup.js(runs before test files load) - Console:
console.errorandconsole.warnare mocked intests/setup.tsto reduce test noise - Fetch: Global fetch is wrapped to automatically include the
x-test-mode: trueheader for CSRF bypass - External services: MSW (Mock Service Worker) is used for mocking HTTP requests to external APIs
API Integration Tests (tests/api/)
API integration tests run against a live Next.js server on localhost:3000 with real PostgreSQL and Redis backends. There are 23 test files covering every API domain.
Test Files
| File | Domain |
|---|---|
auth.api.test.ts | Registration, login, logout, password reset |
auth-2fa-refresh.api.test.ts | Two-factor authentication and token refresh |
authorization.api.test.ts | Role-based access control and permissions |
bots.api.test.ts | Bot CRUD operations |
bots-extended.api.test.ts | Bot start/stop, backtest, performance |
dashboard.api.test.ts | Dashboard data aggregation |
defi.api.test.ts | DeFi staking, farming, liquidity endpoints |
exchange.api.test.ts | Exchange connections (Binance, OKX) |
health.api.test.ts | Health check endpoint |
help.api.test.ts | Help and FAQ endpoints |
market.api.test.ts | Market data and ticker information |
marketplace.api.test.ts | Bot template marketplace |
notifications.api.test.ts | Notification CRUD and preferences |
portfolio.api.test.ts | Portfolio balances and history |
referrals.api.test.ts | Referral codes and rewards |
settings.api.test.ts | User settings and preferences |
signal-providers.api.test.ts | Signal provider management |
subscriptions.api.test.ts | Subscription plans and Stripe integration |
support.api.test.ts | Support ticket management |
templates.api.test.ts | Bot template CRUD |
trading.api.test.ts | Order placement, trade history |
user.api.test.ts | User profile management |
webhooks.api.test.ts | Stripe webhook processing |
Authentication in Tests
API tests use the authenticatedFetch() helper from tests/utils/test-helpers.ts:
const response = await authenticatedFetch('http://localhost:3000/api/bots', {
method: 'POST',
token: userToken,
body: JSON.stringify({ name: 'My Bot', type: 'DCA', ... }),
});
This helper:
- Sets
Content-Type: application/json(unless the body is FormData) - Sets
x-test-mode: truefor CSRF bypass in test mode - Adds the
Authorization: Bearer <token>header if a token is provided - Fetches and includes the CSRF token for mutation requests (POST, PUT, DELETE, PATCH)
Test Helpers
The tests/utils/test-helpers.ts module provides factory functions for test data:
| Helper | Purpose |
|---|---|
createTestUser(overrides?) | Creates a user in the database with hashed password |
generateTestToken(userId, email?) | Generates a valid JWT access token |
cleanupTestUser(userId) | Deletes a user and ignores errors |
cleanupTestData() | Deletes all test data in foreign-key-safe order |
createTestExchangeConnection(userId) | Creates a Binance exchange connection |
createTestExchangeKey(userId, exchange?) | Creates an exchange connection with permissions |
createTestBot(userId, token, overrides?) | Creates a bot via the API |
createTestTicket(userId, token) | Creates a support ticket via the API |
createTestTemplate(userId, token) | Creates a bot template via the API |
createTestExchange(name?) | Creates an Exchange record in the database |
createTestSignalProvider(id?) | Creates a SignalProvider record |
createTestSubscription(userId, overrides?) | Creates a Subscription record |
createTestMarketplaceListing(userId, overrides?) | Creates a marketplace listing with template |
generateStripeWebhookSignature(payload, secret) | Generates valid Stripe webhook HMAC-SHA256 signatures |
waitFor(condition, timeout?, interval?) | Polls a condition until true or timeout |
getCsrfToken() | Fetches and caches a CSRF token from /api/csrf |
testData | Object with generator functions for emails, passwords, API keys |
Setup and Teardown
tests/jest-setup.js: Runs before test files load. Sets up environment variables, mocks for logger and Redis.tests/setup.ts: Runs after test framework initialization. Mocks console, wraps global fetch, and closes the database pool inafterAll.tests/globalTeardown.js: Runs once after all test suites complete. Safety net to close any remaining pg pool connections.
E2E Tests (e2e/)
End-to-end tests use Playwright to drive a real browser against the running application. There are 10 spec files covering the major user flows.
Spec Files
| File | Flow |
|---|---|
auth-flow.spec.ts | Signup, login, logout, 2FA enrollment |
bot-flow.spec.ts | Bot creation, configuration, start/stop, deletion |
trading-flow.spec.ts | Exchange connection, order placement |
defi-flow.spec.ts | DeFi staking, farming, liquidity provision |
subscription-flow.spec.ts | Plan selection, payment, upgrade/downgrade |
marketplace-flow.spec.ts | Browse, purchase, and deploy bot templates |
analytics-flow.spec.ts | Dashboard analytics, performance charts |
notifications-flow.spec.ts | Notification preferences, mark as read |
support-flow.spec.ts | Create and manage support tickets |
exchange-connection-flow.spec.ts | Connect Binance/OKX exchanges |
Playwright Configuration
Key settings from playwright.config.ts:
- Test directory:
./e2e - Test timeout: 120 seconds per test (accounts for login, bot creation, and assertion time)
- Parallel execution: Disabled (
fullyParallel: false) to avoid state conflicts - Retries: 1 in CI, 0 locally
- Workers: 2 in CI (tests are user-isolated), 1 locally
- Base URL:
http://localhost:3000(overridable viaPLAYWRIGHT_BASE_URL)
Browser Configuration
- CI: Chromium only (for speed)
- Local: Chromium, Firefox, and WebKit (Safari)
- WebKit uses extended navigation timeout (90 seconds) due to slower rendering
Timeouts
| Setting | Value |
|---|---|
| Test timeout | 120 seconds |
| Action timeout | 30 seconds |
| Navigation timeout | 60 seconds (90 seconds for WebKit) |
Artifacts
- Traces: Collected on first retry
- Screenshots: Captured only on failure
- Video: Retained only on failure
- Reports: HTML report (
playwright-report/) and JSON results (test-results/results.json) - CI retention: 30 days for both reports and test results
Local Development
When running locally, Playwright auto-starts a dev server (pnpm dev) if one is not already running. In CI, the server is started manually before tests run, and the webServer configuration is disabled.
Load Testing (tests/load/)
Load tests use k6 for stress, soak, and spike testing of the application under various traffic patterns.
Test Scripts
There are 19 load test scripts:
| Script | Target |
|---|---|
api-auth.js | Authentication endpoints |
api-bots.js | Bot management endpoints |
api-trading.js | Trading endpoints |
api-market.js | Market data endpoints |
api-portfolio.js | Portfolio endpoints |
api-defi.js | DeFi endpoints |
api-notifications.js | Notification endpoints |
api-subscriptions.js | Subscription endpoints |
api-dashboard.js | Dashboard endpoints |
api-admin.js | Admin endpoints |
api-health.js | Health check endpoint |
api-user-settings.js | User settings endpoints |
websocket-stress.js | WebSocket/Socket.io connections |
db-stress.js | Database query performance |
redis-stress.js | Redis operations performance |
rate-limiter.js | Rate limiter behavior under load |
user-journey.js | Full user journey simulation |
spike-test.js | Sudden traffic spike handling |
soak-test.js | Extended duration stability |
Helper Modules
The tests/load/helpers/ directory contains 6 shared modules:
| Module | Purpose |
|---|---|
config.js | Base URL, thresholds, and stage configurations |
auth.js | Authentication helpers for load test users |
checks.js | Response validation and assertion helpers |
metrics.js | Custom metric definitions and aggregation |
data.js | Test data generation |
seed-users.js | Pre-seeds test users for load tests |
Running Load Tests
# Run a specific load test
k6 run tests/load/api-auth.js
# Run with custom virtual users and duration
k6 run --vus 50 --duration 5m tests/load/api-health.js
Test Infrastructure
Jest Configuration Highlights
Key settings from jest.config.js:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/__tests__', '<rootDir>/tests'],
moduleNameMapper: { '^@/(.*)$': '<rootDir>/$1' },
testTimeout: 10000,
clearMocks: true,
resetMocks: false,
restoreMocks: false,
setupFiles: ['<rootDir>/tests/jest-setup.js'],
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
globalTeardown: '<rootDir>/tests/globalTeardown.js',
};
- E2E exclusion: The
testPathIgnorePatternssetting excludese2e/from Jest (Playwright handles those) - ESM support:
transformIgnorePatternsallows transforming specific ESM-only node_modules packages (jsdom, next, noble, etc.)
Test Identification
All test requests include the x-test-mode: true header. This allows the application to:
- Bypass CSRF validation in test mode
- Identify test traffic in logs
- Apply test-specific behavior where needed
Required Services
All test types (unit, API, E2E) require:
- PostgreSQL 16: For database operations. Schema is set up via
pnpm db:pushin CI - Redis 7: For caching, rate limiting, and pub/sub
In CI, both are provided as GitHub Actions service containers. Locally, use Docker Compose:
docker compose up -d postgres redis
Known Issues
pg Pool Timers
The pg connection pool (exported from lib/db.ts) creates background health-check timers. While pool.end() is called in afterAll (in tests/setup.ts) and in globalTeardown.js, these internal timers may not fully clear within Jest's default wait period.
The current configuration relies on .unref() timers and pool.end() in setup/teardown to let workers exit cleanly once all pool clients are idle. The pool does not keep the worker alive when idle.
If you observe "Jest did not exit" warnings, all test resources are properly cleaned up -- the warning is cosmetic and does not indicate a leak.
CI Integration
Parallel Execution
Tests run in parallel across 7 CI jobs in the test.yml workflow:
- Unit Tests -- Jest unit tests + Decimal precision + Codecov upload
- API Tests -- Build, start server, run API integration tests
- E2E Tests -- Install Playwright Chromium, build, start server, run E2E specs
- Code Quality --
tsc --noEmit+pnpm lint - Security Audit --
pnpm audit+ optional Snyk - Build Check --
pnpm build+ verify.next/output - Docker Build -- Build image + verify container starts
All 7 jobs must pass for the all-tests-passed gate, which is required before merging.
Service Containers
CI uses GitHub Actions service containers:
services:
postgres:
image: postgres:16-alpine
ports: ["5432:5432"]
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:7-alpine
ports: ["6379:6379"]
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
Coverage
- Unit test coverage is collected via
pnpm test:unit --coverage - Coverage reports are uploaded to Codecov with the
unittestsflag - On pull requests, coverage is commented directly on the PR via
lcov-reporter-action - Coverage thresholds are enforced in
jest.config.js(branches 55%, functions 65%, lines 68%, statements 68%)
Artifacts
- Playwright report: HTML report uploaded as
playwright-reportartifact (30-day retention) - Test results: JSON results uploaded as
test-resultsartifact (30-day retention) - Coverage: Uploaded to Codecov (external service)