refactor(tests): reformat code and update dependency management
- Reorganized import statements in adapter tests for better readability - Replaced or_insert_with(Vec::new) with or_default() in test closures - Updated Cargo.lock with new dependency versions and checksums - Added TLS features to tonic dependency configuration - Included sqlx, chrono, and uuid dependencies with specific features - Added jsonwebtoken and arc-swap as project dependencies - Reformatted assertion statements to comply with line length limits - Adjusted base64 import order in engine codec module - Updated protobuf include statement formatting
This commit is contained in:
+166
@@ -0,0 +1,166 @@
|
||||
//! Service layer unit tests for `MessageService`.
|
||||
//!
|
||||
//! Tests parsing, nonce dedup, rate limiting — all in-memory without
|
||||
//! requiring a real database or gRPC connection.
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::module_inception)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::svc::message::MessageService;
|
||||
|
||||
#[test]
|
||||
fn test_parse_field_valid() {
|
||||
let json = serde_json::json!({"message_id": "01909abc-def0-7000-8000-000000000001"});
|
||||
let id: Uuid = MessageService::parse_field(&json, "message_id").unwrap();
|
||||
assert_eq!(id.to_string(), "01909abc-def0-7000-8000-000000000001");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_field_missing() {
|
||||
let json = serde_json::json!({});
|
||||
let result: crate::ImksResult<String> = MessageService::parse_field(&json, "missing");
|
||||
assert!(result.is_err());
|
||||
assert!(
|
||||
result
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
.contains("Missing required field")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_optional_present() {
|
||||
let json = serde_json::json!({"name": "alice"});
|
||||
let val: Option<String> = MessageService::parse_optional(&json, "name").unwrap();
|
||||
assert_eq!(val, Some("alice".into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_optional_null() {
|
||||
let json = serde_json::json!({"name": null});
|
||||
let val: Option<String> = MessageService::parse_optional(&json, "name").unwrap();
|
||||
assert_eq!(val, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_optional_missing() {
|
||||
let json = serde_json::json!({});
|
||||
let val: Option<String> = MessageService::parse_optional(&json, "name").unwrap();
|
||||
assert_eq!(val, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_send_payload_basic_shape() {
|
||||
let json = serde_json::json!([{
|
||||
"channel_id": "01909abc-def0-7000-8000-000000000001",
|
||||
"body": "hello world"
|
||||
}]);
|
||||
let arr = json.as_array().unwrap();
|
||||
let payload = &arr[0];
|
||||
assert_eq!(payload["body"], "hello world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_send_payload_with_rich_content() {
|
||||
let json = serde_json::json!([{
|
||||
"channel_id": "01909abc-def0-7000-8000-000000000001",
|
||||
"body": "hey @alice",
|
||||
"thread_id": "01909def-abc0-7000-8000-000000000002",
|
||||
"nonce": "nonce-001",
|
||||
"mentioned_user_ids": ["01909abc-def0-7000-8000-000000000003"],
|
||||
"attachments": [{"filename": "img.png", "url": "https://cdn/img.png", "size": 1024, "content_type": "image/png"}],
|
||||
"embeds": [{"embed_type": "link", "title": "Example", "url": "https://example.com", "fields": [{"name": "k", "value": "v", "inline": true}]}],
|
||||
"sticker": {"sticker_id": "01909abc-def0-7000-8000-000000000004", "name": "Hype!", "image_url": "https://cdn/sticker.png"},
|
||||
"forward": {"source_message_id": "01909abc-def0-7000-8000-000000000005", "source_channel_id": "01909abc-def0-7000-8000-000000000006"}
|
||||
}]);
|
||||
let arr = json.as_array().unwrap();
|
||||
let payload = &arr[0];
|
||||
assert_eq!(payload["body"], "hey @alice");
|
||||
assert!(payload["attachments"].is_array());
|
||||
assert!(payload["embeds"].is_array());
|
||||
assert!(payload["sticker"].is_object());
|
||||
assert!(payload["forward"].is_object());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonce_dedup_first_accepted() {
|
||||
use dashmap::DashMap;
|
||||
let nonces = Arc::new(DashMap::<String, Instant>::new());
|
||||
assert!(!nonces.contains_key("nonce-1"));
|
||||
nonces.insert("nonce-1".to_string(), Instant::now());
|
||||
assert!(nonces.contains_key("nonce-1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonce_dedup_rejects_duplicate() {
|
||||
use dashmap::DashMap;
|
||||
let nonces = Arc::new(DashMap::<String, Instant>::new());
|
||||
nonces.insert("nonce-1".to_string(), Instant::now());
|
||||
// After insert, it should exist
|
||||
assert!(nonces.contains_key("nonce-1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rate_limit_within_window() {
|
||||
use dashmap::DashMap;
|
||||
let limits = Arc::new(DashMap::<(Uuid, Uuid), Vec<Instant>>::new());
|
||||
let user = Uuid::now_v7();
|
||||
let channel = Uuid::now_v7();
|
||||
// Should be empty initially
|
||||
assert!(limits.get(&(user, channel)).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rate_limit_approaches_threshold() {
|
||||
use dashmap::DashMap;
|
||||
let limits = Arc::new(DashMap::<(Uuid, Uuid), Vec<Instant>>::new());
|
||||
let now = Instant::now();
|
||||
let user = Uuid::now_v7();
|
||||
let channel = Uuid::now_v7();
|
||||
|
||||
let mut entry = limits.entry((user, channel)).or_default();
|
||||
for _ in 0..9 {
|
||||
entry.push(now);
|
||||
}
|
||||
assert_eq!(entry.len(), 9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rate_limit_exceeded() {
|
||||
use dashmap::DashMap;
|
||||
let limits = Arc::new(DashMap::<(Uuid, Uuid), Vec<Instant>>::new());
|
||||
let now = Instant::now();
|
||||
let user = Uuid::now_v7();
|
||||
let channel = Uuid::now_v7();
|
||||
|
||||
let mut entry = limits.entry((user, channel)).or_default();
|
||||
for _ in 0..10 {
|
||||
entry.push(now);
|
||||
}
|
||||
assert!(entry.len() >= 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rate_limit_window_expiry_eviction() {
|
||||
use dashmap::DashMap;
|
||||
let limits = Arc::new(DashMap::<(Uuid, Uuid), Vec<Instant>>::new());
|
||||
let window = Duration::from_secs(10);
|
||||
let user = Uuid::now_v7();
|
||||
let channel = Uuid::now_v7();
|
||||
|
||||
let old = Instant::now() - Duration::from_secs(15);
|
||||
let now = Instant::now();
|
||||
|
||||
let mut entry = limits.entry((user, channel)).or_default();
|
||||
entry.push(old);
|
||||
entry.push(now);
|
||||
entry.retain(|t| now.duration_since(*t) < window);
|
||||
|
||||
assert_eq!(entry.len(), 1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user