// ABOUTME: Unit-Tests für Bragi-Protokoll Packet-Bau und Response-Parsing. // ABOUTME: Testet build_packet, parse_response und BragiResponse-Methoden. use corsairctl::bragi::protocol::*; use corsairctl::error::CorsairError; #[test] fn build_get_packet_has_correct_header() { let packet = build_get_packet(ENDPOINT_HEADSET, 0x0F); assert_eq!(packet[0], 0x00, "Report ID"); assert_eq!(packet[1], PROTOCOL_MARKER, "Protokoll-Marker"); assert_eq!(packet[2], ENDPOINT_HEADSET, "Endpoint"); assert_eq!(packet[3], CMD_GET, "Command"); assert_eq!(packet[4], 0x0F, "Property"); } #[test] fn build_get_packet_is_zero_padded() { let packet = build_get_packet(ENDPOINT_RECEIVER, 0x13); assert_eq!(packet.len(), PACKET_SIZE); // Alle Bytes nach der Property sollten 0 sein for &byte in &packet[5..] { assert_eq!(byte, 0x00); } } #[test] fn build_set_packet_includes_data() { let packet = build_set_packet(ENDPOINT_HEADSET, 0x03, &[0x00, 0x02]); assert_eq!(packet[3], CMD_SET, "Command"); assert_eq!(packet[4], 0x03, "Property"); assert_eq!(packet[5], 0x00, "Data byte 0"); assert_eq!(packet[6], 0x02, "Data byte 1"); // Rest sollte 0 sein assert_eq!(packet[7], 0x00); } #[test] fn build_packet_total_size() { let packet = build_packet(0x08, 0x01, 0x03, &[0x00, 0x02]); assert_eq!(packet.len(), 65); } #[test] fn parse_response_extracts_fields() { // Simulierte Response: [report_type, endpoint, command, status, data0, data1, ...] // Laut Protokoll-Doku: raw[0]=0x01 Report-Typ, raw[1]=Endpoint, raw[2]=Command, raw[3]=Status let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0xE8, 0x03]; raw.resize(REPORT_SIZE, 0x00); let resp = parse_response(&raw).unwrap(); assert_eq!(resp.endpoint, RESPONSE_ENDPOINT_HEADSET); assert_eq!(resp.command, CMD_GET); assert_eq!(resp.status, STATUS_OK); assert_eq!(resp.data[0], 0xE8); assert_eq!(resp.data[1], 0x03); } #[test] fn parse_response_value_u16_little_endian() { let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0xE8, 0x03]; raw.resize(REPORT_SIZE, 0x00); let resp = parse_response(&raw).unwrap(); let value = resp.value_u16().unwrap(); // 0xE8 | (0x03 << 8) = 232 + 768 = 1000 assert_eq!(value, 1000); } #[test] fn parse_response_value_u8() { let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0x03]; raw.resize(REPORT_SIZE, 0x00); let resp = parse_response(&raw).unwrap(); assert_eq!(resp.value_u8().unwrap(), 0x03); } #[test] fn parse_response_error_f0() { let raw = vec![0x01, 0xF0, CMD_GET, STATUS_OK]; let result = parse_response(&raw); assert!(result.is_err()); match result.unwrap_err() { CorsairError::DeviceError { status } => assert_eq!(status, 0xF0), other => panic!("Erwarteter DeviceError, bekommen: {other:?}"), } } #[test] fn parse_response_error_f1() { let raw = vec![0x01, 0xF1, CMD_GET, STATUS_OK]; let result = parse_response(&raw); assert!(result.is_err()); match result.unwrap_err() { CorsairError::DeviceError { status } => assert_eq!(status, 0xF1), other => panic!("Erwarteter DeviceError, bekommen: {other:?}"), } } #[test] fn parse_response_too_short() { let raw = vec![0x01, 0x00]; let result = parse_response(&raw); assert!(result.is_err()); assert!(matches!( result.unwrap_err(), CorsairError::ResponseTooShort { .. } )); } #[test] fn value_u16_on_empty_data_returns_error() { let raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK]; // Keine Daten-Bytes let resp = parse_response(&raw).unwrap(); let result = resp.value_u16(); assert!(result.is_err()); assert!(matches!( result.unwrap_err(), CorsairError::ResponseTooShort { expected: 2, got: 0 } )); } #[test] fn battery_level_full_is_1000_promille() { // Battery Level = 1000 (voll) → 0xE8, 0x03 LE let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0xE8, 0x03]; raw.resize(REPORT_SIZE, 0x00); let resp = parse_response(&raw).unwrap(); let promille = resp.value_u16().unwrap(); assert_eq!(promille, 1000); } #[test] fn battery_level_half_is_500_promille() { // Battery Level = 500 (50%) → 0xF4, 0x01 LE let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0xF4, 0x01]; raw.resize(REPORT_SIZE, 0x00); let resp = parse_response(&raw).unwrap(); let promille = resp.value_u16().unwrap(); assert_eq!(promille, 500); } // --- Schritt 4: parse_response_validated Tests --- #[test] fn parse_response_validated_rejects_wrong_endpoint() { let mut raw = vec![0x01, RESPONSE_ENDPOINT_RECEIVER, CMD_GET, STATUS_OK, 0x00]; raw.resize(REPORT_SIZE, 0x00); let result = parse_response_validated(&raw, Some(RESPONSE_ENDPOINT_HEADSET), None); assert!(result.is_err()); assert!(matches!( result.unwrap_err(), CorsairError::UnexpectedResponse { .. } )); } #[test] fn parse_response_validated_rejects_wrong_command() { let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_SET, STATUS_OK, 0x00]; raw.resize(REPORT_SIZE, 0x00); let result = parse_response_validated(&raw, None, Some(CMD_GET)); assert!(result.is_err()); assert!(matches!( result.unwrap_err(), CorsairError::UnexpectedResponse { .. } )); } #[test] fn parse_response_validated_accepts_matching() { let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0xE8, 0x03]; raw.resize(REPORT_SIZE, 0x00); let resp = parse_response_validated( &raw, Some(RESPONSE_ENDPOINT_HEADSET), Some(CMD_GET), ) .unwrap(); assert_eq!(resp.endpoint, RESPONSE_ENDPOINT_HEADSET); assert_eq!(resp.command, CMD_GET); assert_eq!(resp.status, STATUS_OK); }