# Epic Technical Specification: Topology Operations

Date: 2025-12-29
Author: Luciano
Epic ID: epic-3
Status: Draft

---

## Overview

Epic 3 delivers comprehensive RabbitMQ topology management capabilities through the MCP interface, enabling users to manage queues, exchanges, and bindings programmatically. This epic builds on the foundation established in Epic 1 (MCP Protocol + Semantic Discovery) and Epic 2 (Connection Management) to provide enterprise-grade infrastructure control with built-in safety validations that prevent accidental data loss and resource misconfiguration.

The topology operations exposed through the `call-id` tool map directly to RabbitMQ Management API HTTP endpoints, with parameter validation handled by Pydantic schemas and enhanced safety checks that block destructive operations (e.g., queue deletion with messages, exchange deletion with active bindings) unless explicitly forced. This provides zero context switching for DevOps engineers managing RabbitMQ infrastructure during incident response or routine maintenance.

## Objectives and Scope

**In Scope:**
- List operations: queues.list, exchanges.list, bindings.list with filtering and sorting
- Create operations: queues.create, exchanges.create, bindings.create with property validation
- Delete operations: queues.delete, exchanges.delete, bindings.delete with safety checks
- Queue-specific: queues.purge operation
- Vhost validation middleware: pre-flight checks for all topology operations
- Safety validations: block destructive operations unless --force flag provided
- Support for all exchange types: direct, topic, fanout, headers
- Exchange-to-queue and exchange-to-exchange bindings
- Topic wildcard routing keys: * (one word), # (zero or more words)

**Out of Scope (Future Epics):**
- Vhost management (create, delete, list vhosts) - separate administrative epic
- User/permission management - Epic 13 (Advanced Security & RBAC)
- Policy management (HA policies, federation, shovel) - Epic 10
- Configuration import/export - Epic 11
- Cluster management - Phase 2 feature
- Message browsing/inspection - Epic 4 (Message Publishing & Consumption)
- Dead letter exchange (DLX) configuration - Epic 10 (Advanced Retry & DLQ)

## System Architecture Alignment

**Architecture Components Referenced:**
- **HTTP Client Layer** (Story 2.3): Connection pooling, 30-second timeouts, async httpx client
- **Operation Executor** (Story 1.10): Parameter validation, HTTP request construction, response parsing
- **Pydantic Schema Validation** (Story 1.4): Queue/exchange/binding models with custom validators
- **Semantic Discovery** (Story 1.8): Operation IDs discoverable via natural language search
- **Structured Logging** (Epic 7): Audit trail for all create/delete operations with correlation IDs
- **Vhost Middleware**: New component that validates vhost existence before topology operations

**Design Constraints:**
- All operations derived from RabbitMQ Management API OpenAPI spec (v3.13.x)
- URL encoding required for vhost names: "/" becomes "%2F"
- System exchanges (amq.* prefix) and default exchange ("") are immutable
- Queue/exchange names limited to 255 characters: alphanumeric, hyphen, underscore, period
- Connection pool reuse from Epic 2 (5 connections default, blocking when full)

**Non-Functional Constraints:**
- List operations: <200ms for up to 1000 resources (NFR-Performance)
- Create/delete operations: <100ms (NFR-Performance)
- Pre-deletion safety checks add <50ms overhead
- Operations logged for audit trail compliance (NFR-Security)

## Detailed Design

### Services and Modules

| Service/Module | Responsibilities | Inputs | Outputs | Owner |
|----------------|------------------|--------|---------|-------|
| **QueueOperations** | List, create, delete, purge queues | vhost, queue name, properties | Queue details, statistics | `src/rabbitmq_mcp/operations/topology/queues.py` |
| **ExchangeOperations** | List, create, delete exchanges | vhost, exchange name, type, properties | Exchange details | `src/rabbitmq_mcp/operations/topology/exchanges.py` |
| **BindingOperations** | List, create, delete bindings | source, destination, routing_key, vhost | Binding details | `src/rabbitmq_mcp/operations/topology/bindings.py` |
| **VhostValidator** | Pre-flight vhost existence validation | vhost name | validation result, error | `src/rabbitmq_mcp/middleware/vhost_validator.py` |
| **SafetyValidator** | Pre-deletion safety checks | resource type, resource details | allow/block decision | `src/rabbitmq_mcp/middleware/safety_validator.py` |
| **TopologyCache** | Cache vhost list and topology metadata | N/A | cached data with TTL | `src/rabbitmq_mcp/cache/topology_cache.py` |

