Testing
Fleet uses Vitest for testing. The test suite covers core modules, commands, MCP tools, templates, and TUI state management.
Running tests
# Run all testsnpm test
# Run a specific filenpx vitest run src/core/health.test.ts
# Run tests matching a patternnpx vitest run --grep "validates app name"
# Run in watch modenpx vitestTest file location
Test files live next to their source files:
src/core/secrets.tssrc/core/secrets.test.tssrc/commands/deploy.tssrc/commands/deploy.test.tsException: TUI integration tests go in src/tui/tests/.
Mocking patterns
Mocking child_process
Most core modules call external commands via execSafe. Mock the exec module:
import { vi } from 'vitest';
vi.mock('../core/exec.js', () => ({ execSafe: vi.fn(() => ({ ok: true, stdout: '', stderr: '' })),}));Mocking the filesystem
For modules that read/write files:
vi.mock('node:fs', () => ({ existsSync: vi.fn(() => true), readFileSync: vi.fn(() => 'file content'), writeFileSync: vi.fn(), mkdirSync: vi.fn(), unlinkSync: vi.fn(), readdirSync: vi.fn(() => []),}));Mocking secrets
Secrets tests have a specific pattern because same-module functions can’t be mocked at the module level:
Mock node:fs directly — the secrets module reads files internally.
Mock ./secrets.js as a module — secrets-ops imports from secrets.
Security test patterns
Every module that accepts user input should have security tests:
describe('security', () => { it('rejects command injection in app name', () => { expect(() => addCommand(['$(rm -rf /)'])).toThrow(); });
it('rejects path traversal', () => { expect(() => addCommand(['../../etc/passwd'])).toThrow(); });
it('rejects empty input', () => { expect(() => addCommand([])).toThrow(); });});Cover at minimum:
- Command injection (
; rm -rf /,$(cmd),`cmd`) - Path traversal (
../../etc/passwd,/etc/shadow) - Invalid / missing arguments
- Oversized input
Integration tests
Tests that need Docker or systemd skip in CI:
const describeIntegration = process.env.CI ? describe.skip : describe;
describeIntegration('boot order', () => { // tests that call docker compose...});Test configuration
Vitest config is in package.json (no separate config file). The test runner uses the same TypeScript/ESM setup as the main build.