mod common;
use tempfile::TempDir;
fn stdout( out : &std::process::Output ) -> String
{
String::from_utf8_lossy( &out.stdout ).into_owned()
}
fn stderr( out : &std::process::Output ) -> String
{
String::from_utf8_lossy( &out.stderr ).into_owned()
}
fn assert_exit( out : &std::process::Output, code : i32 )
{
assert_eq!(
out.status.code().unwrap_or( -1 ),
code,
"expected exit {code}, got {:?}; stderr: {}",
out.status.code(),
stderr( out )
);
}
fn setup_history( home : &std::path::Path, session_dir : &std::path::Path )
{
let storage_root = home.join( ".claude" );
common::write_path_project_session( &storage_root, session_dir, "session-test", 2 );
}
#[ test ]
fn ec_1_strategy_resume_accepted()
{
let home = TempDir::new().unwrap();
let base = TempDir::new().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.ensure" )
.arg( format!( "path::{}", base.path().display() ) )
.arg( "strategy::resume" )
.output()
.unwrap();
assert_exit( &out, 0 );
let output = stdout( &out );
let lines : Vec< &str > = output.lines().collect();
assert_eq!( lines.len(), 2, "EC-1: must output exactly 2 lines; got: {output}" );
assert_eq!( lines[ 1 ], "resume", "EC-1: line 2 must be 'resume'; got: {}", lines[ 1 ] );
}
#[ test ]
fn ec_2_strategy_fresh_accepted()
{
let home = TempDir::new().unwrap();
let base = TempDir::new().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.ensure" )
.arg( format!( "path::{}", base.path().display() ) )
.arg( "strategy::fresh" )
.output()
.unwrap();
assert_exit( &out, 0 );
let output = stdout( &out );
let lines : Vec< &str > = output.lines().collect();
assert_eq!( lines.len(), 2, "EC-2: must output exactly 2 lines; got: {output}" );
assert_eq!( lines[ 1 ], "fresh", "EC-2: line 2 must be 'fresh'; got: {}", lines[ 1 ] );
}
#[ test ]
fn ec_3_strategy_invalid_rejected()
{
let base = TempDir::new().unwrap();
let out = common::clg_cmd()
.arg( ".session.ensure" )
.arg( format!( "path::{}", base.path().display() ) )
.arg( "strategy::auto" )
.output()
.unwrap();
assert_exit( &out, 1 );
let err = stderr( &out );
assert!(
err.contains( "strategy" ) && ( err.contains( "resume" ) || err.contains( "fresh" ) ),
"EC-3: error must mention strategy and valid values; got: {err}"
);
}
#[ test ]
fn ec_4_strategy_resume_case_insensitive()
{
let home = TempDir::new().unwrap();
let base = TempDir::new().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.ensure" )
.arg( format!( "path::{}", base.path().display() ) )
.arg( "strategy::Resume" )
.output()
.unwrap();
assert_exit( &out, 0 );
let output = stdout( &out );
let lines : Vec< &str > = output.lines().collect();
assert_eq!( lines.len(), 2, "EC-4: must output exactly 2 lines; got: {output}" );
assert_eq!( lines[ 1 ], "resume", "EC-4: line 2 must be 'resume'; got: {}", lines[ 1 ] );
}
#[ test ]
fn ec_5_strategy_fresh_case_insensitive()
{
let home = TempDir::new().unwrap();
let base = TempDir::new().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.ensure" )
.arg( format!( "path::{}", base.path().display() ) )
.arg( "strategy::FRESH" )
.output()
.unwrap();
assert_exit( &out, 0 );
let output = stdout( &out );
let lines : Vec< &str > = output.lines().collect();
assert_eq!( lines.len(), 2, "EC-5: must output exactly 2 lines; got: {output}" );
assert_eq!( lines[ 1 ], "fresh", "EC-5: line 2 must be 'fresh'; got: {}", lines[ 1 ] );
}
#[ test ]
fn ec_6_strategy_absent_fresh_when_no_history()
{
let home = TempDir::new().unwrap();
let base = TempDir::new().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.ensure" )
.arg( format!( "path::{}", base.path().display() ) )
.output()
.unwrap();
assert_exit( &out, 0 );
let output = stdout( &out );
let lines : Vec< &str > = output.lines().collect();
assert_eq!( lines.len(), 2, "EC-6: must output exactly 2 lines; got: {output}" );
assert_eq!( lines[ 1 ], "fresh", "EC-6: line 2 must be 'fresh' with no history; got: {}", lines[ 1 ] );
}
#[ test ]
fn ec_7_strategy_absent_resume_when_history_exists()
{
let home = TempDir::new().unwrap();
let base = TempDir::new().unwrap();
let session_dir = base.path().join( "-default_topic" );
setup_history( home.path(), &session_dir );
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.ensure" )
.arg( format!( "path::{}", base.path().display() ) )
.output()
.unwrap();
assert_exit( &out, 0 );
let output = stdout( &out );
let lines : Vec< &str > = output.lines().collect();
assert_eq!( lines.len(), 2, "EC-7: must output exactly 2 lines; got: {output}" );
assert_eq!( lines[ 1 ], "resume", "EC-7: line 2 must be 'resume' with history; got: {}", lines[ 1 ] );
}
#[ test ]
fn ec_8_strategy_resume_overrides_auto_fresh()
{
let home = TempDir::new().unwrap();
let base = TempDir::new().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.ensure" )
.arg( format!( "path::{}", base.path().display() ) )
.arg( "strategy::resume" )
.output()
.unwrap();
assert_exit( &out, 0 );
let output = stdout( &out );
let lines : Vec< &str > = output.lines().collect();
assert_eq!( lines.len(), 2, "EC-8: must output exactly 2 lines; got: {output}" );
assert_eq!( lines[ 1 ], "resume", "EC-8: strategy::resume must force 'resume' even without history; got: {}", lines[ 1 ] );
}
#[ test ]
fn ec_9_strategy_fresh_overrides_auto_resume()
{
let home = TempDir::new().unwrap();
let base = TempDir::new().unwrap();
let session_dir = base.path().join( "-default_topic" );
setup_history( home.path(), &session_dir );
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.ensure" )
.arg( format!( "path::{}", base.path().display() ) )
.arg( "strategy::fresh" )
.output()
.unwrap();
assert_exit( &out, 0 );
let output = stdout( &out );
let lines : Vec< &str > = output.lines().collect();
assert_eq!( lines.len(), 2, "EC-9: must output exactly 2 lines; got: {output}" );
assert_eq!( lines[ 1 ], "fresh", "EC-9: strategy::fresh must force 'fresh' despite history; got: {}", lines[ 1 ] );
}