koprogo_api/application/use_cases/
audit_log_use_cases.rs

1use crate::application::dto::PageRequest;
2use crate::application::ports::{AuditLogFilters, AuditLogRepository};
3use crate::infrastructure::audit::AuditLogEntry;
4use std::sync::Arc;
5
6pub struct AuditLogUseCases {
7    repo: Arc<dyn AuditLogRepository>,
8}
9
10impl AuditLogUseCases {
11    pub fn new(repo: Arc<dyn AuditLogRepository>) -> Self {
12        Self { repo }
13    }
14
15    pub async fn find_all_paginated(
16        &self,
17        page_request: &PageRequest,
18        filters: &AuditLogFilters,
19    ) -> Result<(Vec<AuditLogEntry>, i64), String> {
20        self.repo.find_all_paginated(page_request, filters).await
21    }
22}
23
24#[cfg(test)]
25mod tests {
26    use super::*;
27    use crate::infrastructure::audit::{AuditEventType, AuditLogEntry};
28    use async_trait::async_trait;
29    use uuid::Uuid;
30
31    struct MockAuditLogRepository {
32        entries: Vec<AuditLogEntry>,
33    }
34
35    #[async_trait]
36    impl AuditLogRepository for MockAuditLogRepository {
37        async fn create(&self, entry: &AuditLogEntry) -> Result<AuditLogEntry, String> {
38            Ok(entry.clone())
39        }
40        async fn find_by_id(&self, _id: Uuid) -> Result<Option<AuditLogEntry>, String> {
41            Ok(None)
42        }
43        async fn find_all_paginated(
44            &self,
45            _page_request: &PageRequest,
46            _filters: &AuditLogFilters,
47        ) -> Result<(Vec<AuditLogEntry>, i64), String> {
48            let total = self.entries.len() as i64;
49            Ok((self.entries.clone(), total))
50        }
51        async fn find_recent(&self, _limit: i64) -> Result<Vec<AuditLogEntry>, String> {
52            Ok(self.entries.clone())
53        }
54        async fn find_failed_operations(
55            &self,
56            _page_request: &PageRequest,
57            _organization_id: Option<Uuid>,
58        ) -> Result<(Vec<AuditLogEntry>, i64), String> {
59            Ok((vec![], 0))
60        }
61        async fn delete_older_than(
62            &self,
63            _timestamp: chrono::DateTime<chrono::Utc>,
64        ) -> Result<i64, String> {
65            Ok(0)
66        }
67        async fn count_by_filters(&self, _filters: &AuditLogFilters) -> Result<i64, String> {
68            Ok(self.entries.len() as i64)
69        }
70    }
71
72    fn make_entry() -> AuditLogEntry {
73        AuditLogEntry::new(AuditEventType::UserLogin, Some(Uuid::new_v4()), None)
74    }
75
76    #[tokio::test]
77    async fn test_find_all_paginated_returns_entries() {
78        let entry = make_entry();
79        let repo = Arc::new(MockAuditLogRepository {
80            entries: vec![entry],
81        });
82        let use_cases = AuditLogUseCases::new(repo);
83        let page_request = PageRequest {
84            page: 1,
85            per_page: 20,
86            sort_by: None,
87            order: crate::application::dto::SortOrder::Desc,
88        };
89        let filters = AuditLogFilters::default();
90        let (entries, total) = use_cases
91            .find_all_paginated(&page_request, &filters)
92            .await
93            .unwrap();
94        assert_eq!(total, 1);
95        assert_eq!(entries.len(), 1);
96    }
97
98    #[tokio::test]
99    async fn test_find_all_paginated_empty_returns_zero() {
100        let repo = Arc::new(MockAuditLogRepository { entries: vec![] });
101        let use_cases = AuditLogUseCases::new(repo);
102        let page_request = PageRequest {
103            page: 1,
104            per_page: 20,
105            sort_by: None,
106            order: crate::application::dto::SortOrder::Desc,
107        };
108        let filters = AuditLogFilters::default();
109        let (entries, total) = use_cases
110            .find_all_paginated(&page_request, &filters)
111            .await
112            .unwrap();
113        assert_eq!(total, 0);
114        assert!(entries.is_empty());
115    }
116}