Skip to content

Commit da70a0a

Browse files
committed
feat: implement test_trunk_based_development_flow
1 parent d4738bb commit da70a0a

File tree

5 files changed

+320
-67
lines changed

5 files changed

+320
-67
lines changed

.claude/plan/34-zerv-flow-implementation-steps.md

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Zerv Flow Implementation Plan
22

3-
**Status**: In Progress
3+
**Status**: Phase 2 Complete
44
**Priority**: High
55
**Context**: Step-by-step implementation plan for the `zerv flow` command based on CLI design in document #33.
66

7-
## Current Progress Summary (as of Oct 29, 2025)
7+
## Current Progress Summary (as of Oct 30, 2025)
88

99
### **Completed Work:**
1010

@@ -22,17 +22,32 @@
2222
- Eliminated code duplication between commands
2323
- Comprehensive test coverage (35 tests) for shared components
2424

25-
- **Code Quality Improvements**: Completed
25+
- **Code Quality Improvements**: Completed
2626
- Added BoolResolution utility for opposing boolean flags
2727
- Cleaned up verbose documentation comments
2828
- Fixed inconsistent module structures
2929
- Added comprehensive test coverage to common args
3030

31+
- **Phase 2**: Flow Logic as Translation Layer - 100% Complete ✅
32+
- **Step 4**: Basic Flow Pipeline Implementation ✅
33+
- Successfully integrates with existing `zerv version` command
34+
- RON output parsing to Zerv object working correctly
35+
- `OutputFormatter::format_output()` integration complete
36+
- Translation layer approach proven successful
37+
38+
- **Test Infrastructure**: Completed ✅
39+
- Pattern assertion utilities in `src/test_utils/pattern_assertions.rs`
40+
- Enhanced GitRepoFixture with branch and dirty operations:
41+
- `with_distance_and_branch(tag, commits, branch)`
42+
- `checkout_branch(branch)`
43+
- `make_dirty()`
44+
- Step-by-step git flow testing with comprehensive validation
45+
- All tests passing (10+ assertion tests, 9 fixture tests, 1 flow test)
46+
3147
### 🔄 **Next Steps:**
3248

33-
- **Phase 2**: Implement basic flow pipeline structure with version command integration (IN PROGRESS)
34-
- **Phase 3**: Add branch pattern system and flow-to-version translation logic
35-
- **Phase 4**: Complete pipeline assembly and comprehensive testing
49+
- **Phase 3**: Add branch pattern system and flow-to-version translation logic (PENDING)
50+
- **Phase 4**: Complete pipeline assembly and comprehensive testing (PENDING)
3651

3752
## Goals
3853

src/cli/flow/pipeline.rs

Lines changed: 61 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
use ron::from_str;
22

3-
use crate::cli::common::args::{
4-
InputConfig,
5-
OutputConfig,
6-
};
3+
use crate::cli::common::args::OutputConfig;
74
use crate::cli::flow::args::FlowArgs;
85
use crate::cli::utils::output_formatter::OutputFormatter;
96
use crate::cli::version::args::VersionArgs;
107
use crate::cli::version::pipeline::run_version_pipeline;
118
use crate::error::ZervError;
12-
use crate::utils::constants::*;
139

