-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Labels
Milestone
Description
Problem
The codebase evolved from bash scripts to Rust CLI, resulting in:
- Monolithic command functions that are hard to test in isolation
- Heavy coupling to external services (GitHub API, Docker, S3, etc.)
- Side effects scattered throughout (file I/O, network calls, command execution)
- Limited ability to test error conditions and edge cases
- No clear separation between business logic and I/O
Current State Analysis
Hard-to-test patterns:
- Commands directly instantiate external clients (Octocrab, ObjectStore, etc.)
- Business logic intertwined with I/O operations
- No dependency injection or trait-based abstractions
- Tests require real git repositories and cargo workspaces
- Parallel execution logic hard to test without real semaphores
Proposed Refactoring Strategy
Phase 1: Extract Core Business Logic
1.1 Create domain models
// src/domain/mod.rs
pub mod package; // Pure Package representations
pub mod dependency; // Dependency graph logic
pub mod change_set; // Changed packages computation
pub mod publish_plan; // What to publish, where, how1.2 Define service traits
// src/services/traits.rs
pub trait GitService {
fn diff_files(&self, base: &str, head: &str) -> Result<Vec<PathBuf>>;
fn resolve_ref(&self, ref_name: &str) -> Result<String>;
}
pub trait GithubService {
async fn find_release(&self, tag: &str) -> Result<Option<Release>>;
async fn upload_asset(&self, release_id: u64, name: &str, data: Vec<u8>) -> Result<()>;
}
pub trait CargoRegistry {
async fn package_exists(&self, name: &str, version: &str) -> Result<bool>;
async fn publish(&self, package_path: &Path, registry: &str) -> Result<()>;
}1.3 Implement production and test implementations
Phase 2: Refactor Commands to Use Services
Extract testable functions from monolithic commands. Split into:
- Pure planning functions (no I/O)
- Execution functions using service traits (testable with mocks)
Phase 3: Improve CrateGraph Testability
Make CrateGraph independent of git2 by extracting git operations to a trait.
Implementation Plan
Week 1-2: Foundation
- Create
src/domain/with core business models - Define service traits in
src/services/traits.rs - Create
src/testing/with test utilities and mocks - Add builder patterns for test fixtures
Week 3-4: Refactor CrateGraph
- Extract git operations to
GitServicetrait - Make
CrateGraph::affected_packagesuse trait instead ofgit2::Repository - Write comprehensive tests for dependency graph logic
- Add property-based tests for change detection
Week 5-6: Refactor Publish Command
- Extract publish planning logic (pure functions)
- Implement service interfaces for GitHub, Cargo registry
- Refactor
publish/mod.rsto use services - Write unit tests for publish planning
- Write integration tests with mocked services
Week 7-8: Refactor Tests Command
- Extract test execution planning
- Abstract database and container management
- Implement testable parallel execution
- Add tests for test filtering and selection
Week 9-10: Remaining Commands
- Apply pattern to
check_workspace,fix_lock_files, etc. - Update all commands to use
CommandContext - Ensure 80%+ test coverage for business logic
Success Metrics
- 80%+ unit test coverage for business logic (not including I/O)
- All commands have integration tests with mocked external services
- Test execution time < 30s
- Can test error conditions without real external services
- New features can be developed with TDD
Breaking Changes
- Internal refactoring only; CLI interface remains the same
- May need to update some internal imports if used by external consumers
Labels
refactoring, testing, priority:high