story-kit: merge 171_story_persist_test_results_to_story_files
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
use serde::Deserialize;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct StoryMetadata {
|
||||
pub name: Option<String>,
|
||||
pub coverage_baseline: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -23,6 +26,7 @@ impl std::fmt::Display for StoryMetaError {
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct FrontMatter {
|
||||
name: Option<String>,
|
||||
coverage_baseline: Option<String>,
|
||||
}
|
||||
|
||||
pub fn parse_front_matter(contents: &str) -> Result<StoryMetadata, StoryMetaError> {
|
||||
@@ -53,9 +57,58 @@ pub fn parse_front_matter(contents: &str) -> Result<StoryMetadata, StoryMetaErro
|
||||
fn build_metadata(front: FrontMatter) -> StoryMetadata {
|
||||
StoryMetadata {
|
||||
name: front.name,
|
||||
coverage_baseline: front.coverage_baseline,
|
||||
}
|
||||
}
|
||||
|
||||
/// Write or update a `coverage_baseline:` field in the YAML front matter of a story file.
|
||||
///
|
||||
/// If front matter is present, adds or replaces `coverage_baseline:` before the closing `---`.
|
||||
/// If no front matter is present, this is a no-op (returns Ok).
|
||||
pub fn write_coverage_baseline(path: &Path, coverage_pct: f64) -> Result<(), String> {
|
||||
let contents =
|
||||
fs::read_to_string(path).map_err(|e| format!("Failed to read story file: {e}"))?;
|
||||
|
||||
let updated = set_front_matter_field(&contents, "coverage_baseline", &format!("{coverage_pct:.1}%"));
|
||||
fs::write(path, &updated).map_err(|e| format!("Failed to write story file: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Insert or update a key: value pair in the YAML front matter of a markdown string.
|
||||
///
|
||||
/// If no front matter (opening `---`) is found, returns the content unchanged.
|
||||
fn set_front_matter_field(contents: &str, key: &str, value: &str) -> String {
|
||||
let mut lines: Vec<String> = contents.lines().map(String::from).collect();
|
||||
if lines.is_empty() || lines[0].trim() != "---" {
|
||||
return contents.to_string();
|
||||
}
|
||||
|
||||
// Find closing --- (search from index 1)
|
||||
let close_idx = match lines[1..].iter().position(|l| l.trim() == "---") {
|
||||
Some(i) => i + 1,
|
||||
None => return contents.to_string(),
|
||||
};
|
||||
|
||||
let key_prefix = format!("{key}:");
|
||||
let existing_idx = lines[1..close_idx]
|
||||
.iter()
|
||||
.position(|l| l.trim_start().starts_with(&key_prefix))
|
||||
.map(|i| i + 1);
|
||||
|
||||
let new_line = format!("{key}: {value}");
|
||||
if let Some(idx) = existing_idx {
|
||||
lines[idx] = new_line;
|
||||
} else {
|
||||
lines.insert(close_idx, new_line);
|
||||
}
|
||||
|
||||
let mut result = lines.join("\n");
|
||||
if contents.ends_with('\n') {
|
||||
result.push('\n');
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn parse_unchecked_todos(contents: &str) -> Vec<String> {
|
||||
contents
|
||||
.lines()
|
||||
@@ -82,12 +135,49 @@ workflow: tdd
|
||||
"#;
|
||||
|
||||
let meta = parse_front_matter(input).expect("front matter");
|
||||
assert_eq!(
|
||||
meta,
|
||||
StoryMetadata {
|
||||
name: Some("Establish the TDD Workflow and Gates".to_string()),
|
||||
}
|
||||
);
|
||||
assert_eq!(meta.name.as_deref(), Some("Establish the TDD Workflow and Gates"));
|
||||
assert_eq!(meta.coverage_baseline, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_coverage_baseline_from_front_matter() {
|
||||
let input = "---\nname: Test Story\ncoverage_baseline: 78.5%\n---\n# Story\n";
|
||||
let meta = parse_front_matter(input).expect("front matter");
|
||||
assert_eq!(meta.coverage_baseline.as_deref(), Some("78.5%"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_front_matter_field_inserts_new_key() {
|
||||
let input = "---\nname: My Story\n---\n# Body\n";
|
||||
let output = set_front_matter_field(input, "coverage_baseline", "55.0%");
|
||||
assert!(output.contains("coverage_baseline: 55.0%"));
|
||||
assert!(output.contains("name: My Story"));
|
||||
assert!(output.ends_with('\n'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_front_matter_field_updates_existing_key() {
|
||||
let input = "---\nname: My Story\ncoverage_baseline: 40.0%\n---\n# Body\n";
|
||||
let output = set_front_matter_field(input, "coverage_baseline", "55.0%");
|
||||
assert!(output.contains("coverage_baseline: 55.0%"));
|
||||
assert!(!output.contains("40.0%"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_front_matter_field_no_op_without_front_matter() {
|
||||
let input = "# No front matter\n";
|
||||
let output = set_front_matter_field(input, "coverage_baseline", "55.0%");
|
||||
assert_eq!(output, input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_coverage_baseline_updates_file() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let path = tmp.path().join("story.md");
|
||||
std::fs::write(&path, "---\nname: Test\n---\n# Story\n").unwrap();
|
||||
write_coverage_baseline(&path, 82.3).unwrap();
|
||||
let contents = std::fs::read_to_string(&path).unwrap();
|
||||
assert!(contents.contains("coverage_baseline: 82.3%"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user