1410
/// Main flow pipeline handler
1511
///
@@ -28,11 +24,7 @@ pub fn run_flow_pipeline(args: FlowArgs) -> Result<String, ZervError> {
2824
// For now, just call version command with zerv output format
2925
// TODO: Phase 2+ - Translate flow arguments to version arguments
3026
let version_args = VersionArgs {
31-
input: InputConfig {
32-
source: sources::GIT.to_string(),
33-
input_format: "auto".to_string(),
34-
directory: None,
35-
},
27+
input: args.input.clone(),
3628
output: OutputConfig {
3729
output_format: "zerv".to_string(),
3830
output_template: None,
@@ -64,66 +56,74 @@ pub fn run_flow_pipeline(args: FlowArgs) -> Result<String, ZervError> {
6456
#[cfg(test)]
6557
mod tests {
6658
use super::*;
59+
use crate::test_utils::{
60+
GitRepoFixture,
61+
assert_version_expectation,
62+
should_run_docker_tests,
63+
};
6764

68-
#[test]
69-
fn test_run_flow_pipeline_basic() {
70-
let args = FlowArgs::default();
71-
let result = run_flow_pipeline(args);
72-
// Should work now (assuming git repo exists)
73-
// Note: This test may fail in environments without git
74-
match result {
75-
Ok(_) => {} // Success
76-
Err(ZervError::VcsNotFound(_)) => {
77-
// Expected in test environments without git
78-
}
79-
Err(e) => {
80-
panic!("Unexpected error: {:?}", e);
81-
}
82-
}
83-
}
84-
85-
#[test]
86-
fn test_run_flow_pipeline_different_output_formats() {
87-
let formats = ["semver", "pep440", "zerv"];
65+
fn test_flow_pipeline_with_fixture(
66+
fixture_path: &str,
67+
semver_expectation: &str,
68+
pep440_expectation: &str,
69+
) {
70+
let test_cases = vec![
71+
("semver", semver_expectation),
72+
("pep440", pep440_expectation),
73+
];
8874

89-
for format in formats.iter() {
75+
for (format_name, expectation) in test_cases {
9076
let mut args = FlowArgs::default();
91-
args.output.output_format = format.to_string();
77+
args.input.directory = Some(fixture_path.to_string());
78+
args.output.output_format = format_name.to_string();
9279

9380
let result = run_flow_pipeline(args);
94-
match result {
95-
Ok(_) => {} // Success
96-
Err(ZervError::VcsNotFound(_)) => {
97-
// Expected in test environments without git
98-
}
99-
Err(e) => {
100-
panic!("Unexpected error for format '{}': {:?}", format, e);
101-
}
102-
}
81+
assert!(
82+
result.is_ok(),
83+
"Flow pipeline should succeed with {} format at {}",
84+
format_name,
85+
fixture_path
86+
);
87+
let output = result.unwrap();
88+
assert!(
89+
!output.is_empty(),
90+
"Flow pipeline should produce output for {} format",
91+
format_name
92+
);
93+
94+
assert_version_expectation(expectation, &output);
95+
96+
println!("✓ Flow pipeline output ({}): {}", format_name, output);
10397
}
10498
}
10599

106100
#[test]
107-
fn test_run_flow_pipeline_with_output_prefix() {
108-
let mut args = FlowArgs::default();
109-
args.output.output_prefix = Some("v".to_string());
110-
111-
let result = run_flow_pipeline(args);
112-
match result {
113-
Ok(output) => {
114-
// Output should start with the prefix
115-
assert!(
116-
output.starts_with('v'),
117-
"Output should start with prefix 'v': {}",
118-
output
119-
);
120-
}
121-
Err(ZervError::VcsNotFound(_)) => {
122-
// Expected in test environments without git
123-
}
124-
Err(e) => {
125-
panic!("Unexpected error: {:?}", e);
126-
}
101+
fn test_trunk_based_development_flow() {
102+
if !should_run_docker_tests() {
103+
return; // Skip when `ZERV_TEST_DOCKER` are disabled
127104
}
105+
106+
// Step 1: Start with a clean tag
107+
let fixture =
108+
GitRepoFixture::tagged("v1.0.0").expect("Failed to create git fixture with tag");
109+
let fixture_path = fixture.path().to_string_lossy();
110+
111+
test_flow_pipeline_with_fixture(&fixture_path, r"1.0.0", r"1.0.0");
112+
113+
// Step 2: Checkout feature branch
114+
fixture
115+
.checkout_branch("feature-1")
116+
.expect("Failed to checkout feature-1 branch");
117+
118+
test_flow_pipeline_with_fixture(&fixture_path, r"1.0.0", r"1.0.0");
119+
120+
// Step 3: Make dirty working directory
121+
fixture.make_dirty().expect("Failed to make fixture dirty");
122+
123+
test_flow_pipeline_with_fixture(
124+
&fixture_path,
125+
r"1.0.0+feature.1.0.{{commit_hash_7}}",
126+
r"1.0.0+feature.1.0.{{commit_hash_7}}",
127+
);
128128
}
129129
}

src/test_utils/git/fixtures.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,21 @@ impl GitRepoFixture {
5858
Ok(fixture)
5959
}
6060

61+
/// Checkout to an existing or new branch
62+
pub fn checkout_branch(&self, branch: &str) -> Result<(), Box<dyn std::error::Error>> {
63+
self.git_impl
64+
.create_branch(&self.test_dir, branch)
65+
.map_err(|e| format!("Failed to checkout branch '{}': {e}", branch))?;
66+
Ok(())
67+
}
68+
69+
/// Make the working directory dirty with uncommitted changes
70+
pub fn make_dirty(&self) -> Result<(), Box<dyn std::error::Error>> {
71+
self.test_dir
72+
.create_file("dirty_file.txt", "dirty content")?;
73+
Ok(())
74+
}
75+
6176
/// Create a repository with dirty working directory (Tier 3: major.minor.patch.dev<timestamp>+branch.<commit>)
6277
pub fn dirty(tag: &str) -> Result<Self, Box<dyn std::error::Error>> {
6378
let fixture = Self::tagged(tag)?;
@@ -85,6 +100,51 @@ mod tests {
85100
use super::*;
86101
use crate::test_utils::should_run_docker_tests;
87102

103+
#[test]
104+
#[serial(fixture_methods)]
105+
fn test_checkout_branch() {
106+
if !should_run_docker_tests() {
107+
return;
108+
}
109+
110+
let fixture = GitRepoFixture::tagged("v1.0.0").expect("Failed to create fixture with tag");
111+
112+
// Checkout a new branch
113+
fixture
114+
.checkout_branch("feature-test")
115+
.expect("Failed to checkout feature-test branch");
116+
117+
// Verify branch was created
118+
let current_branch = fixture
119+
.git_impl
120+
.execute_git(&fixture.test_dir, &["branch", "--show-current"])
121+
.expect("Failed to get current branch");
122+
assert_eq!(current_branch.trim(), "feature-test");
123+
}
124+
125+
#[test]
126+
#[serial(fixture_methods)]
127+
fn test_make_dirty() {
128+
if !should_run_docker_tests() {
129+
return;
130+
}
131+
132+
let fixture = GitRepoFixture::tagged("v1.0.0").expect("Failed to create fixture with tag");
133+
134+
// Make the working directory dirty
135+
fixture.make_dirty().expect("Failed to make fixture dirty");
136+
137+
// Verify the dirty file exists
138+
assert!(fixture.path().join("dirty_file.txt").exists());
139+
140+
// Verify git status shows uncommitted changes
141+
let status = fixture
142+
.git_impl
143+
.execute_git(&fixture.test_dir, &["status", "--porcelain"])
144+
.expect("Failed to get git status");
145+
assert!(status.contains("dirty_file.txt"));
146+
}
147+
88148
static SHARED_V1_FIXTURE: Mutex<Option<(std::path::PathBuf, tempfile::TempDir)>> =
89149
Mutex::new(None);
90150

src/test_utils/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod dir;
22
pub mod git;
33
pub mod output;
4+
pub mod pattern_assertions;
45
pub mod types;
56
pub mod vcs_fixtures;
67
pub mod version_args;
@@ -14,6 +15,7 @@ pub use git::{
1415
NativeGit,
1516
};
1617
pub use output::TestOutput;
18+
pub use pattern_assertions::assert_version_expectation;
1719
pub use types::{
1820
BumpType,
1921
OverrideType,

0 commit comments

Comments
 (0)