mod mock;
use rustigram_types::message::ParseMode;
use serde_json::json;
fn message_result() -> serde_json::Value {
json!({
"message_id": 1,
"date": 1_700_000_000,
"chat": { "id": 42, "type": "private" }
})
}
#[tokio::test]
async fn send_message_puts_the_expected_body_on_the_wire() {
let (server, client) = mock::spawn().await;
mock::mount_ok(&server, "sendMessage", message_result()).await;
let sent = client
.send_message(42_i64, "hello")
.parse_mode(ParseMode::HTML)
.disable_notification(true)
.reply_to(7)
.await
.expect("the mocked call succeeds");
assert_eq!(sent.message_id, 1, "the result decodes back into a Message");
let request = mock::only_request(&server).await;
let body = mock::json_body(&request);
mock::assert_field(&body, "chat_id", json!(42));
mock::assert_field(&body, "text", json!("hello"));
mock::assert_field(&body, "parse_mode", json!("HTML"));
mock::assert_field(&body, "disable_notification", json!(true));
assert_eq!(
body["reply_parameters"]["message_id"], 7,
"reply_to sets reply_parameters.message_id"
);
}
#[tokio::test]
async fn unset_parameters_are_absent_from_the_body() {
let (server, client) = mock::spawn().await;
mock::mount_ok(&server, "sendMessage", message_result()).await;
client
.send_message(42_i64, "hello")
.await
.expect("the mocked call succeeds");
let body = mock::json_body(&mock::only_request(&server).await);
for key in [
"parse_mode",
"disable_notification",
"reply_parameters",
"business_connection_id",
"message_effect_id",
] {
mock::assert_absent(&body, key);
}
}
#[tokio::test]
async fn multipart_uploads_are_observable() {
use rustigram_types::file::InputFile;
let (server, client) = mock::spawn().await;
mock::mount_ok(&server, "sendPhoto", message_result()).await;
client
.send_photo(
42_i64,
InputFile::Bytes {
filename: "p.jpg".to_owned(),
data: b"\xff\xd8\xff".to_vec(),
mime_type: "image/jpeg".to_owned(),
},
)
.caption("a caption")
.protect_content(true)
.await
.expect("the mocked call succeeds");
let request = mock::only_request(&server).await;
let content_type = request
.headers
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or_default();
assert!(
content_type.starts_with("multipart/form-data"),
"byte uploads must use multipart, got {content_type}"
);
mock::assert_multipart_fields(
&request,
&["caption", "chat_id", "photo", "protect_content"],
);
}
#[tokio::test]
#[should_panic(expected = "expected exactly one request")]
async fn only_request_rejects_an_unexpected_call_count() {
let (server, client) = mock::spawn().await;
mock::mount_ok(&server, "sendMessage", message_result()).await;
client.send_message(42_i64, "one").await.unwrap();
client.send_message(42_i64, "two").await.unwrap();
mock::only_request(&server).await;
}
#[tokio::test]
#[should_panic(expected = "field `text`")]
async fn field_assertions_fail_on_a_wrong_value() {
let (server, client) = mock::spawn().await;
mock::mount_ok(&server, "sendMessage", message_result()).await;
client.send_message(42_i64, "hello").await.unwrap();
let body = mock::json_body(&mock::only_request(&server).await);
mock::assert_field(&body, "text", json!("something else"));
}
#[tokio::test]
async fn servers_are_isolated_from_each_other() {
let (server_a, client_a) = mock::spawn().await;
let (server_b, client_b) = mock::spawn().await;
mock::mount_ok(&server_a, "sendMessage", message_result()).await;
mock::mount_ok(&server_b, "sendMessage", message_result()).await;
assert_ne!(
server_a.uri(),
server_b.uri(),
"each server gets its own port"
);
client_a.send_message(1_i64, "a").await.unwrap();
client_b.send_message(2_i64, "b").await.unwrap();
client_b.send_message(3_i64, "c").await.unwrap();
assert_eq!(mock::requests(&server_a).await.len(), 1);
assert_eq!(mock::requests(&server_b).await.len(), 2);
}
#[tokio::test]
async fn the_mock_server_is_local_only() {
let (server, _client) = mock::spawn().await;
let uri = server.uri();
assert!(
uri.starts_with("http://127.0.0.1:") || uri.starts_with("http://localhost:"),
"mock server must bind to loopback, got {uri}"
);
}