Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions src/500-application/501-rust-telemetry/services/receiver/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,108 @@ impl PayloadSerialize for Payload {
Ok(Payload(payload))
}
}

#[cfg(test)]
mod tests {
use super::*;
use azure_iot_operations_protocol::common::payload_serialize::{
FormatIndicator, PayloadSerialize,
};

#[test]
fn deserialize_valid_json_with_correct_content_type() {
let json_bytes = br#"{"temperature": 25.5}"#;
let content_type = "application/json".to_string();
let result = Payload::deserialize(
json_bytes,
Some(&content_type),
&FormatIndicator::Utf8EncodedCharacterData,
);
assert!(result.is_ok());
let payload = result.unwrap();
assert_eq!(payload.0["temperature"], 25.5);
}

#[test]
fn deserialize_valid_json_without_content_type() {
let json_bytes = br#"{"key": "value"}"#;
let result = Payload::deserialize(
json_bytes,
None,
&FormatIndicator::Utf8EncodedCharacterData,
);
assert!(result.is_ok());
assert_eq!(result.unwrap().0["key"], "value");
}

#[test]
fn deserialize_rejects_invalid_content_type() {
let json_bytes = br#"{"key": "value"}"#;
let content_type = "text/plain".to_string();
let result = Payload::deserialize(
json_bytes,
Some(&content_type),
&FormatIndicator::Utf8EncodedCharacterData,
);
assert!(matches!(
result,
Err(DeserializationError::UnsupportedContentType(_))
));
}

#[test]
fn deserialize_rejects_invalid_utf8() {
let bad_bytes: &[u8] = &[0xff, 0xfe, 0xfd];
let content_type = "application/json".to_string();
let result = Payload::deserialize(
bad_bytes,
Some(&content_type),
&FormatIndicator::Utf8EncodedCharacterData,
);
assert!(matches!(
result,
Err(DeserializationError::InvalidPayload(_))
));
}

#[test]
fn deserialize_rejects_invalid_json() {
let bad_json = b"not valid json {{{";
let content_type = "application/json".to_string();
let result = Payload::deserialize(
bad_json,
Some(&content_type),
&FormatIndicator::Utf8EncodedCharacterData,
);
assert!(matches!(
result,
Err(DeserializationError::InvalidPayload(_))
));
}

#[test]
fn deserialize_handles_nested_json() {
let json_bytes = br#"{"sensor": {"id": 1, "readings": [10, 20, 30]}}"#;
let result = Payload::deserialize(
json_bytes,
None,
&FormatIndicator::Utf8EncodedCharacterData,
);
assert!(result.is_ok());
let payload = result.unwrap();
assert_eq!(payload.0["sensor"]["id"], 1);
assert_eq!(payload.0["sensor"]["readings"][1], 20);
}

#[test]
fn deserialize_handles_empty_object() {
let json_bytes = b"{}";
let result = Payload::deserialize(
json_bytes,
None,
&FormatIndicator::Utf8EncodedCharacterData,
);
assert!(result.is_ok());
assert!(result.unwrap().0.as_object().unwrap().is_empty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,69 @@ pub fn handle_receive_trace(custom_user_data: &Vec<(String, String)>) {
// This creates a continuous trace across services
span.set_parent(cx);
}

#[cfg(test)]
mod tests {
use super::*;
use opentelemetry::propagation::Extractor;

#[test]
fn extractor_get_returns_value_for_existing_key() {
let data = vec![
("traceparent".to_string(), "00-abc-def-01".to_string()),
("tracestate".to_string(), "vendor=opaque".to_string()),
];
let extractor = CustomUserDataExtractor(&data);
assert_eq!(extractor.get("traceparent"), Some("00-abc-def-01"));
}

#[test]
fn extractor_get_returns_none_for_missing_key() {
let data = vec![("traceparent".to_string(), "value".to_string())];
let extractor = CustomUserDataExtractor(&data);
assert_eq!(extractor.get("missing"), None);
}

#[test]
fn extractor_get_returns_none_for_empty_data() {
let data: Vec<(String, String)> = vec![];
let extractor = CustomUserDataExtractor(&data);
assert_eq!(extractor.get("traceparent"), None);
}

#[test]
fn extractor_keys_returns_all_keys() {
let data = vec![
("traceparent".to_string(), "val1".to_string()),
("tracestate".to_string(), "val2".to_string()),
];
let extractor = CustomUserDataExtractor(&data);
let keys = extractor.keys();
assert_eq!(keys.len(), 2);
assert!(keys.contains(&"traceparent"));
assert!(keys.contains(&"tracestate"));
}

#[test]
fn extractor_keys_returns_empty_for_empty_data() {
let data: Vec<(String, String)> = vec![];
let extractor = CustomUserDataExtractor(&data);
assert!(extractor.keys().is_empty());
}

#[test]
fn extractor_get_returns_first_match_for_duplicate_keys() {
let data = vec![
("key".to_string(), "first".to_string()),
("key".to_string(), "second".to_string()),
];
let extractor = CustomUserDataExtractor(&data);
assert_eq!(extractor.get("key"), Some("first"));
}

#[test]
fn extract_trace_context_returns_context_for_empty_data() {
let data: Vec<(String, String)> = vec![];
let _ctx = extract_trace_context(&data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,72 @@ impl PayloadSerialize for Payload {
unimplemented!()
}
}

#[cfg(test)]
mod tests {
use super::*;
use azure_iot_operations_protocol::common::payload_serialize::{
FormatIndicator, PayloadSerialize,
};

#[test]
fn serialize_json_object() {
let payload = Payload(serde_json::json!({"temperature": 42}));
let result = payload.serialize();
assert!(result.is_ok());
let serialized = result.unwrap();
assert_eq!(serialized.content_type, "application/json");
assert_eq!(
serialized.format_indicator,
FormatIndicator::Utf8EncodedCharacterData
);
let parsed: serde_json::Value =
serde_json::from_slice(&serialized.payload).unwrap();
assert_eq!(parsed["temperature"], 42);
}

#[test]
fn serialize_nested_json() {
let payload = Payload(serde_json::json!({
"sensor": {"id": 1, "readings": [10, 20]}
}));
let result = payload.serialize();
assert!(result.is_ok());
let serialized = result.unwrap();
let parsed: serde_json::Value =
serde_json::from_slice(&serialized.payload).unwrap();
assert_eq!(parsed["sensor"]["id"], 1);
assert_eq!(parsed["sensor"]["readings"][0], 10);
}

#[test]
fn serialize_simple_value() {
let payload = Payload(serde_json::json!(99.5));
let result = payload.serialize();
assert!(result.is_ok());
let serialized = result.unwrap();
let parsed: serde_json::Value =
serde_json::from_slice(&serialized.payload).unwrap();
assert_eq!(parsed, 99.5);
}

#[test]
fn serialize_empty_object() {
let payload = Payload(serde_json::json!({}));
let result = payload.serialize();
assert!(result.is_ok());
let serialized = result.unwrap();
let parsed: serde_json::Value =
serde_json::from_slice(&serialized.payload).unwrap();
assert!(parsed.as_object().unwrap().is_empty());
}

#[test]
fn serialize_null_value() {
let payload = Payload(serde_json::Value::Null);
let result = payload.serialize();
assert!(result.is_ok());
let serialized = result.unwrap();
assert_eq!(std::str::from_utf8(&serialized.payload).unwrap(), "null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,63 @@ pub fn inject_current_context() -> Vec<(String, String)> {

carrier
}

#[cfg(test)]
mod tests {
use super::*;
use opentelemetry::propagation::Injector;

#[test]
fn injector_set_adds_key_value_pair() {
let mut carrier = Vec::new();
let mut injector = VecInjector(&mut carrier);
injector.set("traceparent", "00-abc-def-01".to_string());
assert_eq!(carrier.len(), 1);
assert_eq!(carrier[0].0, "traceparent");
assert_eq!(carrier[0].1, "00-abc-def-01");
}

#[test]
fn injector_set_appends_multiple_pairs() {
let mut carrier = Vec::new();
let mut injector = VecInjector(&mut carrier);
injector.set("traceparent", "tp-value".to_string());
injector.set("tracestate", "ts-value".to_string());
assert_eq!(carrier.len(), 2);
assert_eq!(carrier[0].0, "traceparent");
assert_eq!(carrier[1].0, "tracestate");
}

#[test]
fn injector_set_allows_duplicate_keys() {
let mut carrier = Vec::new();
let mut injector = VecInjector(&mut carrier);
injector.set("key", "first".to_string());
injector.set("key", "second".to_string());
assert_eq!(carrier.len(), 2);
assert_eq!(carrier[0].1, "first");
assert_eq!(carrier[1].1, "second");
}

#[test]
fn injector_set_handles_empty_values() {
let mut carrier = Vec::new();
let mut injector = VecInjector(&mut carrier);
injector.set("key", String::new());
assert_eq!(carrier.len(), 1);
assert!(carrier[0].1.is_empty());
}

#[test]
fn inject_current_context_returns_empty_for_default_propagator() {
let result = inject_current_context();
assert!(result.is_empty());
}

#[test]
fn inject_current_context_returns_empty_for_w3c_propagator_without_active_span() {
global::set_text_map_propagator(TraceContextPropagator::new());
let result = inject_current_context();
assert!(result.is_empty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,62 @@ pub fn validate_json(schema: Value, instance: Value, device_id: &str) -> Result<

validate_instance(&compiled, instance, device_id)
}

#[cfg(test)]
mod tests {
use super::*;

fn simple_schema() -> Value {
serde_json::json!({
"type": "object",
"properties": {
"temperature": { "type": "number" }
},
"required": ["temperature"]
})
}

#[test]
fn parse_json_schema_valid() {
let schema = parse_json_schema(r#"{"type": "object"}"#);
assert!(schema.is_ok());
assert_eq!(schema.unwrap()["type"], "object");
}

#[test]
fn parse_json_schema_invalid() {
let result = parse_json_schema("not json");
assert!(result.is_err());
}

#[test]
fn validate_json_matching_instance() {
let schema = simple_schema();
let instance = serde_json::json!({"temperature": 42.5});
let result = validate_json(schema, instance, "dev-1");
assert_eq!(result.unwrap(), "Validation successful!");
}

#[test]
fn validate_json_missing_required_field() {
let schema = simple_schema();
let instance = serde_json::json!({});
let result = validate_json(schema, instance, "dev-1");
assert!(result.is_err());
let err_json: Value = serde_json::from_str(&result.unwrap_err()).unwrap();
assert_eq!(err_json["deviceId"], "dev-1");
assert_eq!(err_json["errorCode"], "004");
assert!(err_json["errorMessage"].as_str().unwrap().contains("required"));
}

#[test]
fn validate_json_wrong_type() {
let schema = simple_schema();
let instance = serde_json::json!({"temperature": "hot"});
let result = validate_json(schema, instance, "sensor-5");
assert!(result.is_err());
let err_json: Value = serde_json::from_str(&result.unwrap_err()).unwrap();
assert_eq!(err_json["deviceId"], "sensor-5");
assert!(err_json["errorMessage"].as_str().unwrap().contains("type"));
}
}
Loading
Loading