#[ cfg( feature = "enabled" ) ]
mod adapter
{
use claude_profile::adapter::argv_to_unilang_tokens;
fn s( vals : &[ &str ] ) -> Vec< String >
{
vals.iter().map( std::string::ToString::to_string ).collect()
}
#[ test ]
fn adapter_empty_argv_returns_help()
{
let ( tokens, needs_help ) = argv_to_unilang_tokens( &[] ).unwrap();
assert_eq!( tokens, vec![ ".help" ] );
assert!( needs_help );
}
#[ test ]
fn adapter_single_command()
{
let ( tokens, needs_help ) = argv_to_unilang_tokens( &s( &[ ".account.list" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".account.list" ] );
assert!( !needs_help );
}
#[ test ]
fn adapter_dot_returns_help()
{
let ( tokens, needs_help ) = argv_to_unilang_tokens( &s( &[ "." ] ) ).unwrap();
assert_eq!( tokens, vec![ ".help" ] );
assert!( needs_help );
}
#[ test ]
fn adapter_double_dash_help()
{
let ( tokens, needs_help ) = argv_to_unilang_tokens( &s( &[ "--help" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".help" ] );
assert!( needs_help );
}
#[ test ]
fn adapter_dash_h_help()
{
let ( tokens, needs_help ) = argv_to_unilang_tokens( &s( &[ "-h" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".help" ] );
assert!( needs_help );
}
#[ test ]
fn adapter_rejects_unknown_flag()
{
let err = argv_to_unilang_tokens( &s( &[ "-verbose" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "unexpected flag" ), "got: {msg}" );
}
#[ test ]
fn adapter_rejects_param_as_command()
{
let err = argv_to_unilang_tokens( &s( &[ "key::value" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "expected command name" ), "got: {msg}" );
}
#[ test ]
fn adapter_verbosity_alias_0()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "v::0" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "verbosity::0" ] );
}
#[ test ]
fn adapter_verbosity_alias_1()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "v::1" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "verbosity::1" ] );
}
#[ test ]
fn adapter_verbosity_alias_2()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "v::2" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "verbosity::2" ] );
}
#[ test ]
fn adapter_verbosity_out_of_range()
{
let err = argv_to_unilang_tokens( &s( &[ ".cmd", "v::3" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "out of range" ), "got: {msg}" );
}
#[ test ]
fn adapter_verbosity_non_numeric()
{
let err = argv_to_unilang_tokens( &s( &[ ".cmd", "v::abc" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "verbosity" ), "got: {msg}" );
}
#[ test ]
fn adapter_verbosity_canonical_key()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "verbosity::1" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "verbosity::1" ] );
}
#[ test ]
fn adapter_dry_true_normalizes()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "dry::true" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "dry::1" ] );
}
#[ test ]
fn adapter_dry_false_normalizes()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "dry::false" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "dry::0" ] );
}
#[ test ]
fn adapter_dry_1_passthrough()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "dry::1" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "dry::1" ] );
}
#[ test ]
fn adapter_dry_0_passthrough()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "dry::0" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "dry::0" ] );
}
#[ test ]
fn adapter_dry_invalid_value()
{
let err = argv_to_unilang_tokens( &s( &[ ".cmd", "dry::maybe" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "invalid value for dry" ), "got: {msg}" );
}
#[ test ]
fn adapter_missing_separator()
{
let err = argv_to_unilang_tokens( &s( &[ ".cmd", "bareword" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "param::value" ), "got: {msg}" );
}
#[ test ]
fn adapter_rejects_flag_in_params()
{
let err = argv_to_unilang_tokens( &s( &[ ".cmd", "--verbose" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "unexpected flag" ), "got: {msg}" );
}
#[ test ]
fn adapter_duplicate_last_wins()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "k::v1", "k::v2" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "k::v2" ] );
}
#[ test ]
fn adapter_alias_then_canonical_dedup()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "v::0", "verbosity::2" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "verbosity::2" ] );
}
#[ test ]
fn adapter_multi_separator_split()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "key::val::extra" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "key::val::extra" ] );
}
#[ test ]
fn adapter_empty_value_allowed()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "key::" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "key::" ] );
}
#[ test ]
fn adapter_verbosity_negative()
{
let err = argv_to_unilang_tokens( &s( &[ ".cmd", "v::-1" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "verbosity" ), "got: {msg}" );
}
#[ test ]
fn adapter_verbosity_decimal()
{
let err = argv_to_unilang_tokens( &s( &[ ".cmd", "v::2.5" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "verbosity" ), "got: {msg}" );
}
#[ test ]
fn adapter_verbosity_empty()
{
let err = argv_to_unilang_tokens( &s( &[ ".cmd", "v::" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "verbosity" ), "got: {msg}" );
}
#[ test ]
fn adapter_multiple_params()
{
let ( tokens, _ ) = argv_to_unilang_tokens( &s( &[ ".cmd", "v::1", "format::json", "dry::1" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".cmd", "verbosity::1", "format::json", "dry::1" ] );
}
#[ test ]
fn adapter_help_as_command()
{
let ( tokens, needs_help ) = argv_to_unilang_tokens( &s( &[ ".help" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".help" ] );
assert!( needs_help );
}
#[ test ]
fn adapter_verbosity_overflow()
{
let err = argv_to_unilang_tokens( &s( &[ ".cmd", "v::256" ] ) ).unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "verbosity" ), "got: {msg}" );
}
#[ test ]
fn adapter_dot_help_in_second_position()
{
let ( tokens, needs_help ) = argv_to_unilang_tokens( &s( &[ ".account.list", ".help" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".help" ] );
assert!( needs_help );
}
#[ test ]
fn adapter_bare_help_in_second_position()
{
let ( tokens, needs_help ) = argv_to_unilang_tokens( &s( &[ ".account.list", "help" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".help" ] );
assert!( needs_help );
}
#[ test ]
fn adapter_bare_help_as_sole_arg()
{
let ( tokens, needs_help ) = argv_to_unilang_tokens( &s( &[ "help" ] ) ).unwrap();
assert_eq!( tokens, vec![ ".help" ] );
assert!( needs_help );
}
}
#[ cfg( feature = "enabled" ) ]
mod output
{
use claude_profile::output::{ OutputFormat, OutputOptions, json_escape };
use std::collections::HashMap;
use unilang::data::CommandDefinition;
use unilang::semantic::VerifiedCommand;
use unilang::types::Value;
fn make_cmd( args : Vec< ( &str, Value ) > ) -> VerifiedCommand
{
let mut arguments = HashMap::new();
for ( k, v ) in args
{
arguments.insert( k.to_string(), v );
}
let definition = CommandDefinition::former()
.name( ".test" )
.description( "test command" )
.end();
VerifiedCommand { definition, arguments }
}
#[ test ]
fn output_format_text()
{
let cmd = make_cmd( vec![ ( "format", Value::String( "text".to_string() ) ) ] );
let opts = OutputOptions::from_cmd( &cmd ).unwrap();
assert_eq!( opts.format, OutputFormat::Text );
}
#[ test ]
fn output_format_json()
{
let cmd = make_cmd( vec![ ( "format", Value::String( "json".to_string() ) ) ] );
let opts = OutputOptions::from_cmd( &cmd ).unwrap();
assert_eq!( opts.format, OutputFormat::Json );
}
#[ test ]
fn output_format_xml_rejected()
{
let cmd = make_cmd( vec![ ( "format", Value::String( "xml".to_string() ) ) ] );
let err = OutputOptions::from_cmd( &cmd ).unwrap_err();
assert!( err.message.contains( "unknown format" ), "got: {}", err.message );
}
#[ test ]
fn output_format_default()
{
let cmd = make_cmd( vec![] );
let opts = OutputOptions::from_cmd( &cmd ).unwrap();
assert_eq!( opts.format, OutputFormat::Text );
}
#[ test ]
fn output_format_case_json_rejected()
{
let cmd = make_cmd( vec![ ( "format", Value::String( "JSON".to_string() ) ) ] );
let err = OutputOptions::from_cmd( &cmd ).unwrap_err();
assert!( err.message.contains( "unknown format" ), "got: {}", err.message );
}
#[ test ]
fn output_format_case_text_rejected()
{
let cmd = make_cmd( vec![ ( "format", Value::String( "Text".to_string() ) ) ] );
let err = OutputOptions::from_cmd( &cmd ).unwrap_err();
assert!( err.message.contains( "unknown format" ), "got: {}", err.message );
}
#[ test ]
fn output_verbosity_0()
{
let cmd = make_cmd( vec![ ( "verbosity", Value::Integer( 0 ) ) ] );
let opts = OutputOptions::from_cmd( &cmd ).unwrap();
assert_eq!( opts.verbosity, 0 );
}
#[ test ]
fn output_verbosity_1()
{
let cmd = make_cmd( vec![ ( "verbosity", Value::Integer( 1 ) ) ] );
let opts = OutputOptions::from_cmd( &cmd ).unwrap();
assert_eq!( opts.verbosity, 1 );
}
#[ test ]
fn output_verbosity_2()
{
let cmd = make_cmd( vec![ ( "verbosity", Value::Integer( 2 ) ) ] );
let opts = OutputOptions::from_cmd( &cmd ).unwrap();
assert_eq!( opts.verbosity, 2 );
}
#[ test ]
fn output_verbosity_default()
{
let cmd = make_cmd( vec![] );
let opts = OutputOptions::from_cmd( &cmd ).unwrap();
assert_eq!( opts.verbosity, 1 );
}
#[ test ]
fn json_escape_plain()
{
assert_eq!( json_escape( "hello" ), "hello" );
}
#[ test ]
fn json_escape_quote()
{
assert_eq!( json_escape( r#"he"lo"# ), r#"he\"lo"# );
}
#[ test ]
fn json_escape_backslash()
{
assert_eq!( json_escape( "he\\lo" ), "he\\\\lo" );
}
#[ test ]
fn json_escape_newline()
{
assert_eq!( json_escape( "a\nb" ), "a\\nb" );
}
#[ test ]
fn json_escape_tab()
{
assert_eq!( json_escape( "a\tb" ), "a\\tb" );
}
#[ test ]
fn json_escape_cr()
{
assert_eq!( json_escape( "a\rb" ), "a\\rb" );
}
#[ test ]
fn json_escape_empty()
{
assert_eq!( json_escape( "" ), "" );
}
#[ test ]
fn json_escape_mixed()
{
assert_eq!( json_escape( "a\"b\\c\nd" ), "a\\\"b\\\\c\\nd" );
}
#[ test ]
fn json_escape_unicode()
{
assert_eq!( json_escape( "cafe\u{0301}" ), "cafe\u{0301}" );
}
}
#[ cfg( feature = "enabled" ) ]
mod format_duration
{
use claude_profile::output::format_duration_secs;
#[ test ]
fn dur_zero_seconds_shows_0m()
{
assert_eq!( format_duration_secs( 0 ), "0m" );
}
#[ test ]
fn dur_sub_minute_rounds_to_0m()
{
assert_eq!( format_duration_secs( 1 ), "0m" );
}
#[ test ]
fn dur_59s_rounds_to_0m()
{
assert_eq!( format_duration_secs( 59 ), "0m" );
}
#[ test ]
fn dur_60s_shows_1m()
{
assert_eq!( format_duration_secs( 60 ), "1m" );
}
#[ test ]
fn dur_3599s_shows_59m()
{
assert_eq!( format_duration_secs( 3599 ), "59m" );
}
#[ test ]
fn dur_3600s_shows_1h_no_minutes()
{
assert_eq!( format_duration_secs( 3600 ), "1h" );
}
#[ test ]
fn dur_3660s_shows_1h_1m()
{
assert_eq!( format_duration_secs( 3660 ), "1h 1m" );
}
#[ test ]
fn dur_86400s_shows_1d_no_hours()
{
assert_eq!( format_duration_secs( 86400 ), "1d" );
}
#[ test ]
fn dur_86460s_shows_1d_1m()
{
assert_eq!( format_duration_secs( 86460 ), "1d 1m" );
}
#[ test ]
fn dur_90000s_shows_1d_1h_no_minutes()
{
assert_eq!( format_duration_secs( 90000 ), "1d 1h" );
}
#[ test ]
fn dur_90060s_shows_1d_1h_1m()
{
assert_eq!( format_duration_secs( 90060 ), "1d 1h 1m" );
}
#[ test ]
fn dur_max_u64_does_not_panic()
{
let _ = format_duration_secs( u64::MAX );
}
}