Comprehensive guide to testing Synapse applications using the Theater System and standard testing tools.
Synapse provides a complete testing ecosystem through the Theater System, inspired by how medical students learn anatomy through observation and experimentation.
/\
/E2E\ <- Few, slow, high confidence
/------\
/ Integ \ <- Some, medium speed
/----------\
/ Unit \ <- Many, fast, focused
/--------------\
The Theater System provides a complete development and testing environment.
- Stage - Test runner and component mounting
- Laboratory - Test orchestration and experiments
- Hypothesis - Assertions and expectations
- Amphitheater - Component gallery and documentation
- Instruments - Profiling, tracing, and monitoring
import { Stage, Laboratory, Hypothesis } from '@synapse-framework/core';
import { MyComponent } from './my-component'; // Placeholder for example component
import { UserService } from './user-service'; // Placeholder for example service
// Create a stage
const stage = new Stage({
title: 'My Component Tests',
darkMode: false,
});
// Mount component
stage.mount('my-component', MyComponent);
// Create laboratory
const lab = new Laboratory({
stage,
verbose: true,
});
// Define experiment (test)
lab.experiment('should process data correctly', async () => {
const component = stage.getComponent('my-component');
const result = await component.process({ data: 'test' });
Hypothesis.expect(result.success).toBe(true);
Hypothesis.expect(result.data).toEqual({ processed: 'test' });
});
// Run experiments
await lab.runAll();import { NeuralNode } from '@synapse-framework/core';
import { describe, it, expect } from '@jest/globals';
import { UserService } from './user-service'; // Placeholder for example service
describe('UserService', () => {
let service: UserService;
beforeEach(async () => {
service = new UserService({
id: 'test-service',
type: 'cortical',
threshold: 0.5,
});
await service.activate();
});
afterEach(async () => {
await service.deactivate();
});
it('should activate successfully', () => {
expect(service.getStatus()).toBe('active');
});
it('should process signals above threshold', async () => {
// Send signals
await service.receive({
id: '1',
sourceId: 'test',
type: 'excitatory',
strength: 0.6,
payload: { action: 'create' },
timestamp: new Date(),
});
// Check integration
const decision = service.integrate();
expect(decision.shouldFire).toBe(true);
});
it('should maintain state across calls', async () => {
await service.process({ data: { userId: '123' } });
const state = service.getState();
expect(state.userId).toBe('123');
});
it('should emit health metrics', () => {
const health = service.healthCheck();
expect(health.healthy).toBe(true);
expect(health.errors).toBe(0);
expect(health.uptime).toBeGreaterThan(0);
});
});import { Stage, Laboratory, Hypothesis, TestSubject } from '@synapse-framework/core';
import { UserService } from './user-service'; // Placeholder for example service
describe('UserService (Theater)', () => {
const stage = new Stage({ title: 'UserService Tests' });
const lab = new Laboratory({ stage });
beforeAll(async () => {
// Mount service on stage
stage.mount('user-service', new UserService());
await stage.start();
});
afterAll(async () => {
await stage.stop();
});
lab.experiment('should create user', async () => {
const service = stage.getComponent('user-service');
const result = await service.createUser({
email: 'test@example.com',
name: 'Test User',
});
Hypothesis.expect(result.id).toBeDefined();
Hypothesis.expect(result.email).toBe('test@example.com');
});
lab.experiment('should validate email format', async () => {
const service = stage.getComponent('user-service');
await Hypothesis.expectAsync(
service.createUser({
email: 'invalid-email',
name: 'Test User',
})
).toReject();
});
// Run all experiments
it('runs all experiments', async () => {
const results = await lab.runAll();
expect(results.passed).toBe(results.total);
});
});import { Muscle, Bone } from '@synapse-framework/core';
import { z } from 'zod';
describe('DataTransform Muscle', () => {
const transform = new Muscle(
'transform',
(data: { value: number }) => ({
value: data.value * 2,
}),
{ deterministic: true }
);
it('should transform data', () => {
const result = transform.execute({ value: 5 });
expect(result.value).toBe(10);
});
it('should cache deterministic results', () => {
const input = { value: 7 };
// First call
const result1 = transform.execute(input);
// Second call should use cache
const result2 = transform.execute(input);
expect(result1).toBe(result2); // Same object reference
});
it('should validate input schema', () => {
const muscle = new Muscle(
'validated',
(data: number) => data * 2,
{
inputSchema: new Bone('Number', z.number()),
}
);
expect(() => muscle.execute('invalid')).toThrow();
});
});import { Heart, PublishSubscribe } from '@synapse-framework/core';
describe('PublishSubscribe', () => {
let heart: Heart;
let pubsub: PublishSubscribe;
beforeEach(() => {
heart = new Heart();
pubsub = new PublishSubscribe(heart);
});
it('should deliver messages to subscribers', (done) => {
pubsub.subscribe('test.event', (data) => {
expect(data.message).toBe('Hello');
done();
});
pubsub.publish('test.event', { message: 'Hello' });
});
it('should support multiple subscribers', async () => {
const received: number[] = [];
pubsub.subscribe('test.event', () => received.push(1));
pubsub.subscribe('test.event', () => received.push(2));
await pubsub.publish('test.event', {});
// Give time for async processing
await new Promise((resolve) => setTimeout(resolve, 100));
expect(received).toEqual([1, 2]);
});
it('should support wildcard subscriptions', (done) => {
pubsub.subscribe('user.*', (data) => {
expect(data.action).toBe('created');
done();
});
pubsub.publish('user.created', { action: 'created' });
});
});import { TCell, BCell, Macrophage } from '@synapse-framework/core';
describe('Security Layer', () => {
let auth: TCell;
let authz: BCell;
let sanitizer: Macrophage;
beforeEach(async () => {
auth = new TCell({
id: 'test-auth',
algorithm: 'HS256',
secretKey: 'test-secret',
});
authz = new BCell({ id: 'test-authz' });
sanitizer = new Macrophage({
id: 'test-sanitizer',
xss: true,
});
await auth.activate();
await authz.activate();
await sanitizer.activate();
});
describe('Authentication', () => {
it('should create valid tokens', async () => {
const result = await auth.createToken({
userId: '123',
email: 'test@example.com',
issuedAt: new Date(),
});
expect(result.success).toBe(true);
expect(result.token).toBeDefined();
});
it('should verify valid tokens', async () => {
const { token } = await auth.createToken({
userId: '123',
email: 'test@example.com',
issuedAt: new Date(),
});
const verification = await auth.verifyToken(token!);
expect(verification.valid).toBe(true);
expect(verification.session?.userId).toBe('123');
});
it('should reject expired tokens', async () => {
// Create auth with very short expiration
const shortAuth = new TCell({
id: 'short-auth',
algorithm: 'HS256',
secretKey: 'test-secret',
expiresIn: '1ms',
});
const { token } = await shortAuth.createToken({
userId: '123',
email: 'test@example.com',
issuedAt: new Date(),
});
// Wait for expiration
await new Promise((resolve) => setTimeout(resolve, 10));
const verification = await shortAuth.verifyToken(token!);
expect(verification.valid).toBe(false);
});
});
describe('Authorization', () => {
beforeEach(() => {
authz.createPermission({
id: 'users:read',
resource: 'users',
action: 'read',
});
authz.createRole({
id: 'user',
name: 'User',
permissions: ['users:read'],
});
authz.assignRole('user-123', 'user');
});
it('should allow authorized actions', async () => {
const result = await authz.authorize({
userId: 'user-123',
resource: 'users',
action: 'read',
});
expect(result.allowed).toBe(true);
});
it('should deny unauthorized actions', async () => {
const result = await authz.authorize({
userId: 'user-123',
resource: 'users',
action: 'delete',
});
expect(result.allowed).toBe(false);
});
});
describe('Sanitization', () => {
it('should remove XSS attempts', () => {
const result = sanitizer.sanitize({
comment: '<script>alert("xss")</script>',
});
expect(result.safe).toBe(true);
expect(result.sanitized.comment).not.toContain('<script>');
});
it('should detect threats', () => {
const result = sanitizer.sanitize({
query: "'; DROP TABLE users; --",
});
expect(result.threats.length).toBeGreaterThan(0);
expect(result.threats[0].type).toBe('sql-injection');
});
});
});import {
Heart,
PublishSubscribe,
CorticalNeuron,
Astrocyte,
TCell,
BCell,
} from '@synapse-framework/core';
import { UserService } from './user-service'; // Placeholder for example service
import { EmailService } from './email-service'; // Placeholder for example service
describe('User Registration Flow', () => {
let heart: Heart;
let pubsub: PublishSubscribe;
let userService: UserService;
let emailService: EmailService;
let cache: Astrocyte;
let auth: TCell;
let authz: BCell;
beforeEach(async () => {
// Set up infrastructure
heart = new Heart();
pubsub = new PublishSubscribe(heart);
cache = new Astrocyte({ id: 'cache' });
auth = new TCell({
id: 'auth',
algorithm: 'HS256',
secretKey: 'test-secret',
});
authz = new BCell({ id: 'authz' });
// Set up services
userService = new UserService({ cache, auth, authz, pubsub });
emailService = new EmailService({ pubsub });
// Activate everything
await cache.activate();
await auth.activate();
await authz.activate();
await userService.activate();
await emailService.activate();
});
afterEach(async () => {
await userService.deactivate();
await emailService.deactivate();
await cache.deactivate();
});
it('should complete full registration flow', async () => {
const emailSent = new Promise((resolve) => {
pubsub.subscribe('email.sent', resolve);
});
// Register user
const user = await userService.register({
email: 'newuser@example.com',
password: 'SecurePass123!',
name: 'New User',
});
// Verify user created
expect(user.id).toBeDefined();
expect(user.email).toBe('newuser@example.com');
// Verify cached
const cached = cache.get(`user:${user.id}`);
expect(cached).toBeDefined();
// Verify email sent
await emailSent;
// Verify can login
const token = await userService.login({
email: 'newuser@example.com',
password: 'SecurePass123!',
});
expect(token).toBeDefined();
// Verify token works
const session = await auth.verifyToken(token);
expect(session.valid).toBe(true);
});
});import { Stage, Laboratory } from '@synapse-framework/core';
import { Database, setupTestDatabase, teardownTestDatabase } from './database-utils'; // Placeholder for example database utilities
describe('Database Integration', () => {
const stage = new Stage({ title: 'DB Tests' });
const lab = new Laboratory({ stage });
let database: Database;
beforeAll(async () => {
// In real tests, use testcontainers for database
database = await setupTestDatabase();
stage.mount('db', database);
await stage.start();
});
afterAll(async () => {
await teardownTestDatabase();
await stage.stop();
});
lab.experiment('should persist and retrieve data', async () => {
const db = stage.getComponent('db');
const user = await db.users.create({
email: 'test@example.com',
name: 'Test User',
});
const retrieved = await db.users.findById(user.id);
Hypothesis.expect(retrieved).toEqual(user);
});
});Synapse uses Playwright for end-to-end testing.
import { test, expect } from '@playwright/test';
test.describe('User Registration', () => {
test('should register new user', async ({ page }) => {
// Navigate to registration page
await page.goto('http://localhost:3000/register');
// Fill form
await page.fill('[name="email"]', 'newuser@example.com');
await page.fill('[name="password"]', 'SecurePass123!');
await page.fill('[name="name"]', 'New User');
// Submit
await page.click('button[type="submit"]');
// Verify success
await expect(page.locator('.success-message')).toBeVisible();
await expect(page).toHaveURL('http://localhost:3000/dashboard');
});
test('should show validation errors', async ({ page }) => {
await page.goto('http://localhost:3000/register');
// Submit without filling form
await page.click('button[type="submit"]');
// Verify errors
await expect(page.locator('.error-message')).toContainText('Email is required');
});
});import { test, expect } from '@playwright/test';
test('should receive real-time updates', async ({ page }) => {
await page.goto('http://localhost:3000/chat');
// Wait for WebSocket connection
await page.waitForSelector('.connected-indicator');
// Listen for messages
const messagePromise = page.waitForSelector('.message:has-text("Hello")');
// Trigger message from another source
await sendMessageViaAPI('Hello');
// Verify received
await messagePromise;
});import { UserService } from './user-service'; // Placeholder for example service
it('should process user data', async () => {
// Arrange
const service = new UserService();
await service.activate();
const input = { name: 'Alice', email: 'alice@example.com' };
// Act
const result = await service.process(input);
// Assert
expect(result.success).toBe(true);
expect(result.data.name).toBe('Alice');
});lab.experiment('user creation', async () => {
// Given: A new user registration request
const request = {
email: 'test@example.com',
password: 'SecurePass123!',
};
// When: The user is registered
const user = await userService.register(request);
// Then: The user should be created and cached
Hypothesis.expect(user.id).toBeDefined();
Hypothesis.expect(cache.get(`user:${user.id}`)).toBeDefined();
});import crypto from 'crypto';
import { TCell } from '@synapse-framework/core';
class TestFixtures {
static async createUser(overrides = {}) {
return {
id: crypto.randomUUID(),
email: 'test@example.com',
name: 'Test User',
createdAt: new Date(),
...overrides,
};
}
static async createAuthToken(userId: string) {
const auth = new TCell({
id: 'test-auth',
secretKey: 'test-secret',
});
const result = await auth.createToken({
userId,
email: 'test@example.com',
issuedAt: new Date(),
});
return result.token!;
}
}
// Usage
it('should validate user permissions', async () => {
const user = await TestFixtures.createUser();
const token = await TestFixtures.createAuthToken(user.id);
// Test with fixtures
const result = await api.request('/users/me', {
headers: { Authorization: `Bearer ${token}` },
});
expect(result.user.id).toBe(user.id);
});// Good: Each test is independent
describe('UserService', () => {
let service: UserService;
beforeEach(() => {
service = new UserService(); // Fresh instance
});
it('test 1', () => {
// Use service
});
it('test 2', () => {
// Use fresh service
});
});
// Bad: Shared state between tests
describe('UserService', () => {
const service = new UserService(); // Shared!
it('test 1', () => {
service.createUser({}); // Mutates shared state
});
it('test 2', () => {
// Depends on test 1's state
});
});// Mock external dependencies
class MockEmailService {
async send(email: string, subject: string, body: string) {
// Don't actually send email
return { success: true };
}
}
import { UserService } from './user-service'; // Placeholder for example service
it('should send welcome email', async () => {
const mockEmail = new MockEmailService();
const service = new UserService({ emailService: mockEmail });
await service.register({ email: 'test@example.com' });
// Verify email service was called (use spy)
expect(mockEmail.send).toHaveBeenCalledWith('test@example.com', 'Welcome', expect.any(String));
});describe('Error Handling', () => {
it('should handle invalid input', async () => {
await expect(service.process(null)).rejects.toThrow('Invalid input');
});
it('should handle network errors', async () => {
// Simulate network failure
mockHttp.simulateError('ECONNREFUSED');
await expect(service.fetchData()).rejects.toThrow('Connection failed');
});
it('should handle timeout', async () => {
mockHttp.simulateDelay(10000); // 10s delay
await expect(service.fetchData({ timeout: 1000 })).rejects.toThrow('Timeout');
});
});import { PerformanceProfiler } from '@synapse-framework/core';
describe('Performance', () => {
const profiler = new PerformanceProfiler({ id: 'test-profiler' });
it('should complete within time budget', async () => {
const mark = profiler.start('operation');
await service.heavyOperation();
const duration = profiler.end(mark);
expect(duration).toBeLessThan(1000); // 1 second
});
it('should handle load', async () => {
const promises = [];
// Simulate 100 concurrent requests
for (let i = 0; i < 100; i++) {
promises.push(service.process({ id: i }));
}
await expect(Promise.all(promises)).resolves.toBeDefined();
});
});import { Stage, PerformanceProfiler, SignalTracer, HealthMonitor } from '@synapse-framework/core';
import { myService } from './my-service'; // Placeholder for example service
describe('Service Performance', () => {
const stage = new Stage({ title: 'Performance Tests' });
const profiler = new PerformanceProfiler({ stage });
const tracer = new SignalTracer({ stage });
const healthMonitor = new HealthMonitor({ stage });
beforeAll(async () => {
stage.mount('service', myService);
await stage.start();
});
it('should profile execution', async () => {
profiler.startProfiling();
await myService.process({ data: 'test' });
const profile = profiler.getProfile();
expect(profile.totalTime).toBeLessThan(100);
expect(profile.callCount).toBe(1);
});
it('should trace signals', async () => {
tracer.startTracing();
await myService.process({ data: 'test' });
const trace = tracer.getTrace();
expect(trace.signals.length).toBeGreaterThan(0);
expect(trace.signals[0].type).toBe('excitatory');
});
it('should monitor health', async () => {
const health = healthMonitor.check(myService);
expect(health.healthy).toBe(true);
expect(health.errors).toBe(0);
});
});- Unit Tests: 80%+ line coverage
- Integration Tests: Critical paths covered
- E2E Tests: User workflows covered
# Run with coverage
npm run test:coverage
# View coverage report
open coverage/index.html# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install
run: npm ci
- name: Unit Tests
run: npm test
- name: E2E Tests
run: npm run test:e2e
- name: Upload Coverage
uses: codecov/codecov-action@v3- Architecture - Understand design decisions
- Examples - See complete examples
- API Reference - Detailed API docs