Testing

Run the full suite (backend and frontend) with:

bun run test

This runs the backend tests with Bun's built-in test runner and the frontend tests with Vitest. The tests need no ClickHouse® server and no external service.

Backend tests

The backend suite runs under bun test. It spans six directories:

DirectoryWhat it holds
tests/backendMost unit tests. Each file runs in its own process.
tests/isolatedTests with module mocks that must not reach other files.
tests/no-mocksTests that use no module mocks.
tests/dbTests of the database code against an in-memory SQLite database.
tests/netTests that open real network sockets, for example TLS and timeouts.
tests/securityTests that start the real server and call the API as each role. See tests/security/README.md.

Run all backend suites, one directory, or a single file:

bun run test:backend
bun test tests/db
bun test tests/backend/crypto.test.js    # single file

bun run test:backend runs scripts/test-each-file.mjs for tests/backend. This script starts one bun test process for each file, so a mock.module call in one file cannot change the result of another file. Then the script runs the other five directories with bun test.

The suites in tests/security need data/crypto.salt in the repository root. Run bun run dev:backend one time to make it.

Most backend tests live in tests/backend/. The suite grows over time, so treat the directory listing as the authoritative list. It currently covers these areas.

AreaRepresentative files
Authentication, sessions, and securityauth-logout.test.js, auth-env-fallback.test.js, jwt.test.js, crypto.test.js, chCredStore.test.js, securityHeaders.test.js, rateLimiter.test.js, middleware.test.js, bodyLimits.test.js
Users and roles (RBAC)rbac.test.js, users.test.js, appUsers.test.js
ClickHouse® connection and queriesclickhouse.test.js, query.test.js, cluster.test.js, clusterUtils.test.js, cluster-tls-sync.test.js
Alerting and notificationsalertScheduler.test.js, alert-node-states.test.js, alert-templates.test.js, alerts.test.js, notifier.test.js, notifier-delivery.test.js, notifier-emails.test.js
App data, settings, and the DB layerdrizzle.test.js, appUserMigrations.test.js, dashboards.test.js, dashboardFilters.test.js, settings.test.js, config.test.js, env.test.js
Schema Studioschema-studio.test.js, schema-studio-routes.test.js
AI providers and QuriozaiService-gemini.test.js, aiService-claude.test.js, aiService-mistral.test.js, aiService-ollama.test.js, aiService-constructor.test.js, aiRouteHelpers.test.js, SQLGenerationService.test.js, tokenEstimator.test.js, apiKeys-ollama-route.test.js
ExportsexportFormats.test.js, exportStream.test.js, exportCompress.test.js, exportjobs.test.js, sqlExport.test.js
Kubernetesk8sClient.test.js, k8sErrors.test.js, akocProvider.test.js, ockoProvider.test.js, k8s-health.test.js, k8s-health-alerts.test.js, k8s-zone-risks.test.js, disk-forecast.test.js
SQL parameters and miscsqlParams.test.js, storage-profile-test-sql.test.js
Cross-cutting integration and errorsintegration.test.js, error-handler.test.js

Representative coverage across these files includes: the four-tier role hierarchy and canChangeRole logic for every caller and target combination; the RBAC middleware (requireAdmin, requireSuperAdminOnly, requireEditor); AES-256-GCM encrypt and decrypt with random IVs, tamper detection, legacy plaintext fallback, and the per-install salt; JWT create and verify, the jti claim, and revocation via the blocklist; the encrypted per-session ClickHouse® credential store; FORMAT detection and EXPLAIN handling in the ClickHouse® client; threshold evaluation and per-node filtering in the alert scheduler; cluster validation (limits, naming, node uniqueness); SSRF prevention through the node allowlist; protected settings keys; and Drizzle CRUD with cascade and set-null behavior.

Frontend tests

The frontend suite runs under Vitest. Run it on its own, or point it at a single file:

bunx vitest run tests/frontend
bunx vitest run tests/frontend/backups.test.js    # single file

Do not start Vitest with bun --bun, because vi.mock then does nothing and the suites that use it fail. bun run test and the bunx vitest command start Vitest with Node.

The frontend tests live in tests/frontend/. Vitest runs every *.test.js and *.test.jsx file in that folder. As with the backend suite, the directory listing is the authoritative list. It currently covers these areas.