### Data Models and Contracts

**Queue Model (Pydantic):**
```python
class QueueProperties(BaseModel):
    name: str  # Max 255 chars, alphanumeric + hyphen + underscore + period
    vhost: str = "/"
    durable: bool = True
    auto_delete: bool = False
    arguments: Optional[Dict[str, Any]] = None  # x-message-ttl, x-max-length, x-dead-letter-exchange
    
class QueueInfo(QueueProperties):
    messages: int
    messages_ready: int
    messages_unacknowledged: int
    consumers: int
    memory: int  # bytes
    state: Literal["running", "idle", "flow"]
    message_stats: Optional[MessageStats] = None
```

**Exchange Model (Pydantic):**
```python
class ExchangeProperties(BaseModel):
    name: str  # Max 255 chars, cannot start with "amq." unless system exchange
    vhost: str = "/"
    type: Literal["direct", "topic", "fanout", "headers"]
    durable: bool = True
    auto_delete: bool = False
    internal: bool = False  # Cannot publish directly if True
    arguments: Optional[Dict[str, Any]] = None  # alternate-exchange
    
class ExchangeInfo(ExchangeProperties):
    message_stats: Optional[MessageStats] = None
```

**Binding Model (Pydantic):**
```python
class BindingProperties(BaseModel):
    source: str  # Exchange name
    destination: str  # Queue or exchange name
    destination_type: Literal["queue", "exchange"]
    routing_key: str = ""  # Empty for fanout, wildcards for topic
    arguments: Optional[Dict[str, Any]] = None  # Headers exchange routing
    vhost: str = "/"
    properties_key: Optional[str] = None  # Unique identifier (server-generated)
```

**Validation Rules (Custom Validators):**
- Queue/exchange name: regex `^[a-zA-Z0-9._-]{1,255}$`
- System exchange protection: raise error if name starts with "amq." and not system exchange
- Default exchange protection: raise error if name is empty string
- Routing key validation: fanout ignores, direct no wildcards, topic allows `*` and `#`
- x-message-ttl: must be positive integer (milliseconds)
- x-max-length: must be positive integer (max messages in queue)

### APIs and Interfaces

**RabbitMQ Management API Endpoints:**

| Operation | HTTP Method | Endpoint | Request Body | Response |
|-----------|-------------|----------|--------------|----------|
| queues.list | GET | `/api/queues/{vhost}` | None | List[QueueInfo] |
| queues.create | PUT | `/api/queues/{vhost}/{name}` | QueueProperties JSON | 201 Created |
| queues.delete | DELETE | `/api/queues/{vhost}/{name}` | Query: ?if-empty=true, ?if-unused=true | 204 No Content |
| queues.purge | DELETE | `/api/queues/{vhost}/{name}/contents` | None | `{"message_count": N}` |
| exchanges.list | GET | `/api/exchanges/{vhost}` | None | List[ExchangeInfo] |
| exchanges.create | PUT | `/api/exchanges/{vhost}/{name}` | ExchangeProperties JSON | 201 Created |
| exchanges.delete | DELETE | `/api/exchanges/{vhost}/{name}` | Query: ?if-unused=true | 204 No Content |
| bindings.list | GET | `/api/bindings/{vhost}` | None | List[BindingProperties] |
| bindings.create | POST | `/api/bindings/{vhost}/e/{source}/q/{dest}` | `{"routing_key": "...", "arguments": {...}}` | 201 Created |
| bindings.delete | DELETE | `/api/bindings/{vhost}/e/{source}/q/{dest}/{props_key}` | None | 204 No Content |
| vhosts.list | GET | `/api/vhosts` | None | List[VhostInfo] |

