TDD Patterns Guide
**Phase:** 298 - Frontend-Backend Unification
**Last Updated:** 2026-04-12
---
Overview
This guide explains Test-Driven Development (TDD) patterns used in Phase 298 to discover bugs, prevent regressions, and ensure code quality.
What is TDD?
Test-Driven Development is a development process where:
- **Red:** Write a failing test first
- **Green:** Write minimal code to pass the test
- **Refactor:** Clean up the code while keeping tests green
Why TDD?
- **Catches Bugs Early:** Tests find bugs before production
- **Prevents Regressions:** Tests catch breaking changes
- **Improves Design:** Testable code is better designed
- **Documents Behavior:** Tests are living documentation
- **Enables Refactoring:** Tests make refactoring safe
---
Property-Based Testing
What is Property-Based Testing?
Property-based testing generates hundreds of random inputs to test invariants (properties that must always be true). Unlike example-based testing (which tests specific examples), property-based testing tests general properties.
When to Use
- Testing mathematical/logical operations
- Testing data transformations
- Testing serialization/deserialization
- Testing business rules with invariants
Python Example (Hypothesis)
# backend-saas/core/__tests__/property/api_properties.py
from hypothesis import given, strategies as st
from decimal import Decimal
@given(st.decimals(min_value=0, max_value=1000, places=2))
def test_acu_precision_preserved(acu_value: Decimal):
"""Test that ACU precision (2 decimal places) is preserved"""
# Serialize to JSON
serialized = json.dumps({"acu": float(acu_value)})
# Deserialize from JSON
deserialized = json.loads(serialized)
# Property: Precision should be preserved
assert abs(deserialized["acu"] - float(acu_value)) < 0.01
@given(st.lists(st.text(min_size=1, max_size=50)))
def test_agent_capabilities_preserved(capabilities: list[str]):
"""Test that agent capabilities list is preserved round-trip"""
# Create agent with capabilities
agent = {"capabilities": capabilities}
# Serialize to JSON
serialized = json.dumps(agent)
# Deserialize from JSON
deserialized = json.loads(serialized)
# Property: Capabilities should be preserved
assert deserialized["capabilities"] == capabilitiesTypeScript Example (fast-check)
// src/lib/api/__tests__/property-based/api-property-tests.test.ts
import fc from 'fast-check';
describe('Agent API Properties', () => {
it('should preserve agent maturity enum values', () => {
fc.assert(
fc.property(
fc.constantFrom<'student' | 'intern' | 'supervised' | 'autonomous'>(
'student',
'intern',
'supervised',
'autonomous'
),
(maturity) => {
// Property: Maturity value should be valid
expect(['student', 'intern', 'supervised', 'autonomous']).toContain(maturity);
}
),
{ numRuns: 1000 }
);
});
it('should preserve ACU precision (2 decimal places)', () => {
fc.assert(
fc.property(
fc.float({ min: 0, max: 1000, noNaN: true }),
(acu) => {
// Serialize to string with 2 decimal places
const serialized = acu.toFixed(2);
// Deserialize from string
const deserialized = parseFloat(serialized);
// Property: Precision should be preserved (within 0.01)
expect(Math.abs(deserialized - acu)).toBeLessThan(0.01);
}
),
{ numRuns: 1000 }
);
});
});Common Properties
- **Round-Trip:**
serialize(deserialize(x)) == x - **Idempotence:**
f(f(x)) == f(x) - **Commutativity:**
f(x, y) == f(y, x) - **Associativity:**
f(f(x, y), z) == f(x, f(y, z)) - **Identity:**
f(x, identity) == x - **Determinism:**
f(x) == f(x)(same input = same output)
---
Fuzz Testing
What is Fuzz Testing?
Fuzz testing generates random, malformed, or malicious inputs to test input validation and error handling.
When to Use
- Testing input validation
- Testing error handling
- Testing security boundaries
- Testing API robustness
Python Example
# backend-saas/api/__tests__/fuzz/api_fuzz_tests.py
import pytest
from hypothesis import given, strategies as st
@given(st.text(min_size=0, max_size=1000))
def test_agent_name_handles_unicode(name: str):
"""Test that agent name handles Unicode characters"""
# Create agent with Unicode name
response = client.post(
"/api/agents",
json={"name": name, "maturity": "student"}
)
# Should accept valid Unicode or reject with clear error
assert response.status_code in [200, 400, 422]
if response.status_code == 200:
# Name should be preserved
assert response.json()["name"] == name
@given(st.integers(min_value=-1000, max_value=1000))
def test_acu_value_handles_invalid_input(acu: int):
"""Test that ACU value handles invalid input"""
# Create usage with invalid ACU value
response = client.post(
"/api/billing/usage",
json={"acu": acu, "tokens": 100}
)
# Should reject negative values
if acu < 0:
assert response.status_code == 422
else:
assert response.status_code == 200TypeScript Example
// src/lib/api/__tests__/fuzzy/api-fuzz-tests.test.ts
import fc from 'fast-check';
describe('API Fuzz Tests', () => {
it('should handle malicious SQL injection attempts', () => {
fc.assert(
fc.property(
fc.stringOf(fc.constantFrom(...'\'";--\\/*'.split(''))),
async (maliciousInput) => {
// Try to create agent with malicious input
const response = await createAgent({
name: maliciousInput,
maturity: 'student'
});
// Should either reject or sanitize
if (response.ok) {
const agent = await response.json();
// Name should be sanitized or rejected
expect(agent.name).not.toContain('\'');
expect(agent.name).not.toContain('"');
expect(agent.name).not.toContain('--');
}
}
),
{ numRuns: 100 }
);
});
it('should handle extremely long strings', () => {
fc.assert(
fc.property(
fc.string({ maxLength: 10000 }),
async (longString) => {
// Try to create agent with extremely long name
const response = await createAgent({
name: longString,
maturity: 'student'
});
// Should reject or truncate
if (response.ok) {
const agent = await response.json();
expect(agent.name.length).toBeLessThanOrEqual(255);
} else {
expect(response.status).toBe(400);
}
}
),
{ numRuns: 50 }
);
});
});---
Concurrency Testing
What is Concurrency Testing?
Concurrency testing tests code under concurrent access to detect race conditions, deadlocks, and data corruption.
When to Use
- Testing shared state
- Testing database operations
- Testing quota/usage tracking
- Testing concurrent updates
Python Example
# backend-saas/core/__tests__/concurrency/api_concurrency_tests.py
import pytest
import threading
import time
def test_concurrent_quota_deductions():
"""Test that quota deductions are atomic (no double-spend)"""
tenant_id = "test-tenant"
initial_quota = 100
# Set initial quota
db.set_quota(tenant_id, initial_quota)
# Deduct quota concurrently
def deduct_quota():
for _ in range(10):
db.deduct_quota(tenant_id, 1)
threads = [threading.Thread(target=deduct_quota) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
# Verify final quota
final_quota = db.get_quota(tenant_id)
assert final_quota == initial_quota - 100 # 10 threads * 10 deductions
def test_concurrent_agent_updates():
"""Test that concurrent agent updates don't lose data"""
agent_id = create_agent()
# Update agent concurrently
def update_agent():
for i in range(10):
db.update_agent(agent_id, {"name": f"Agent-{i}"})
threads = [threading.Thread(target=update_agent) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
# Verify agent is not corrupted
agent = db.get_agent(agent_id)
assert agent.name is not None
assert len(agent.name) > 0TypeScript Example
// src/lib/api/__tests__/concurrency/api-concurrency-tests.test.ts
describe('Concurrency Tests', () => {
it('should handle concurrent quota checks', async () => {
const tenantId = 'test-tenant';
const initialQuota = 100;
// Set initial quota
await setQuota(tenantId, initialQuota);
// Check quota concurrently
const promises = Array.from({ length: 100 }, () =>
checkQuota(tenantId)
);
const results = await Promise.all(promises);
// All checks should see consistent data
const uniqueResults = new Set(results);
expect(uniqueResults.size).toBeLessThanOrEqual(10);
});
it('should handle concurrent agent updates', async () => {
const agentId = await createAgent({ name: 'Test Agent' });
// Update agent concurrently
const promises = Array.from({ length: 10 }, (_, i) =>
updateAgent(agentId, { name: `Agent-${i}` })
);
await Promise.all(promises);
// Verify agent is not corrupted
const agent = await getAgent(agentId);
expect(agent.name).toBeTruthy();
expect(agent.name.length).toBeGreaterThan(0);
});
});Race Condition Patterns
- **Test-Check-Test Race:**
# Good: Atomic operation
deduct_quota_if_available(tenant_id, 10)
```
- **Read-Modify-Write Race:**
# Good: Optimistic locking
agent = db.get_agent(agent_id)
agent.name = new_name
agent.version += 1
db.save_agent(agent, version=agent.version)
```
- **Deadlock:**
# Good: Consistent lock order
with lock1:
with lock2:
...
with lock1:
with lock2:
...
```
---
Contract Testing
What is Contract Testing?
Contract testing validates that frontend and backend APIs are aligned in terms of request/response structure, types, and validation rules.
When to Use
- Testing API boundaries
- Testing type mappings (TypeScript ↔ Python)
- Testing validation consistency
- Testing auth/tenant requirements
Example (from Phase 298)
# backend-saas/api/__tests__/contract/agent_routes_contract.py
def test_create_agent_contract():
"""Test POST /api/agents contract"""
request = {
"name": "Test Agent",
"maturity": "student",
"capabilities": ["task-execution", "web-browsing"]
}
response = client.post(
"/api/agents",
json=request,
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
# Verify response structure
assert "id" in data
assert isinstance(data["id"], str)
assert "name" in data
assert data["name"] == request["name"]
assert "maturity" in data
assert data["maturity"] == request["maturity"]
assert "capabilities" in data
assert isinstance(data["capabilities"], list)
assert data["capabilities"] == request["capabilities"]// src/lib/api/__tests__/contract-tests/agent-api-contracts.test.ts
describe('Agent API Contracts', () => {
it('should create agent with correct structure', async () => {
const request = {
name: 'Test Agent',
maturity: 'student' as const,
capabilities: ['task-execution', 'web-browsing']
};
const response = await createAgent(request);
// Verify response structure
expect(response).toHaveProperty('id');
expect(typeof response.id).toBe('string');
expect(response).toHaveProperty('name');
expect(response.name).toBe(request.name);
expect(response).toHaveProperty('maturity');
expect(response.maturity).toBe(request.maturity);
expect(response).toHaveProperty('capabilities');
expect(Array.isArray(response.capabilities)).toBe(true);
expect(response.capabilities).toEqual(request.capabilities);
});
});---
Integration Testing
What is Integration Testing?
Integration testing tests multiple components together to verify they work correctly as a system.
When to Use
- Testing end-to-end workflows
- Testing data flow across boundaries
- Testing state management
- Testing error handling
Example (E2E Workflow)
// src/lib/api/__tests__/integration/e2e-workflows.test.ts
describe('Agent Lifecycle E2E', () => {
it('should complete agent lifecycle workflow', async () => {
// Step 1: Create agent
const agent = await createAgent({
name: 'Test Agent',
maturity: 'student',
capabilities: ['task-execution']
});
expect(agent.id).toBeTruthy();
// Step 2: Execute agent
const execution = await executeAgent(agent.id, {
task: 'Test task'
});
expect(execution.status).toBe('completed');
// Step 3: Submit feedback
const feedback = await submitFeedback(execution.id, {
rating: 5,
comment: 'Great work'
});
expect(feedback.id).toBeTruthy();
// Step 4: Update agent
const updated = await updateAgent(agent.id, {
maturity: 'intern'
});
expect(updated.maturity).toBe('intern');
// Step 5: Delete agent
await deleteAgent(agent.id);
// Verify deletion
const deleted = await getAgent(agent.id);
expect(deleted).toBeNull();
});
});---
Testing Best Practices
1. Test Behavior, Not Implementation
// ❌ Bad: Tests implementation
it('should call the database', () => {
expect(db.query).toHaveBeenCalled();
});
// ✅ Good: Tests behavior
it('should return agents for tenant', async () => {
const agents = await getAgents(tenantId);
expect(agents).toHaveLength(5);
});2. Use Descriptive Test Names
// ❌ Bad: Vague test name
it('should work', () => { ... });
// ✅ Good: Descriptive test name
it('should return 400 when agent name is empty', () => { ... });3. Follow AAA Pattern (Arrange, Act, Assert)
it('should deduct quota correctly', async () => {
// Arrange
const tenantId = 'test-tenant';
await setQuota(tenantId, 100);
// Act
await deductQuota(tenantId, 10);
// Assert
const remaining = await getQuota(tenantId);
expect(remaining).toBe(90);
});4. Use Test Helpers
// test-helpers.ts
export async function createTestAgent(overrides?: Partial<Agent>) {
return await createAgent({
name: 'Test Agent',
maturity: 'student',
capabilities: [],
...overrides
});
}
// test.ts
it('should execute agent', async () => {
const agent = await createTestAgent();
const execution = await executeAgent(agent.id, { task: 'Test' });
expect(execution.status).toBe('completed');
});5. Mock External Dependencies
it('should call external API correctly', async () => {
// Mock fetch
const fetchMock = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({ result: 'success' })
});
global.fetch = fetchMock;
// Test
await callExternalAPI();
// Assert
expect(fetchMock).toHaveBeenCalledWith(
'https://api.example.com/endpoint',
expect.objectContaining({
method: 'POST'
})
);
});---
Additional Resources
Documentation
Test Files from Phase 298
src/lib/api/__tests__/property-based/api-property-tests.test.tssrc/lib/api/__tests__/fuzzy/api-fuzz-tests.test.tssrc/lib/api/__tests__/concurrency/api-concurrency-tests.test.tssrc/lib/api/__tests__/integration/e2e-workflows.test.tsbackend-saas/core/__tests__/property/api_properties.py
Tools
- **Python:** pytest, Hypothesis (property-based), pytest-asyncio
- **TypeScript:** Vitest, fast-check (property-based), jest-dom
---
**Last Updated:** 2026-04-12
**Phase:** 298 - Frontend-Backend Unification