Documentation Index
Fetch the complete documentation index at: https://mintlify.com/berkayoztunc/orquestra/llms.txt
Use this file to discover all available pages before exploring further.
Testing is a critical part of the Orquestra development workflow. This guide covers running tests locally and understanding the automated CI/CD checks.
Test Stack
Orquestra uses:
- Bun Test - Fast, built-in test runner
- TypeScript - Type checking for compile-time safety
- ESLint - Linting for code quality
- GitHub Actions - Automated CI/CD pipeline
Running Tests Locally
Unit Tests
Run the test suite:
# Run all tests
bun test
# Run tests in worker package
bun test --cwd packages/worker
# Run tests in watch mode
bun test --watch
# Run specific test file
bun test packages/worker/src/services/auth.test.ts
Test Output
$ bun test
bun test v1.0.0
✓ packages/worker/src/services/auth.test.ts
✓ validateApiKey
✓ returns true for valid API keys
✓ returns false for invalid API keys
✓ handles null and undefined
✓ generateApiKey
✓ generates 32 character keys
✓ generates unique keys
5 tests passed (234ms)
Type Checking
Run TypeScript compiler to check types across all packages:
# Check all packages
bun run type-check
# Output shows any type errors
$ bun run type-check
packages/shared/tsconfig.json: ✓
packages/worker/tsconfig.json: ✓
packages/frontend/tsconfig.json: ✓
packages/cli/tsconfig.json: ✓
All type checks passed!
Type Check Individual Packages
# Check worker types
tsc --noEmit -p packages/worker/tsconfig.json
# Check frontend types
tsc --noEmit -p packages/frontend/tsconfig.json
# Check shared types
tsc --noEmit -p packages/shared/tsconfig.json
Common Type Errors
Missing return type:
// Error: Missing return type on function
export async function createProject(data) {
return await db.insert(data);
}
// Fix: Add explicit return type
export async function createProject(data: ProjectInput): Promise<Project> {
return await db.insert(data);
}
Implicit any:
// Error: Parameter 'req' implicitly has an 'any' type
app.post('/api/projects', (req, res) => {
// ...
});
// Fix: Add types
import type { Request, Response } from 'hono';
app.post('/api/projects', (req: Request, res: Response) => {
// ...
});
Null safety:
// Error: Object is possibly 'null'
const project = await getProject(id);
console.log(project.name);
// Fix: Handle null case
const project = await getProject(id);
if (project) {
console.log(project.name);
} else {
throw new Error('Project not found');
}
Linting
Run ESLint to check code quality:
# Check all packages
bun run lint
# Auto-fix issues
bun run lint:fix
# Lint specific package
bun run lint --prefix packages/frontend
Common Lint Issues
Unused variables:
// Warning: 'request' is defined but never used
function handler(request: Request, response: Response) {
return response.json({ status: 'ok' });
}
// Fix: Prefix with underscore
function handler(_request: Request, response: Response) {
return response.json({ status: 'ok' });
}
Missing return types:
// Warning: Missing return type on exported function
export function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Fix: Add explicit return type
export function calculateTotal(items: CartItem[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
Format code with Prettier:
# Format all code
bun run format
# Check formatting without modifying files
bun run prettier --check "packages/**/src/**/*.(ts|tsx|json|css)"
Writing Tests
Test Structure
Organize tests with describe and it blocks:
import { describe, it, expect } from 'bun:test';
import { isValidSolanaAddress } from './validators';
describe('isValidSolanaAddress', () => {
it('returns true for valid addresses', () => {
const valid = '11111111111111111111111111111111';
expect(isValidSolanaAddress(valid)).toBe(true);
});
it('returns false for invalid addresses', () => {
expect(isValidSolanaAddress('invalid')).toBe(false);
expect(isValidSolanaAddress('')).toBe(false);
expect(isValidSolanaAddress('too-short')).toBe(false);
});
it('handles null and undefined', () => {
expect(isValidSolanaAddress(null as any)).toBe(false);
expect(isValidSolanaAddress(undefined as any)).toBe(false);
});
});
Unit Tests
Test individual functions in isolation:
import { describe, it, expect } from 'bun:test';
import { generateApiKey, hashApiKey } from './auth';
describe('API Key utilities', () => {
describe('generateApiKey', () => {
it('generates 32 character keys', () => {
const key = generateApiKey();
expect(key.length).toBe(32);
});
it('generates unique keys', () => {
const key1 = generateApiKey();
const key2 = generateApiKey();
expect(key1).not.toBe(key2);
});
it('generates alphanumeric keys', () => {
const key = generateApiKey();
expect(key).toMatch(/^[a-zA-Z0-9]+$/);
});
});
describe('hashApiKey', () => {
it('produces consistent hashes', () => {
const key = 'test-key-123';
const hash1 = hashApiKey(key);
const hash2 = hashApiKey(key);
expect(hash1).toBe(hash2);
});
it('produces different hashes for different keys', () => {
const hash1 = hashApiKey('key1');
const hash2 = hashApiKey('key2');
expect(hash1).not.toBe(hash2);
});
});
});
Integration Tests
Test component interactions:
import { describe, it, expect, beforeEach } from 'bun:test';
import { Hono } from 'hono';
import { projectRouter } from './routes/projects';
describe('Project API', () => {
let app: Hono;
beforeEach(() => {
app = new Hono();
app.route('/api/projects', projectRouter);
});
it('creates a new project', async () => {
const response = await app.request('/api/projects', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer test-api-key',
},
body: JSON.stringify({
name: 'Test Project',
idl: { /* IDL data */ },
}),
});
expect(response.status).toBe(201);
const data = await response.json();
expect(data.id).toBeDefined();
expect(data.name).toBe('Test Project');
});
it('returns 401 without API key', async () => {
const response = await app.request('/api/projects', {
method: 'POST',
});
expect(response.status).toBe(401);
});
});
Test Best Practices
Arrange-Act-Assert pattern:
it('calculates order total correctly', () => {
// Arrange: Set up test data
const items = [
{ name: 'Item 1', price: 10 },
{ name: 'Item 2', price: 20 },
];
// Act: Execute the function
const total = calculateTotal(items);
// Assert: Verify the result
expect(total).toBe(30);
});
Test edge cases:
describe('parseIdl', () => {
it('parses valid IDL', () => {
const idl = { /* valid IDL */ };
expect(parseIdl(idl)).toBeDefined();
});
it('throws on invalid IDL', () => {
expect(() => parseIdl(null)).toThrow();
expect(() => parseIdl({})).toThrow();
expect(() => parseIdl({ version: 'invalid' })).toThrow();
});
it('handles missing optional fields', () => {
const minimalIdl = { version: '0.1.0', name: 'test' };
expect(parseIdl(minimalIdl)).toBeDefined();
});
});
Use descriptive test names:
// Good: Clear what is being tested and expected outcome
it('returns empty array when user has no projects', () => {
// ...
});
// Bad: Unclear what is being tested
it('works', () => {
// ...
});
CI/CD Pipeline
Orquestra uses GitHub Actions for automated testing and deployment.
CI Checks
Every pull request runs these checks:
1. Lint & Type Check
jobs:
lint:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Bun
- name: Install dependencies
run: bun install
- name: Type check
run: bun run type-check
2. Test
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Bun
- name: Install dependencies
run: bun install
- name: Run tests
run: bun test
working-directory: packages/worker
3. Build
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Bun
- name: Install dependencies
run: bun install
- name: Build all packages
run: bun run build
Workflow Triggers
CI runs on:
- Push to main/develop: Full CI + deployment
- Pull requests: Full CI (no deployment)
- Manual trigger: Via GitHub Actions UI
Database Migrations
Separate workflow for database changes:
name: Database Migrations
on:
push:
branches: [ main, develop ]
paths:
- 'migrations/**'
jobs:
migrate:
runs-on: ubuntu-latest
strategy:
matrix:
environment: [ development, production ]
Migrations run automatically when files in migrations/ change.
Local Development Workflow
Recommended workflow before committing:
# 1. Make your changes
vim packages/worker/src/routes/projects.ts
# 2. Run type check
bun run type-check
# 3. Run linting
bun run lint:fix
# 4. Run tests
bun test
# 5. Format code
bun run format
# 6. Commit changes
git add .
git commit -m "feat: add project archiving functionality"
# 7. Push and create PR
git push origin feature/project-archiving
Debugging Failed Tests
View Detailed Output
# Run with verbose output
bun test --verbose
# Run specific failing test
bun test packages/worker/src/services/auth.test.ts
Add Debug Logging
it('processes complex data', () => {
const input = { /* ... */ };
console.log('Input:', input);
const result = processData(input);
console.log('Result:', result);
expect(result).toBeDefined();
});
Check CI Logs
If tests pass locally but fail in CI:
- Go to your PR on GitHub
- Click “Details” next to failed check
- Review the full log output
- Look for environment-specific issues (paths, dependencies, etc.)
Test Coverage
While Orquestra doesn’t enforce strict coverage percentages, aim to:
- Test all public APIs
- Test error cases and edge cases
- Test integration between components
- Test critical business logic thoroughly
Focus on:
- Authentication and authorization
- Data validation
- API endpoints
- Database operations
- IDL parsing and transformation
For performance-critical code:
import { describe, it } from 'bun:test';
describe('Performance', () => {
it('processes large IDL in under 100ms', () => {
const largeIdl = generateLargeIdl(1000);
const start = performance.now();
processIdl(largeIdl);
const duration = performance.now() - start;
expect(duration).toBeLessThan(100);
});
});
Continuous Improvement
- Add tests when fixing bugs
- Improve tests when refactoring
- Keep tests simple and focused
- Update tests when requirements change
- Remove obsolete tests
Consistent testing ensures Orquestra remains stable, reliable, and maintainable as it grows.