**MCP Tool Signatures:**
```python
# Example: call-id tool with operation_id="queues.list"
{
  "operation_id": "queues.list",
  "parameters": {
    "vhost": "/",
    "filter": {"name_pattern": "order.*", "state": "running"},
    "sort_by": "messages",
    "page_size": 100
  }
}

# Example: call-id tool with operation_id="queues.delete"
{
  "operation_id": "queues.delete",
  "parameters": {
    "vhost": "/",
    "name": "orders-queue",
    "force": true  # Bypass safety check
  }
}
```

**Error Responses:**
- 404 Not Found: Resource (queue/exchange/vhost) does not exist
- 400 Bad Request: Invalid parameters (validation failed)
- 409 Conflict: Resource already exists with different properties
- 403 Forbidden: Attempting to delete system exchange or default exchange
- 422 Unprocessable Entity: Safety check blocked operation (use --force)

### Workflows and Sequencing

**Workflow 1: List Queues with Filtering**
1. Client: `search-ids("list queues")` → Returns `["queues.list", "queues.list_by_vhost"]`
2. Client: `get-id("queues.list")` → Returns schema with filter parameters
3. Client: `call-id("queues.list", {"vhost": "/", "filter": {"state": "running"}})`
4. Server: VhostValidator checks vhost "/" exists (cache hit, TTL 5min)
5. Server: HTTP GET `/api/queues/%2F` with connection pool
6. Server: Filter results where `state == "running"` (client-side filtering)
7. Server: Return validated List[QueueInfo] to client
8. Latency: <200ms for 1000 queues

**Workflow 2: Create Queue with Safety Checks**
1. Client: `call-id("queues.create", {"vhost": "/", "name": "orders", "durable": true})`
2. Server: VhostValidator checks vhost "/" exists
3. Server: Pydantic validates queue name matches regex `^[a-zA-Z0-9._-]{1,255}$`
4. Server: HTTP PUT `/api/queues/%2F/orders` with JSON body
5. RabbitMQ: Returns 201 Created (idempotent if same properties)
6. Server: Log audit trail: `{action: "queue.create", resource: "orders", vhost: "/", correlation_id: "..."}`
7. Latency: <100ms

**Workflow 3: Delete Queue with Safety Validation**
1. Client: `call-id("queues.delete", {"vhost": "/", "name": "orders"})`  # No force flag
2. Server: VhostValidator checks vhost "/" exists
3. Server: SafetyValidator HTTP GET `/api/queues/%2F/orders` to check stats
4. RabbitMQ: Returns `{"messages": 100, "consumers": 2}`
5. Server: Safety check FAILS (messages > 0 OR consumers > 0)
6. Server: Return error: `{"error": "Queue 'orders' has 100 messages and 2 consumers. Use force=true to confirm deletion.", "code": 422}`
7. Client: Retry with `call-id("queues.delete", {"vhost": "/", "name": "orders", "force": true})`
8. Server: SafetyValidator bypassed (force=true)
9. Server: HTTP DELETE `/api/queues/%2F/orders`
10. Server: Log audit trail: `{action: "queue.delete", resource: "orders", vhost: "/", messages: 100, consumers: 2, forced: true}`
11. Latency: <100ms (safety check adds <50ms)

**Workflow 4: Create Binding with Validation**
1. Client: `call-id("bindings.create", {"source": "events", "destination": "orders-queue", "routing_key": "order.*", "vhost": "/"})`
2. Server: VhostValidator checks vhost "/" exists
3. Server: Validate source exchange exists: HTTP GET `/api/exchanges/%2F/events`
4. Server: Validate destination queue exists: HTTP GET `/api/queues/%2F/orders-queue`
5. Server: Validate routing key for exchange type (topic allows wildcards)
6. Server: HTTP POST `/api/bindings/%2F/e/events/q/orders-queue` with routing_key
7. RabbitMQ: Returns 201 Created (idempotent if same binding)
8. Server: Log audit trail: `{action: "binding.create", source: "events", dest: "orders-queue", routing_key: "order.*"}`
9. Latency: <150ms (includes 2 pre-validation checks)

## Non-Functional Requirements

### Performance

