Quote Service Test Plan - Review Enhancements

Quote Service Test Plan - Review Enhancements

Version: 3.0 Enhancement Addendum Last Updated: December 31, 2025 Parent Doc: 26-QUOTE-SERVICE-TEST-PLAN.md v2.0 Status: ⭐ CRITICAL ADDITIONS based on review feedback

This document contains CRITICAL TEST ADDITIONS based on Gemini and ChatGPT reviews. These tests MUST be added to the main test plan before production deployment.


🎯 Executive Summary

New Critical Test Categories (from reviews):

  1. βœ… Torn Read Prevention Tests (ChatGPT Critical Issue #1)
  2. βœ… Confidence Score Validation Tests (ChatGPT Critical Issue #3)
  3. βœ… 1-Second AMM Refresh Tests (Gemini Performance Enhancement)
  4. βœ… Explicit Timeout Tests (ChatGPT Tail Latency Fix)
  5. βœ… Parallel Paired Quote Tests (ChatGPT Exceptional Feature)
  6. βœ… Dual Shared Memory Tests (Architecture Enhancement)

Total Additional Test Effort: +18-24 hours Priority: P0 - CRITICAL (must complete before production)


2.8 Torn Read Prevention Tests (NEW) ⭐ CRITICAL

Priority: P0 - CORRECTNESS Estimated Effort: 4 hours Review Source: ChatGPT critique #1 Design Doc: 30.2-SHARED-MEMORY-HYBRID-CHANGE-DETECTION.md

Purpose

Validate that the shared memory reader’s double-read verification protocol prevents torn reads (reading partially-written structs) under concurrent write pressure.

Test Cases

Test 2.8.1: No Torn Reads Under Heavy Contention

Scenario: Multiple concurrent writers (1000 writes/sec) while readers continuously poll

Setup:

// 10 writer threads
for i in 0..10 {
    spawn(move || {
        for j in 0..100 {
            writer.write_quote(i, create_test_quote(j));
            thread::sleep(Duration::from_millis(10)); // 100 writes/sec per thread
        }
    });
}

// 5 reader threads
for _ in 0..5 {
    spawn(move || {
        for _ in 0..10_000 {
            let quotes = reader.read_changed_quotes();
            validate_no_torn_reads(&quotes);
        }
    });
}

Validation:

fn validate_no_torn_reads(quotes: &[(u32, QuoteMetadata)]) {
    for (idx, quote) in quotes {
        let v1 = quote.version.load(Ordering::Acquire);

        // βœ… Version must be even (readable)
        assert_eq!(v1 % 2, 0, "Torn read detected: odd version");

        // βœ… All fields must be consistent
        assert!(quote.output_amount > 0, "Invalid output amount");
        assert!(quote.price_impact_bps < 10000, "Invalid price impact");

        // βœ… Oracle price must be valid
        assert!(quote.oracle_price_usd > 0.0, "Invalid oracle price");
    }
}

Assertions:

  • βœ… 0 torn reads in 50,000 total reads
  • βœ… All versions are even (readable state)
  • βœ… All field values are valid (no corruption)
  • βœ… No panics or crashes

Acceptance Criteria:

  • Pass: 0 torn reads detected
  • Fail: Any torn read (odd version or corrupted data)

Test 2.8.2: Retry Mechanism Under Active Writes

Scenario: Reader attempts to read while writer is actively writing (odd version)

Setup:

// Writer continuously updates quote
spawn(move || {
    loop {
        writer.write_quote(0, create_test_quote(rng.gen()));
        thread::sleep(Duration::from_micros(100)); // Very frequent writes
    }
});

// Reader attempts reads
for _ in 0..1000 {
    let result = reader.read_quote_safe(&quote_at_index_0);
    assert!(result.is_some(), "Reader gave up after 10 retries");
}

Validation:

fn read_quote_safe(&self, quote: &QuoteMetadata) -> Option<QuoteMetadata> {
    let mut retry_count = 0;

    for _ in 0..10 {  // Max 10 retries
        let v1 = quote.version.load(Ordering::Acquire);

        if v1 % 2 != 0 {
            retry_count += 1;
            std::hint::spin_loop();
            continue;  // Retry if odd (writing)
        }

        let quote_copy = /* copy struct */;
        let v2 = quote.version.load(Ordering::Acquire);

        if v1 == v2 {
            METRICS.record_retry_count(retry_count);
            return Some(quote_copy);  // βœ… Success
        }

        retry_count += 1;
    }

    None  // Failed after 10 retries
}

Assertions:

  • βœ… 100% success rate (no None returns)
  • βœ… Average retry count < 3
  • βœ… p99 retry count < 10
  • βœ… Total latency < 500ns (p99)

Acceptance Criteria:

  • Pass: All reads succeed within 10 retries
  • Fail: Any None return (reader gave up)

Test 2.8.3: Performance Under No Contention

Scenario: Single writer, single reader (no contention)

Setup:

// Write once
writer.write_quote(0, test_quote);

// Read 10,000 times
let start = Instant::now();
for _ in 0..10_000 {
    let quote = reader.read_quote_safe(&quote_at_index_0).unwrap();
}
let elapsed = start.elapsed();

let avg_latency = elapsed / 10_000;

Assertions:

  • βœ… Average latency < 50ns
  • βœ… p95 latency < 100ns
  • βœ… p99 latency < 200ns
  • βœ… 0 retries (first read succeeds)

Acceptance Criteria:

  • Pass: p99 latency < 200ns
  • Fail: p99 latency > 500ns

Code Coverage Target

  • Line Coverage: >90% for read_quote_safe() function
  • Branch Coverage: 100% (all retry paths tested)
  • Concurrency Coverage: 1000 writes/sec sustained

Tools

  • Rust standard testing (#[test])
  • criterion for benchmarking
  • loom for concurrency testing (optional, advanced)
  • Thread sanitizer (-Zsanitizer=thread)

Files to Create

  • rust/scanner/src/shared_memory/torn_read_test.rs (NEW)
  • rust/scanner/benches/shared_memory_bench.rs (NEW)

2.9 Confidence Score Validation Tests (NEW) ⭐ CRITICAL

Priority: P0 - HFT REQUIREMENT Estimated Effort: 4 hours Review Source: ChatGPT critique #3 Design Doc: 30.4-CHATGPT-REVIEW-RESPONSE.md

Purpose

Validate that the 5-factor confidence scoring algorithm produces deterministic, correct scores in [0.0, 1.0] range and enables proper scanner decision-making.

Test Cases

Test 2.9.1: High Confidence Quote (Fresh, On-Chain, Accurate)

Scenario: Fresh pool state, direct swap, perfect oracle match

Input:

quote := &Quote{
    PoolLastUpdate:  time.Now().Add(-3 * time.Second),  // 3s old
    RouteHops:       1,                                   // Direct swap
    OutputAmount:    154_000_000,                        // 154 USDC
    InputAmount:     1_000_000_000,                      // 1 SOL
    PriceImpactBps:  20,                                 // 0.2%
    Pool: &Pool{Depth: 5_000_000_000},                  // 5000 SOL depth
    Provider:        "local",
}

oracle := &OraclePrice{PriceUSD: 154.0}  // Matches quote

Expected Confidence Factors:

// 1. Pool State Age: 3s old β†’ 1.0 - (3/60) = 0.95
poolAgeFactor := 0.95

// 2. Route Hop Count: 1 hop β†’ 1.0 - (0 * 0.2) = 1.0
routeFactor := 1.0

// 3. Oracle Deviation: 154 vs 154 β†’ 0% β†’ 1.0
oracleFactor := 1.0

// 4. Provider Reliability: local = 100% β†’ 1.0
providerFactor := 1.0

// 5. Slippage vs Depth: 0.2% in 5000 SOL β†’ expected = actual β†’ 1.0
slippageFactor := 1.0

// Weighted sum
confidence := 0.95*0.30 + 1.0*0.20 + 1.0*0.30 + 1.0*0.10 + 1.0*0.10
            = 0.285 + 0.20 + 0.30 + 0.10 + 0.10
            = 0.985

Assertions:

  • βœ… confidence >= 0.95 (high confidence)
  • βœ… poolAgeFactor >= 0.95
  • βœ… oracleFactor == 1.0
  • βœ… Scanner decision: Strategy::Execute

Acceptance Criteria:

  • Pass: Confidence in [0.95, 1.0]
  • Fail: Confidence < 0.95

Test 2.9.2: Low Confidence Quote (Stale, Multi-Hop, Oracle Mismatch)

Scenario: Stale pool, 3-hop route, significant oracle deviation

Input:

quote := &Quote{
    PoolLastUpdate:  time.Now().Add(-45 * time.Second), // 45s old
    RouteHops:       3,                                   // 3-hop route
    OutputAmount:    140_000_000,                        // 140 USDC
    InputAmount:     1_000_000_000,                      // 1 SOL
    PriceImpactBps:  500,                                // 5%
    Pool: &Pool{Depth: 500_000_000},                    // 500 SOL (low depth)
    Provider:        "Jupiter",
}

oracle := &OraclePrice{PriceUSD: 154.0}  // 9% deviation

Expected Confidence Factors:

// 1. Pool State Age: 45s old β†’ 1.0 - (45/60) = 0.25
poolAgeFactor := 0.25

// 2. Route Hop Count: 3 hops β†’ 1.0 - (2 * 0.2) = 0.6
routeFactor := 0.6

// 3. Oracle Deviation: (140-154)/154 = -9% β†’ 1.0 - (0.09 * 10) = 0.1
oracleFactor := 0.1

// 4. Provider Reliability: Jupiter 95% uptime β†’ 0.95
providerFactor := 0.95

// 5. Slippage vs Depth: 5% actual vs expected 1% β†’ ratio = 0.2
slippageFactor := 0.2

// Weighted sum
confidence := 0.25*0.30 + 0.6*0.20 + 0.1*0.30 + 0.95*0.10 + 0.2*0.10
            = 0.075 + 0.12 + 0.03 + 0.095 + 0.02
            = 0.34

Assertions:

  • βœ… confidence < 0.5 (low confidence)
  • βœ… poolAgeFactor < 0.5
  • βœ… oracleFactor < 0.2
  • βœ… Scanner decision: Strategy::Skip

Acceptance Criteria:

  • Pass: Confidence in [0.3, 0.5]
  • Fail: Confidence > 0.5

Test 2.9.3: Deterministic Calculation (Same Inputs β†’ Same Output)

Scenario: Same quote inputs should always produce same confidence score

Test:

func TestConfidenceCalculator_Deterministic(t *testing.T) {
    calc := confidence.NewCalculator()
    quote := createTestQuote()
    oracle := createTestOracle()

    // Calculate 100 times
    scores := make([]float64, 100)
    for i := 0; i < 100; i++ {
        scores[i] = calc.Calculate(quote, oracle)
    }

    // All scores must be identical
    for i := 1; i < 100; i++ {
        assert.Equal(t, scores[0], scores[i],
            "Confidence calculation is not deterministic")
    }
}

Assertions:

  • βœ… All 100 calculations produce identical score
  • βœ… No randomness or time-dependent factors
  • βœ… Score is pure function of inputs

Acceptance Criteria:

  • Pass: 100% identical scores
  • Fail: Any variation in scores

Test 2.9.4: Scanner Decision Thresholds

Scenario: Validate that scanner correctly maps confidence to execution strategy

Test:

#[test]
fn test_scanner_decision_thresholds() {
    let test_cases = vec![
        (0.95, Strategy::Execute),      // High confidence
        (0.85, Strategy::Verify),       // Medium-high
        (0.75, Strategy::Verify),       // Medium
        (0.65, Strategy::Cautious),     // Medium-low
        (0.55, Strategy::Cautious),     // Low
        (0.45, Strategy::Skip),         // Very low
        (0.25, Strategy::Skip),         // Reject
    ];

    for (confidence, expected_strategy) in test_cases {
        let strategy = match confidence {
            0.9..=1.0  => Strategy::Execute,
            0.7..=0.9  => Strategy::Verify,
            0.5..=0.7  => Strategy::Cautious,
            _          => Strategy::Skip,
        };

        assert_eq!(strategy, expected_strategy,
            "Wrong strategy for confidence {}", confidence);
    }
}

Assertions:

  • βœ… Confidence β‰₯0.9 β†’ Execute
  • βœ… Confidence 0.7-0.9 β†’ Verify
  • βœ… Confidence 0.5-0.7 β†’ Cautious
  • βœ… Confidence <0.5 β†’ Skip

Acceptance Criteria:

  • Pass: All thresholds correct
  • Fail: Any misclassification

Integration Test: End-to-End Confidence Flow

Scenario: Quote generation β†’ Confidence calculation β†’ Scanner decision β†’ Trade execution

Test:

#[tokio::test]
async fn test_confidence_based_arbitrage_detection() {
    // 1. Generate quotes (local + external)
    let local_quote = local_service.get_quote(pair).await.unwrap();
    let external_quote = external_service.get_quote(pair).await.unwrap();

    // 2. Aggregate with confidence scoring
    let aggregated = aggregator.merge_quotes(local_quote, external_quote).await.unwrap();

    // 3. Scanner decision based on confidence
    let decision = scanner.decide(&aggregated);

    // 4. Validate decision logic
    if aggregated.local_confidence > 0.9 && aggregated.external_confidence < 0.5 {
        assert_eq!(decision, Strategy::Execute);
        assert_eq!(aggregated.best_source, QuoteSource::LOCAL);
    }
}

Code Coverage Target

  • Line Coverage: >95% for ConfidenceCalculator
  • Branch Coverage: 100% (all factor combinations)
  • Integration Coverage: Full quote β†’ confidence β†’ decision flow

Files to Create

  • go/internal/quote-aggregator-service/confidence/calculator_test.go (ENHANCE)
  • go/internal/quote-aggregator-service/confidence/integration_test.go (NEW)
  • rust/scanner/src/confidence/decision_test.rs (NEW)

2.10 1-Second AMM Refresh Tests (NEW) ⭐ PERFORMANCE

Priority: P1 - QUICK WIN VALIDATION Estimated Effort: 3 hours Review Source: Gemini critique Design Doc: 30.3-REFRESH-RATE-ANALYSIS.md

Purpose

Validate that AMM pools refresh every 1 second (not 10s) and measure opportunity capture rate improvement.

Test Cases

Test 2.10.1: Refresh Frequency Validation

Scenario: Monitor AMM pool refresh for 10 seconds, expect 10 refresh cycles

Test:

func TestAMMRefreshFrequency(t *testing.T) {
    manager := refresh.NewManager(1 * time.Second) // 1s interval

    refreshEvents := make(chan time.Time, 20)
    manager.OnRefresh(func(poolID string, timestamp time.Time) {
        refreshEvents <- timestamp
    })

    manager.Start()
    time.Sleep(10 * time.Second)
    manager.Stop()

    // Should have ~10 refresh events (Β±1 for timing jitter)
    assert.InDelta(t, 10, len(refreshEvents), 1,
        "Expected 10 refreshes in 10 seconds")

    // Validate intervals between refreshes
    timestamps := drainChannel(refreshEvents)
    for i := 1; i < len(timestamps); i++ {
        interval := timestamps[i].Sub(timestamps[i-1])
        assert.InDelta(t, 1000, interval.Milliseconds(), 100,
            "Refresh interval should be 1s Β±100ms")
    }
}

Assertions:

  • βœ… 10 refresh cycles in 10 seconds (Β±1)
  • βœ… Each interval is 1s Β±100ms
  • βœ… No missed refreshes

Acceptance Criteria:

  • Pass: 9-11 refreshes in 10 seconds
  • Fail: <9 or >11 refreshes

Test 2.10.2: Opportunity Capture Rate Improvement

Scenario: Simulate price changes every 5s, measure detection time

Test:

func TestOpportunityCaptureRate(t *testing.T) {
    // Baseline: 10s refresh
    baseline := simulateArbitrageDetection(10 * time.Second)

    // Enhanced: 1s refresh
    enhanced := simulateArbitrageDetection(1 * time.Second)

    // Calculate capture rates
    baselineCaptureRate := float64(baseline.Detected) / float64(baseline.Total)
    enhancedCaptureRate := float64(enhanced.Detected) / float64(enhanced.Total)

    // Should see significant improvement
    assert.Greater(t, enhancedCaptureRate, 0.95,
        "Enhanced capture rate should be >95%")
    assert.InDelta(t, 0.90, baselineCaptureRate, 0.05,
        "Baseline capture rate should be ~90%")

    improvement := (enhancedCaptureRate - baselineCaptureRate) / baselineCaptureRate
    assert.Greater(t, improvement, 0.05,
        "Should see >5% improvement")
}

func simulateArbitrageDetection(refreshInterval time.Duration) CaptureStats {
    opportunities := generatePriceChanges(5 * time.Second) // Every 5s
    detected := 0

    for _, opp := range opportunities {
        // Simulate refresh delay (worst case = refresh interval)
        detectionDelay := rand.Intn(int(refreshInterval.Milliseconds()))

        if detectionDelay < opp.WindowMs {
            detected++  // Captured
        }
    }

    return CaptureStats{Total: len(opportunities), Detected: detected}
}

Assertions:

  • βœ… Baseline (10s): ~90% capture rate
  • βœ… Enhanced (1s): >95% capture rate
  • βœ… Improvement: >5%

Acceptance Criteria:

  • Pass: Enhanced capture rate β‰₯95%
  • Fail: Enhanced capture rate <95%

Test 2.10.3: Redis Load Impact

Scenario: Measure Redis read load increase from 10s β†’ 1s refresh

Test:

func TestRedisLoadImpact(t *testing.T) {
    redisMonitor := startRedisMonitor()

    // Measure baseline (10s)
    manager := refresh.NewManager(10 * time.Second)
    manager.Start()
    time.Sleep(30 * time.Second)
    baselineLoad := redisMonitor.GetReadRate()
    manager.Stop()

    // Measure enhanced (1s)
    manager = refresh.NewManager(1 * time.Second)
    manager.Start()
    time.Sleep(30 * time.Second)
    enhancedLoad := redisMonitor.GetReadRate()
    manager.Stop()

    // Load should increase by ~10Γ— (10s β†’ 1s)
    loadIncrease := enhancedLoad / baselineLoad
    assert.InDelta(t, 10.0, loadIncrease, 2.0,
        "Redis load should increase ~10Γ—")

    // But absolute increase should be small
    absoluteIncrease := enhancedLoad - baselineLoad
    assert.Less(t, absoluteIncrease, 50.0, // <50 reads/sec increase
        "Absolute Redis load increase should be <50 req/s")
}

Assertions:

  • βœ… Load increases ~10Γ— (expected)
  • βœ… Absolute increase <50 reads/sec
  • βœ… Redis CPU usage <5% increase

Acceptance Criteria:

  • Pass: Redis load increase acceptable (<50 req/s)
  • Fail: Redis becomes bottleneck (>100 req/s increase)

Code Coverage Target

  • Line Coverage: >85% for RefreshManager
  • Timing Coverage: All refresh intervals tested

Files to Create

  • go/internal/local-quote-service/refresh/manager_1s_test.go (NEW)
  • tests/integration/refresh_rate_validation_test.go (NEW)

2.11 Parallel Paired Quote Tests (NEW) ⭐ CRITICAL

Priority: P0 - CORRECTNESS Estimated Effort: 4 hours Review Source: ChatGPT praise #1 (Exceptional feature) Design Doc: 30-QUOTE-SERVICE-ARCHITECTURE.md Section 5.3

Purpose

Validate that parallel paired quote calculation (forward + reverse) uses the same pool snapshot and eliminates fake arbitrage from slot drift.

Test Cases

Test 2.11.1: Same Pool Snapshot for Forward + Reverse

Scenario: Both quotes must use identical pool state

Test:

func TestPairedQuotes_SamePoolSnapshot(t *testing.T) {
    service := local_quote.NewService()

    // Get paired quotes
    paired, err := service.CalculatePairedQuotes(
        SOL_MINT, USDC_MINT, 1_000_000_000,
    )
    assert.NoError(t, err)

    // βœ… Both quotes must reference same pool ID
    assert.Equal(t, paired.Forward.PoolID, paired.Reverse.PoolID,
        "Forward and reverse must use same pool")

    // βœ… Both quotes must have same pool state timestamp
    assert.Equal(t, paired.Forward.PoolStateAge, paired.Reverse.PoolStateAge,
        "Pool state age must be identical")

    // βœ… Verify arbitrage consistency (no slot drift)
    // If SOL→USDC→SOL is profitable, it should be real, not from slot drift
    initialSOL := 1_000_000_000  // 1 SOL
    finalSOL := (paired.Forward.OutputAmount * initialSOL) / paired.Reverse.OutputAmount

    profit := float64(finalSOL - initialSOL) / float64(initialSOL)

    // Real arbitrage should be consistent
    if profit > 0.001 { // >0.1% profit
        // Verify pool reserves support this
        assert.True(t, validateArbitrageWithPoolState(paired),
            "Arbitrage profit must be supported by pool state")
    }
}

Assertions:

  • βœ… Same pool ID for both quotes
  • βœ… Same pool state timestamp
  • βœ… No fake arbitrage from slot drift

Acceptance Criteria:

  • Pass: Identical pool snapshots
  • Fail: Different pool states

Test 2.11.2: Parallel Execution Performance (2Γ— Speedup)

Scenario: Parallel should be ~2Γ— faster than sequential

Test:

func BenchmarkPairedQuotes_Sequential(b *testing.B) {
    service := local_quote.NewService()

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        // Sequential
        forward, _ := service.CalculateQuote(SOL_MINT, USDC_MINT, 1_000_000_000)
        reverse, _ := service.CalculateQuote(USDC_MINT, SOL_MINT, forward.OutputAmount)
    }
}

func BenchmarkPairedQuotes_Parallel(b *testing.B) {
    service := local_quote.NewService()

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        // Parallel
        paired, _ := service.CalculatePairedQuotes(SOL_MINT, USDC_MINT, 1_000_000_000)
    }
}

func TestPairedQuotes_SpeedupRatio(t *testing.T) {
    sequentialTime := testing.Benchmark(BenchmarkPairedQuotes_Sequential).NsPerOp()
    parallelTime := testing.Benchmark(BenchmarkPairedQuotes_Parallel).NsPerOp()

    speedup := float64(sequentialTime) / float64(parallelTime)

    // Should be ~2Γ— faster (within margin for overhead)
    assert.Greater(t, speedup, 1.5,
        "Parallel should be at least 1.5Γ— faster")
    assert.Less(t, speedup, 2.5,
        "Speedup should not exceed 2.5Γ— (indicates measurement error)")
}

Assertions:

  • βœ… Parallel is 1.5-2.5Γ— faster than sequential
  • βœ… Latency: Sequential >100ms, Parallel <60ms

Acceptance Criteria:

  • Pass: 1.5Γ— < speedup < 2.5Γ—
  • Fail: Speedup <1.5Γ—

Code Coverage Target

  • Line Coverage: >90% for CalculatePairedQuotes()
  • Concurrency Coverage: Goroutine synchronization tested

Files to Create

  • go/internal/local-quote-service/calculator/paired_calculator_test.go (NEW)
  • go/internal/external-quote-service/quoters/paired_quoter_test.go (NEW)

2.12 Explicit Timeout Tests (NEW) ⭐ CRITICAL

Priority: P0 - TAIL LATENCY Estimated Effort: 3 hours Review Source: ChatGPT critique #2 Design Doc: 30.4-CHATGPT-REVIEW-RESPONSE.md

Purpose

Validate that aggregator enforces explicit timeouts (local: 10ms, external: 100ms) and emits local-only results immediately.

Test Cases

Test 2.12.1: Local Quote Timeout Enforcement

Scenario: Local service is slow (>10ms), aggregator should timeout

Test:

func TestAggregator_LocalTimeoutEnforcement(t *testing.T) {
    // Mock slow local service (20ms)
    slowLocal := &MockLocalService{Delay: 20 * time.Millisecond}
    aggregator := NewAggregator(slowLocal, fastExternal)

    start := time.Now()
    quote, err := aggregator.GetQuote(ctx, request)
    elapsed := time.Since(start)

    // Should timeout after 10ms
    assert.Error(t, err, "Expected timeout error")
    assert.Contains(t, err.Error(), "timeout")
    assert.InDelta(t, 10, elapsed.Milliseconds(), 5,
        "Should timeout at 10ms Β±5ms")
}

Assertions:

  • βœ… Timeout triggers at 10ms Β±5ms
  • βœ… Error message indicates timeout
  • βœ… External quote not affected

Acceptance Criteria:

  • Pass: Timeout at 10ms
  • Fail: Waits longer than 15ms

Test 2.12.2: Non-Blocking Local-Only Emit

Scenario: Local quote fast (5ms), external slow (500ms), should emit local immediately

Test:

func TestAggregator_NonBlockingLocalEmit(t *testing.T) {
    fastLocal := &MockLocalService{Delay: 5 * time.Millisecond}
    slowExternal := &MockExternalService{Delay: 500 * time.Millisecond}

    aggregator := NewAggregator(fastLocal, slowExternal)
    stream := &MockStream{}

    go aggregator.StreamQuotes(request, stream)

    // βœ… Should emit local-only quote within 10ms
    select {
    case quote := <-stream.Quotes:
        assert.NotNil(t, quote.BestLocal)
        assert.Nil(t, quote.BestExternal, "External not ready yet")
        assert.Equal(t, QuoteSource_LOCAL, quote.BestSource)
    case <-time.After(15 * time.Millisecond):
        t.Fatal("Local quote not emitted within 15ms")
    }

    // βœ… Should emit updated quote with external after 500ms
    select {
    case quote := <-stream.Quotes:
        assert.NotNil(t, quote.BestLocal)
        assert.NotNil(t, quote.BestExternal, "External should be ready")
        assert.NotNil(t, quote.Comparison)
    case <-time.After(600 * time.Millisecond):
        t.Fatal("Updated quote not emitted")
    }
}

Assertions:

  • βœ… First emit: <15ms (local-only)
  • βœ… Second emit: ~500ms (with external)
  • βœ… Local never blocks on external

Acceptance Criteria:

  • Pass: Local-only emitted <15ms
  • Fail: First emit >15ms

Code Coverage Target

  • Line Coverage: >90% for timeout logic
  • Edge Case Coverage: All timeout paths tested

Files to Create

  • go/internal/quote-aggregator-service/aggregator/timeout_test.go (NEW)

πŸ“Š Summary of New Test Additions

Test CategoryPriorityEffortLOCCoverage Target
Torn Read PreventionP04h~300>90%
Confidence ScoringP04h~400>95%
1s AMM RefreshP13h~200>85%
Parallel Paired QuotesP04h~350>90%
Explicit TimeoutsP03h~250>90%
TOTAL-18h~1500>90%

🎯 Integration with Main Test Plan

These tests should be inserted into 26-QUOTE-SERVICE-TEST-PLAN.md as follows:

  1. Section 2.8: Torn Read Prevention Tests (after Shared Memory IPC tests)
  2. Section 2.9: Confidence Score Validation (after Quote Validation tests)
  3. Section 2.10: 1s AMM Refresh Tests (after Refresh Manager tests)
  4. Section 2.11: Parallel Paired Quote Tests (after Calculator tests)
  5. Section 2.12: Explicit Timeout Tests (after Aggregator tests)

βœ… Acceptance Criteria for Production

Before deploying to production, ALL of these tests must:

  • βœ… Pass with 0 failures
  • βœ… Achieve >90% code coverage
  • βœ… Run in CI/CD pipeline
  • βœ… Be documented in test report

Status: ⭐ CRITICAL - These tests are MANDATORY for production deployment


Last Updated: 2025-12-31 Document Version: 1.0 Status: Ready for implementation Next Action: Add these sections to main test plan before development starts