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 cc_1_project_resolves_same_project_in_show_and_search()
{
let root = TempDir::new().unwrap();
let project_dir = root.path().join( "myproject" );
common::write_test_session_with_last_message(
root.path(),
&claude_storage_core::encode_path( &project_dir ).unwrap(),
"-default_topic",
2,
"hello from myproject",
);
let show_out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".show" )
.arg( "session_id::-default_topic" )
.arg( format!( "project::{}", project_dir.display() ) )
.output()
.unwrap();
let search_out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".search" )
.arg( "query::hello" )
.arg( format!( "project::{}", project_dir.display() ) )
.output()
.unwrap();
assert_exit( &show_out, 0 );
assert_exit( &search_out, 0 );
let show_s = stdout( &show_out );
assert!(
!show_s.is_empty(),
"CC-1: .show with project:: must produce output; stderr: {}",
stderr( &show_out )
);
let search_s = stdout( &search_out );
assert!(
search_s.contains( "hello" ) || search_s.contains( "myproject" ),
"CC-1: .search must find 'hello' in project-scoped results; got:\n{search_s}"
);
}
#[ test ]
fn cc_2_project_with_absolute_path_works_in_export()
{
let root = TempDir::new().unwrap();
let project_dir = root.path().join( "exportproj" );
common::write_path_project_session( root.path(), &project_dir, "-default_topic", 2 );
let output_file = root.path().join( "cc2-export.jsonl" );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".export" )
.arg( "session_id::-default_topic" )
.arg( format!( "project::{}", project_dir.display() ) )
.arg( format!( "output::{}", output_file.display() ) )
.output()
.unwrap();
assert_exit( &out, 0 );
assert!(
output_file.exists(),
"CC-2: .export with project:: must create the output file; stderr: {}",
stderr( &out )
);
let content = std::fs::read_to_string( &output_file ).unwrap();
assert!(
!content.is_empty(),
"CC-2: exported file must not be empty"
);
}
#[ test ]
fn cc_3_project_with_uuid_format_works_in_count()
{
let root = TempDir::new().unwrap();
let uuid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
common::write_test_session( root.path(), uuid, "session-cc3", 10 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".count" )
.arg( format!( "project::{uuid}" ) )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
!s.trim().is_empty(),
"CC-3: .count with UUID project:: must produce output; stderr: {}",
stderr( &out )
);
let trimmed = s.trim();
assert!(
trimmed.parse::< u64 >().is_ok() || trimmed.contains( "10" ),
"CC-3: .count must return a numeric entry count; got:\n{s}"
);
}
#[ test ]
fn cc_4_absent_project_defaults_to_cwd_in_show()
{
let root = TempDir::new().unwrap();
let project_dir = root.path().join( "cwdproj" );
std::fs::create_dir_all( &project_dir ).unwrap();
common::write_path_project_session( root.path(), &project_dir, "-default_topic", 2 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.current_dir( &project_dir )
.arg( ".show" )
.arg( "session_id::-default_topic" )
.output()
.unwrap();
assert_exit( &out, 0 );
assert!(
!stdout( &out ).is_empty(),
"CC-4: .show without project:: must resolve via cwd and produce output; stderr: {}",
stderr( &out )
);
}
#[ test ]
fn cc_5_absent_project_defaults_to_cwd_in_export()
{
let root = TempDir::new().unwrap();
let project_dir = root.path().join( "cwdexport" );
std::fs::create_dir_all( &project_dir ).unwrap();
common::write_path_project_session( root.path(), &project_dir, "-default_topic", 2 );
let output_file = root.path().join( "cc5-export.jsonl" );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.current_dir( &project_dir )
.arg( ".export" )
.arg( "session_id::-default_topic" )
.arg( format!( "output::{}", output_file.display() ) )
.output()
.unwrap();
assert_exit( &out, 0 );
assert!(
output_file.exists(),
"CC-5: .export without project:: must create output file via cwd resolution; stderr: {}",
stderr( &out )
);
}
#[ test ]
fn cc_6_same_project_value_returns_same_project_in_all_commands()
{
let root = TempDir::new().unwrap();
let project_dir = root.path().join( "allcmds" );
common::write_path_project_session( root.path(), &project_dir, "s001", 4 );
common::write_path_project_session( root.path(), &project_dir, "s002", 4 );
let project_arg = format!( "project::{}", project_dir.display() );
let output_file = root.path().join( "cc6-export.jsonl" );
let list_out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( &project_arg )
.output()
.unwrap();
assert_exit( &list_out, 0 );
assert!(
stdout( &list_out ).contains( "allcmds" ),
"CC-6: .list must show the specified project; got:\n{}",
stdout( &list_out )
);
let count_out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".count" )
.arg( &project_arg )
.output()
.unwrap();
assert_exit( &count_out, 0 );
let show_out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".show" )
.arg( "session_id::s001" )
.arg( &project_arg )
.output()
.unwrap();
assert_exit( &show_out, 0 );
let export_out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".export" )
.arg( "session_id::s001" )
.arg( &project_arg )
.arg( format!( "output::{}", output_file.display() ) )
.output()
.unwrap();
assert_exit( &export_out, 0 );
let search_out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".search" )
.arg( "query::entry" )
.arg( &project_arg )
.output()
.unwrap();
assert_exit( &search_out, 0 );
}