**Latency Targets (from NFR-Performance):**
- List operations (queues, exchanges, bindings): <200ms p95 for up to 1000 resources
- Create operations: <100ms p95
- Delete operations: <100ms p95 (excluding safety check overhead)
- Purge operations: <500ms p95 for up to 10,000 messages
- Vhost validation cache hit: <5ms (TTL: 5 minutes)
- Safety check overhead: <50ms (single GET request for queue/exchange stats)

**Throughput Targets:**
- Concurrent list operations: 100+ requests/second
- Topology changes (create/delete): 50+ operations/second
- Connection pool utilization: 5 concurrent HTTP connections (Epic 2 baseline)

**Optimization Strategies:**
- **Caching**: Vhost list cached in memory (TTL 5min), cache invalidated on vhost create/delete
- **Connection Pooling**: Reuse HTTP client from Epic 2 (httpx with 5 connections)
- **Client-Side Filtering**: List operations return full dataset, filtering/sorting done in-memory to reduce API calls
- **Batch Validation**: Single safety check per delete operation (not per-queue in batch)

**Performance Testing:**
- Measure list operation latency with 100, 1000, 5000 queues
- Measure create/delete operation latency under concurrent load (10, 50, 100 concurrent requests)
- Measure safety check overhead (should add <50ms to delete operations)

### Security

**Authentication (from NFR-Security):**
- All HTTP requests use credentials from Epic 2 configuration (username/password)
- TLS/SSL support for Management API connections (--insecure flag for self-signed certs)
- No credential storage in logs (automatic sanitization from Epic 7)

**Authorization & Access Control:**
- RabbitMQ Management API enforces user permissions (admin, management, monitoring)
- Operations respect RabbitMQ user tags: queues.create requires 'management' tag minimum
- System exchange protection: cannot delete amq.* or default exchange (hardcoded validation)

**Audit Trail (FR-017, NFR-Security):**
- All create/delete operations logged with correlation ID
- Audit log format: `{timestamp, correlation_id, action, resource_type, resource_name, vhost, user, forced, result}`
- Example: `{action: "queue.delete", resource: "orders", vhost: "/", messages: 100, consumers: 2, forced: true, result: "success"}`
- Logs stored in `./logs/rabbitmq-mcp-{date}.log` with 30-day retention

**Data Protection:**
- Queue/exchange arguments may contain sensitive data (e.g., DLX names) - not sanitized (business data)
- Connection strings sanitized in logs: `http://user:password@host` → `http://[REDACTED]@host`

**Safety Validations (FR-023):**
- Queue deletion blocked if messages > 0 OR consumers > 0 (unless force=true)
- Exchange deletion blocked if active bindings exist (unless force=true)
- Vhost validation prevents operations on non-existent vhosts (fail-fast)

### Reliability/Availability

**Error Handling:**
- Network failures: HTTP client timeout 30 seconds (Epic 2 baseline), fail-fast with clear error
- 404 Not Found: Return structured error: `"Queue 'orders' not found in vhost '/'"`
- 409 Conflict: Return structured error: `"Queue 'orders' already exists with different configuration"`
- 422 Validation Blocked: Return structured error: `"Queue 'orders' has 100 messages. Use force=true."`

**Idempotency:**
- Create operations: Idempotent if same properties (RabbitMQ returns 201, not 409)
- Delete operations: Idempotent (deleting non-existent resource returns 404, not error)
- Binding create: Idempotent if same source/destination/routing_key

**Retry Strategy:**
- No automatic retries for topology operations (user-driven retries via MCP client)
- Connection pool handles transient failures (Epic 2 auto-reconnection)

**Degraded Mode:**
- If RabbitMQ Management API unreachable, all topology operations fail immediately (no fallback)
- Cache serves stale vhost list if API unreachable (TTL expired) - prevent cascading failures

**Availability:**
- Server stateless design enables horizontal scaling (multiple MCP server instances)
- No shared state between operations (except in-memory cache per instance)

### Observability

**Structured Logging (FR-014, Epic 7):**
- All topology operations logged with: `{correlation_id, operation_id, vhost, resource_name, parameters, result, latency_ms}`
- Safety check decisions logged: `{action: "safety_check", resource: "orders", messages: 100, consumers: 2, decision: "blocked"}`
- Cache events logged: `{action: "cache_hit", cache_type: "vhost_list", ttl_remaining_sec: 180}`

