diff --git a/device/mock_device/src/mock_device_ffi.rs b/device/mock_device/src/mock_device_ffi.rs index 8c6f70ebe..1aaa9cf51 100644 --- a/device/mock_device/src/mock_device_ffi.rs +++ b/device/mock_device/src/mock_device_ffi.rs @@ -70,60 +70,84 @@ pub async fn start_service() { } async fn init(client: ServiceClient) { + info!("@@@NNA: Entered init function"); if let Some(mut extn_client) = client.get_extn_client() { + info!("@@@NNA: extn_client found, spawning websocket server"); let client_c_for_init = client.clone(); tokio::spawn(async move { match boot_ws_server(extn_client.clone()).await { Ok(server) => { + info!("@@@NNA: Websocket server started successfully"); let state = MockDeviceState::new(server); let mut methods = Methods::new(); let _ = methods.merge(MockDeviceController::new(state).into_rpc()); + info!("@@@NNA: RPC methods merged"); let processor = RPCRequestProcessor::new( extn_client.clone(), methods, ExtnId::new_channel(ExtnClassId::Device, "mock_device".into()), ); + info!("@@@NNA: RPCRequestProcessor created, adding to extn_client"); extn_client.add_request_processor(processor); } - Err(err) => panic!("websocket server failed to start. {}", err), + Err(err) => { + error!("@@@NNA: websocket server failed to start. {}", err); + panic!("websocket server failed to start. {}", err); + } }; }); + info!("@@@NNA: Initializing client_c_for_init"); client_c_for_init.initialize().await; + info!("@@@NNA: client_c_for_init initialized"); } else { - error!("Service client does not hold an extn client. Cannot start eos extension."); + error!("@@@NNA: Service client does not hold an extn client. Cannot start eos extension."); } } fn start() { + info!("@@@NNA: Entered start function"); let Ok((extn_manifest, _device_manifest)) = RippleManifestLoader::initialize() else { - error!("Error initializing manifests"); + error!("@@@NNA: Error initializing manifests"); return; }; + info!("@@@NNA: Manifests initialized successfully"); + let runtime = match Runtime::new() { - Ok(r) => r, + Ok(r) => { + info!("@@@NNA: Tokio runtime created successfully"); + r + } Err(err) => { - error!("Error creating runtime: {}", err); + error!("@@@NNA: Error creating runtime: {}", err); return; } }; let id = ExtnId::new_channel(ExtnClassId::Device, EXTN_NAME.to_string()).to_string(); + info!("@@@NNA: Generated ExtnId: {}", id); + let symbol = extn_manifest.get_extn_symbol(&id); if symbol.is_none() { - error!("Error getting symbol"); + error!("@@@NNA: Error getting symbol"); return; } + info!("@@@NNA: Extension symbol found"); + let service_client = if let Some(symbol) = symbol { + info!("@@@NNA: Building ServiceClient with extension symbol"); ServiceClient::builder().with_extension(symbol).build() } else { + info!("@@@NNA: Building ServiceClient without extension symbol"); ServiceClient::builder().build() }; + info!("@@@NNA: Starting async init in runtime"); runtime.block_on(async move { init(service_client.clone()).await; }); + info!("@@@NNA: Exiting start function"); } fn init_extn_channel() -> ExtnChannel { diff --git a/device/mock_device/src/utils.rs b/device/mock_device/src/utils.rs index 9e3aa1fac..cf9e88af3 100644 --- a/device/mock_device/src/utils.rs +++ b/device/mock_device/src/utils.rs @@ -37,53 +37,79 @@ use crate::{ pub async fn boot_ws_server( mut client: ExtnClient, ) -> Result, MockDeviceError> { - debug!("Booting WS Server for mock device"); + debug!("@@@NNA Booting WS Server for mock device"); + debug!( + "@@@NNA Booting WS Server for mock device client:{:?}", + client + ); let gateway = platform_gateway_url(&mut client).await?; + debug!("@@@NNA platform_gateway_url returned: {:?}", gateway); if gateway.scheme() != "ws" { + debug!("@@@NNA Bad URL scheme: {}", gateway.scheme()); return Err(BootFailedError::BadUrlScheme)?; } if !is_valid_host(gateway.host()) { + debug!("@@@NNA Bad hostname: {:?}", gateway.host()); return Err(BootFailedError::BadHostname)?; } let config = load_config(&client); + debug!("@@@NNA Loaded config: {:?}", config); let mut server_config = WsServerParameters::new(); let mock_data_v2 = load_mock_data_v2(client.clone()).await?; + debug!("@@@NNA Loaded mock data v2"); + server_config .port(gateway.port().unwrap_or(0)) .path(gateway.path()); + debug!( + "@@@NNA Server config: port={}, path={}", + gateway.port().unwrap_or(0), + gateway.path() + ); + let ws_server = MockWebSocketServer::new(mock_data_v2, server_config, config) .await .map_err(BootFailedError::ServerStartFailed)?; + debug!("@@@NNA MockWebSocketServer created"); let ws_server = Arc::new(ws_server); let server = ws_server.clone(); tokio::spawn(async move { + debug!("@@@NNA Starting WebSocket server"); server.start_server().await; }); + debug!("@@@NNA Returning ws_server Arc"); Ok(ws_server) } async fn platform_gateway_url(client: &mut ExtnClient) -> Result { - debug!("sending request for config.platform_parameters"); + debug!( + "@@@NNA platform_gateway_url called with client: {:?}", + client + ); + debug!("@@@NNA sending request for config.platform_parameters"); if let Ok(response) = client.request(Config::PlatformParameters).await { + debug!("@@@NNA received response: {:?}", response); if let Some(ExtnResponse::Value(value)) = response.payload.extract() { + debug!("@@@NNA extracted value: {:?}", value); let gateway: Url = value .as_object() .and_then(|obj| obj.get("gateway")) .and_then(|val| val.as_str()) .and_then(|s| s.parse().ok()) .ok_or(BootFailedError::GetPlatformGatewayFailed)?; - debug!("{}", gateway); + debug!("@@@NNA parsed gateway URL: {}", gateway); return Ok(gateway); } } + error!("@@@NNA Failed to get platform gateway URL"); Err(BootFailedError::GetPlatformGatewayFailed)? } diff --git a/device/thunder_ripple_sdk/src/processors/thunder_device_info.rs b/device/thunder_ripple_sdk/src/processors/thunder_device_info.rs index 7092f3692..efe480947 100644 --- a/device/thunder_ripple_sdk/src/processors/thunder_device_info.rs +++ b/device/thunder_ripple_sdk/src/processors/thunder_device_info.rs @@ -295,6 +295,7 @@ impl ThunderDeviceInfoRequestProcessor { } async fn model(state: CachedState, req: ExtnMessage) -> bool { + ripple_sdk::log::info!("@@@NNA...Entered ThunderDeviceInfoRequestProcessor::model"); let response = Self::get_model(&state).await; Self::respond(state.get_client(), req, ExtnResponse::String(response)) .await @@ -942,6 +943,11 @@ impl ExtnRequestProcessor for ThunderDeviceInfoRequestProcessor { msg: ExtnMessage, extracted_message: Self::VALUE, ) -> bool { + // Debug log: print contract name for incoming request + ripple_sdk::log::info!( + "@@@NNA...Incoming DeviceInfoRequest: {:?}", + extracted_message + ); match extracted_message { DeviceInfoRequest::Model => Self::model(state.clone(), msg).await, DeviceInfoRequest::Audio => Self::audio(state.clone(), msg).await, diff --git a/device/thunder_ripple_sdk/src/tests/contracts/thunder_controller_pacts.rs b/device/thunder_ripple_sdk/src/tests/contracts/thunder_controller_pacts.rs index ca7828d3d..189db056b 100644 --- a/device/thunder_ripple_sdk/src/tests/contracts/thunder_controller_pacts.rs +++ b/device/thunder_ripple_sdk/src/tests/contracts/thunder_controller_pacts.rs @@ -56,6 +56,42 @@ async fn test_register_state_change_event() { .await; } +// //added to avoid RESPONSE: {"jsonrpc":"2.0","error":{"code":-32600,"message":"Cannot find a matching response for the received request","data":""}} +// #[tokio::test(flavor = "multi_thread")] +// #[cfg_attr(not(feature = "websocket_contract_tests"), ignore)] +// async fn test_controller_status_no_callsign() { +// mock_websocket_server!( +// builder, +// server, +// server_url, +// "controller_status_no_callsign", +// json!({ +// "pact:content-type": "application/json", +// "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 0)", "method": "Controller.1.status"}, +// "requestMetadata": { +// "path": "/jsonrpc" +// }, +// "response": [{ +// "jsonrpc": "matching(type, '2.0')", +// "id": "matching(integer, 0)", +// "result": [{ +// "state": "activated" +// }] +// }] +// }) +// ); + +// send_thunder_call_message!( +// server_url.to_string(), +// json!({ +// "jsonrpc": "2.0", +// "id": 3, +// "method": "Controller.1.status" +// }) +// ) +// .await; +// } + #[tokio::test(flavor = "multi_thread")] #[cfg_attr(not(feature = "websocket_contract_tests"), ignore)] async fn test_device_info_plugin_status() { @@ -66,13 +102,13 @@ async fn test_device_info_plugin_status() { "device_info_plugin_status", json!({ "pact:content-type": "application/json", - "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 1)", "method": "Controller.1.status@DeviceInfo"}, + "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 0)", "method": "Controller.1.status@DeviceInfo"}, "requestMetadata": { "path": "/jsonrpc" }, "response": [{ "jsonrpc": "matching(type, '2.0')", - "id": "matching(integer, 1)", + "id": "matching(integer, 0)", "result": [{ "callsign": "matching(type, 'string')", "locator": "matching(type, 'string')", @@ -149,13 +185,13 @@ async fn test_display_settings_plugin_status() { "display_settings_plugin_status", json!({ "pact:content-type": "application/json", - "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 3)", "method": "Controller.1.status@org.rdk.DisplaySettings"}, + "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 0)", "method": "Controller.1.status@org.rdk.DisplaySettings"}, "requestMetadata": { "path": "/jsonrpc" }, "response": [{ "jsonrpc": "matching(type, '2.0')", - "id": "matching(integer, 3)", + "id": "matching(integer, 0)", "result": [{ "callsign": "matching(type, 'string')", "locator": "matching(type, 'string')", @@ -231,13 +267,13 @@ async fn test_status_org_rdk_system() { "status_org_rdk_system", json!({ "pact:content-type": "application/json", - "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 5)", "method": "Controller.1.status@org.rdk.System"}, + "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 0)", "method": "Controller.1.status@org.rdk.System"}, "requestMetadata": { "path": "/jsonrpc" }, "response": [{ "jsonrpc": "matching(type, '2.0')", - "id": "matching(integer, 5)", + "id": "matching(integer, 0)", "result": [{ "callsign": "matching(type, 'string')", "locator": "matching(type, 'string')", @@ -313,13 +349,13 @@ async fn test_status_org_rdk_hdcp_profile() { "status_org_rdk_hdcp_profile", json!({ "pact:content-type": "application/json", - "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 7)", "method": "Controller.1.status@org.rdk.HdcpProfile"}, + "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 0)", "method": "Controller.1.status@org.rdk.HdcpProfile"}, "requestMetadata": { "path": "/jsonrpc" }, "response": [{ "jsonrpc": "matching(type, '2.0')", - "id": "matching(integer, 7)", + "id": "matching(integer, 0)", "result": [{ "callsign": "matching(type, 'string')", "locator": "matching(type, 'string')", @@ -395,13 +431,13 @@ async fn test_status_org_rdk_telemetry() { "status_org_rdk_telemetry", json!({ "pact:content-type": "application/json", - "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 9)", "method": "Controller.1.status@org.rdk.Telemetry"}, + "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 0)", "method": "Controller.1.status@org.rdk.Telemetry"}, "requestMetadata": { "path": "/jsonrpc" }, "response": [{ "jsonrpc": "matching(type, '2.0')", - "id": "matching(integer, 9)", + "id": "matching(integer, 0)", "result": [{ "callsign": "matching(type, 'string')", "locator": "matching(type, 'string')", diff --git a/device/thunder_ripple_sdk/src/tests/contracts/thunder_device_info_pacts.rs b/device/thunder_ripple_sdk/src/tests/contracts/thunder_device_info_pacts.rs index b2bdd978e..0a15ceca2 100644 --- a/device/thunder_ripple_sdk/src/tests/contracts/thunder_device_info_pacts.rs +++ b/device/thunder_ripple_sdk/src/tests/contracts/thunder_device_info_pacts.rs @@ -43,10 +43,13 @@ use tokio::time::{timeout, Duration}; #[cfg_attr(not(feature = "websocket_contract_tests"), ignore)] async fn test_device_get_info_mac_address() { // Define Pact request and response - Start + println!("[TEST] Starting test_device_get_info_mac_address"); let mut pact_builder_async = get_pact_builder_async_obj().await; + println!("[TEST] Pact builder initialized"); pact_builder_async .synchronous_message_interaction("A request to get the device info", |mut i| async move { + println!("[TEST] Defining synchronous message interaction for device info"); i.contents_from(json!({ "pact:content-type": "application/json", "request": {"jsonrpc": "matching(type, '2.0')", "id": "matching(integer, 0)", "method": "org.rdk.System.1.getDeviceInfo", "params": {"params": ["estb_mac"]}}, @@ -62,16 +65,23 @@ async fn test_device_get_info_mac_address() { } }] })).await; + println!("[TEST] Interaction contents defined"); i.test_name("get_device_info_mac_address"); + println!("[TEST] Test name set: get_device_info_mac_address"); i }).await; // Define Pact request and response - End // Initialize mock server + println!("[TEST] Starting mock server for device info"); let mock_server = pact_builder_async .start_mock_server_async(Some("websockets/transport/websockets")) .await; + println!( + "[TEST] Mock server started at: {}", + mock_server.path("/jsonrpc") + ); send_thunder_call_message!( url::Url::parse(mock_server.path("/jsonrpc").as_str()) @@ -87,8 +97,201 @@ async fn test_device_get_info_mac_address() { }) ) .await; + println!("[TEST] Thunder call message sent for device info"); +} + +// #[tokio::test(flavor = "multi_thread")] +// #[cfg_attr(not(feature = "websocket_contract_tests"), ignore)] +// async fn test_device_get_model() { +// println!("@@@NNA Starting test_device_get_model"); +// let mut pact_builder_async = get_pact_builder_async_obj().await; +// println!("@@@NNA Pact builder initialized"); + +// let mut result = HashMap::new(); + +// println!("@@@NNA Preparing expected result matchers..."); +// result.insert( +// "stbVersion".into(), +// ContractMatcher::MatchRegex( +// r"^[A-Z0-9]+_VBN_[A-Za-z0-9]+_sprint_\d{14}sdy(_[A-Z0-9_]+)*$".into(), +// "AX061AEI_VBN_1911_sprint_20200109040424sdy".into(), +// ), +// ); + +// result.insert( +// "receiverVersion".into(), +// ContractMatcher::MatchRegex(r"^\d+\.\d+\.\d+\.\d+$".into(), "3.14.0.0".into()), +// ); + +// result.insert( +// "stbTimestamp".into(), +// ContractMatcher::MatchRegex( +// r"^\w{3} \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} [A-Z ]*UTC$".into(), +// "Thu 09 Jan 2020 04:04:24 AP UTC".into(), +// ), +// ); +// result.insert("success".into(), ContractMatcher::MatchBool(true)); + +// println!("@@@NNA Setting up Pact interaction..."); +// pact_builder_async +// .synchronous_message_interaction("A request to get the device model", |mut i| async move { +// println!("@@@NNA Defining synchronous message interaction for device model"); +// i.given("System Version info is set"); +// i.contents_from(get_pact!( +// "org.rdk.System.1.getSystemVersions", +// ContractResult { result } +// )) +// .await; +// println!("@@@NNA Interaction contents defined"); +// i.test_name("get_device_model"); +// println!("@@@NNA Test name set: get_device_model"); +// i +// }) +// .await; + +// println!("@@@NNA Starting Pact mock server..."); +// let mock_server = pact_builder_async +// .start_mock_server_async(Some("websockets/transport/websockets")) +// .await; +// println!( +// "@@@NNA Mock server started at: {}", +// mock_server.path("/jsonrpc") +// ); + +// let url = url::Url::parse(mock_server.path("/jsonrpc").as_str()) +// .unwrap() +// .to_string(); +// println!("@@@NNA Sending Thunder call message to: {}", url); +// send_thunder_call_message!( +// url, +// json!({ +// "jsonrpc": "2.0", +// "id": 42, +// "method": "org.rdk.System.1.getSystemVersions" +// }) +// ) +// .await; +// println!("@@@NNA Thunder call message sent for device model"); +// println!("@@@NNA test_device_get_model completed."); +// } + +#[tokio::test(flavor = "multi_thread")] +#[cfg_attr(not(feature = "websocket_contract_tests"), ignore)] +async fn test_device_get_system_verisons() { + let mut pact_builder_async = get_pact_builder_async_obj().await; + + let mut result = HashMap::new(); + result.insert( + "stbVersion".into(), + ContractMatcher::MatchRegex( + r"^[A-Z0-9]+_VBN_[A-Za-z0-9]+_sprint_\d{14}sdy(_[A-Z0-9_]+)*$".into(), + "AX061AEI_VBN_1911_sprint_20200109040424sdy".into(), + ), + ); + result.insert( + "receiverVersion".into(), + ContractMatcher::MatchRegex(r"^\d+\.\d+\.\d+\.\d+$".into(), "3.14.0.0".into()), + ); + result.insert( + "stbTimestamp".into(), + ContractMatcher::MatchRegex( + r"^\w{3} \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} [A-Z ]*UTC$".into(), + "Thu 09 Jan 2020 04:04:24 AP UTC".into(), + ), + ); + result.insert("success".into(), ContractMatcher::MatchBool(true)); + + pact_builder_async + .synchronous_message_interaction("A request to get system versions", |mut i| async move { + i.given("System Version info is set"); + i.contents_from(get_pact!( + "org.rdk.System.1.getSystemVersions", + ContractResult { result } + )) + .await; + i.test_name("get_system_versions"); + i + }) + .await; + + let mock_server = pact_builder_async + .start_mock_server_async(Some("websockets/transport/websockets")) + .await; + + let url = url::Url::parse(mock_server.path("/jsonrpc").as_str()) + .unwrap() + .to_string(); + send_thunder_call_message!( + url, + json!({ + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.System.1.getSystemVersions" + }) + ) + .await; } +// #[tokio::test(flavor = "multi_thread")] +// #[cfg_attr(not(feature = "websocket_contract_tests"), ignore)] +// async fn test_device_get_model() { +// let mut pact_builder_async = get_pact_builder_async_obj().await; + +// pact_builder_async +// .synchronous_message_interaction("A request to get the device model", |mut i| async move { +// i.given("Device model is set"); +// i.contents_from(json!({ +// "pact:content-type": "application/json", +// "request": { +// "jsonrpc": "2.0", +// "id": 0, +// "method": "Device.model" +// }, +// "requestMetadata": { +// "path": "/jsonrpc" +// }, +// "response": [{ +// "jsonrpc": "2.0", +// "id": 0, +// "result": "xi7" +// }] +// })) +// .await; +// i.test_name("get_device_model"); +// i +// }) +// .await; + +// let mock_server = pact_builder_async +// .start_mock_server_async(Some("websockets/transport/websockets")) +// .await; + +// let url = url::Url::parse(mock_server.path("/jsonrpc").as_str()) +// .unwrap() +// .to_string(); +// send_thunder_call_message!( +// url, +// json!({ +// "jsonrpc": "2.0", +// "id": 0, +// "method": "Device.model" +// }) +// ) +// .await; + +// send_thunder_call_message!( +// url::Url::parse(mock_server.path("/jsonrpc").as_str()) +// .unwrap() +// .to_string(), +// json!({ +// "jsonrpc": "2.0", +// "id": 0, +// "method": "device.model" +// }) +// ) +// .await; +// } + #[tokio::test(flavor = "multi_thread")] #[cfg_attr(not(feature = "websocket_contract_tests"), ignore)] async fn test_device_get_model() {