use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;
use super::EventKind;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AuditLogEntry {
pub id: Uuid,
pub event_type: EventKind,
pub payload: Value,
pub run_id: Option<Uuid>,
pub step_id: Option<Uuid>,
pub user_id: Option<Uuid>,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone)]
pub struct NewAuditLogEntry {
pub event_type: EventKind,
pub payload: Value,
pub run_id: Option<Uuid>,
pub step_id: Option<Uuid>,
pub user_id: Option<Uuid>,
}
#[derive(Debug, Clone, Default)]
pub struct AuditLogFilter {
pub event_type: Option<EventKind>,
pub run_id: Option<Uuid>,
pub from: Option<DateTime<Utc>>,
pub to: Option<DateTime<Utc>>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn audit_log_entry_serde_roundtrip() {
let entry = AuditLogEntry {
id: Uuid::now_v7(),
event_type: EventKind::RunStatusChanged,
payload: json!({"run_id": "abc", "from": "pending", "to": "running"}),
run_id: Some(Uuid::now_v7()),
step_id: None,
user_id: None,
created_at: Utc::now(),
};
let json = serde_json::to_string(&entry).expect("serialize");
let back: AuditLogEntry = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.event_type, EventKind::RunStatusChanged);
assert_eq!(back.id, entry.id);
assert!(json.contains("run_status_changed"));
}
#[test]
fn new_audit_log_entry_creation() {
let new_entry = NewAuditLogEntry {
event_type: EventKind::StepFailed,
payload: json!({"step_id": "xyz"}),
run_id: Some(Uuid::now_v7()),
step_id: Some(Uuid::now_v7()),
user_id: None,
};
assert_eq!(new_entry.event_type, EventKind::StepFailed);
assert!(new_entry.run_id.is_some());
assert!(new_entry.step_id.is_some());
assert!(new_entry.user_id.is_none());
}
#[test]
fn audit_log_filter_default_is_empty() {
let filter = AuditLogFilter::default();
assert!(filter.event_type.is_none());
assert!(filter.run_id.is_none());
assert!(filter.from.is_none());
assert!(filter.to.is_none());
}
#[test]
fn audit_log_filter_with_all_fields() {
let now = Utc::now();
let filter = AuditLogFilter {
event_type: Some(EventKind::RunCreated),
run_id: Some(Uuid::now_v7()),
from: Some(now),
to: Some(now),
};
assert_eq!(filter.event_type, Some(EventKind::RunCreated));
assert!(filter.run_id.is_some());
assert!(filter.from.is_some());
assert!(filter.to.is_some());
}
}