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
72 changes: 72 additions & 0 deletions crates/rmcp/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,75 @@ impl RmcpError {
}
}
}

#[cfg(test)]
mod tests {
use std::io;

use super::*;
use crate::model::{ErrorCode, ErrorData};

#[test]
fn test_error_data_display_without_data() {
let error = ErrorData {
code: ErrorCode(-32600),
message: "Invalid Request".into(),
data: None,
};
assert_eq!(format!("{}", error), "-32600: Invalid Request");
}

#[test]
fn test_error_data_display_with_data() {
let error = ErrorData {
code: ErrorCode(-32600),
message: "Invalid Request".into(),
data: Some(serde_json::json!({"detail": "missing field"})),
};
assert_eq!(
format!("{}", error),
"-32600: Invalid Request({\"detail\":\"missing field\"})"
);
}

#[test]
fn test_rmcp_error_transport_creation() {
struct DummyTransport;
let io_error = io::Error::other("connection failed");
let error = RmcpError::transport_creation::<DummyTransport>(io_error);

match error {
RmcpError::TransportCreation {
into_transport_type_name,
into_transport_type_id,
..
} => {
assert!(into_transport_type_name.contains("DummyTransport"));
assert_eq!(
into_transport_type_id,
std::any::TypeId::of::<DummyTransport>()
);
}
_ => panic!("Expected TransportCreation variant"),
}
}

#[test]
fn test_rmcp_error_display() {
struct DummyTransport;
let io_error = io::Error::other("connection failed");
let error = RmcpError::transport_creation::<DummyTransport>(io_error);
let display = format!("{}", error);
assert!(display.contains("Transport creation error"));
}

#[test]
fn test_error_data_is_std_error() {
let error = ErrorData {
code: ErrorCode(-32600),
message: "Invalid Request".into(),
data: None,
};
let _: &dyn std::error::Error = &error;
}
}
241 changes: 241 additions & 0 deletions crates/rmcp/src/model/annotated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,244 @@ pub trait AnnotateAble: sealed::Sealed {
self.with_timestamp(Utc::now())
}
}

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

#[test]
fn test_annotations_default() {
let annotations = Annotations::default();
assert_eq!(annotations.audience, None);
assert_eq!(annotations.priority, None);
assert_eq!(annotations.last_modified, None);
}

#[test]
fn test_annotations_for_resource() {
let timestamp = Utc::now();
let annotations = Annotations::for_resource(0.5, timestamp);
assert_eq!(annotations.priority, Some(0.5));
assert_eq!(annotations.last_modified, Some(timestamp));
assert_eq!(annotations.audience, None);
}

#[test]
#[should_panic(expected = "Priority")]
fn test_annotations_for_resource_invalid_priority_high() {
let timestamp = Utc::now();
Annotations::for_resource(1.5, timestamp);
}

#[test]
#[should_panic(expected = "Priority")]
fn test_annotations_for_resource_invalid_priority_low() {
let timestamp = Utc::now();
Annotations::for_resource(-0.1, timestamp);
}

#[test]
fn test_annotated_new() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = Annotated::new(content.clone(), None);
assert_eq!(annotated.raw, content);
assert_eq!(annotated.annotations, None);
}

#[test]
fn test_annotated_deref() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = Annotated::new(content.clone(), None);
assert_eq!(annotated.text, "test");
}

#[test]
fn test_annotated_deref_mut() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let mut annotated = Annotated::new(content, None);
annotated.text = "modified".to_string();
assert_eq!(annotated.text, "modified");
}

#[test]
fn test_annotated_remove_annotation() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let mut annotated = Annotated::new(content, Some(Annotations::default()));
assert!(annotated.annotations.is_some());
let removed = annotated.remove_annotation();
assert!(removed.is_some());
assert!(annotated.annotations.is_none());
}

#[test]
fn test_annotated_getters() {
let timestamp = Utc::now();
let annotations = Annotations {
audience: Some(vec![Role::User]),
priority: Some(0.7),
last_modified: Some(timestamp),
};
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = Annotated::new(content, Some(annotations));

assert_eq!(annotated.audience(), Some(&vec![Role::User]));
assert_eq!(annotated.priority(), Some(0.7));
assert_eq!(annotated.timestamp(), Some(timestamp));
}

#[test]
fn test_annotated_with_audience() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = Annotated::new(content, None);
let with_audience = annotated.with_audience(vec![Role::User, Role::Assistant]);

assert_eq!(
with_audience.audience(),
Some(&vec![Role::User, Role::Assistant])
);
}

#[test]
fn test_annotated_with_priority() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = Annotated::new(content, None);
let with_priority = annotated.with_priority(0.9);

assert_eq!(with_priority.priority(), Some(0.9));
}

#[test]
fn test_annotated_with_timestamp() {
let timestamp = Utc::now();
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = Annotated::new(content, None);
let with_timestamp = annotated.with_timestamp(timestamp);

assert_eq!(with_timestamp.timestamp(), Some(timestamp));
}

#[test]
fn test_annotated_with_timestamp_now() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = Annotated::new(content, None);
let with_timestamp = annotated.with_timestamp_now();

assert!(with_timestamp.timestamp().is_some());
}

#[test]
fn test_annotate_able_optional_annotate() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = content.optional_annotate(None);
assert_eq!(annotated.annotations, None);
}

#[test]
fn test_annotate_able_annotate() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotations = Annotations::default();
let annotated = content.annotate(annotations);
assert!(annotated.annotations.is_some());
}

#[test]
fn test_annotate_able_no_annotation() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = content.no_annotation();
assert_eq!(annotated.annotations, None);
}

#[test]
fn test_annotate_able_with_audience() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = content.with_audience(vec![Role::User]);
assert_eq!(annotated.audience(), Some(&vec![Role::User]));
}

#[test]
fn test_annotate_able_with_priority() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = content.with_priority(0.5);
assert_eq!(annotated.priority(), Some(0.5));
}

#[test]
fn test_annotate_able_with_timestamp() {
let timestamp = Utc::now();
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = content.with_timestamp(timestamp);
assert_eq!(annotated.timestamp(), Some(timestamp));
}

#[test]
fn test_annotate_able_with_timestamp_now() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let annotated = content.with_timestamp_now();
assert!(annotated.timestamp().is_some());
}

#[test]
fn test_chaining_annotations() {
let content = RawTextContent {
text: "test".to_string(),
meta: None,
};
let timestamp = Utc::now();
let annotated = Annotated::new(content, None)
.with_audience(vec![Role::User])
.with_priority(0.8)
.with_timestamp(timestamp);

assert_eq!(annotated.audience(), Some(&vec![Role::User]));
assert_eq!(annotated.priority(), Some(0.8));
assert_eq!(annotated.timestamp(), Some(timestamp));
}
}
Loading