**OpenTelemetry Traces (FR-019):**
- Span per topology operation: `call-id → validate_vhost → safety_check → http_request → response_validation`
- Trace propagation through correlation IDs
- Span attributes: `{operation_id, vhost, resource_name, forced, result_code, latency_ms}`

**Metrics (FR-019):**
- Counter: `topology_operations_total{operation_id, vhost, result}` (success/failure)
- Histogram: `topology_operation_latency_ms{operation_id}` (p50, p95, p99)
- Gauge: `vhost_cache_entries` (number of cached vhosts)
- Counter: `safety_check_blocked_total{resource_type}` (queues, exchanges)

**Health Checks:**
- Vhost list endpoint: GET `/api/vhosts` used as health check proxy (if fails, topology operations unavailable)
- Cache staleness indicator: log warning if cache TTL expired and refresh fails

**Debugging Support:**
- Correlation IDs enable full request tracing across logs, traces, metrics
- DEBUG log level includes raw HTTP requests/responses (sanitized credentials)
- Error responses include request details for troubleshooting

## Dependencies and Integrations

**Core Dependencies (from pyproject.toml):**
- `httpx>=0.27`: Async HTTP client with connection pooling (Epic 2 baseline)
- `pydantic>=2.0`: Schema validation for queue/exchange/binding models (Epic 1 baseline)
- `pydantic-settings>=2.0`: Configuration management (Epic 2 baseline)
- `structlog>=24.1`: Structured logging for audit trail (Epic 7 baseline)
- `opentelemetry-sdk>=1.22`: Distributed tracing (Epic 7 baseline)
- `mcp>=1.0.0`: MCP protocol implementation (Epic 1 baseline)

**RabbitMQ Management API Integration:**
- Version compatibility: 3.11.x, 3.12.x, 3.13.x (FR-021)
- Management API plugin must be enabled (standard in most installations)
- Default port: 15672 (HTTP), 15671 (HTTPS)
- Authentication: Basic Auth (username/password from Epic 2 config)

**Internal Component Dependencies:**
- **Story 2.3 (HTTP Client)**: Provides `HTTPClient` class with connection pooling
- **Story 1.4 (Pydantic Schemas)**: Provides base validation models
- **Story 1.8 (Semantic Search)**: Enables operation discovery (`search-ids("list queues")`)
- **Story 1.10 (Operation Executor)**: Orchestrates validation → HTTP → response parsing
- **Epic 7 (Structured Logging)**: Provides audit trail and correlation ID tracking

**New Component Dependencies (Epic 3 Introduces):**
- **VhostValidator**: Middleware that caches vhost list (TTL 5min) and validates before operations
- **SafetyValidator**: Middleware that performs pre-deletion checks (messages, consumers, bindings)
- **TopologyCache**: In-memory cache for vhost list with TTL management

**External Integrations:**
- RabbitMQ Management API HTTP REST endpoints (see API Contracts section)
- OpenTelemetry OTLP exporter for traces/metrics (optional, configured via env vars)
- Structured logs compatible with ELK, Splunk, CloudWatch (JSON format)

**Version Constraints:**
- Python 3.12+ (async/await, type hints)
- RabbitMQ Management API plugin enabled (check via GET `/api/overview`)
- Network access to RabbitMQ Management API port (default 15672)

## Acceptance Criteria (Authoritative)

**AC-1: List Queues Operation (Story 3.1)**
- GIVEN connected to RabbitMQ with queues in vhost "/"
- WHEN user executes `search-ids("list queues")` → `get-id("queues.list")` → `call-id("queues.list", {"vhost": "/"})`
- THEN server returns list of all queues with: name, vhost, durable, auto_delete, arguments, messages, messages_ready, messages_unacknowledged, consumers, memory, state
- AND operation completes in <200ms for up to 1000 queues
- AND empty vhost returns empty list [] (not error)
- AND non-existent vhost returns error: "Virtual host '/invalid' does not exist"

