#![cfg_attr(coverage_nightly, coverage(off))]
use crate::cli::commands::SpecOutputFormat;
use crate::services::spec_parser::{ParsedSpec, SpecParser};
use std::fs;
use std::path::Path;
include!("spec_handlers_commands.rs");
include!("spec_handlers_scoring.rs");
include!("spec_handlers_sync.rs");
#[cfg(test)]
#[path = "tests.rs"]
mod tests;
#[cfg(test)]
mod sync_pure_tests {
use super::*;
use crate::models::roadmap::RoadmapItem;
use std::path::PathBuf;
fn drift(path: &str, has_ticket: bool, ticket_id: Option<&str>, linked: bool) -> DriftInfo {
DriftInfo {
path: PathBuf::from(path),
title: format!("{path}-title"),
has_ticket,
ticket_id: ticket_id.map(String::from),
linked_in_roadmap: linked,
}
}
#[test]
fn test_update_roadmap_item_no_match_no_changes() {
let mut items = vec![RoadmapItem::new("OTHER-1".into(), "x".into())];
let mut updates = Vec::new();
update_roadmap_item(
&mut items,
"PMAT-1",
Path::new("docs/foo.md"),
false,
&mut updates,
);
assert!(updates.is_empty());
assert!(items[0].spec.is_none());
}
#[test]
fn test_update_roadmap_item_case_insensitive_match() {
let mut items = vec![RoadmapItem::new("PMAT-100".into(), "x".into())];
let mut updates = Vec::new();
update_roadmap_item(
&mut items,
"pmat-100",
Path::new("docs/spec.md"),
false,
&mut updates,
);
assert_eq!(updates.len(), 1);
assert_eq!(items[0].spec, Some(PathBuf::from("docs/spec.md")));
}
#[test]
fn test_update_roadmap_item_dry_run_records_but_no_mutation() {
let mut items = vec![RoadmapItem::new("PMAT-1".into(), "x".into())];
let mut updates = Vec::new();
update_roadmap_item(
&mut items,
"PMAT-1",
Path::new("docs/spec.md"),
true, &mut updates,
);
assert_eq!(updates.len(), 1);
assert!(items[0].spec.is_none());
}
#[test]
fn test_update_roadmap_item_already_correct_no_update() {
let mut items = vec![RoadmapItem::new("PMAT-1".into(), "x".into())];
items[0].spec = Some(PathBuf::from("docs/spec.md"));
let mut updates = Vec::new();
update_roadmap_item(
&mut items,
"PMAT-1",
Path::new("docs/spec.md"),
false,
&mut updates,
);
assert!(updates.is_empty());
}
#[test]
fn test_print_drift_text_empty_emits_pass_message() {
print_drift_text(&[]);
}
#[test]
fn test_print_drift_text_with_orphans() {
let orphans = vec![
drift("docs/a.md", true, Some("PMAT-1"), false),
drift("docs/b.md", false, None, false),
];
print_drift_text(&orphans);
}
#[test]
fn test_print_drift_markdown_empty() {
print_drift_markdown(&[]);
}
#[test]
fn test_print_drift_markdown_with_orphans_emits_table() {
let orphans = vec![drift("docs/a.md", true, Some("PMAT-1"), true)];
print_drift_markdown(&orphans);
}
#[test]
fn test_print_drift_json_returns_ok() {
let orphans = vec![drift("docs/a.md", false, None, false)];
print_drift_json(&orphans).unwrap();
}
#[test]
fn test_print_drift_json_empty() {
print_drift_json(&[]).unwrap();
}
#[test]
fn test_print_drift_report_dispatcher_arms() {
let orphans = vec![drift("docs/a.md", true, Some("X"), true)];
print_drift_report(&orphans, SpecOutputFormat::Text).unwrap();
print_drift_report(&orphans, SpecOutputFormat::Json).unwrap();
print_drift_report(&orphans, SpecOutputFormat::Markdown).unwrap();
}
}