ATOM Documentation

← Back to App

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:

  1. **Red:** Write a failing test first
  2. **Green:** Write minimal code to pass the test
  3. **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"] == capabilities

TypeScript 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

  1. **Round-Trip:** serialize(deserialize(x)) == x
  2. **Idempotence:** f(f(x)) == f(x)
  3. **Commutativity:** f(x, y) == f(y, x)
  4. **Associativity:** f(f(x, y), z) == f(x, f(y, z))
  5. **Identity:** f(x, identity) == x
  6. **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 == 200

TypeScript 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) > 0

TypeScript 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

  1. **Test-Check-Test Race:**

# Good: Atomic operation

deduct_quota_if_available(tenant_id, 10)

```

  1. **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)

```

  1. **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.ts
  • src/lib/api/__tests__/fuzzy/api-fuzz-tests.test.ts
  • src/lib/api/__tests__/concurrency/api-concurrency-tests.test.ts
  • src/lib/api/__tests__/integration/e2e-workflows.test.ts
  • backend-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