**AC-2: Create Queue Operation (Story 3.2)**
- GIVEN valid queue configuration
- WHEN user executes `call-id("queues.create", {"vhost": "/", "name": "orders", "durable": true})`
- THEN queue is created on RabbitMQ server with specified properties
- AND duplicate queue with same properties is idempotent (returns success, not error)
- AND duplicate queue with different properties returns error: "Queue 'orders' already exists with different configuration"
- AND invalid queue name (e.g., "order$queue") returns validation error before HTTP call
- AND operation completes in <100ms

**AC-3: Delete Queue with Safety Validation (Story 3.3)**
- GIVEN queue "orders" with messages=100 and consumers=2
- WHEN user attempts `call-id("queues.delete", {"vhost": "/", "name": "orders"})` without force flag
- THEN operation is blocked with error: "Queue 'orders' has 100 messages and 2 consumers. Use force=true to confirm deletion."
- AND user retries with `force=true`, deletion succeeds
- AND empty queue (messages=0, consumers=0) deletes without requiring force
- AND deletion is logged in audit trail with correlation ID

**AC-4: Purge Queue Operation (Story 3.4)**
- GIVEN queue "orders" with messages=500
- WHEN user executes `call-id("queues.purge", {"vhost": "/", "name": "orders"})`
- THEN all messages are removed, queue remains with same configuration
- AND operation returns: `{"status": "success", "purged_message_count": 500}`
- AND operation completes in <500ms for up to 10,000 messages

**AC-5: List Exchanges Operation (Story 3.5)**
- GIVEN connected to RabbitMQ with exchanges in vhost "/"
- WHEN user executes `call-id("exchanges.list", {"vhost": "/"})`
- THEN server returns list of all exchanges including system exchanges (amq.*, default "")
- AND each exchange includes: name, vhost, type (direct/topic/fanout/headers), durable, auto_delete, internal, arguments
- AND operation completes in <200ms for up to 1000 exchanges

**AC-6: Create Exchange Operation (Story 3.6)**
- GIVEN valid exchange configuration
- WHEN user executes `call-id("exchanges.create", {"vhost": "/", "name": "events", "type": "topic", "durable": true})`
- THEN exchange is created with specified type and properties
- AND attempting to create exchange with "amq." prefix returns error: "Cannot create exchanges with 'amq.' prefix (reserved)"
- AND duplicate exchange with different type returns error: "Exchange 'events' already exists with type 'direct', cannot change to 'topic'"

**AC-7: Delete Exchange with Protection (Story 3.7)**
- GIVEN exchange "events" with 5 active bindings
- WHEN user attempts `call-id("exchanges.delete", {"vhost": "/", "name": "events"})` without force flag
- THEN operation is blocked with error: "Exchange 'events' has 5 active bindings. Use force=true to confirm deletion."
- AND attempting to delete system exchange (e.g., "amq.topic") returns error: "Cannot delete system exchange"
- AND attempting to delete default exchange ("") returns error: "Cannot delete default exchange"

**AC-8: List Bindings Operation (Story 3.8)**
- GIVEN bindings configured in vhost "/"
- WHEN user executes `call-id("bindings.list", {"vhost": "/"})`
- THEN server returns list of all bindings with: source, destination, destination_type (queue/exchange), routing_key, arguments, vhost
- AND operation completes in <200ms for up to 10,000 bindings

**AC-9: Create Binding Operation (Story 3.9)**
- GIVEN exchange "events" (topic) and queue "orders-queue" exist
- WHEN user executes `call-id("bindings.create", {"source": "events", "destination": "orders-queue", "routing_key": "order.*", "vhost": "/"})`
- THEN binding is created with routing key validation (topic supports wildcards)
- AND binding to non-existent exchange returns error: "Source exchange 'events' not found"
- AND binding to non-existent queue returns error: "Destination queue 'orders-queue' not found"

**AC-10: Delete Binding Operation (Story 3.10)**
- GIVEN binding exists: source="events", destination="orders-queue", routing_key="order.*"
- WHEN user executes `call-id("bindings.delete", {"source": "events", "destination": "orders-queue", "routing_key": "order.*", "vhost": "/"})`
- THEN specific binding is deleted, other bindings between same source/destination remain intact
- AND operation completes in <100ms

