use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScanResponse {
pub is_valid: bool,
pub risk_score: f32,
pub sanitized_text: String,
pub scanner_results: Vec<ScannerResult>,
pub scan_time_ms: u64,
#[serde(default)]
pub cache_hit: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScannerResult {
pub scanner: String,
pub is_valid: bool,
pub risk_score: f32,
#[serde(default)]
pub risk_factors: Vec<RiskFactorDto>,
#[serde(default)]
pub entities: Vec<EntityDto>,
#[serde(skip_serializing_if = "Option::is_none")]
pub execution_time_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RiskFactorDto {
pub description: String,
pub severity: String,
pub score: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EntityDto {
pub entity_type: String,
pub text: String,
pub start: usize,
pub end: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence: Option<f32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BatchScanResponse {
pub results: Vec<ScanResponse>,
pub total_time_ms: u64,
pub success_count: usize,
pub failure_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AnonymizeResponse {
pub anonymized_text: String,
pub session_id: String,
pub entities: Vec<AnonymizedEntityDto>,
pub processing_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AnonymizedEntityDto {
pub entity_type: String,
pub placeholder: String,
pub start: usize,
pub end: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DeanonymizeResponse {
pub text: String,
pub restored_count: usize,
pub processing_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScannerMetadataResponse {
pub name: String,
pub scanner_type: String,
pub version: String,
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListScannersResponse {
pub scanners: Vec<ScannerMetadataResponse>,
pub total_count: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scan_response_serialization() {
let response = ScanResponse {
is_valid: true,
risk_score: 0.1,
sanitized_text: "Test text".to_string(),
scanner_results: vec![],
scan_time_ms: 50,
cache_hit: false,
};
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains("\"isValid\":true"));
assert!(json.contains("\"riskScore\":0.1"));
assert!(json.contains("\"scanTimeMs\":50"));
}
#[test]
fn test_scanner_result_serialization() {
let result = ScannerResult {
scanner: "toxicity".to_string(),
is_valid: true,
risk_score: 0.0,
risk_factors: vec![],
entities: vec![],
execution_time_ms: Some(10),
};
let json = serde_json::to_string(&result).unwrap();
assert!(json.contains("\"scanner\":\"toxicity\""));
assert!(json.contains("\"executionTimeMs\":10"));
}
#[test]
fn test_risk_factor_dto() {
let risk_factor = RiskFactorDto {
description: "Toxic language detected".to_string(),
severity: "high".to_string(),
score: 0.8,
metadata: Some(serde_json::json!({"details": "test"})),
};
let json = serde_json::to_string(&risk_factor).unwrap();
assert!(json.contains("\"description\""));
assert!(json.contains("\"severity\":\"high\""));
}
#[test]
fn test_entity_dto() {
let entity = EntityDto {
entity_type: "EMAIL".to_string(),
text: "test@example.com".to_string(),
start: 10,
end: 26,
confidence: Some(0.95),
};
let json = serde_json::to_string(&entity).unwrap();
assert!(json.contains("\"entityType\":\"EMAIL\""));
assert!(json.contains("\"confidence\":0.95"));
}
#[test]
fn test_batch_scan_response() {
let response = BatchScanResponse {
results: vec![],
total_time_ms: 100,
success_count: 5,
failure_count: 0,
};
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains("\"totalTimeMs\":100"));
assert!(json.contains("\"successCount\":5"));
}
#[test]
fn test_anonymize_response() {
let response = AnonymizeResponse {
anonymized_text: "My email is [EMAIL_1]".to_string(),
session_id: "session123".to_string(),
entities: vec![],
processing_time_ms: 25,
};
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains("\"sessionId\":\"session123\""));
assert!(json.contains("\"processingTimeMs\":25"));
}
#[test]
fn test_deanonymize_response() {
let response = DeanonymizeResponse {
text: "My email is test@example.com".to_string(),
restored_count: 1,
processing_time_ms: 15,
};
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains("\"restoredCount\":1"));
}
#[test]
fn test_scanner_metadata_response() {
let metadata = ScannerMetadataResponse {
name: "toxicity".to_string(),
scanner_type: "input".to_string(),
version: "1.0.0".to_string(),
description: "Detects toxic language".to_string(),
};
let json = serde_json::to_string(&metadata).unwrap();
assert!(json.contains("\"scannerType\":\"input\""));
}
#[test]
fn test_list_scanners_response() {
let response = ListScannersResponse {
scanners: vec![],
total_count: 0,
};
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains("\"totalCount\":0"));
}
#[test]
fn test_camel_case_serialization() {
let response = ScanResponse {
is_valid: true,
risk_score: 0.0,
sanitized_text: "test".to_string(),
scanner_results: vec![],
scan_time_ms: 10,
cache_hit: true,
};
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains("isValid"));
assert!(json.contains("riskScore"));
assert!(json.contains("sanitizedText"));
assert!(json.contains("scannerResults"));
assert!(json.contains("scanTimeMs"));
assert!(json.contains("cacheHit"));
}
}