story-kit: merge 272_story_clear_merge_error_front_matter_when_story_leaves_merge_stage
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
//! the event so connected clients stay in sync.
|
//! the event so connected clients stay in sync.
|
||||||
|
|
||||||
use crate::config::{ProjectConfig, WatcherConfig};
|
use crate::config::{ProjectConfig, WatcherConfig};
|
||||||
|
use crate::io::story_metadata::clear_front_matter_field;
|
||||||
use crate::slog;
|
use crate::slog;
|
||||||
use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher, recommended_watcher};
|
use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher, recommended_watcher};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -190,6 +191,15 @@ fn flush_pending(
|
|||||||
("remove", item.to_string(), format!("story-kit: remove {item}"))
|
("remove", item.to_string(), format!("story-kit: remove {item}"))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Strip stale merge_failure front matter from any story that has left 4_merge/.
|
||||||
|
for (path, stage) in &additions {
|
||||||
|
if *stage != "4_merge"
|
||||||
|
&& let Err(e) = clear_front_matter_field(path, "merge_failure")
|
||||||
|
{
|
||||||
|
slog!("[watcher] Warning: could not clear merge_failure from {}: {e}", path.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
slog!("[watcher] flush: {commit_msg}");
|
slog!("[watcher] flush: {commit_msg}");
|
||||||
match git_add_work_and_commit(git_root, &commit_msg) {
|
match git_add_work_and_commit(git_root, &commit_msg) {
|
||||||
Ok(committed) => {
|
Ok(committed) => {
|
||||||
@@ -672,6 +682,128 @@ mod tests {
|
|||||||
assert!(rx.try_recv().is_err(), "no event for empty pending map");
|
assert!(rx.try_recv().is_err(), "no event for empty pending map");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── flush_pending clears merge_failure ─────────────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flush_pending_clears_merge_failure_when_leaving_merge_stage() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
init_git_repo(tmp.path());
|
||||||
|
let stage_dir = make_stage_dir(tmp.path(), "2_current");
|
||||||
|
let story_path = stage_dir.join("50_story_retry.md");
|
||||||
|
fs::write(
|
||||||
|
&story_path,
|
||||||
|
"---\nname: Retry Story\nmerge_failure: \"conflicts detected\"\n---\n# Story\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (tx, _rx) = tokio::sync::broadcast::channel(16);
|
||||||
|
let mut pending = HashMap::new();
|
||||||
|
pending.insert(story_path.clone(), "2_current".to_string());
|
||||||
|
|
||||||
|
flush_pending(&pending, tmp.path(), &tx);
|
||||||
|
|
||||||
|
let contents = fs::read_to_string(&story_path).unwrap();
|
||||||
|
assert!(
|
||||||
|
!contents.contains("merge_failure"),
|
||||||
|
"merge_failure should be stripped when story lands in 2_current"
|
||||||
|
);
|
||||||
|
assert!(contents.contains("name: Retry Story"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flush_pending_clears_merge_failure_when_moving_to_upcoming() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
init_git_repo(tmp.path());
|
||||||
|
let stage_dir = make_stage_dir(tmp.path(), "1_upcoming");
|
||||||
|
let story_path = stage_dir.join("51_story_reset.md");
|
||||||
|
fs::write(
|
||||||
|
&story_path,
|
||||||
|
"---\nname: Reset Story\nmerge_failure: \"gate failed\"\n---\n# Story\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (tx, _rx) = tokio::sync::broadcast::channel(16);
|
||||||
|
let mut pending = HashMap::new();
|
||||||
|
pending.insert(story_path.clone(), "1_upcoming".to_string());
|
||||||
|
|
||||||
|
flush_pending(&pending, tmp.path(), &tx);
|
||||||
|
|
||||||
|
let contents = fs::read_to_string(&story_path).unwrap();
|
||||||
|
assert!(
|
||||||
|
!contents.contains("merge_failure"),
|
||||||
|
"merge_failure should be stripped when story lands in 1_upcoming"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flush_pending_clears_merge_failure_when_moving_to_done() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
init_git_repo(tmp.path());
|
||||||
|
let stage_dir = make_stage_dir(tmp.path(), "5_done");
|
||||||
|
let story_path = stage_dir.join("52_story_done.md");
|
||||||
|
fs::write(
|
||||||
|
&story_path,
|
||||||
|
"---\nname: Done Story\nmerge_failure: \"stale error\"\n---\n# Story\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (tx, _rx) = tokio::sync::broadcast::channel(16);
|
||||||
|
let mut pending = HashMap::new();
|
||||||
|
pending.insert(story_path.clone(), "5_done".to_string());
|
||||||
|
|
||||||
|
flush_pending(&pending, tmp.path(), &tx);
|
||||||
|
|
||||||
|
let contents = fs::read_to_string(&story_path).unwrap();
|
||||||
|
assert!(
|
||||||
|
!contents.contains("merge_failure"),
|
||||||
|
"merge_failure should be stripped when story lands in 5_done"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flush_pending_preserves_merge_failure_when_in_merge_stage() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
init_git_repo(tmp.path());
|
||||||
|
let stage_dir = make_stage_dir(tmp.path(), "4_merge");
|
||||||
|
let story_path = stage_dir.join("53_story_merging.md");
|
||||||
|
fs::write(
|
||||||
|
&story_path,
|
||||||
|
"---\nname: Merging Story\nmerge_failure: \"conflicts\"\n---\n# Story\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (tx, _rx) = tokio::sync::broadcast::channel(16);
|
||||||
|
let mut pending = HashMap::new();
|
||||||
|
pending.insert(story_path.clone(), "4_merge".to_string());
|
||||||
|
|
||||||
|
flush_pending(&pending, tmp.path(), &tx);
|
||||||
|
|
||||||
|
let contents = fs::read_to_string(&story_path).unwrap();
|
||||||
|
assert!(
|
||||||
|
contents.contains("merge_failure"),
|
||||||
|
"merge_failure should be preserved when story is in 4_merge"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flush_pending_no_op_when_no_merge_failure() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
init_git_repo(tmp.path());
|
||||||
|
let stage_dir = make_stage_dir(tmp.path(), "2_current");
|
||||||
|
let story_path = stage_dir.join("54_story_clean.md");
|
||||||
|
let original = "---\nname: Clean Story\n---\n# Story\n";
|
||||||
|
fs::write(&story_path, original).unwrap();
|
||||||
|
|
||||||
|
let (tx, _rx) = tokio::sync::broadcast::channel(16);
|
||||||
|
let mut pending = HashMap::new();
|
||||||
|
pending.insert(story_path.clone(), "2_current".to_string());
|
||||||
|
|
||||||
|
flush_pending(&pending, tmp.path(), &tx);
|
||||||
|
|
||||||
|
let contents = fs::read_to_string(&story_path).unwrap();
|
||||||
|
assert_eq!(contents, original, "file without merge_failure should be unchanged");
|
||||||
|
}
|
||||||
|
|
||||||
// ── stage_for_path (additional edge cases) ────────────────────────────────
|
// ── stage_for_path (additional edge cases) ────────────────────────────────
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user