**AC-11: Vhost Validation Middleware (Story 3.11)**
- GIVEN any topology operation
- WHEN operation is executed with vhost="/invalid"
- THEN validation occurs before HTTP API call
- AND non-existent vhost returns error: "Virtual host '/invalid' does not exist. Available vhosts: [/, /staging, /prod]"
- AND validation check completes in <50ms (cache hit)
- AND vhost list is cached with TTL 5 minutes

## Traceability Mapping

| AC # | PRD FR | Spec Section | Component/API | Test Strategy |
|------|--------|--------------|---------------|---------------|
| AC-1 | FR-008 | APIs: queues.list | `GET /api/queues/{vhost}` | Integration: Create 3 queues, list, verify statistics match |
| AC-2 | FR-008 | APIs: queues.create | `PUT /api/queues/{vhost}/{name}` | Unit: Name validation; Integration: Create with x-message-ttl, verify properties |
| AC-3 | FR-023 | Detailed Design: SafetyValidator | Pre-delete safety check | Integration: Create queue with messages, attempt delete, verify blocked; retry with force |
| AC-4 | FR-008 | APIs: queues.purge | `DELETE /api/queues/{vhost}/{name}/contents` | Integration: Publish 500 msgs, purge, verify count returned and queue empty |
| AC-5 | FR-009 | APIs: exchanges.list | `GET /api/exchanges/{vhost}` | Integration: Verify system exchanges present (amq.*, default "") |
| AC-6 | FR-009 | APIs: exchanges.create | `PUT /api/exchanges/{vhost}/{name}` | Unit: Name validation (reject "amq." prefix); Integration: Create topic exchange |
| AC-7 | FR-023 | Detailed Design: SafetyValidator | Pre-delete binding check | Integration: Create exchange with bindings, attempt delete, verify blocked |
| AC-8 | FR-010 | APIs: bindings.list | `GET /api/bindings/{vhost}` | Integration: Create 2 bindings, list, verify routing_key and destination_type |
| AC-9 | FR-010 | APIs: bindings.create | `POST /api/bindings/{vhost}/e/{src}/q/{dst}` | Unit: Routing key wildcard validation; Integration: Create topic binding with "order.*" |
| AC-10 | FR-010 | APIs: bindings.delete | `DELETE /api/bindings/{vhost}/.../{props_key}` | Integration: Create 2 bindings same src/dst, delete 1, verify other remains |
| AC-11 | FR-023 | Detailed Design: VhostValidator | Vhost validation middleware | Unit: Mock vhost cache; Integration: Attempt operation on "/invalid", verify error |

**Test Coverage Requirements:**
- Unit tests: Pydantic validators, cache logic, safety check logic (>80% coverage)
- Integration tests: All 11 stories with real RabbitMQ (testcontainers)
- Performance tests: List operations with 1000 queues/exchanges, measure latency
- Contract tests: Verify MCP protocol compliance for all operation_ids
- Negative tests: Invalid parameters, non-existent resources, blocked safety checks

## Risks, Assumptions, Open Questions

**Risks:**

**R1 - Safety Check Performance Overhead**: Pre-deletion safety checks add latency (GET request before DELETE)
- **Mitigation**: Cache queue/exchange stats for 30 seconds, accept <50ms overhead as acceptable tradeoff for safety
- **Severity**: Low - Acceptable performance impact for critical safety feature

**R2 - RabbitMQ Management API Version Differences**: Different RabbitMQ versions may have schema differences
- **Mitigation**: Lock to single OpenAPI version per deployment (FR-021), test against 3.11.x, 3.12.x, 3.13.x
- **Severity**: Medium - Requires versioned schemas and testing matrix

**R3 - Large Topology Scaling**: List operations may be slow with 10,000+ queues/exchanges
- **Mitigation**: Document recommended pagination strategy, test with 5000 resources as upper bound
- **Severity**: Low - Enterprise deployments typically <1000 queues per vhost