AreaRepresentative files
SQL Editor and query toolsSqlEditor.test.jsx, SqlEditor-tabs.test.jsx, sql-editor.test.js, sql-classify.test.js, sqlHighlight.test.js, editor-session.test.js, QueryTabs.test.jsx, useQueryTabs.test.js, query-compare.test.js, query-profiler.test.js, query-metrics.test.jsx, MaxRowsControl.test.jsx, functionDocs.test.js
Charts and dashboardschart-builder-preview.test.js, chart-toolbar.test.js, chartTypes.test.js, DashboardFilters.test.jsx, dashboard-filters-wiring.test.js, dashboardParams.test.js, dragPayload.test.js
Schema Studio and schema routesschema-studio-ddl.test.js, schema-studio-engine.test.js, schema-studio-ui.test.js, schema-routes.test.js
Administration, users, and auth UXadmin.test.js, user-management.test.jsx, api-management.test.jsx, app-data-backup.test.jsx, force-password-change.test.js, idle-timeout.test.js, login-carousel.test.js
Navigation, layout, and searchsidebar-routing.test.js, navbar.test.js, global-search.test.js, session-log.test.js, tooltipHost.test.jsx
Monitoring, overview, and logsplayback.test.js, heatmaps-logs.test.js, overview-charts.test.js, overview-live.test.jsx, overview-page.test.jsx, podLogs.test.js
Indexes and chart/tree utilitiesindexes.test.js, treeChart.test.js
Backupsbackups.test.js
Export and shareexport-wizard.test.jsx, ExportWizard-resume.test.jsx, BookmarkExport.test.jsx, ShareDialog.test.jsx, shareLink.test.js
Shared components and UIDataTable.test.jsx, DataTable-virtual.test.jsx, Toast.test.jsx, components.test.jsx, select-component.test.jsx, scrollbars.test.js, icon-tabler-only.test.js
API and utilitiesapiUtils.test.js

Testing approach

Frontend tests come in two styles. Many read source files as strings and assert on structure (imports, function signatures, route tables, CSS classes). This is intentional: it catches breaking changes without a browser, and it is why runtime line coverage looks low for those files, because their code paths are not executed. The .jsx files (for example, DataTable.test.jsx, Toast.test.jsx, components.test.jsx, query-metrics.test.jsx, api-management.test.jsx, app-data-backup.test.jsx, user-management.test.jsx) render components in jsdom, and several .js files (for example, treeChart.test.js, apiUtils.test.js, sql-classify.test.js) import and execute source modules directly. Those contribute actual runtime coverage. vite.config.js and tests/frontend/setup.js configure the jsdom environment and shared setup.

Security-specific tests

Several files exercise the security hardening:

  • crypto.test.js: AES-256-GCM roundtrip, random IV uniqueness, legacy plaintext backward compatibility, tamper detection, per-install salt, and the 32-character secret minimum.
  • jwt.test.js: the signing key that CHOps makes by itself, the jti claim, and token revocation via the blocklist.
  • tests/db/jwtKeys.test.js and tests/db/legacySecrets.test.js: CHOps stores the signing keys and old plain-text secrets encrypted.
  • securityHeaders.test.js: strict CSP for app routes (script-src 'self'), the relaxed CSP for /docs/* that allows the Docsify CDN, HSTS with max-age=31536000, and frame-ancestors 'none'.
  • rbac.test.js: the four-tier role hierarchy, canChangeRole for every caller and target combination, and admin-level middleware that allows both admin and superadmin.
  • chCredStore.test.js and rateLimiter.test.js: the encrypted per-session credential store and the per-IP rate limiter.
  • integration.test.js and schema-routes.test.js: SSRF prevention (node and webhook URL validation) and RBAC enforcement on alert, backup, dashboard, settings, and user write routes.
  • tests/security/: each role calls the real API. routeGuards.test.js fails when an API route has no entry in its list of expected role checks.

Code coverage

Run both suites with coverage:

bun run test:coverage

This runs the backend coverage first, then the frontend coverage.

Backend coverage: the test:backend:coverage script (scripts/test-backend-coverage.mjs) runs the same suites as test:backend, in the same processes. Each run writes its own lcov report to coverage/backend/parts/. scripts/coverage-report.mjs merges these reports with the frontend report into one summary.

bun run test:backend:coverage

Frontend coverage uses the Istanbul provider through @vitest/coverage-istanbul, configured in vite.config.js to write into coverage/frontend:

bun run test:frontend:coverage
# or directly:
bunx vitest run tests/frontend --coverage

Because many frontend tests read source as strings rather than running it, frontend line coverage understates how much is verified. The jsdom and runtime tests noted above are what drive the executed portion.

Continuous integration

The Run Unit Tests GitHub Actions workflow runs on pull requests and on pushes to main and testing. It sets up Bun 1.4.2, installs dependencies (bun install --frozen-lockfile), checks for sensitive data in console logging (bun run check:sensitive-logging), runs the full suite with coverage (bun run test:coverage), and runs the linter (bun run lint). The full suite includes every frontend test file. A lint error fails the build, but a lint warning does not. It also generates a coverage summary with scripts/coverage-report.mjs and uploads it as an artifact. A separate Post Coverage Comment workflow then posts that summary as a PR comment.

A separate Verify Binary Build job compiles the standalone binary as a build-only check with bun run build:standalone:linux, and smoke-tests the result. This confirms the binary still compiles and starts.