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 )
);
}
#[ test ]
fn int_1_path_with_default_topic_produces_default_topic_dir()
{
let home = TempDir::new().unwrap();
let project = TempDir::new().unwrap();
let base = project.path().to_str().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.dir" )
.arg( format!( "path::{base}" ) )
.output()
.unwrap();
assert_exit( &out, 0 );
let expected = format!( "{base}/-default_topic\n" );
assert_eq!(
stdout( &out ).as_str(),
expected.as_str(),
"output must be {{base}}/-default_topic"
);
}
#[ test ]
fn int_2_path_with_custom_topic_produces_topic_dir()
{
let home = TempDir::new().unwrap();
let project = TempDir::new().unwrap();
let base = project.path().to_str().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.dir" )
.arg( format!( "path::{base}" ) )
.arg( "topic::work" )
.output()
.unwrap();
assert_exit( &out, 0 );
let expected = format!( "{base}/-work\n" );
assert_eq!(
stdout( &out ).as_str(),
expected.as_str(),
"output must be {{base}}/-work"
);
}
#[ test ]
fn int_3_missing_path_returns_error_per_spec()
{
let home = TempDir::new().unwrap();
let project = TempDir::new().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.current_dir( project.path() )
.arg( ".session.dir" )
.output()
.unwrap();
let code = out.status.code().unwrap_or( -1 );
assert!(
code == 0 || code == 1,
"exit code must be 0 (cwd-default) or 1 (spec-required); got {code}"
);
}
#[ test ]
fn int_4_output_is_single_absolute_line()
{
let home = TempDir::new().unwrap();
let project = TempDir::new().unwrap();
let base = project.path().to_str().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.dir" )
.arg( format!( "path::{base}" ) )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
let content = s.trim_end_matches( '\n' );
let lines : Vec< &str > = content.split( '\n' ).filter( | l | !l.is_empty() ).collect();
assert_eq!(
lines.len(), 1,
"output must be exactly one non-empty line; got:\n{s}"
);
assert!(
content.starts_with( '/' ),
"output must be an absolute path (starts with '/'); got:\n{s}"
);
}
#[ test ]
fn int_5_tilde_expanded_in_path()
{
let home = TempDir::new().unwrap();
let home_str = home.path().to_str().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.dir" )
.arg( "path::~/projects/myapp" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
!s.contains( '~' ),
"output must not contain literal '~'; got:\n{s}"
);
assert!(
s.contains( home_str ) || s.contains( "projects" ),
"output must contain expanded home path; got:\n{s}"
);
assert!(
s.contains( "-default_topic" ),
"output must end with /-default_topic; got:\n{s}"
);
}
#[ test ]
fn int_6_path_dot_resolves_to_cwd()
{
let home = TempDir::new().unwrap();
let project = TempDir::new().unwrap();
let base = project.path().to_str().unwrap();
let out_dot = common::clg_cmd()
.env( "HOME", home.path() )
.current_dir( project.path() )
.arg( ".session.dir" )
.arg( "path::." )
.output()
.unwrap();
let out_explicit = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.dir" )
.arg( format!( "path::{base}" ) )
.output()
.unwrap();
assert_exit( &out_dot, 0 );
assert_exit( &out_explicit, 0 );
assert_eq!(
stdout( &out_dot ),
stdout( &out_explicit ),
"path::. must produce same output as explicit cwd path"
);
}
#[ test ]
fn int_7_empty_topic_rejected()
{
let home = TempDir::new().unwrap();
let project = TempDir::new().unwrap();
let base = project.path().to_str().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.dir" )
.arg( format!( "path::{base}" ) )
.arg( "topic::" )
.output()
.unwrap();
assert_exit( &out, 1 );
let combined = format!( "{}{}", stderr( &out ), stdout( &out ) );
assert!(
!combined.is_empty(),
"must produce error output for empty topic::"
);
}
#[ test ]
fn int_8_topic_with_slash_rejected()
{
let home = TempDir::new().unwrap();
let project = TempDir::new().unwrap();
let base = project.path().to_str().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.dir" )
.arg( format!( "path::{base}" ) )
.arg( "topic::sub/dir" )
.output()
.unwrap();
assert_exit( &out, 1 );
let combined = format!( "{}{}", stderr( &out ), stdout( &out ) );
assert!(
!combined.is_empty(),
"must produce error output for slash-containing topic::"
);
}
#[ test ]
fn int_9_does_not_create_directory()
{
let home = TempDir::new().unwrap();
let project = TempDir::new().unwrap();
let base = project.path().to_str().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.dir" )
.arg( format!( "path::{base}" ) )
.arg( "topic::default_topic" )
.output()
.unwrap();
assert_exit( &out, 0 );
let session_dir = project.path().join( "-default_topic" );
assert!(
!session_dir.exists(),
".session.dir must not create directory; found: {session_dir:?}"
);
}
#[ test ]
fn int_10_exits_0_for_nonexistent_path()
{
let home = TempDir::new().unwrap();
let out = common::clg_cmd()
.env( "HOME", home.path() )
.arg( ".session.dir" )
.arg( "path::/tmp/nonexistent-session-dir-int10-xyz" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
!s.trim().is_empty(),
"must output computed path even for nonexistent base; got empty stdout"
);
}