**R4 - Race Conditions on Delete**: Queue deleted between safety check and actual delete
- **Mitigation**: Use RabbitMQ server-side query parameters (`?if-empty=true`) as fallback, log race condition events
- **Severity**: Low - Rare occurrence, graceful degradation

**Assumptions:**

**A1**: RabbitMQ Management API plugin is enabled (required for HTTP operations)
- **Validation**: Check via GET `/api/overview` at server startup, fail-fast if unavailable

**A2**: Vhost list is relatively static (changes infrequent enough for 5-minute cache TTL)
- **Validation**: Monitor cache hit rate, adjust TTL if needed

**A3**: Client-side filtering is acceptable for list operations (no server-side filtering)
- **Validation**: RabbitMQ Management API supports query parameters, but simpler to filter in-memory for MVP

**A4**: Force flag is explicit boolean (not implicit from context)
- **Validation**: Require `force=true` parameter, reject `force=false` or missing as "no force"

**Open Questions:**

**Q1**: Should safety checks be configurable (disabled globally or per-operation)?
- **Decision Needed**: Epic 3 implements non-configurable safety (always on), Phase 2 may add config flag
- **Impact**: Low - Current implementation is safer default

**Q2**: Should vhost validation be opt-in or mandatory?
- **Decision**: Mandatory for all operations (fail-fast pattern)
- **Rationale**: Prevents obscure 404 errors, provides helpful error messages

**Q3**: Should bindings support batch create/delete operations?
- **Decision**: Deferred to Phase 2 (Epic 11: Configuration Import/Export)
- **Rationale**: Single binding operations sufficient for MVP, batch operations require transaction support

**Q4**: Should exchange deletion cascade to bindings?
- **Decision**: No cascade in Epic 3, require explicit binding deletion first
- **Rationale**: Safer default, explicit control over topology changes

## Test Strategy Summary

**Test Pyramid:**
- **Unit Tests (60%)**: Pydantic validators, cache logic, safety check logic, routing key validation
- **Integration Tests (30%)**: All 11 stories with real RabbitMQ (testcontainers), end-to-end workflows
- **Performance Tests (5%)**: List operations with 1000+ resources, measure p95 latency
- **Contract Tests (5%)**: MCP protocol compliance, JSON-RPC 2.0 validation

**Test Environment:**
- **Unit Tests**: Mock HTTP client, in-memory cache, isolated Pydantic validation
- **Integration Tests**: RabbitMQ Docker container (testcontainers), per-test vhost isolation, cleanup fixtures
- **Performance Tests**: Dedicated RabbitMQ instance, pre-populated with 1000 queues/exchanges

**Test Data Strategy:**
- **Fixtures**: Queue/exchange/binding factories with random names (avoid conflicts)
- **Teardown**: Delete all test resources after each test (queue, exchange, binding cleanup)
- **Vhost Isolation**: Each integration test uses unique vhost (e.g., `/test-{uuid}`)

**Key Test Scenarios:**

**Positive Tests:**
- List operations: Verify all fields present, statistics accurate
- Create operations: Verify idempotency, properties set correctly
- Delete operations: Verify resource removed, audit log entry created
- Binding creation: Verify routing key patterns work (topic wildcards)

**Negative Tests:**
- Invalid parameters: Queue name with special chars, exchange type "invalid"
- Non-existent resources: Delete queue that doesn't exist, bind to non-existent exchange
- Safety checks blocked: Delete queue with messages, delete exchange with bindings
- Vhost validation: Attempt operation on non-existent vhost

**Edge Cases:**
- Empty vhost: List operations return empty list (not error)
- Duplicate create: Same properties returns success (idempotent)
- Race conditions: Delete queue between safety check and actual delete
- System exchange protection: Cannot delete amq.* or default exchange

**Performance Tests:**
- List 1000 queues: Measure latency (target <200ms p95)
- Concurrent creates: 50 queues created concurrently (target <5 seconds total)
- Safety check overhead: Measure delete latency with/without safety check (target <50ms overhead)

**Coverage Target:**
- Overall: >80% line coverage
- Critical paths (safety checks, validators): 100% branch coverage
- Integration tests: 100% AC coverage (all 11 stories)
