use super::{MessageValidator, ValidationResult};
use crate::storage::Storage;
use async_trait::async_trait;
use std::sync::Arc;
use tap_msg::didcomm::PlainMessage;
pub struct UniquenessValidator {
storage: Arc<Storage>,
}
impl UniquenessValidator {
pub fn new(storage: Arc<Storage>) -> Self {
Self { storage }
}
}
#[async_trait]
impl MessageValidator for UniquenessValidator {
async fn validate(&self, message: &PlainMessage) -> ValidationResult {
match self.storage.get_message_by_id(&message.id).await {
Ok(Some(_)) => {
ValidationResult::Reject(format!(
"Duplicate message: message with ID {} already processed",
message.id
))
}
Ok(None) => {
ValidationResult::Accept
}
Err(e) => {
ValidationResult::Reject(format!("Unable to verify message uniqueness: {}", e))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::MessageDirection;
use tempfile::tempdir;
#[tokio::test]
async fn test_unique_message() {
let dir = tempdir().unwrap();
let storage = Arc::new(
Storage::new(Some(dir.path().join("test.db")))
.await
.unwrap(),
);
let validator = UniquenessValidator::new(storage);
let message = PlainMessage::new(
"test_msg_1".to_string(),
"test_type".to_string(),
serde_json::json!({}),
"did:example:sender".to_string(),
)
.with_recipient("did:example:receiver");
match validator.validate(&message).await {
ValidationResult::Accept => {} ValidationResult::Reject(reason) => panic!("Expected accept, got reject: {}", reason),
}
}
#[tokio::test]
async fn test_duplicate_message() {
let dir = tempdir().unwrap();
let storage = Arc::new(
Storage::new(Some(dir.path().join("test.db")))
.await
.unwrap(),
);
let validator = UniquenessValidator::new(storage.clone());
let message = PlainMessage::new(
"test_msg_1".to_string(),
"test_type".to_string(),
serde_json::json!({}),
"did:example:sender".to_string(),
)
.with_recipient("did:example:receiver");
storage
.log_message(&message, MessageDirection::Incoming)
.await
.unwrap();
match validator.validate(&message).await {
ValidationResult::Accept => panic!("Expected reject, got accept"),
ValidationResult::Reject(reason) => {
assert!(reason.contains("Duplicate message"));
assert!(reason.contains(&message.id));
}
}
}
}