Files
dd0c/products/04-lightweight-idp/test-architecture/test-architecture.md

624 lines
27 KiB
Markdown
Raw Normal View History

# dd0c/portal — Test Architecture & TDD Strategy
**Product:** Lightweight Internal Developer Portal
**Phase:** 6 — Architecture Design
**Date:** 2026-02-28
**Status:** Draft
---
## 1. Testing Philosophy & TDD Workflow
### Core Principle
dd0c/portal's most critical logic — ownership inference, discovery reconciliation, and confidence scoring — is pure algorithmic code with well-defined inputs and outputs. This is ideal TDD territory. The test suite is the specification.
The product's >80% discovery accuracy target is not a QA metric — it's a product promise. Tests enforce it continuously.
### Red-Green-Refactor Adapted to This Product
```
RED → Write a failing test that encodes a discovery heuristic or ownership rule
GREEN → Write the minimum code to pass it (no clever abstractions yet)
REFACTOR → Clean up once the rule is proven correct against real-world fixtures
```
**Adapted cycle for discovery heuristics:**
1. Capture a real-world failure case (e.g., "Lambda functions named `payment-*` were not grouped into a service")
2. Write a unit test encoding the expected grouping behavior using a fixture of that Lambda response
3. Fix the heuristic
4. Add the fixture to the regression suite permanently
This means every production accuracy bug becomes a permanent test. The test suite grows as a living record of every edge case the discovery engine has encountered.
### When to Write Tests First vs. Integration Tests Lead
| Scenario | Approach | Rationale |
|----------|----------|-----------|
| Ownership scoring algorithm | Unit-first TDD | Pure function, deterministic, no I/O |
| Discovery heuristics (CFN → service mapping) | Unit-first TDD | Deterministic logic over fixture data |
| GitHub GraphQL query construction | Unit-first TDD | Query builder logic is pure |
| AWS API pagination handling | Integration-first | Behavior depends on real API shape |
| Meilisearch index sync | Integration-first | Depends on Meilisearch document model |
| DynamoDB schema migrations | Integration-first | Requires real DynamoDB Local behavior |
| WebSocket progress events | E2E-first | Requires full pipeline to be meaningful |
| Stripe webhook handling | Integration-first | Depends on Stripe event payload shape |
### Test Naming Conventions
All tests follow the pattern: `[unit under test]_[scenario]_[expected outcome]`
**TypeScript/Node.js (Jest):**
```typescript
describe('OwnershipInferenceEngine', () => {
describe('scoreOwnership', () => {
it('returns_primary_owner_when_codeowners_present_with_high_confidence', () => {})
it('marks_service_unowned_when_top_score_below_threshold', () => {})
it('marks_service_ambiguous_when_top_two_scores_within_tolerance', () => {})
})
})
```
**Python (pytest):**
```python
class TestOwnershipScorer:
def test_codeowners_signal_weighted_highest_among_all_signals(self): ...
def test_git_blame_frequency_used_when_codeowners_absent(self): ...
def test_confidence_below_threshold_flags_service_as_unowned(self): ...
```
**File naming:**
- Unit tests: `*.test.ts` / `test_*.py` co-located with source
- Integration tests: `*.integration.test.ts` / `test_*_integration.py` in `tests/integration/`
- E2E tests: `tests/e2e/*.spec.ts` (Playwright)
---
## 2. Test Pyramid
### Recommended Ratio: 70 / 20 / 10
```
┌─────────────┐
│ E2E / Smoke│ 10% (~30 tests)
│ (Playwright)│ Critical user journeys only
├─────────────┤
│ Integration │ 20% (~80 tests)
│ (real deps) │ Service boundaries, API contracts
├─────────────┤
│ Unit │ 70% (~280 tests)
│ (pure logic)│ All heuristics, scoring, parsing
└─────────────┘
```
### Unit Test Targets (per component)
| Component | Language | Test Framework | Target Coverage |
|-----------|----------|---------------|----------------|
| AWS Scanner (heuristics) | Python | pytest | 90% |
| GitHub Scanner (parsers) | Node.js | Jest | 90% |
| Reconciliation Engine | Node.js | Jest | 85% |
| Ownership Inference | Python | pytest | 95% |
| Portal API (route handlers) | Node.js | Jest + Supertest | 80% |
| Search proxy + cache logic | Node.js | Jest | 85% |
| Slack Bot command handlers | Node.js | Jest | 80% |
| Feature flag evaluation | Node.js/Python | Jest/pytest | 95% |
| Governance policy engine | Node.js | Jest | 95% |
| Schema migration validators | Node.js | Jest | 100% |
### Integration Test Boundaries
| Boundary | What to Test | Tool |
|----------|-------------|------|
| Discovery → GitHub API | GraphQL query shape, pagination, rate limit handling | MSW (mock service worker) or nock |
| Discovery → AWS APIs | boto3 call sequences, pagination, error handling | moto (AWS mock library) |
| Reconciler → PostgreSQL | Upsert logic, conflict resolution, RLS enforcement | Testcontainers (PostgreSQL) |
| Inference → PostgreSQL | Ownership write, confidence update, correction propagation | Testcontainers (PostgreSQL) |
| API → Meilisearch | Index sync, search query construction, tenant filter injection | Meilisearch test instance (Docker) |
| API → Redis | Cache set/get/invalidation, TTL behavior | ioredis-mock or Testcontainers (Redis) |
| Slack Bot → Portal API | Command → search → format response | Supertest against local API |
| Stripe webhook → API | Subscription activation, plan change, cancellation | Stripe CLI webhook forwarding |
### E2E / Smoke Test Scenarios
1. Full onboarding: GitHub OAuth → AWS connection → discovery trigger → catalog populated
2. Cmd+K search returns results in <200ms after discovery
3. Ownership correction propagates to similar services
4. Slack `/dd0c who owns` returns correct owner
5. Discovery accuracy: synthetic org with known ground truth scores >80%
6. Governance strict mode: discovery populates pending queue, not catalog directly
7. Panic mode: all catalog writes return 503
---
## 3. Unit Test Strategy (Per Component)
### 3.1 AWS Scanner (Python / pytest)
**What to test:**
- Resource-to-service grouping heuristics (the core logic)
- Confidence score assignment per signal type
- Pagination handling for each AWS API
- Cross-region scan aggregation
- Error handling for throttling, missing permissions, empty accounts
**Key test cases:**
```python
# tests/unit/test_cfn_scanner.py
class TestCloudFormationScanner:
def test_stack_name_becomes_service_name_with_high_confidence(self):
# Given a CFN stack named "payment-api"
# Expect service entity with name="payment-api", confidence=0.95
def test_stack_tags_extracted_as_service_metadata(self):
# Given stack with tags {"service": "payment", "team": "payments"}
# Expect service.metadata includes both tags
def test_stacks_in_multiple_regions_deduplicated_by_name(self):
# Given same stack name in us-east-1 and us-west-2
# Expect single service entity with both regions in infrastructure
def test_deleted_stacks_excluded_from_results(self):
# Given stack with status DELETE_COMPLETE
# Expect it is not included in discovered services
def test_pagination_fetches_all_stacks_beyond_first_page(self):
# Given mock returning 2 pages of stacks
# Expect all stacks from both pages are processed
class TestLambdaScanner:
def test_lambdas_with_shared_prefix_grouped_into_single_service(self):
# Given ["payment-webhook", "payment-processor", "payment-refund"]
# Expect single service "payment" with confidence=0.60
def test_lambda_with_apigw_trigger_gets_higher_confidence(self):
# Given Lambda with API Gateway event source mapping
# Expect confidence=0.85 (not 0.60)
def test_standalone_lambda_without_prefix_pattern_kept_as_individual(self):
# Given Lambda named "data-export-job" with no siblings
# Expect individual service entity, not grouped
class TestServiceGroupingHeuristics:
def test_cfn_stack_takes_priority_over_ecs_service_for_same_name(self):
# Given CFN stack "payment-api" AND ECS service "payment-api"
# Expect single service entity (not duplicate), source=cloudformation
def test_explicit_github_repo_tag_overrides_name_matching(self):
# Given AWS resource with tag github_repo="acme/payments-v2"
# Expect repo_link="acme/payments-v2" with confidence=0.95
# (not fuzzy name match result)
```
**Mocking strategy:**
- Use `moto` to mock all boto3 calls — no real AWS calls in unit tests
- Fixture files in `tests/fixtures/aws/` contain realistic API response payloads
- Each fixture named after the scenario: `cfn_stacks_multi_region.json`, `lambda_functions_with_apigw.json`
```python
@pytest.fixture
def mock_aws(aws_credentials):
with mock_cloudformation(), mock_ecs(), mock_lambda_():
yield
def test_full_scan_produces_expected_service_count(mock_aws, cfn_fixture):
setup_mock_cfn_stacks(cfn_fixture)
result = AWSScanner(tenant_id="test", role_arn="arn:aws:iam::123:role/test").scan()
assert len(result.services) == cfn_fixture["expected_service_count"]
```
---
### 3.2 GitHub Scanner (Node.js / Jest)
**What to test:**
- GraphQL query construction and batching
- CODEOWNERS file parsing (all valid formats)
- README first-paragraph extraction
- Deploy workflow target extraction
- Rate limit detection and backoff
**Key test cases:**
```typescript
// tests/unit/github-scanner/codeowners-parser.test.ts
describe('CODEOWNERSParser', () => {
it('parses_simple_wildcard_ownership_to_team', () => {
const input = '* @acme/platform-team'
expect(parse(input)).toEqual([{ pattern: '*', owners: ['@acme/platform-team'] }])
})
it('parses_path_specific_ownership', () => {
const input = '/src/payments/ @acme/payments-team'
expect(parse(input)).toEqual([{ pattern: '/src/payments/', owners: ['@acme/payments-team'] }])
})
it('handles_multiple_owners_per_pattern', () => {
const input = '*.ts @acme/frontend @acme/platform'
expect(parse(input).owners).toHaveLength(2)
})
it('ignores_comment_lines', () => {
const input = '# This is a comment\n* @acme/team'
expect(parse(input)).toHaveLength(1)
})
it('returns_empty_array_for_missing_codeowners_file', () => {
expect(parse(null)).toEqual([])
})
it('handles_individual_user_ownership_not_just_teams', () => {
const input = '* @sarah-chen'
expect(parse(input)[0].owners[0]).toBe('@sarah-chen')
})
})
describe('READMEExtractor', () => {
it('extracts_first_non_heading_non_badge_paragraph', () => {
const readme = `# Payment Gateway\n\n![build](badge.svg)\n\nHandles Stripe checkout flows.`
expect(extractDescription(readme)).toBe('Handles Stripe checkout flows.')
})
it('returns_null_when_readme_has_only_headings_and_badges', () => {
const readme = `# Title\n\n![badge](url)`
expect(extractDescription(readme)).toBeNull()
})
})
describe('WorkflowTargetExtractor', () => {
it('extracts_ecs_service_name_from_deploy_workflow', () => {
const yaml = loadFixture('deploy-workflow-ecs.yml')
expect(extractDeployTarget(yaml)).toEqual({
type: 'ecs_service',
name: 'payment-api',
cluster: 'production'
})
})
it('extracts_lambda_function_name_from_serverless_deploy', () => {
const yaml = loadFixture('deploy-workflow-lambda.yml')
expect(extractDeployTarget(yaml)).toEqual({
type: 'lambda_function',
name: 'payment-webhook-handler'
})
})
})
```
**Mocking strategy:**
- Use `nock` or `msw` to intercept GitHub GraphQL API calls
- Fixture files in `tests/fixtures/github/` for realistic API responses
- Test the GraphQL query builder separately from the HTTP client
---
### 3.3 Reconciliation Engine (Node.js / Jest)
**What to test:**
- Cross-referencing AWS resources with GitHub repos (all 5 matching rules)
- Deduplication when multiple signals point to the same service
- Conflict resolution when signals disagree
- Batch processing of SQS messages
**Key test cases:**
```typescript
describe('ReconciliationEngine', () => {
describe('matchAWSToGitHub', () => {
it('explicit_tag_match_takes_highest_priority', () => {
const awsService = buildAWSService({ tags: { github_repo: 'acme/payment-gateway' } })
const ghRepo = buildGHRepo({ name: 'payment-gateway', org: 'acme' })
const result = reconcile([awsService], [ghRepo])
expect(result[0].repoLinkSource).toBe('explicit_tag')
expect(result[0].repoLinkConfidence).toBe(0.95)
})
it('deploy_workflow_match_used_when_no_explicit_tag', () => {
const awsService = buildAWSService({ name: 'payment-api' })
const ghRepo = buildGHRepo({ deployTarget: 'payment-api' })
const result = reconcile([awsService], [ghRepo])
expect(result[0].repoLinkSource).toBe('deploy_workflow')
})
it('fuzzy_name_match_used_as_fallback', () => {
const awsService = buildAWSService({ name: 'payment-service' })
const ghRepo = buildGHRepo({ name: 'payment-svc' })
const result = reconcile([awsService], [ghRepo])
expect(result[0].repoLinkSource).toBe('name_match')
expect(result[0].repoLinkConfidence).toBe(0.75)
})
it('no_match_produces_aws_only_service_entity', () => {
const awsService = buildAWSService({ name: 'legacy-monolith' })
const result = reconcile([awsService], [])
expect(result[0].repoUrl).toBeNull()
expect(result[0].discoverySources).toContain('cloudformation')
expect(result[0].discoverySources).not.toContain('github_repo')
})
it('deduplicates_cfn_stack_and_ecs_service_with_same_name', () => {
const cfnService = buildAWSService({ source: 'cloudformation', name: 'payment-api' })
const ecsService = buildAWSService({ source: 'ecs_service', name: 'payment-api' })
const result = reconcile([cfnService, ecsService], [])
expect(result).toHaveLength(1)
expect(result[0].discoverySources).toContain('cloudformation')
expect(result[0].discoverySources).toContain('ecs_service')
})
})
})
```
---
### 3.4 Ownership Inference Engine (Python / pytest)
This is the highest-value unit test target. Ownership inference is the most complex logic and the most likely source of accuracy failures.
**Key test cases:**
```python
class TestOwnershipScorer:
def test_codeowners_weighted_highest_at_0_40(self):
signals = [Signal(type='codeowners', team='payments', raw_score=1.0)]
result = score_ownership(signals)
assert result['payments'].weighted_score == pytest.approx(0.40)
def test_multiple_signals_summed_correctly(self):
signals = [
Signal(type='codeowners', team='payments', raw_score=1.0), # 0.40
Signal(type='cfn_tag', team='payments', raw_score=1.0), # 0.20
Signal(type='git_blame_frequency', team='payments', raw_score=1.0), # 0.25
]
result = score_ownership(signals)
assert result['payments'].total_score == pytest.approx(0.85)
def test_primary_owner_is_highest_scoring_team(self):
signals = [
Signal(type='codeowners', team='payments', raw_score=1.0),
Signal(type='git_blame_frequency', team='platform', raw_score=1.0),
]
result = score_ownership(signals)
assert result.primary_owner == 'payments'
def test_service_marked_unowned_when_top_score_below_0_50(self):
signals = [Signal(type='git_blame_frequency', team='unknown', raw_score=0.3)]
result = score_ownership(signals)
assert result.status == 'unowned'
def test_service_marked_ambiguous_when_top_two_within_0_10(self):
signals = [
Signal(type='codeowners', team='payments', raw_score=0.8),
Signal(type='codeowners', team='platform', raw_score=0.75),
]
result = score_ownership(signals)
assert result.status == 'ambiguous'
def test_user_correction_overrides_all_inference_with_score_1_00(self):
signals = [
Signal(type='codeowners', team='payments', raw_score=1.0),
Signal(type='user_correction', team='platform', raw_score=1.0),
]
result = score_ownership(signals)
assert result.primary_owner == 'platform'
assert result.primary_confidence == 1.00
assert result.primary_source == 'user_correction'
def test_correction_propagation_applies_to_matching_repo_prefix(self):
correction = Correction(repo='payment-gateway', team='payments')
candidates = ['payment-processor', 'payment-webhook', 'auth-service']
propagated = propagate_correction(correction, candidates)
assert 'payment-processor' in propagated
assert 'payment-webhook' in propagated
assert 'auth-service' not in propagated
```
---
### 3.5 Portal API — Route Handlers (Node.js / Jest + Supertest)
**What to test:**
- Tenant isolation enforcement (tenant_id injected into every query)
- Search endpoint proxies to Meilisearch with mandatory tenant filter
- PATCH /services enforces correction logging
- Auth middleware rejects unauthenticated requests
```typescript
describe('GET /api/v1/services/search', () => {
it('injects_tenant_id_filter_into_meilisearch_query', async () => {
const spy = jest.spyOn(meilisearchClient, 'search')
await request(app).get('/api/v1/services/search?q=payment').set('Authorization', `Bearer ${tenantAToken}`)
expect(spy).toHaveBeenCalledWith(expect.objectContaining({
filter: expect.stringContaining(`tenant_id = '${TENANT_A_ID}'`)
}))
})
it('returns_401_when_no_auth_token_provided', async () => {
const res = await request(app).get('/api/v1/services/search?q=payment')
expect(res.status).toBe(401)
})
it('tenant_a_cannot_see_tenant_b_services', async () => {
// Seed Meilisearch with services for both tenants
// Query as tenant A, assert no tenant B results
})
})
describe('PATCH /api/v1/services/:id', () => {
it('stores_correction_in_corrections_table', async () => {
await request(app)
.patch(`/api/v1/services/${SERVICE_ID}`)
.send({ team_id: NEW_TEAM_ID })
.set('Authorization', `Bearer ${adminToken}`)
const correction = await db.corrections.findFirst({ where: { service_id: SERVICE_ID } })
expect(correction).toBeDefined()
expect(correction.new_value).toMatchObject({ team_id: NEW_TEAM_ID })
})
it('sets_confidence_to_1_00_on_user_correction', async () => {
await request(app).patch(`/api/v1/services/${SERVICE_ID}`).send({ team_id: NEW_TEAM_ID })
const ownership = await db.service_ownership.findFirst({ where: { service_id: SERVICE_ID } })
expect(ownership.confidence).toBe(1.00)
expect(ownership.source).toBe('user_correction')
})
})
```
### 3.6 Slack Bot Command Handlers (Node.js / Jest)
**What to test:**
- Command parsing (`/dd0c who owns <service>`)
- Typo tolerance matching logic (delegated to search, but bot needs to handle 0 results)
- Block kit message formatting
- Error handling (unauthorized workspace, missing service)
### 3.7 Feature Flags & Governance Policy (Node.js / Jest)
**What to test:**
- Flag evaluation (`openfeature` provider)
- Governance strict vs. audit mode
- Panic mode blocking writes
---
## 4. Integration Test Strategy
Integration tests verify that our code correctly interacts with external boundaries: databases, caches, search indices, and third-party APIs.
### 4.1 Service Boundary Tests
- **Discovery ↔ GitHub/GitLab:** Use `nock` or `MSW` to mock the GitHub GraphQL endpoint. Assert that the Node.js scanner constructs the correct query and handles rate limits (HTTP 403/429) via retries.
- **Catalog ↔ PostgreSQL:** Use Testcontainers for PostgreSQL to verify complex `upsert` queries, foreign key constraints, and RLS (Row-Level Security) tenant isolation.
- **API ↔ Meilisearch:** Use a Meilisearch Docker container. Assert that document syncing (PostgreSQL -> SQS -> Meilisearch) completes and search queries with `tenant_id` filters return the expected subset of data.
### 4.2 Git Provider API Contract Tests
- Write scheduled "contract tests" that run against the *live* GitHub API daily using a dedicated test org.
- These detect if GitHub changes their GraphQL schema or rate limit behavior.
- Assert that `HEAD:CODEOWNERS` blob extraction still works.
### 4.3 Testcontainers for Local Infrastructure
- **Database:** `testcontainers-node` spinning up `postgres:15-alpine`.
- **Search:** `getmeili/meilisearch:latest`.
- **Cache:** `redis:7-alpine`.
- Run these in GitHub Actions via Docker-in-Docker.
---
## 5. E2E & Smoke Tests
E2E tests treat the system as a black box, interacting only through the API and the React UI. We keep these fast and focused on the "5-Minute Miracle" critical path.
### 5.1 Critical User Journeys (Playwright)
1. **The Onboarding Flow:** Mock GitHub OAuth login -> Connect AWS (mock CFN role ARN validation) -> Trigger Discovery -> Wait for WebSocket completion -> Verify 147 services appear in catalog.
2. **Cmd+K Search:** Open modal (`Cmd+K`) -> type "pay" -> assert "payment-gateway" is highlighted in < 200ms -> press Enter -> assert service detail card opens.
3. **Correcting Ownership:** Open service detail -> Click "Correct Owner" -> select new team -> assert badge changes to 100% confidence -> assert Meilisearch is updated.
### 5.2 The >80% Auto-Discovery Accuracy Validation
- **The "Party Mode" Org:** Maintain a real GitHub org and a mock AWS environment with exactly 100 known services, 10 known teams, and specific chaotic naming conventions.
- **The Assertion:** Run discovery. Assert that > 80 of the services are correctly inferred with the right primary owner and repo link.
- *This is the most important test in the suite. If a PR drops this below 80%, it cannot be merged.*
### 5.3 Synthetic Topology Generation
- Script to generate `N` mock CFN stacks, `M` ECS services, and `K` GitHub repos to feed the E2E environment without hitting AWS/GitHub limits.
---
## 6. Performance & Load Testing
Load tests ensure the serverless architecture scales correctly and the Cmd+K search remains instantaneous.
### 6.1 Discovery Scan Benchmarks
- **Target:** 500 AWS resources + 500 GitHub repos scanned and reconciled in < 120 seconds.
- **Tooling:** K6 or Artillery. Push 5,000 synthetic SQS messages into the Reconciler queue and measure Lambda batch processing throughput.
### 6.2 Catalog Query Latency
- **Target:** API search endpoint returns in < 100ms at the 99th percentile.
- **Test:** Load Meilisearch with 10,000 service documents. Fire 50 concurrent Cmd+K search requests per second. Assert p99 latency.
### 6.3 Concurrent Scorecard Evaluation
- Ensure the Python inference Lambda can evaluate 1,000 services concurrently without database connection exhaustion (using Aurora Serverless v2 connection pooling).
---
## 7. CI/CD Pipeline Integration
The test pyramid is enforced through GitHub Actions.
### 7.1 Test Stages
- **Pre-commit:** Husky runs ESLint, Prettier, and fast unit tests (Jest/pytest) for changed files only.
- **PR Gate:** Runs the full Unit and Integration test suites. Blocks merge if coverage drops or tests fail.
- **Merge (Main):** Deploys to Staging. Runs E2E Critical User Journeys and the 80% Accuracy Validation suite against the Party Mode org.
- **Post-Deploy:** Smoke tests verify health endpoints and ALB routing in production.
### 7.2 Coverage Thresholds
- Global Unit Test Coverage: 80%
- Ownership Inference & Reconciliation Logic: 95%
- Feature Flag & Governance Evaluators: 100%
### 7.3 Test Parallelization
- Jest tests run with `--maxWorkers=50%` locally, `100%` in CI.
- Integration tests using Testcontainers run serially per file to avoid database port conflicts, or use dynamic port binding and separate schemas for parallel execution.
---
## 8. Transparent Factory Tenet Testing
Testing the governance and compliance features of the IDP itself.
### 8.1 Feature Flag Circuit Breakers
- **Test:** Enable a flagged discovery heuristic that generates 10 phantom services.
- **Assert:** The system detects the threshold (>5 unconfirmed), auto-disables the flag, and marks the 10 services as `status: quarantined`.
### 8.2 Schema Migration Validation
- **Test:** Attempt to apply a PR that drops a column from the `services` table.
- **Assert:** CI migration validator script fails the build (additive-only rule).
### 8.3 Decision Log Enforcement
- **Test:** Run a discovery scan where service ownership is inferred from `git blame`.
- **Assert:** A `decision_log` entry is written to PostgreSQL with the prompt/reasoning, alternatives, and confidence.
### 8.4 OTEL Span Assertions
- **Test:** Run the Reconciler Lambda.
- **Assert:** The `catalog_scan` parent span contains child spans for `ownership_inference` with attributes for `catalog.service_id`, `catalog.ownership_signals`, and `catalog.confidence_score`. Use an in-memory OTEL exporter for testing.
### 8.5 Governance Policy Enforcement
- **Test:** Set tenant policy to `strict` mode. Simulate auto-discovery finding a new service.
- **Assert:** Service is placed in the "pending review" queue and NOT visible in the main catalog.
- **Test:** Set `panic_mode: true`. Attempt a `PATCH /api/v1/services/123`.
- **Assert:** HTTP 503 Service Unavailable.
---
## 9. Test Data & Fixtures
High-quality fixtures are the lifeblood of this TDD strategy.
### 9.1 GitHub/GitLab API Response Factories
- JSON files containing real obfuscated GraphQL responses for Repositories, `CODEOWNERS` blobs, and Team memberships.
- Use factories (e.g., `fishery` or custom functions) to easily override fields: `buildGHRepo({ name: 'auth-service', languages: ['Go'] })`.
### 9.2 Synthetic Topology Generators
- Scripts that generate interconnected AWS resources (e.g., a CFN stack containing an API Gateway routing to 3 Lambdas interacting with 1 RDS instance).
### 9.3 `CODEOWNERS` and Git Blame Mocks
- Diverse `CODEOWNERS` files covering edge cases: wildcard matching, deep path matching, invalid syntax, user-vs-team owners.
---
## 10. TDD Implementation Order
To bootstrap the platform efficiently, testing and development should follow this sequence based on Epic dependencies:
1. **Epic 2 (GitHub Parsers):** Write pure unit tests for `CODEOWNERS` parser and `README` extractor. *Value: High ROI, zero dependencies.*
2. **Epic 1 (AWS Heuristics):** Write unit tests for mapping CFN stacks and Tags to Service entities. *Value: Core product logic.*
3. **Epic 2 (Ownership Inference):** TDD the scoring algorithm. Build the weighting math. *Value: The brain of the platform.*
4. **Epic 3 (Service Catalog Schema):** Integration tests for PostgreSQL RLS and upserting services. *Value: Data durability.*
5. **Epic 2 (Reconciliation):** Unit tests merging AWS and GitHub mock entities. *Value: Pipeline glue.*
6. **Epic 4 (Search Sync):** Integration tests for pushing DB updates to Meilisearch.
7. **Epic 5 (API & UI):** E2E test for the Cmd+K search flow.
8. **Epic 10 (Governance & Flags):** Unit tests for feature flag circuit breakers and strict mode.
9. **Epic 9 (Onboarding):** Playwright E2E for the 5-Minute Miracle flow.
This sequence ensures the most complex algorithmic logic is proven before it is wired to databases and APIs.