fslabs / fslabs/fslabscli

Refactor for Testability - Core Architecture

Open
#209 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement priority:high
Dominant language
Rust
Stars
5
Forks
1
Avg merge
17h 48m
Merged PRs (30d)
2

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:

  1. Commands directly instantiate external clients (Octocrab, ObjectStore, etc.)
  2. Business logic intertwined with I/O operations
  3. No dependency injection or trait-based abstractions
  4. Tests require real git repositories and cargo workspaces
  5. 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, how

1.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 GitService trait
  • Make CrateGraph::affected_packages use trait instead of git2::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.rs to 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

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read the proposed structure in src/domain/, src/services/traits.rs, and src/testing/, then inspect CrateGraph and publish/mod.rs to understand the current command and I/O boundaries. The plan spans service traits, mocks, command refactors, and broad test coverage; done means the listed phases and success metrics are completed without changing the CLI interface.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, testing-qa, tooling
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.