Testing
Run the full suite (backend and frontend) with:
bun run testThis 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:
| Directory | What it holds |
|---|---|
tests/backend | Most unit tests. Each file runs in its own process. |
tests/isolated | Tests with module mocks that must not reach other files. |
tests/no-mocks | Tests that use no module mocks. |
tests/db | Tests of the database code against an in-memory SQLite database. |
tests/net | Tests that open real network sockets, for example TLS and timeouts. |
tests/security | Tests 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 filebun 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.
| Area | Representative files |
|---|---|
| Authentication, sessions, and security | auth-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 queries | clickhouse.test.js, query.test.js, cluster.test.js, clusterUtils.test.js, cluster-tls-sync.test.js |
| Alerting and notifications | alertScheduler.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 layer | drizzle.test.js, appUserMigrations.test.js, dashboards.test.js, dashboardFilters.test.js, settings.test.js, config.test.js, env.test.js |
| Schema Studio | schema-studio.test.js, schema-studio-routes.test.js |
| AI providers and Qurioz | aiService-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 |
| Exports | exportFormats.test.js, exportStream.test.js, exportCompress.test.js, exportjobs.test.js, sqlExport.test.js |
| Kubernetes | k8sClient.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 misc | sqlParams.test.js, storage-profile-test-sql.test.js |
| Cross-cutting integration and errors | integration.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 fileDo 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.
| Area | Representative files |
|---|---|
| SQL Editor and query tools | SqlEditor.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 dashboards | chart-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 routes | schema-studio-ddl.test.js, schema-studio-engine.test.js, schema-studio-ui.test.js, schema-routes.test.js |
| Administration, users, and auth UX | admin.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 search | sidebar-routing.test.js, navbar.test.js, global-search.test.js, session-log.test.js, tooltipHost.test.jsx |
| Monitoring, overview, and logs | playback.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 utilities | indexes.test.js, treeChart.test.js |
| Backups | backups.test.js |
| Export and share | export-wizard.test.jsx, ExportWizard-resume.test.jsx, BookmarkExport.test.jsx, ShareDialog.test.jsx, shareLink.test.js |
| Shared components and UI | DataTable.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 utilities | apiUtils.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, thejticlaim, and token revocation via the blocklist.tests/db/jwtKeys.test.jsandtests/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 withmax-age=31536000, andframe-ancestors 'none'.rbac.test.js: the four-tier role hierarchy,canChangeRolefor every caller and target combination, and admin-level middleware that allows both admin and superadmin.chCredStore.test.jsandrateLimiter.test.js: the encrypted per-session credential store and the per-IP rate limiter.integration.test.jsandschema-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.jsfails when an API route has no entry in its list of expected role checks.
Code coverage
Run both suites with coverage:
bun run test:coverageThis 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:coverageFrontend 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 --coverageBecause 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.