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_default_list_shows_all_projects()
{
let root = TempDir::new().unwrap();
let alpha = root.path().join( "alpha" );
let beta = root.path().join( "beta" );
let gamma = root.path().join( "gamma" );
common::write_path_project_session( root.path(), &alpha, "s001", 2 );
common::write_path_project_session( root.path(), &beta, "s001", 2 );
common::write_path_project_session( root.path(), &gamma, "s001", 2 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( "alpha" ),
"INT-1: project 'alpha' must appear in .list output; got:\n{s}"
);
assert!(
s.contains( "beta" ),
"INT-1: project 'beta' must appear in .list output; got:\n{s}"
);
assert!(
s.contains( "gamma" ),
"INT-1: project 'gamma' must appear in .list output; got:\n{s}"
);
}
#[ test ]
fn int_2_type_uuid_filters_to_uuid_projects_only()
{
let root = TempDir::new().unwrap();
let uuid_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
common::write_test_session( root.path(), uuid_id, "s001", 2 );
let path_proj1 = root.path().join( "myproject-one" );
let path_proj2 = root.path().join( "myproject-two" );
common::write_path_project_session( root.path(), &path_proj1, "s001", 2 );
common::write_path_project_session( root.path(), &path_proj2, "s001", 2 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( "type::uuid" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( uuid_id ) || s.contains( "a1b2c3d4" ),
"INT-2: UUID project must appear with type::uuid filter; got:\n{s}"
);
assert!(
!s.contains( "myproject-one" ) && !s.contains( "myproject-two" ),
"INT-2: path-encoded projects must be absent with type::uuid filter; got:\n{s}"
);
}
#[ test ]
fn int_3_type_path_filters_to_path_encoded_projects_only()
{
let root = TempDir::new().unwrap();
let uuid_id = "b2c3d4e5-f6a7-8901-bcde-f12345678901";
common::write_test_session( root.path(), uuid_id, "s001", 2 );
let path_proj1 = root.path().join( "encoded-alpha" );
let path_proj2 = root.path().join( "encoded-beta" );
common::write_path_project_session( root.path(), &path_proj1, "s001", 2 );
common::write_path_project_session( root.path(), &path_proj2, "s001", 2 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( "type::path" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( "encoded" ),
"INT-3: path-encoded projects must appear with type::path filter; got:\n{s}"
);
assert!(
!s.contains( uuid_id ) && !s.contains( "b2c3d4e5" ),
"INT-3: UUID project must be absent with type::path filter; got:\n{s}"
);
}
#[ test ]
fn int_4_sessions_1_expands_session_list_per_project()
{
let root = TempDir::new().unwrap();
let alpha = root.path().join( "list4-alpha" );
let beta = root.path().join( "list4-beta" );
common::write_path_project_session( root.path(), &alpha, "s-alpha-001", 2 );
common::write_path_project_session( root.path(), &alpha, "s-alpha-002", 2 );
common::write_path_project_session( root.path(), &beta, "s-beta-001", 2 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( "show_sessions::1" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( "s-alpha-001" ),
"INT-4: session 's-alpha-001' must appear with show_sessions::1; got:\n{s}"
);
assert!(
s.contains( "s-alpha-002" ),
"INT-4: session 's-alpha-002' must appear with show_sessions::1; got:\n{s}"
);
assert!(
s.contains( "s-beta-001" ),
"INT-4: session 's-beta-001' must appear with show_sessions::1; got:\n{s}"
);
}
#[ test ]
fn int_5_path_substring_filters_project_list()
{
let root = TempDir::new().unwrap();
let alpha = root.path().join( "projects" ).join( "alpha" );
let beta = root.path().join( "projects" ).join( "beta" );
let other = root.path().join( "other" );
common::write_path_project_session( root.path(), &alpha, "s001", 2 );
common::write_path_project_session( root.path(), &beta, "s001", 2 );
common::write_path_project_session( root.path(), &other, "s001", 2 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( "path::pro" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( "alpha" ) || s.contains( "beta" ),
"INT-5: projects under 'projects/' must appear with path::pro filter; got:\n{s}"
);
assert!(
!s.contains( "other" ),
"INT-5: project '/other' must be absent with path::pro filter; got:\n{s}"
);
}
#[ test ]
fn int_6_session_filter_auto_enables_sessions_display()
{
let root = TempDir::new().unwrap();
let alpha = root.path().join( "list6-alpha" );
let beta = root.path().join( "list6-beta" );
common::write_path_project_session( root.path(), &alpha, "abc-session", 2 );
common::write_path_project_session( root.path(), &beta, "other-session", 2 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( "session::abc" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( "abc-session" ),
"INT-6: session 'abc-session' must appear when session::abc filter auto-enables display; got:\n{s}"
);
}
#[ test ]
fn int_7_agent_1_filters_to_agent_sessions_only()
{
let root = TempDir::new().unwrap();
let alpha_path = root.path().join( "list7-alpha" );
let encoded = common::write_path_project_session(
root.path(), &alpha_path, "main-session-001", 2
);
common::write_flat_agent_session(
root.path(), &encoded, "agent-001", "main-session-001", 2
);
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( "agent::1" )
.arg( "show_sessions::1" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( "agent-001" ),
"INT-7: agent session 'agent-001' must appear with agent::1; got:\n{s}"
);
assert!(
!s.contains( "main-session-001" ),
"INT-7: main session must be absent with agent::1 filter; got:\n{s}"
);
}
#[ test ]
fn int_8_agent_0_filters_to_main_sessions_only()
{
let root = TempDir::new().unwrap();
let alpha_path = root.path().join( "list8-alpha" );
let encoded = common::write_path_project_session(
root.path(), &alpha_path, "main-session-002", 2
);
common::write_flat_agent_session(
root.path(), &encoded, "agent-002", "main-session-002", 2
);
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( "agent::0" )
.arg( "show_sessions::1" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( "main-session-002" ),
"INT-8: main session must appear with agent::0 filter; got:\n{s}"
);
assert!(
!s.contains( "agent-002" ),
"INT-8: agent session must be absent with agent::0 filter; got:\n{s}"
);
}
#[ test ]
fn int_9_min_entries_auto_enables_sessions_display()
{
let root = TempDir::new().unwrap();
let alpha = root.path().join( "list9-alpha" );
common::write_path_project_session( root.path(), &alpha, "s1-many", 15 );
common::write_path_project_session( root.path(), &alpha, "s2-few", 3 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( "min_entries::10" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( "s1-many" ),
"INT-9: session with 15 entries must appear with min_entries::10; got:\n{s}"
);
assert!(
!s.contains( "s2-few" ),
"INT-9: session with 3 entries must be absent with min_entries::10; got:\n{s}"
);
}
#[ test ]
fn int_10_sessions_0_suppresses_display_even_with_session_filter()
{
let root = TempDir::new().unwrap();
let alpha = root.path().join( "list10-alpha" );
common::write_path_project_session( root.path(), &alpha, "abc-override", 2 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( "session::abc" )
.arg( "show_sessions::0" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( "abc-override" ),
"INT-10: session:: filter shows sessions even when show_sessions::0 is set; got:\n{s}"
);
}
#[ test ]
fn int_11_combined_path_session_filter()
{
let root = TempDir::new().unwrap();
let alpha = root.path().join( "projects" ).join( "alpha" );
let other = root.path().join( "unrelated" ).join( "other" );
common::write_path_project_session( root.path(), &alpha, "s-abc", 2 );
common::write_path_project_session( root.path(), &other, "s-abc", 2 );
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.arg( "path::pro" )
.arg( "session::abc" )
.output()
.unwrap();
assert_exit( &out, 0 );
let s = stdout( &out );
assert!(
s.contains( "alpha" ),
"INT-11: project 'alpha' (path contains 'pro') must appear; got:\n{s}"
);
assert!(
!s.contains( "other" ),
"INT-11: project 'other' (path lacks 'pro') must be absent; got:\n{s}"
);
}
#[ test ]
fn int_12_exit_code_0_on_empty_storage()
{
let root = TempDir::new().unwrap();
std::fs::create_dir_all( root.path().join( "projects" ) ).unwrap();
let out = common::clg_cmd()
.env( "CLAUDE_STORAGE_ROOT", root.path() )
.arg( ".list" )
.output()
.unwrap();
assert_exit( &out, 0 );
}