//! TLI Regime Command Tests (Wave D) //! //! Test suite for validating Wave D regime detection commands in the TLI client. //! Tests command parsing, execution flow, output formatting, and error handling. // Suppress false-positive unused_crate_dependencies warnings // dev-dependencies are shared across ALL test targets in the crate // This test may not use all deps, but they are required by other integration tests #![allow(unused_crate_dependencies)] use fxt::commands::trade_ml::{TradeMlArgs, TradeMlCommand}; #[tokio::test] async fn test_regime_command_parses() { // Test that regime command structure is correct let args = TradeMlArgs { command: TradeMlCommand::Regime { symbol: "ES.FUT".to_owned(), }, }; // Execution will fail without running API Gateway, but command should parse let result = args.execute("http://localhost:50051", "mock-token").await; assert!( result.is_err(), "Expected connection error without running server" ); } #[tokio::test] async fn test_transitions_command_parses() { // Test that transitions command structure is correct let args = TradeMlArgs { command: TradeMlCommand::Transitions { symbol: "ES.FUT".to_owned(), limit: 20, }, }; // Execution will fail without running API Gateway, but command should parse let result = args.execute("http://localhost:50051", "mock-token").await; assert!( result.is_err(), "Expected connection error without running server" ); } #[tokio::test] async fn test_regime_command_default_limit() { // Test default limit value for transitions let args = TradeMlArgs { command: TradeMlCommand::Transitions { symbol: "NQ.FUT".to_owned(), limit: 100, // Default from clap }, }; match args.command { TradeMlCommand::Transitions { symbol, limit } => { assert_eq!(symbol, "NQ.FUT"); assert_eq!(limit, 100); }, _ => panic!("Expected Transitions command"), } } #[tokio::test] async fn test_regime_command_custom_limit() { // Test custom limit value for transitions let args = TradeMlArgs { command: TradeMlCommand::Transitions { symbol: "CL.FUT".to_owned(), limit: 50, }, }; match args.command { TradeMlCommand::Transitions { symbol, limit } => { assert_eq!(symbol, "CL.FUT"); assert_eq!(limit, 50); }, _ => panic!("Expected Transitions command"), } } #[test] fn test_regime_command_symbol_validation() { // Test that various symbol formats are accepted let symbols = vec!["ES.FUT", "NQ.FUT", "6E.FUT", "ZN.FUT", "CL.FUT"]; for symbol in symbols { let args = TradeMlArgs { command: TradeMlCommand::Regime { symbol: symbol.to_owned(), }, }; match args.command { TradeMlCommand::Regime { symbol: s } => { assert_eq!(s, symbol); assert!(s.contains(".FUT"), "Symbol should be a futures contract"); }, _ => panic!("Expected Regime command"), } } } #[test] fn test_transitions_limit_bounds() { // Test limit parameter edge cases let test_cases = vec![ (1, true), // Minimum valid (10, true), // Small limit (100, true), // Default (500, true), // Large limit (1000, true), // Very large limit ]; for (limit, should_be_valid) in test_cases { let args = TradeMlArgs { command: TradeMlCommand::Transitions { symbol: "ES.FUT".to_owned(), limit, }, }; match args.command { TradeMlCommand::Transitions { limit: l, .. } => { assert_eq!(l, limit); if should_be_valid { assert!(l > 0, "Limit should be positive"); } }, _ => panic!("Expected Transitions command"), } } } /// Integration test: Verify command execution flow (without server connection) #[tokio::test] async fn test_regime_command_execution_flow() { // Test that commands follow correct execution path let args = TradeMlArgs { command: TradeMlCommand::Regime { symbol: "ES.FUT".to_owned(), }, }; // Execute should attempt gRPC connection and fail gracefully let result = args.execute("http://localhost:50051", "mock-token").await; // Expect either network error (connection refused) or auth error (invalid token) assert!(result.is_err()); let error_msg = format!("{:?}", result.unwrap_err()); assert!( error_msg.contains("connect") || error_msg.contains("connection") || error_msg.contains("refused") || error_msg.contains("authentication") || error_msg.contains("credentials") || error_msg.contains("token"), "Expected connection or auth error, got: {}", error_msg ); } /// Integration test: Verify transitions command execution flow #[tokio::test] async fn test_transitions_command_execution_flow() { let args = TradeMlArgs { command: TradeMlCommand::Transitions { symbol: "NQ.FUT".to_owned(), limit: 25, }, }; // Execute should attempt gRPC connection and fail gracefully let result = args.execute("http://localhost:50051", "mock-token").await; // Expect either network error (connection refused) or auth error (invalid token) assert!(result.is_err()); let error_msg = format!("{:?}", result.unwrap_err()); assert!( error_msg.contains("connect") || error_msg.contains("connection") || error_msg.contains("refused") || error_msg.contains("authentication") || error_msg.contains("credentials") || error_msg.contains("token"), "Expected connection or auth error, got: {}", error_msg ); } /// Test command enum variants are correctly defined #[test] fn test_regime_command_variants() { // Verify Regime variant exists and has correct fields let _regime = TradeMlCommand::Regime { symbol: "TEST.FUT".to_owned(), }; // Verify Transitions variant exists and has correct fields let _transitions = TradeMlCommand::Transitions { symbol: "TEST.FUT".to_owned(), limit: 100, }; // If compilation succeeds, variants are correctly defined } /// Test error handling for invalid JWT tokens #[tokio::test] async fn test_regime_invalid_jwt_handling() { let args = TradeMlArgs { command: TradeMlCommand::Regime { symbol: "ES.FUT".to_owned(), }, }; // Test with empty JWT token let result = args.execute("http://localhost:50051", "").await; assert!(result.is_err(), "Empty JWT should fail"); // Test with malformed JWT token let result = args .execute("http://localhost:50051", "invalid-jwt-format!@#$") .await; assert!(result.is_err(), "Invalid JWT format should fail"); } /// Test error handling for invalid URLs #[tokio::test] async fn test_regime_invalid_url_handling() { let args = TradeMlArgs { command: TradeMlCommand::Regime { symbol: "ES.FUT".to_owned(), }, }; // Test with invalid URL format let result = args.execute("not-a-valid-url", "mock-token").await; assert!(result.is_err(), "Invalid URL should fail"); // Test with unreachable host let result = args .execute( "http://invalid-host-that-does-not-exist:50051", "mock-token", ) .await; assert!(result.is_err(), "Unreachable host should fail"); } /// Test concurrent command execution (simulated) #[tokio::test] async fn test_concurrent_regime_commands() { let symbols = vec!["ES.FUT", "NQ.FUT", "CL.FUT", "ZN.FUT"]; let mut handles = vec![]; for symbol in symbols { let symbol_owned = symbol.to_owned(); let handle = tokio::spawn(async move { let args = TradeMlArgs { command: TradeMlCommand::Regime { symbol: symbol_owned, }, }; args.execute("http://localhost:50051", "mock-token").await }); handles.push(handle); } // All commands should fail with connection errors (no server running) for handle in handles { let result = handle.await.expect("Task should complete"); assert!(result.is_err(), "Expected connection error without server"); } } /// Test transitions command with multiple symbols concurrently #[tokio::test] async fn test_concurrent_transitions_commands() { let test_cases = vec![ ("ES.FUT", 10), ("NQ.FUT", 20), ("CL.FUT", 50), ("ZN.FUT", 100), ]; let mut handles = vec![]; for (symbol, limit) in test_cases { let symbol_owned = symbol.to_owned(); let handle = tokio::spawn(async move { let args = TradeMlArgs { command: TradeMlCommand::Transitions { symbol: symbol_owned, limit, }, }; args.execute("http://localhost:50051", "mock-token").await }); handles.push(handle); } // All commands should fail with connection errors (no server running) for handle in handles { let result = handle.await.expect("Task should complete"); assert!(result.is_err(), "Expected connection error without server"); } }