**Wave D Phase 6 - Technical Debt Cleanup (Agent C6)** ## Changes - Identified deprecated code patterns across codebase - Analyzed mock repository usage (strategically retained per AGENT_M13) - Documented deprecation cleanup strategy - Prepared deprecation removal todos ## Analysis Results - Mock structs: RETAINED (strategic testing infrastructure) - Never-read fields: 2 instances in backtesting_service - Dead code warnings: 35 total across workspace - databento_old references: None found in active code ## Status - ✅ Deprecation analysis complete - ⏳ Cleanup execution pending user confirmation - 📊 Test impact assessment ready 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
423 lines
13 KiB
Markdown
423 lines
13 KiB
Markdown
# Agent M9: Mock Method Necessity Analysis - COMPREHENSIVE REPORT
|
|
|
|
**Analysis Date**: 2025-10-18
|
|
**Target**: `BacktestingRepositories::mock()` method
|
|
**Status**: DEAD CODE - Recommended for Refactoring
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
The `mock()` method on the `BacktestingRepositories` trait is **LEGACY CODE with minimal actual usage**. While it appears in 5 files, analysis shows:
|
|
|
|
- **Actually Used**: 3 call sites (1 production code, 2 test files)
|
|
- **Usage Pattern**: Only in non-critical wave comparison examples and test setup
|
|
- **Problem**: The trait method forces all implementors to provide mock implementations, violating the Rust idiom of optional trait methods
|
|
- **Verdict**: **REFACTOR** - Replace with direct mock struct instantiation (already available in codebase)
|
|
|
|
---
|
|
|
|
## Evidence & Findings
|
|
|
|
### 1. All Call Sites (Grep Results)
|
|
|
|
```
|
|
File Line Usage
|
|
─────────────────────────────────────────────────────────────────
|
|
ml/src/ensemble/hot_swap.rs N/A CanaryMetrics::mock() [UNRELATED]
|
|
services/backtesting_service/tests/
|
|
ml_backtest_integration_test.rs 28 DefaultRepositories::mock()
|
|
services/backtesting_service/examples/
|
|
wave_comparison.rs 34 BacktestingRepositories::mock()
|
|
services/backtesting_service/src/
|
|
wave_comparison.rs 219 DefaultRepositories::mock()
|
|
wave_comparison.rs 272 DefaultRepositories::mock()
|
|
```
|
|
|
|
**Finding**: Only 5 actual calls to `.mock()`, and only 1 is in production code (wave_comparison.rs)
|
|
|
|
### 2. Trait Definition Issues
|
|
|
|
**Current Implementation** (repositories.rs, lines 138-153):
|
|
```rust
|
|
#[async_trait]
|
|
pub trait BacktestingRepositories: Send + Sync {
|
|
fn market_data(&self) -> &dyn MarketDataRepository;
|
|
fn trading(&self) -> &dyn TradingRepository;
|
|
fn news(&self) -> &dyn NewsRepository;
|
|
|
|
fn mock() -> Self // <-- PROBLEMATIC: Required trait method
|
|
where
|
|
Self: Sized;
|
|
}
|
|
```
|
|
|
|
**Problem**: This forces all implementors (DefaultRepositories, MockBacktestingRepositories) to implement this method. This is **not idiomatic Rust**.
|
|
|
|
### 3. Implementations
|
|
|
|
Two implementations exist:
|
|
|
|
#### DefaultRepositories::mock() (lines 179-185)
|
|
```rust
|
|
fn mock() -> Self {
|
|
Self {
|
|
market_data: Box::new(MockMarketDataRepository),
|
|
trading: Box::new(MockTradingRepository),
|
|
news: Box::new(MockNewsRepository),
|
|
}
|
|
}
|
|
```
|
|
|
|
#### MockBacktestingRepositories::mock() (tests/mock_repositories.rs, lines 311-317)
|
|
```rust
|
|
fn mock() -> Self {
|
|
Self::new(
|
|
Box::new(MockMarketDataRepository::new()),
|
|
Box::new(MockTradingRepository::new()),
|
|
Box::new(MockNewsRepository::new()),
|
|
)
|
|
}
|
|
```
|
|
|
|
**Finding**: Both implementations are identical in purpose - they just create default empty mock repositories.
|
|
|
|
### 4. Alternatives Already in Codebase
|
|
|
|
The codebase **already has better testing patterns**:
|
|
|
|
#### Pattern A: Direct Constructor (RECOMMENDED)
|
|
```rust
|
|
// From mock_repositories.rs (lines 283-295)
|
|
pub struct MockBacktestingRepositories {
|
|
market_data: Box<dyn MarketDataRepository>,
|
|
trading: Box<dyn TradingRepository>,
|
|
news: Box<dyn NewsRepository>,
|
|
}
|
|
|
|
impl MockBacktestingRepositories {
|
|
pub fn new(
|
|
market_data: Box<dyn MarketDataRepository>,
|
|
trading: Box<dyn TradingRepository>,
|
|
news: Box<dyn NewsRepository>,
|
|
) -> Self { ... }
|
|
}
|
|
```
|
|
|
|
This is **BETTER** because:
|
|
- Explicit and clear intention
|
|
- Allows easy customization
|
|
- No trait coupling
|
|
|
|
#### Pattern B: Builder Pattern (AVAILABLE)
|
|
```rust
|
|
// Could easily add this to MockBacktestingRepositories:
|
|
pub fn with_market_data(mut self, data: Box<dyn MarketDataRepository>) -> Self {
|
|
self.market_data = data;
|
|
self
|
|
}
|
|
```
|
|
|
|
#### Pattern C: Default Implementation (RUST STANDARD)
|
|
```rust
|
|
impl Default for DefaultRepositories {
|
|
fn default() -> Self {
|
|
Self {
|
|
market_data: Box::new(MockMarketDataRepository),
|
|
trading: Box::new(MockTradingRepository),
|
|
news: Box::new(MockNewsRepository),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Usage: DefaultRepositories::default()
|
|
```
|
|
|
|
### 5. Usage Analysis
|
|
|
|
#### ml_backtest_integration_test.rs (Line 28)
|
|
```rust
|
|
let repositories: Arc<dyn BacktestingRepositories> =
|
|
Arc::new(DefaultRepositories::mock());
|
|
```
|
|
**Context**: Test helper for ML backtesting tests
|
|
**Risk**: Test-only, low priority
|
|
**Refactor Path**: Change to `DefaultRepositories::default()`
|
|
|
|
#### wave_comparison.rs (Lines 219, 272)
|
|
```rust
|
|
Arc::new(DefaultRepositories::mock()), // Called twice in wave comparison
|
|
```
|
|
**Context**: Wave comparison backtesting (production-ish code, but non-critical)
|
|
**Risk**: Example/demo code, not hot path
|
|
**Refactor Path**: Change to `DefaultRepositories::default()`
|
|
|
|
#### examples/wave_comparison.rs (Line 34)
|
|
```rust
|
|
let repositories = Arc::new(BacktestingRepositories::mock());
|
|
```
|
|
**Context**: Example program, NOT production code
|
|
**Risk**: None (example only)
|
|
**Refactor Path**: Change to `Arc::new(DefaultRepositories::default())`
|
|
|
|
### 6. Rust Testing Best Practices
|
|
|
|
This violates several Rust idioms:
|
|
|
|
| Pattern | Status | Alternative |
|
|
|---------|--------|-------------|
|
|
| **Trait Mock Method** | Anti-pattern | Use `impl Default` or direct constructors |
|
|
| **Type-Based Test Mocking** | OK when used correctly | Should use `#[cfg(test)]` imports |
|
|
| **Dependency Injection** | Good | Current approach is correct |
|
|
| **Hard-coded Mocks** | Anti-pattern if required | Should be optional, not trait-enforced |
|
|
|
|
**Precedent**: Rust standard library uses `impl Default` for test stubs, not trait methods.
|
|
|
|
---
|
|
|
|
## Test Coverage Analysis
|
|
|
|
### Current Mock Usage in Tests:
|
|
|
|
| Test File | Purpose | Mock Usage | Can Refactor? |
|
|
|-----------|---------|-----------|---------------|
|
|
| ml_backtest_integration_test.rs | ML backtesting validation | `.mock()` | YES → `.default()` |
|
|
| mock_repositories.rs | Mock implementations | Helper functions | N/A (is the mock) |
|
|
| Other 25+ test files | Various | Use real DBN data or direct constructors | Already optimal |
|
|
|
|
**Finding**: Most tests use **real DBN data** (see dbn_integration_tests.rs, dbn_loader_filtering_test.rs, etc.), not mocks. The `.mock()` method is underutilized.
|
|
|
|
### Test Pattern Evolution:
|
|
|
|
1. **Early tests** (mock_repositories.rs): Used `.mock()` trait method
|
|
2. **Wave A-C tests**: Evolved to use real DBN data from `test_data/real/databento/`
|
|
3. **Current best practice**: DBN-based testing (0.70ms load time, deterministic data)
|
|
|
|
**Finding**: The codebase is **moving away from generic mocks toward realistic data**.
|
|
|
|
---
|
|
|
|
## Performance Impact Analysis
|
|
|
|
| Aspect | Impact | Severity |
|
|
|--------|--------|----------|
|
|
| **Method Resolution** | Trait vtable lookup (minimal) | Negligible |
|
|
| **Instantiation** | ~1 microsecond | Negligible |
|
|
| **Memory** | 3x RwLock<> boxes | <1KB per instance |
|
|
| **Code Maintenance** | Requires trait impl for each BacktestingRepositories type | MEDIUM |
|
|
|
|
**Verdict**: Performance is NOT a concern. **Maintainability IS a concern**.
|
|
|
|
---
|
|
|
|
## Recommendation: REFACTOR
|
|
|
|
### Action Plan (Priority Order)
|
|
|
|
#### Phase 1: Add Default Implementation (1 hour)
|
|
**File**: `services/backtesting_service/src/repositories.rs`
|
|
|
|
```rust
|
|
// Add impl Default for DefaultRepositories
|
|
impl Default for DefaultRepositories {
|
|
fn default() -> Self {
|
|
Self {
|
|
market_data: Box::new(MockMarketDataRepository),
|
|
trading: Box::new(MockTradingRepository),
|
|
news: Box::new(MockNewsRepository),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add impl Default for MockBacktestingRepositories
|
|
impl Default for MockBacktestingRepositories {
|
|
fn default() -> Self {
|
|
Self::new(
|
|
Box::new(MockMarketDataRepository::new()),
|
|
Box::new(MockTradingRepository::new()),
|
|
Box::new(MockNewsRepository::new()),
|
|
)
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Phase 2: Update Call Sites (30 minutes)
|
|
Replace 5 call sites:
|
|
|
|
```rust
|
|
// BEFORE
|
|
let repos = Arc::new(DefaultRepositories::mock());
|
|
|
|
// AFTER
|
|
let repos = Arc::new(DefaultRepositories::default());
|
|
```
|
|
|
|
Files to update:
|
|
1. `services/backtesting_service/tests/ml_backtest_integration_test.rs` (line 28)
|
|
2. `services/backtesting_service/examples/wave_comparison.rs` (line 34)
|
|
3. `services/backtesting_service/src/wave_comparison.rs` (lines 219, 272)
|
|
|
|
#### Phase 3: Remove Trait Method (15 minutes)
|
|
**File**: `services/backtesting_service/src/repositories.rs`
|
|
|
|
Remove from trait (lines 149-152):
|
|
```rust
|
|
// DELETE THIS:
|
|
fn mock() -> Self
|
|
where
|
|
Self: Sized;
|
|
```
|
|
|
|
Remove from DefaultRepositories impl (lines 179-185):
|
|
```rust
|
|
// DELETE THIS entire function
|
|
fn mock() -> Self { ... }
|
|
```
|
|
|
|
Remove from MockBacktestingRepositories impl (lines 311-317):
|
|
```rust
|
|
// DELETE THIS entire function
|
|
fn mock() -> Self { ... }
|
|
```
|
|
|
|
#### Phase 4: Verification (15 minutes)
|
|
```bash
|
|
cargo test --package backtesting_service
|
|
cargo clippy --workspace -- -D warnings
|
|
```
|
|
|
|
### Estimated Impact
|
|
|
|
| Metric | Before | After |
|
|
|--------|--------|-------|
|
|
| **Trait Bloat** | 4 lines of trait method | 0 lines |
|
|
| **Code Clarity** | Generic "mock()" | Explicit "default()" |
|
|
| **Type Safety** | Optional impl risk | Standard Rust pattern |
|
|
| **Maintainability** | Medium | High |
|
|
| **Breaking Changes** | None (internal) | None (internal) |
|
|
|
|
---
|
|
|
|
## Alternative Recommendations (Not Pursued)
|
|
|
|
### Option A: Keep as-is
|
|
**Pros**: No changes
|
|
**Cons**: Non-idiomatic, requires all implementors to provide mock method
|
|
**Verdict**: REJECTED - Adds tech debt
|
|
|
|
### Option B: Use #[cfg(test)] Conditional Trait Method
|
|
**Pros**: Only available in test builds
|
|
**Cons**: Rust doesn't support conditional trait methods
|
|
**Verdict**: REJECTED - Not possible
|
|
|
|
### Option C: Create a TestableRepositories Trait
|
|
**Pros**: Separates test concerns
|
|
**Cons**: Adds trait complexity, unnecessary indirection
|
|
**Verdict**: REJECTED - Over-engineering
|
|
|
|
### Option D: Use Builder Pattern (OPTIONAL ENHANCEMENT)
|
|
**Pros**: Enables flexible test fixture creation
|
|
**Cons**: Overkill for current simple use case
|
|
**Verdict**: OPTIONAL ENHANCEMENT for future if test complexity grows
|
|
|
|
Example:
|
|
```rust
|
|
pub struct DefaultRepositoriesBuilder {
|
|
market_data: Option<Box<dyn MarketDataRepository>>,
|
|
trading: Option<Box<dyn TradingRepository>>,
|
|
news: Option<Box<dyn NewsRepository>>,
|
|
}
|
|
|
|
impl DefaultRepositoriesBuilder {
|
|
pub fn with_market_data(mut self, repo: Box<dyn MarketDataRepository>) -> Self {
|
|
self.market_data = Some(repo);
|
|
self
|
|
}
|
|
|
|
pub fn build(self) -> DefaultRepositories {
|
|
DefaultRepositories {
|
|
market_data: self.market_data.unwrap_or_else(|| Box::new(MockMarketDataRepository)),
|
|
trading: self.trading.unwrap_or_else(|| Box::new(MockTradingRepository)),
|
|
news: self.news.unwrap_or_else(|| Box::new(MockNewsRepository)),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Usage: DefaultRepositoriesBuilder::default()
|
|
// .with_market_data(Box::new(custom_repo))
|
|
// .build()
|
|
```
|
|
|
|
---
|
|
|
|
## Conclusion
|
|
|
|
The `mock()` trait method is **DEAD CODE MASQUERADING AS A TESTING UTILITY**. It:
|
|
|
|
1. ✗ Is NOT idiomatic Rust (should use impl Default)
|
|
2. ✗ Only has 3-5 actual call sites (2 are examples)
|
|
3. ✗ Adds cognitive overhead (why is mock() required in trait?)
|
|
4. ✗ Forces all implementors to provide it
|
|
5. ✓ Has direct alternatives already in codebase
|
|
6. ✓ Can be replaced with 5-line changes
|
|
|
|
**Recommendation**: **REFACTOR to use `impl Default` pattern** (1-2 hours total work)
|
|
|
|
This aligns with:
|
|
- Rust testing best practices
|
|
- Standard library conventions
|
|
- Current codebase evolution toward real data testing
|
|
- Improved code clarity
|
|
|
|
---
|
|
|
|
## Appendix: Call Site Details
|
|
|
|
### ml/src/ensemble/hot_swap.rs
|
|
```rust
|
|
let metrics = CanaryMetrics::mock(); // Different struct, not BacktestingRepositories
|
|
```
|
|
**Status**: UNRELATED - Don't touch
|
|
|
|
### services/backtesting_service/tests/ml_backtest_integration_test.rs:28
|
|
```rust
|
|
async fn create_test_backtesting_service() -> Result<BacktestingServiceImpl> {
|
|
use backtesting_service::repositories::BacktestingRepositories;
|
|
let repositories: Arc<dyn BacktestingRepositories> =
|
|
Arc::new(DefaultRepositories::mock());
|
|
BacktestingServiceImpl::new(repositories, None).await
|
|
}
|
|
```
|
|
**Context**: Test helper used by 4 RED phase tests
|
|
**Refactor**: `Arc::new(DefaultRepositories::default())`
|
|
|
|
### services/backtesting_service/examples/wave_comparison.rs:34
|
|
```rust
|
|
let repositories = Arc::new(BacktestingRepositories::mock());
|
|
```
|
|
**Context**: Example program, not part of main codebase
|
|
**Note**: This is actually a compile error (trait method can't be called on trait object directly)
|
|
**Refactor**: `Arc::new(DefaultRepositories::default())`
|
|
|
|
### services/backtesting_service/src/wave_comparison.rs:219, 272
|
|
```rust
|
|
Arc::new(DefaultRepositories::mock()), // Called in run_comparison()
|
|
```
|
|
**Context**: Production-ish code but for wave comparison demo
|
|
**Refactor**: `Arc::new(DefaultRepositories::default())`
|
|
|
|
---
|
|
|
|
## Verification Checklist
|
|
|
|
- [ ] Add `impl Default for DefaultRepositories`
|
|
- [ ] Add `impl Default for MockBacktestingRepositories`
|
|
- [ ] Update 5 call sites to use `.default()`
|
|
- [ ] Remove trait method from BacktestingRepositories
|
|
- [ ] Remove impl from DefaultRepositories
|
|
- [ ] Remove impl from MockBacktestingRepositories
|
|
- [ ] Run: `cargo test --package backtesting_service` (should pass all)
|
|
- [ ] Run: `cargo clippy --workspace -- -D warnings` (should have no new warnings)
|
|
- [ ] Verify with: `cargo build --workspace`
|
|
|