- All 6 test architectures patched with Section 11 addendums - P5 (cost) fully rewritten from 232 to ~600 lines - PLG brainstorm + party mode advisory board results - Analytics SDK v2 (PostHog Cloud, Zod strict, Lambda-safe) - Analytics tests v2 (safeParse, no , no timestamp, no PII) - Addresses all Gemini review findings across P1-P6
28 KiB
dd0c/cost — Test Architecture & TDD Strategy
Product: dd0c/cost — AWS Cost Anomaly Detective Author: Test Architecture Phase (v2 — Post-Review Rewrite) Date: March 1, 2026 Status: V1 MVP — Solo Founder Scope
Section 1: Testing Philosophy & TDD Workflow
1.1 Core Philosophy
dd0c/cost sits at the intersection of money and infrastructure. A false negative means a customer loses thousands of dollars. A false positive means alert fatigue and churn. The test suite's primary job is to mathematically prove the anomaly scoring engine works across edge cases.
Guiding principle: Test the math first, test the infrastructure second. The Z-score and novelty algorithms must be exhaustively tested with property-based testing before any AWS APIs are mocked.
Second principle: Every dollar matters. Cost calculations involve floating-point arithmetic on money. Rounding errors, precision loss, and currency handling must be tested with the same rigor as a financial system.
1.2 Red-Green-Refactor Adapted to dd0c/cost
RED → Write a failing test that asserts a specific Z-score and severity
for a given historical baseline and new cost event.
GREEN → Implement the scoring math to make it pass.
REFACTOR → Optimize the baseline lookup, extract novelty checks,
refine the heuristic weights.
When to write tests first (strict TDD):
- All anomaly scoring (Z-scores, novelty checks, composite severity)
- All cold-start heuristics (fast-path for >$5/hr resources)
- All baseline calculation (Welford algorithm, maturity transitions)
- All governance policy (strict vs. audit mode, 14-day auto-promotion, panic mode)
- All Slack signature validation (security-critical)
- All cost calculations (pricing lookup, hourly cost estimation)
- All feature flag circuit breakers
When integration tests lead:
- CloudTrail ingestion (implement against LocalStack EventBridge, then lock in)
- DynamoDB Single-Table schema (build access patterns, then integration test)
- Cross-account STS role assumption (test against LocalStack)
When E2E tests lead:
- Slack alert interaction (format block kit, test "Snooze/Terminate" buttons)
- Onboarding wizard (CloudFormation quick-create → role validation → first alert)
1.3 Test Naming Conventions
// Unit tests
describe('AnomalyScorer', () => {
it('assigns critical severity when Z-score exceeds 3 and hourly cost exceeds $1', () => {});
it('flags actor novelty when IAM role has never launched this service type', () => {});
it('bypasses baseline and triggers fast-path critical for $10/hr instance', () => {});
});
describe('BaselineCalculator', () => {
it('updates running mean using Welford online algorithm', () => {});
it('handles zero standard deviation without division by zero', () => {});
});
// Property-based tests
describe('AnomalyScorer (property-based)', () => {
it('always returns severity between 0 and 100 for any valid input', () => {});
it('monotonically increases score as Z-score increases', () => {});
it('never assigns critical to events below $0.50/hr regardless of Z-score', () => {});
});
Rules:
- Describe the observable outcome, not the implementation
- Use present tense
- If you need "and" in the name, split into two tests
- Property-based tests explicitly state the invariant
Section 2: Test Pyramid
2.1 Ratio
| Level | Target | Count (V1) | Runtime |
|---|---|---|---|
| Unit | 80% | ~350 tests | <25s |
| Integration | 15% | ~65 tests | <4min |
| E2E/Smoke | 5% | ~15 tests | <8min |
Higher unit ratio than other dd0c products because the core value is pure math (scoring, baselines, Z-scores).
2.2 Unit Test Targets
| Component | Key Behaviors | Est. Tests |
|---|---|---|
| CloudTrail Normalizer | Event parsing, pricing lookup, dedup, field extraction | 40 |
| Baseline Engine | Welford algorithm, maturity transitions, feedback loop | 45 |
| Anomaly Scorer | Z-score, novelty, composite scoring, cold-start fast-path | 60 |
| Zombie Hunter | Idle resource detection, cost estimation, age calculation | 25 |
| Notification Formatter | Slack Block Kit, daily digest, CLI command generation | 30 |
| Slack Bot | Command parsing, signature validation, action handling | 25 |
| Remediation Handler | Stop/Terminate logic, IAM role assumption, snooze/dismiss | 20 |
| Dashboard API | CRUD, tenant isolation, pagination, filtering | 25 |
| Governance Policy | Mode enforcement, 14-day promotion, panic mode | 30 |
| Feature Flags | Circuit breaker, flag lifecycle, local evaluation | 15 |
| Onboarding | CFN template validation, role validation, free tier enforcement | 20 |
| Cost Calculations | Pricing precision, rounding, fallback pricing, currency | 15 |
2.3 Integration Test Boundaries
| Boundary | What's Tested | Infrastructure |
|---|---|---|
| EventBridge → SQS FIFO | Cross-account event routing, dedup, ordering | LocalStack |
| SQS → Event Processor Lambda | Batch processing, error handling, DLQ routing | LocalStack |
| Event Processor → DynamoDB | CostEvent writes, baseline updates, transactions | Testcontainers DynamoDB Local |
| Anomaly Scorer → DynamoDB | Baseline reads, anomaly record writes | Testcontainers DynamoDB Local |
| Notifier → Slack API | Block Kit delivery, rate limiting, message updates | WireMock |
| API Gateway → Lambda | Auth (Cognito JWT), routing, throttling | LocalStack |
| STS → Customer Account | Cross-account role assumption, ExternalId validation | LocalStack |
| CDK Synth | Infrastructure snapshot, resource policy validation | CDK assertions |
2.4 E2E/Smoke Scenarios
- Real-Time Anomaly Detection: CloudTrail event → scoring → Slack alert (<30s)
- Interactive Remediation: Slack button click → StopInstances → message update
- Onboarding Flow: Signup → CFN deploy → role validation → first alert
- 14-Day Auto-Promotion: Simulate 14 days → verify strict→audit transition
- Zombie Hunter: Daily scan → detect idle EC2 → Slack digest
- Panic Mode: Enable panic → all alerting stops → anomalies still logged
Section 3: Unit Test Strategy
3.1 CloudTrail Normalizer
describe('CloudTrailNormalizer', () => {
describe('Event Parsing', () => {
it('normalizes EC2 RunInstances to CostEvent schema', () => {});
it('normalizes RDS CreateDBInstance to CostEvent schema', () => {});
it('normalizes Lambda CreateFunction to CostEvent schema', () => {});
it('extracts assumed role ARN as actor (not base STS role)', () => {});
it('extracts instance type, region, and AZ from event detail', () => {});
it('handles batched RunInstances (multiple instances in one call)', () => {});
it('ignores non-cost-generating events (DescribeInstances, ListBuckets)', () => {});
it('handles malformed CloudTrail JSON without crashing', () => {});
it('handles missing optional fields gracefully', () => {});
});
describe('Pricing Lookup', () => {
it('looks up correct on-demand price for us-east-1 m5.xlarge', () => {});
it('looks up correct on-demand price for us-west-2 r6g.2xlarge', () => {});
it('applies fallback pricing when instance type not in static table', () => {});
it('returns $0 for instance types with no pricing data and logs warning', () => {});
it('handles GPU instances (p4d, g5) with correct pricing', () => {});
});
describe('Deduplication', () => {
it('generates deterministic fingerprint from eventID', () => {});
it('detects duplicate CloudTrail events by eventID', () => {});
it('allows same resource type from different events', () => {});
});
describe('Cost Precision', () => {
it('calculates hourly cost with 4 decimal places', () => {});
it('rounds consistently (banker rounding) to avoid accumulation errors', () => {});
it('handles sub-cent costs for Lambda invocations', () => {});
});
});
3.2 Anomaly Scorer
The most critical component. Uses property-based testing via fast-check.
describe('AnomalyScorer', () => {
describe('Z-Score Calculation', () => {
it('returns 0 when event cost exactly matches baseline mean', () => {});
it('returns proportional score for Z-scores between 1.0 and 3.0', () => {});
it('caps Z-score contribution at configurable max threshold', () => {});
it('handles zero standard deviation without division by zero', () => {});
it('handles single data point baseline (stddev undefined)', () => {});
it('handles extremely large values without float overflow', () => {});
it('handles negative cost delta (cost decrease) as non-anomalous', () => {});
});
describe('Novelty Scoring', () => {
it('adds instance novelty penalty when type first seen for account', () => {});
it('adds actor novelty penalty when IAM role is new', () => {});
it('does not penalize known instance type + known actor', () => {});
it('weights instance novelty higher than actor novelty', () => {});
});
describe('Composite Scoring', () => {
it('combines Z-score + novelty into composite severity', () => {});
it('classifies composite < 30 as info', () => {});
it('classifies composite 30-60 as warning', () => {});
it('classifies composite > 60 as critical', () => {});
it('never assigns critical to events below $0.50/hr', () => {});
});
describe('Cold-Start Fast Path', () => {
it('flags $5/hr instance as warning when baseline < 14 days', () => {});
it('flags $25/hr instance as critical immediately, bypassing baseline', () => {});
it('ignores $0.10/hr instances during cold-start learning', () => {});
it('fast-path is always on — not behind a feature flag', () => {});
it('transitions from fast-path to statistical scoring at maturity', () => {});
});
describe('Feedback Loop', () => {
it('reduces score for resources marked as expected', () => {});
it('adds actor to expected list after mark-as-expected', () => {});
it('still flags expected actor if cost is 10x above baseline', () => {});
});
describe('Property-Based Tests (fast-check)', () => {
it('score is always between 0 and 100 for any valid input', () => {
// fc.assert(fc.property(
// fc.record({ cost: fc.float({min: 0}), mean: fc.float({min: 0}), stddev: fc.float({min: 0}) }),
// (input) => { const score = scorer.score(input); return score >= 0 && score <= 100; }
// ))
});
it('score monotonically increases as cost increases (baseline fixed)', () => {});
it('score monotonically increases as Z-score increases', () => {});
it('cold-start fast-path always triggers for cost > $25/hr', () => {});
it('mature baseline never uses fast-path thresholds', () => {});
});
});
3.3 Baseline Engine
describe('BaselineCalculator', () => {
describe('Welford Online Algorithm', () => {
it('updates running mean correctly after each observation', () => {});
it('updates running variance correctly after each observation', () => {});
it('produces correct stddev after 100 observations', () => {});
it('handles first observation (count=1, stddev=0)', () => {});
it('handles identical observations (stddev=0)', () => {});
it('handles catastrophic cancellation with large values', () => {
// Welford is numerically stable — verify this property
});
});
describe('Maturity Transitions', () => {
it('starts in cold-start state', () => {});
it('transitions to learning after 5 events', () => {});
it('transitions to mature after 20 events AND 14 days', () => {});
it('does not mature with 100 events but only 3 days', () => {});
it('does not mature with 14 days but only 5 events', () => {});
});
describe('Actor & Instance Tracking', () => {
it('adds new actor to observed_actors set', () => {});
it('adds new instance type to observed_types set', () => {});
it('does not duplicate existing actors', () => {});
});
describe('Property-Based Tests', () => {
it('mean converges to true mean as observations increase', () => {});
it('variance is always non-negative', () => {});
it('stddev equals sqrt(variance) within float tolerance', () => {});
});
});
3.4 Zombie Hunter
describe('ZombieHunter', () => {
it('detects EC2 instance running >7 days with <5% CPU utilization', () => {});
it('detects RDS instance with 0 connections for >3 days', () => {});
it('detects unattached EBS volumes older than 7 days', () => {});
it('calculates cumulative waste cost for each zombie', () => {});
it('excludes instances tagged dd0c:ignore', () => {});
it('handles API pagination for accounts with 500+ instances', () => {});
it('respects read-only IAM permissions (never modifies resources)', () => {});
});
3.5 Notification Formatter
describe('NotificationFormatter', () => {
describe('Slack Block Kit', () => {
it('formats EC2 anomaly with resource type, region, cost, actor', () => {});
it('formats RDS anomaly with engine, storage, multi-AZ status', () => {});
it('includes "Why this alert" section with anomaly signals', () => {});
it('includes suggested CLI commands for remediation', () => {});
it('includes Snooze/Mark Expected/Stop Instance buttons', () => {});
it('generates correct aws ec2 stop-instances command', () => {});
it('generates correct aws rds stop-db-instance command', () => {});
});
describe('Daily Digest', () => {
it('aggregates 24h of anomalies into summary stats', () => {});
it('includes total estimated spend across all accounts', () => {});
it('highlights top 3 costliest anomalies', () => {});
it('includes zombie resource count and waste estimate', () => {});
it('shows baseline learning progress for new accounts', () => {});
});
});
3.6 Slack Bot
describe('SlackBot', () => {
describe('Signature Validation', () => {
it('validates correct Slack request signature (HMAC-SHA256)', () => {});
it('rejects request with invalid signature', () => {});
it('rejects request with missing X-Slack-Signature header', () => {});
it('rejects request with expired timestamp (>5 min)', () => {});
it('uses timing-safe comparison to prevent timing attacks', () => {});
});
describe('Command Parsing', () => {
it('routes /dd0c status to status handler', () => {});
it('routes /dd0c anomalies to anomaly list handler', () => {});
it('routes /dd0c digest to digest handler', () => {});
it('returns help text for unknown commands', () => {});
it('responds within 3 seconds or defers with 200 OK', () => {});
});
describe('Interactive Actions', () => {
it('validates interactive payload signature', () => {});
it('handles mark_expected action and updates baseline', () => {});
it('handles snooze_1h action and sets snoozeUntil', () => {});
it('handles snooze_24h action', () => {});
it('updates original Slack message after action', () => {});
it('rejects action from user not in authorized workspace', () => {});
});
});
3.7 Governance Policy Engine
describe('GovernancePolicy', () => {
describe('Mode Enforcement', () => {
it('strict mode: logs anomaly but does not send Slack alert', () => {});
it('audit mode: sends Slack alert with full logging', () => {});
it('defaults new accounts to strict mode', () => {});
});
describe('14-Day Auto-Promotion', () => {
it('does not promote account with <14 days of baseline', () => {});
it('does not promote account with >10% false-positive rate', () => {});
it('promotes account on day 15 if FP rate <10%', () => {});
it('calculates false-positive rate from mark-as-expected actions', () => {});
it('auto-promotion check runs daily via cron', () => {});
});
describe('Panic Mode', () => {
it('stops all alerting when panic=true', () => {});
it('continues scoring and logging during panic', () => {});
it('activates in <1 second via Redis key', () => {});
it('activatable via POST /admin/panic', () => {});
it('dashboard API returns "alerting paused" header during panic', () => {});
});
describe('Per-Account Override', () => {
it('account can set stricter mode than system default', () => {});
it('account cannot downgrade from system strict to audit', () => {});
it('merge logic: max_restrictive(system, account)', () => {});
});
describe('Policy Decision Logging', () => {
it('logs "suppressed by strict mode" with anomaly context', () => {});
it('logs "auto-promoted to audit mode" with baseline stats', () => {});
it('logs "panic mode active — alerting paused"', () => {});
});
});
3.8 Dashboard API
describe('DashboardAPI', () => {
describe('Account Management', () => {
it('GET /v1/accounts returns connected accounts for tenant', () => {});
it('DELETE /v1/accounts/:id marks account as disconnecting', () => {});
it('returns 401 without valid Cognito JWT', () => {});
it('scopes all queries to authenticated tenantId', () => {});
});
describe('Anomaly Listing', () => {
it('GET /v1/anomalies returns recent anomalies', () => {});
it('supports since, status, severity filters', () => {});
it('implements cursor-based pagination', () => {});
it('includes slackMessageUrl when alert was sent', () => {});
});
describe('Baseline Overrides', () => {
it('PATCH /v1/accounts/:id/baselines/:service/:type updates sensitivity', () => {});
it('rejects invalid sensitivity values', () => {});
});
describe('Tenant Isolation', () => {
it('never returns anomalies from another tenant', () => {});
it('never returns accounts from another tenant', () => {});
it('enforces tenantId on all DynamoDB queries', () => {});
});
});
3.9 Onboarding & PLG
describe('Onboarding', () => {
describe('CloudFormation Template', () => {
it('generates valid CFN YAML with correct IAM permissions', () => {});
it('includes ExternalId parameter', () => {});
it('includes EventBridge rule for cost-relevant CloudTrail events', () => {});
it('quick-create URL contains correct template URL and parameters', () => {});
});
describe('Role Validation', () => {
it('successfully assumes role with correct ExternalId', () => {});
it('returns clear error on role not found', () => {});
it('returns clear error on ExternalId mismatch', () => {});
it('triggers zombie scan on successful connection', () => {});
});
describe('Free Tier Enforcement', () => {
it('allows first account connection on free tier', () => {});
it('rejects second account with 403 and upgrade prompt', () => {});
it('allows multiple accounts on pro tier', () => {});
});
describe('Stripe Integration', () => {
it('creates Stripe Checkout session with correct pricing', () => {});
it('handles checkout.session.completed webhook', () => {});
it('handles customer.subscription.deleted webhook', () => {});
it('validates Stripe webhook signature', () => {});
it('updates tenant tier to pro on successful payment', () => {});
it('downgrades tenant on subscription cancellation', () => {});
});
});
3.10 Feature Flag Circuit Breaker
describe('AlertVolumeCircuitBreaker', () => {
it('allows alerting when volume is within 3x baseline', () => {});
it('trips breaker when alerts exceed 3x baseline over 1 hour', () => {});
it('auto-disables the scoring flag when breaker trips', () => {});
it('buffers suppressed alerts in DLQ for review', () => {});
it('tracks alert-per-account rate in Redis sliding window', () => {});
it('resets breaker after manual flag re-enable', () => {});
it('fast-path alerts are exempt from circuit breaker', () => {});
});
Section 4: Integration Test Strategy
4.1 DynamoDB Data Layer (Testcontainers)
describe('DynamoDB Integrations', () => {
let dynamodb: StartedTestContainer;
beforeAll(async () => {
dynamodb = await new GenericContainer('amazon/dynamodb-local:latest')
.withExposedPorts(8000).start();
// Create dd0c-cost-main table with GSIs
});
describe('Transactional Writes', () => {
it('writes CostEvent and updates Baseline in single TransactWriteItem', async () => {});
it('fails gracefully if TransactWriteItem encounters ConditionalCheckFailed', async () => {});
it('handles partial failure recovery when Baseline update conflicts', async () => {});
});
describe('Access Patterns', () => {
it('queries all anomalies for tenant within time range (GSI3)', async () => {});
it('fetches tenant config and Slack tokens securely', async () => {});
it('retrieves accurate Baseline snapshot by resource type', async () => {});
});
});
4.2 Cross-Account STS & AWS APIs (LocalStack)
describe('AWS Cross-Account Integrations', () => {
let localstack: StartedTestContainer;
beforeAll(async () => {
localstack = await new GenericContainer('localstack/localstack:3')
.withEnv('SERVICES', 'sts,ec2,rds')
.withExposedPorts(4566).start();
});
describe('Role Assumption', () => {
it('successfully assumes target account remediation role via STS', async () => {});
it('fails when ExternalId does not match (Security)', async () => {});
it('handles STS credential expiration gracefully', async () => {});
});
describe('Remediation Actions', () => {
it('executes ec2:StopInstances when remediation approved', async () => {});
it('executes rds:StopDBInstance when remediation approved', async () => {});
it('fails safely when target IAM role lacks StopInstances permission', async () => {});
});
});
4.3 Slack API Contract (WireMock)
describe('Slack Integration', () => {
it('formats and delivers Block Kit message successfully', async () => {});
it('handles 429 Rate Limit by throwing retryable error for SQS visibility timeout', async () => {});
it('updates existing Slack message when anomaly is snoozed', async () => {});
});
Section 5: E2E & Smoke Tests
5.1 Critical User Journeys
Journey 1: Real-Time Anomaly Detection (The Golden Path)
describe('E2E: Anomaly Detection', () => {
it('detects anomaly and alerts Slack within 30 seconds', async () => {
// 1. Inject synthetic CloudTrail `RunInstances` event (p4d.24xlarge) into SQS Ingestion Queue
// 2. Poll DynamoDB to ensure CostEvent was recorded
// 3. Poll DynamoDB to ensure AnomalyRecord was created (fast-path triggered)
// 4. Assert WireMock received the Slack chat.postMessage call with Block Kit
});
});
Journey 2: Interactive Remediation
describe('E2E: Interactive Remediation', () => {
it('stops EC2 instance when user clicks Stop in Slack', async () => {
// 1. Simulate Slack sending interactive webhook payload for "Stop Instance"
// 2. Validate HMAC signature in API Gateway lambda
// 3. Verify LocalStack EC2 mock receives StopInstances call
// 4. Verify Slack message is updated to "Remediation Successful"
});
});
Journey 3: Onboarding & First Scan
describe('E2E: Onboarding', () => {
it('validates IAM role and triggers initial zombie scan', async () => {
// 1. Trigger POST /v1/accounts with new role ARN
// 2. Verify account marked active
// 3. Verify EventBridge Scheduler creates cron for Zombie Hunter
});
});
Section 6: Performance & Load Testing
6.1 Ingestion & Scoring Throughput
describe('Performance: Alert Storm', () => {
it('processes 1000 CloudTrail events/sec without SQS DLQ overflow', async () => {
// k6 load test hitting SQS directly
});
it('DynamoDB baseline updates complete in <20ms p95 under load', async () => {
// Ensure Single-Table schema does not create hot partitions
});
it('Anomaly Scorer Lambda consumes <256MB memory during burst', async () => {});
});
6.2 Data Scale Tests
describe('Performance: Baseline Scale', () => {
it('calculates Z-score in <5ms even when observed_actors set exceeds 1000', async () => {});
it('handles accounts with 100,000+ daily CostEvents without throttling DynamoDB (On-Demand scaling)', async () => {});
});
Section 7: CI/CD Pipeline Integration
7.1 Pipeline Stages
┌─────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Pre-Commit │───▶│ PR Gate │───▶│ Merge │───▶│ Staging │───▶│ Prod │
│ (local) │ │ (CI) │ │ (CI) │ │ (CD) │ │ (CD) │
└─────────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
lint + type unit tests integration E2E + perf canary
<10s math prop Testcontainers LocalStack <5 mins
tests <1m <4 mins <10 mins
7.2 Coverage Gates
| Component | Threshold |
|---|---|
| Anomaly Scorer (Math) | 100% |
| CloudTrail Normalizer | 95% |
| Governance Policy | 95% |
| Slack Signature Auth | 100% |
| Overall Pipeline | 85% |
Section 8: Transparent Factory Tenet Testing
8.1 Atomic Flagging
describe('Atomic Flagging', () => {
it('auto-disables scoring rule flag if alert volume exceeds 3x baseline in 1hr', () => {});
it('buffers suppressed anomalies in SQS DLQ while flag is off', () => {});
it('fails CI if any flag TTL exceeds 14 days', () => {});
it('evaluates flags strictly locally (in-memory provider)', () => {});
});
8.2 Elastic Schema
describe('Elastic Schema', () => {
it('rejects DynamoDB table definition modifications that alter key schemas', () => {});
it('requires all DynamoDB item updates to use ADD/SET (additive only)', () => {});
it('ignores unknown attributes (V2 fields) in V1 CostEvent decoders', () => {});
});
8.3 Cognitive Durability
describe('Cognitive Durability', () => {
it('requires decision_log.json for any PR modifying Z-score thresholds or weights', () => {});
it('enforces cyclomatic complexity < 10 for all AnomalyScorer math functions', () => {});
});
8.4 Semantic Observability
describe('Semantic Observability', () => {
it('emits OTEL span for every Anomaly Scoring decision', () => {});
it('includes attributes: cost.z_score, cost.anomaly_score, cost.baseline_days', () => {});
it('includes cost.fast_path_triggered flag when baseline is bypassed', () => {});
it('hashes AWS Account ID in spans to protect PII/tenant identity', () => {});
});
8.5 Configurable Autonomy
describe('Configurable Autonomy', () => {
it('keeps new tenant in Strict Mode (log-only) for first 14 days', () => {});
it('auto-promotes to Audit Mode on day 15 if false-positive rate < 10%', () => {});
it('Panic Mode halts ALL Slack alerts in <1 second via Redis check', () => {});
it('Panic Mode does NOT halt baseline recording (read-only tracking continues)', () => {});
});
Section 9: Test Data & Fixtures
9.1 Data Factories
export const makeCloudTrailEvent = (overrides) => ({
eventVersion: '1.08',
userIdentity: { type: 'AssumedRole', arn: 'arn:aws:sts::123:assumed-role/user' },
eventTime: new Date().toISOString(),
eventSource: 'ec2.amazonaws.com',
eventName: 'RunInstances',
requestParameters: { instanceType: 'm5.large' },
...overrides
});
export const makeBaseline = (overrides) => ({
meanHourlyCost: 1.25,
stdDev: 0.15,
eventCount: 45,
ageDays: 16,
observedActors: ['arn:aws:iam::123:role/ci'],
observedInstanceTypes: ['t3.medium', 'm5.large'],
...overrides
});
Section 10: TDD Implementation Order
- Phase 1: Math & Core Logic (Strict TDD)
- Welford algorithm, Z-score math, Novelty scoring,
fast-checkproperty tests.
- Welford algorithm, Z-score math, Novelty scoring,
- Phase 2: Ingestion & Normalization
- CloudTrail parsers, pricing static tables, event deduplication.
- Phase 3: Data Persistence (Integration Led)
- DynamoDB Single-Table setup, TransactWriteItems, Testcontainers tests.
- Phase 4: Notifications & Slack Actions
- Block Kit formatting, Slack signature validation, API Gateway endpoints.
- Phase 5: Governance & Tenets
- 14-day promotion logic, Panic mode, OTEL tracing.
- Phase 6: E2E Pipeline
- CDK definitions, LocalStack event injection, wire everything together.
End of dd0c/cost Test Architecture (v2)