use super::*;
#[test]
fn test_planning_xsd_retry_increments_metrics() {
let state = PipelineState::initial(1, 0);
let event = PipelineEvent::Planning(PlanningEvent::OutputValidationFailed {
iteration: 0,
attempt: 0,
});
let state = reduce(state, event);
assert_eq!(state.metrics.xsd_retry_attempts_total, 1);
assert_eq!(state.metrics.xsd_retry_planning, 1);
assert_eq!(state.metrics.xsd_retry_development, 0);
assert_eq!(state.metrics.xsd_retry_review, 0);
assert_eq!(state.metrics.xsd_retry_fix, 0);
assert_eq!(state.metrics.xsd_retry_commit, 0);
}
#[test]
fn test_development_xsd_retry_increments_metrics() {
let mut state = PipelineState::initial(1, 0);
state = reduce(state, PipelineEvent::development_iteration_started(0));
let event = PipelineEvent::Development(DevelopmentEvent::OutputValidationFailed {
iteration: 0,
attempt: 0,
});
let state = reduce(state, event);
assert_eq!(state.metrics.xsd_retry_attempts_total, 1);
assert_eq!(state.metrics.xsd_retry_development, 1);
assert_eq!(state.metrics.xsd_retry_planning, 0);
assert_eq!(state.metrics.xsd_retry_review, 0);
assert_eq!(state.metrics.xsd_retry_fix, 0);
assert_eq!(state.metrics.xsd_retry_commit, 0);
}
#[test]
fn test_review_xsd_retry_increments_metrics() {
let mut state = PipelineState::initial(0, 1);
state.phase = crate::reducer::event::PipelinePhase::Review;
let event = PipelineEvent::Review(ReviewEvent::OutputValidationFailed {
pass: 0,
attempt: 0,
error_detail: None,
});
let state = reduce(state, event);
assert_eq!(state.metrics.xsd_retry_attempts_total, 1);
assert_eq!(state.metrics.xsd_retry_review, 1);
assert_eq!(state.metrics.xsd_retry_planning, 0);
assert_eq!(state.metrics.xsd_retry_development, 0);
assert_eq!(state.metrics.xsd_retry_fix, 0);
assert_eq!(state.metrics.xsd_retry_commit, 0);
}
#[test]
fn test_fix_xsd_retry_increments_metrics() {
let mut state = PipelineState::initial(0, 1);
state.phase = crate::reducer::event::PipelinePhase::Review;
let event = PipelineEvent::Review(ReviewEvent::FixOutputValidationFailed {
pass: 0,
attempt: 0,
error_detail: None,
});
let state = reduce(state, event);
assert_eq!(state.metrics.xsd_retry_attempts_total, 1);
assert_eq!(state.metrics.xsd_retry_fix, 1);
assert_eq!(state.metrics.xsd_retry_planning, 0);
assert_eq!(state.metrics.xsd_retry_development, 0);
assert_eq!(state.metrics.xsd_retry_review, 0);
assert_eq!(state.metrics.xsd_retry_commit, 0);
}
#[test]
fn test_commit_xsd_retry_increments_metrics() {
use crate::reducer::state::CommitState;
let mut state = PipelineState::initial(0, 0);
state.phase = crate::reducer::event::PipelinePhase::CommitMessage;
state.commit = CommitState::Generating {
attempt: 1,
max_attempts: 10,
};
let event = PipelineEvent::Commit(CommitEvent::CommitXmlValidationFailed {
reason: "invalid xml".to_string(),
attempt: 1,
});
let _state = reduce(state, event);
}
#[test]
fn test_dev_continuation_does_not_increment_iterations_started() {
let mut state = PipelineState::initial(2, 0);
state = reduce(state, PipelineEvent::development_iteration_started(0));
assert_eq!(state.metrics.dev_iterations_started, 1);
let event = PipelineEvent::Development(DevelopmentEvent::ContinuationTriggered {
iteration: 0,
status: DevelopmentStatus::Partial,
summary: "partial work".to_string(),
files_changed: None,
next_steps: None,
});
state = reduce(state, event);
assert_eq!(state.metrics.dev_iterations_started, 1);
assert_eq!(state.metrics.dev_continuation_attempt, 1);
}
#[test]
fn test_dev_continuation_attempt_resets_on_iteration_start() {
let mut state = PipelineState::initial(2, 0);
state = reduce(state, PipelineEvent::development_iteration_started(0));
state = reduce(
state,
PipelineEvent::Development(DevelopmentEvent::ContinuationTriggered {
iteration: 0,
status: DevelopmentStatus::Partial,
summary: "partial".to_string(),
files_changed: None,
next_steps: None,
}),
);
assert_eq!(state.metrics.dev_continuation_attempt, 1);
state = reduce(state, PipelineEvent::development_iteration_started(1));
assert_eq!(state.metrics.dev_continuation_attempt, 0);
}
#[test]
fn test_analysis_per_iteration_counter_resets_on_new_iteration() {
let mut state = PipelineState::initial(2, 0);
state = reduce(state, PipelineEvent::development_iteration_started(0));
state = reduce(
state,
PipelineEvent::Development(DevelopmentEvent::AnalysisAgentInvoked { iteration: 0 }),
);
state = reduce(
state,
PipelineEvent::Development(DevelopmentEvent::AnalysisAgentInvoked { iteration: 0 }),
);
assert_eq!(state.metrics.analysis_attempts_in_current_iteration, 2);
assert_eq!(state.metrics.analysis_attempts_total, 2);
state = reduce(state, PipelineEvent::development_iteration_started(1));
assert_eq!(state.metrics.analysis_attempts_in_current_iteration, 0);
assert_eq!(state.metrics.analysis_attempts_total, 2);
}
#[test]
fn test_fix_continuation_does_not_increment_review_passes_started() {
use crate::reducer::state::FixStatus;
let mut state = PipelineState::initial(0, 2);
state = reduce(state, PipelineEvent::review_pass_started(1));
assert_eq!(state.metrics.review_passes_started, 1);
let event = PipelineEvent::Review(ReviewEvent::FixContinuationTriggered {
pass: 1,
status: FixStatus::IssuesRemain,
summary: Some("more fixes needed".to_string()),
});
state = reduce(state, event);
assert_eq!(state.metrics.review_passes_started, 1);
assert_eq!(state.metrics.fix_continuation_attempt, 1);
assert_eq!(state.metrics.fix_continuations_total, 1);
}
#[test]
fn test_fix_continuation_attempt_resets_on_new_pass_start() {
use crate::reducer::state::FixStatus;
let mut state = PipelineState::initial(0, 2);
state = reduce(state, PipelineEvent::review_pass_started(1));
state = reduce(
state,
PipelineEvent::Review(ReviewEvent::FixContinuationTriggered {
pass: 1,
status: FixStatus::IssuesRemain,
summary: None,
}),
);
assert_eq!(state.metrics.fix_continuation_attempt, 1);
state = reduce(state, PipelineEvent::review_pass_started(2));
assert_eq!(state.metrics.fix_continuation_attempt, 0);
}
#[test]
fn test_xsd_retry_exhaustion_does_not_increment_metrics() {
let mut state = PipelineState::initial(1, 0);
state.phase = crate::reducer::event::PipelinePhase::Development;
state.continuation.xsd_retry_count = 98; state.continuation.max_xsd_retry_count = 99;
let event = PipelineEvent::Development(DevelopmentEvent::OutputValidationFailed {
iteration: 0,
attempt: 0,
});
let state = reduce(state, event);
assert_eq!(state.metrics.xsd_retry_development, 0);
assert_eq!(state.metrics.xsd_retry_attempts_total, 0);
}
#[test]
fn test_same_agent_retry_exhaustion_does_not_increment_metrics() {
let mut state = PipelineState::initial(1, 0);
state.continuation.max_same_agent_retry_count = 3;
state.continuation.same_agent_retry_count = 2;
let event = PipelineEvent::agent_timed_out(
AgentRole::Developer,
AgentName::from("claude"),
TimeoutOutputKind::PartialResult,
Some(".agent/logs/developer_0.log".to_string()),
None,
);
let state = reduce(state, event);
assert_eq!(state.metrics.same_agent_retry_attempts_total, 0);
}
#[test]
fn test_review_clean_pass_increments_completed_on_first_pass() {
let mut state = PipelineState::initial(0, 3);
state = reduce(state, PipelineEvent::review_pass_started(1));
let event = PipelineEvent::Review(ReviewEvent::PassCompletedClean { pass: 1 });
state = reduce(state, event);
assert_eq!(state.metrics.review_passes_completed, 1);
assert_eq!(state.reviewer_pass, 2);
}
#[test]
fn test_continuation_budget_exhausted_does_not_increment_completed() {
let mut state = PipelineState::initial(1, 0);
state = reduce(state, PipelineEvent::development_iteration_started(0));
let event = PipelineEvent::Development(DevelopmentEvent::ContinuationBudgetExhausted {
iteration: 0,
total_attempts: 4,
last_status: DevelopmentStatus::Partial,
});
state = reduce(state, event);
assert_eq!(state.metrics.dev_iterations_completed, 1,
"After wt-39 fix, continuation budget exhaustion completes the iteration, incrementing the counter");
}
#[test]
fn test_fix_continuation_budget_exhausted_does_not_increment_review_passes_completed() {
use crate::reducer::state::FixStatus;
let mut state = PipelineState::initial(0, 1);
state = reduce(state, PipelineEvent::review_pass_started(1));
let event = PipelineEvent::Review(ReviewEvent::FixContinuationBudgetExhausted {
pass: 1,
total_attempts: 4,
last_status: FixStatus::IssuesRemain,
});
state = reduce(state, event);
assert_eq!(state.metrics.review_passes_completed, 0);
}
#[test]
fn test_checkpoint_resume_preserves_all_metrics() {
let mut state = PipelineState::initial(5, 3);
state.metrics.dev_iterations_started = 3;
state.metrics.dev_iterations_completed = 2;
state.metrics.dev_attempts_total = 5;
state.metrics.dev_continuation_attempt = 1;
state.metrics.analysis_attempts_total = 4;
state.metrics.analysis_attempts_in_current_iteration = 2;
state.metrics.review_passes_started = 1;
state.metrics.review_passes_completed = 0;
state.metrics.review_runs_total = 1;
state.metrics.fix_runs_total = 0;
state.metrics.fix_continuations_total = 0;
state.metrics.fix_continuation_attempt = 0;
state.metrics.current_review_pass = 1;
state.metrics.xsd_retry_attempts_total = 3;
state.metrics.xsd_retry_development = 2;
state.metrics.xsd_retry_review = 1;
state.metrics.same_agent_retry_attempts_total = 1;
state.metrics.agent_fallbacks_total = 1;
state.metrics.model_fallbacks_total = 0;
state.metrics.commits_created_total = 2;
let json = serde_json::to_string(&state).expect("serialization failed");
let restored: PipelineState = serde_json::from_str(&json).expect("deserialization failed");
assert_eq!(restored.metrics.dev_iterations_started, 3);
assert_eq!(restored.metrics.dev_iterations_completed, 2);
assert_eq!(restored.metrics.dev_attempts_total, 5);
assert_eq!(restored.metrics.dev_continuation_attempt, 1);
assert_eq!(restored.metrics.analysis_attempts_total, 4);
assert_eq!(restored.metrics.analysis_attempts_in_current_iteration, 2);
assert_eq!(restored.metrics.review_passes_started, 1);
assert_eq!(restored.metrics.review_passes_completed, 0);
assert_eq!(restored.metrics.review_runs_total, 1);
assert_eq!(restored.metrics.fix_runs_total, 0);
assert_eq!(restored.metrics.fix_continuations_total, 0);
assert_eq!(restored.metrics.fix_continuation_attempt, 0);
assert_eq!(restored.metrics.current_review_pass, 1);
assert_eq!(restored.metrics.xsd_retry_attempts_total, 3);
assert_eq!(restored.metrics.xsd_retry_development, 2);
assert_eq!(restored.metrics.xsd_retry_review, 1);
assert_eq!(restored.metrics.same_agent_retry_attempts_total, 1);
assert_eq!(restored.metrics.agent_fallbacks_total, 1);
assert_eq!(restored.metrics.model_fallbacks_total, 0);
assert_eq!(restored.metrics.commits_created_total, 2);
}