huskies: merge 867
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
//! Handler for the `freeze` and `unfreeze` commands.
|
||||
//!
|
||||
//! `freeze <number>` sets `frozen: true` on the story, halting pipeline
|
||||
//! advancement and auto-assign until `unfreeze <number>` clears the flag.
|
||||
//! `freeze <number>` transitions the story to `Stage::Frozen`, halting pipeline
|
||||
//! advancement and auto-assign until `unfreeze <number>` restores the prior stage.
|
||||
|
||||
use super::CommandContext;
|
||||
use crate::io::story_metadata::{
|
||||
clear_front_matter_field_in_content, parse_front_matter, set_front_matter_field,
|
||||
};
|
||||
use crate::io::story_metadata::parse_front_matter;
|
||||
use std::path::Path;
|
||||
|
||||
/// Handle the `freeze` command.
|
||||
@@ -52,23 +50,22 @@ fn freeze_by_story_id(story_id: &str) -> String {
|
||||
|
||||
let story_name = meta.name.as_deref().unwrap_or(story_id).to_string();
|
||||
|
||||
if meta.frozen == Some(true) {
|
||||
// Check if already frozen via the typed stage.
|
||||
if crate::pipeline_state::read_typed(story_id)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|i| i.stage.is_frozen())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return format!("**{story_name}** ({story_id}) is already frozen.");
|
||||
}
|
||||
|
||||
let updated = set_front_matter_field(&contents, "frozen", "true");
|
||||
|
||||
crate::db::write_content(story_id, &updated);
|
||||
let stage = crate::pipeline_state::read_typed(story_id)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|i| i.stage.dir_name().to_string())
|
||||
.unwrap_or_else(|| "2_current".to_string());
|
||||
crate::db::write_item_with_content(story_id, &stage, &updated);
|
||||
|
||||
format!(
|
||||
"Frozen **{story_name}** ({story_id}). Pipeline advancement and auto-assign suppressed until unfrozen."
|
||||
)
|
||||
match crate::pipeline_state::transition_to_frozen(story_id) {
|
||||
Ok(_) => format!(
|
||||
"Frozen **{story_name}** ({story_id}). Pipeline advancement and auto-assign suppressed until unfrozen."
|
||||
),
|
||||
Err(e) => format!("Failed to freeze **{story_name}** ({story_id}): {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle the `unfreeze` command.
|
||||
@@ -112,21 +109,23 @@ fn unfreeze_by_story_id(story_id: &str) -> String {
|
||||
|
||||
let story_name = meta.name.as_deref().unwrap_or(story_id).to_string();
|
||||
|
||||
if meta.frozen != Some(true) {
|
||||
// Check frozen via typed stage.
|
||||
let is_frozen = crate::pipeline_state::read_typed(story_id)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|i| i.stage.is_frozen())
|
||||
.unwrap_or(false);
|
||||
|
||||
if !is_frozen {
|
||||
return format!("**{story_name}** ({story_id}) is not frozen. Nothing to unfreeze.");
|
||||
}
|
||||
|
||||
let updated = clear_front_matter_field_in_content(&contents, "frozen");
|
||||
|
||||
crate::db::write_content(story_id, &updated);
|
||||
let stage = crate::pipeline_state::read_typed(story_id)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|i| i.stage.dir_name().to_string())
|
||||
.unwrap_or_else(|| "2_current".to_string());
|
||||
crate::db::write_item_with_content(story_id, &stage, &updated);
|
||||
|
||||
format!("Unfrozen **{story_name}** ({story_id}). Normal pipeline behaviour resumed.")
|
||||
match crate::pipeline_state::transition_to_unfrozen(story_id) {
|
||||
Ok(_) => {
|
||||
format!("Unfrozen **{story_name}** ({story_id}). Normal pipeline behaviour resumed.")
|
||||
}
|
||||
Err(e) => format!("Failed to unfreeze **{story_name}** ({story_id}): {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -212,8 +211,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn freeze_command_sets_frozen_flag() {
|
||||
fn freeze_command_sets_stage_to_frozen() {
|
||||
let tmp = tempfile::TempDir::new().unwrap();
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
write_story_file(
|
||||
tmp.path(),
|
||||
@@ -226,40 +226,54 @@ mod tests {
|
||||
output.contains("Frozen") && output.contains("Freeze Me"),
|
||||
"should confirm freeze with story name: {output}"
|
||||
);
|
||||
let contents = crate::db::read_content("9940_story_freezeme")
|
||||
.expect("story content should be readable after freeze");
|
||||
let item = crate::pipeline_state::read_typed("9940_story_freezeme")
|
||||
.expect("read_typed should succeed")
|
||||
.expect("item should be present");
|
||||
assert!(
|
||||
contents.contains("frozen: true"),
|
||||
"frozen flag should be set: {contents}"
|
||||
item.stage.is_frozen(),
|
||||
"stage should be Frozen after freeze: {:?}",
|
||||
item.stage
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unfreeze_command_clears_frozen_flag() {
|
||||
fn unfreeze_command_restores_prior_stage() {
|
||||
let tmp = tempfile::TempDir::new().unwrap();
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
write_story_file(
|
||||
tmp.path(),
|
||||
"2_current",
|
||||
"9941_story_frozen.md",
|
||||
"---\nname: Frozen Story\nfrozen: true\n---\n# Story\n",
|
||||
"---\nname: Frozen Story\n---\n# Story\n",
|
||||
);
|
||||
// Freeze first.
|
||||
let freeze_out = freeze_cmd_with_root(tmp.path(), "9941").unwrap();
|
||||
assert!(
|
||||
freeze_out.contains("Frozen"),
|
||||
"should confirm freeze: {freeze_out}"
|
||||
);
|
||||
// Now unfreeze.
|
||||
let output = unfreeze_cmd_with_root(tmp.path(), "9941").unwrap();
|
||||
assert!(
|
||||
output.contains("Unfrozen") && output.contains("Frozen Story"),
|
||||
"should confirm unfreeze with story name: {output}"
|
||||
);
|
||||
let contents = crate::db::read_content("9941_story_frozen")
|
||||
.expect("story content should be readable after unfreeze");
|
||||
let item = crate::pipeline_state::read_typed("9941_story_frozen")
|
||||
.expect("read_typed should succeed")
|
||||
.expect("item should be present");
|
||||
assert!(
|
||||
!contents.contains("frozen:"),
|
||||
"frozen flag should be removed: {contents}"
|
||||
matches!(item.stage, crate::pipeline_state::Stage::Coding),
|
||||
"stage should be restored to Coding: {:?}",
|
||||
item.stage
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unfreeze_command_not_frozen_returns_error() {
|
||||
let tmp = tempfile::TempDir::new().unwrap();
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
write_story_file(
|
||||
tmp.path(),
|
||||
"2_current",
|
||||
@@ -276,12 +290,17 @@ mod tests {
|
||||
#[test]
|
||||
fn freeze_command_already_frozen_returns_message() {
|
||||
let tmp = tempfile::TempDir::new().unwrap();
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
write_story_file(
|
||||
tmp.path(),
|
||||
"2_current",
|
||||
"9943_story_alreadyfrozen.md",
|
||||
"---\nname: Already Frozen\nfrozen: true\n---\n# Story\n",
|
||||
"---\nname: Already Frozen\n---\n# Story\n",
|
||||
);
|
||||
// Freeze it first.
|
||||
freeze_cmd_with_root(tmp.path(), "9943").unwrap();
|
||||
// Try to freeze again.
|
||||
let output = freeze_cmd_with_root(tmp.path(), "9943").unwrap();
|
||||
assert!(
|
||||
output.contains("already frozen"),
|
||||
|
||||
@@ -113,6 +113,7 @@ fn stage_display_name(stage: &str) -> &str {
|
||||
Some(Stage::Merge { .. }) => "merge",
|
||||
Some(Stage::Done { .. }) => "done",
|
||||
Some(Stage::Archived { .. }) => "archived",
|
||||
Some(Stage::Frozen { .. }) => "frozen",
|
||||
None => stage,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user