use mockforge_analytics::{
AnalyticsDatabase, Pillar as AnalyticsPillar, PillarUsageEvent as AnalyticsPillarUsageEvent,
};
use mockforge_core::pillar_tracking::{PillarUsageEvent, PillarUsageRecorder};
use std::sync::Arc;
pub struct AnalyticsPillarRecorder {
db: Arc<AnalyticsDatabase>,
}
impl AnalyticsPillarRecorder {
pub fn new(db: Arc<AnalyticsDatabase>) -> Self {
Self { db }
}
}
#[async_trait::async_trait]
impl PillarUsageRecorder for AnalyticsPillarRecorder {
async fn record(
&self,
event: PillarUsageEvent,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let pillar = match event.pillar {
mockforge_core::pillars::Pillar::Reality => AnalyticsPillar::Reality,
mockforge_core::pillars::Pillar::Contracts => AnalyticsPillar::Contracts,
mockforge_core::pillars::Pillar::DevX => AnalyticsPillar::DevX,
mockforge_core::pillars::Pillar::Cloud => AnalyticsPillar::Cloud,
mockforge_core::pillars::Pillar::Ai => AnalyticsPillar::Ai,
};
let analytics_event = AnalyticsPillarUsageEvent {
workspace_id: event.workspace_id,
org_id: event.org_id,
pillar,
metric_name: event.metric_name,
metric_value: event.metric_value,
timestamp: event.timestamp,
};
self.db
.record_pillar_usage(&analytics_event)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
}
}
pub async fn init_pillar_tracking(analytics_db: Option<Arc<AnalyticsDatabase>>) {
if let Some(db) = analytics_db {
let recorder = Arc::new(AnalyticsPillarRecorder::new(db));
mockforge_core::pillar_tracking::init(recorder).await;
tracing::info!("Pillar tracking initialized with analytics database");
} else {
tracing::debug!("Pillar tracking not initialized (analytics database not available)");
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockforge_core::pillars::Pillar as CorePillar;
use std::path::Path;
#[test]
fn test_analytics_pillar_recorder_new() {
let runtime = tokio::runtime::Runtime::new().unwrap();
runtime.block_on(async {
let db = AnalyticsDatabase::new(Path::new(":memory:")).await.unwrap();
let _ = db.run_migrations().await;
let db_arc = Arc::new(db);
let recorder = AnalyticsPillarRecorder::new(db_arc.clone());
assert!(Arc::ptr_eq(&recorder.db, &db_arc));
});
}
#[test]
fn test_pillar_conversion_reality() {
let core_pillar = CorePillar::Reality;
assert!(matches!(core_pillar, CorePillar::Reality));
}
#[test]
fn test_pillar_conversion_contracts() {
let core_pillar = CorePillar::Contracts;
assert!(matches!(core_pillar, CorePillar::Contracts));
}
#[test]
fn test_pillar_conversion_devx() {
let core_pillar = CorePillar::DevX;
assert!(matches!(core_pillar, CorePillar::DevX));
}
#[test]
fn test_pillar_conversion_cloud() {
let core_pillar = CorePillar::Cloud;
assert!(matches!(core_pillar, CorePillar::Cloud));
}
#[test]
fn test_pillar_conversion_ai() {
let core_pillar = CorePillar::Ai;
assert!(matches!(core_pillar, CorePillar::Ai));
}
#[tokio::test]
async fn test_init_pillar_tracking_with_database() {
let db = AnalyticsDatabase::new(Path::new(":memory:")).await.unwrap();
let _ = db.run_migrations().await;
let db_arc = Arc::new(db);
init_pillar_tracking(Some(db_arc.clone())).await;
}
#[tokio::test]
async fn test_init_pillar_tracking_without_database() {
init_pillar_tracking(None).await;
}
#[tokio::test]
async fn test_record_pillar_event() {
let db = AnalyticsDatabase::new(Path::new(":memory:")).await.unwrap();
let _ = db.run_migrations().await;
let db_arc = Arc::new(db);
let recorder = AnalyticsPillarRecorder::new(db_arc);
let event = PillarUsageEvent {
workspace_id: Some(uuid::Uuid::new_v4().to_string()),
org_id: Some(uuid::Uuid::new_v4().to_string()),
pillar: CorePillar::Reality,
metric_name: "test_metric".to_string(),
metric_value: serde_json::json!(42.0),
timestamp: chrono::Utc::now(),
};
let result = recorder.record(event).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_record_pillar_event_all_pillars() {
let db = AnalyticsDatabase::new(Path::new(":memory:")).await.unwrap();
let _ = db.run_migrations().await;
let db_arc = Arc::new(db);
let recorder = AnalyticsPillarRecorder::new(db_arc);
let workspace_id = Some(uuid::Uuid::new_v4().to_string());
let org_id = Some(uuid::Uuid::new_v4().to_string());
let pillars = vec![
CorePillar::Reality,
CorePillar::Contracts,
CorePillar::DevX,
CorePillar::Cloud,
CorePillar::Ai,
];
for pillar in pillars {
let event = PillarUsageEvent {
workspace_id: workspace_id.clone(),
org_id: org_id.clone(),
pillar,
metric_name: format!("test_metric_{:?}", pillar),
metric_value: serde_json::json!(1.0),
timestamp: chrono::Utc::now(),
};
let result = recorder.record(event).await;
assert!(result.is_ok(), "Failed to record {:?} pillar", pillar);
}
}
#[tokio::test]
async fn test_record_multiple_events_same_workspace() {
let db = AnalyticsDatabase::new(Path::new(":memory:")).await.unwrap();
let _ = db.run_migrations().await;
let db_arc = Arc::new(db);
let recorder = AnalyticsPillarRecorder::new(db_arc);
let workspace_id = Some(uuid::Uuid::new_v4().to_string());
let org_id = Some(uuid::Uuid::new_v4().to_string());
for i in 0..5 {
let event = PillarUsageEvent {
workspace_id: workspace_id.clone(),
org_id: org_id.clone(),
pillar: CorePillar::Reality,
metric_name: format!("test_metric_{}", i),
metric_value: serde_json::json!(i as f64),
timestamp: chrono::Utc::now(),
};
let result = recorder.record(event).await;
assert!(result.is_ok(), "Failed to record event {}", i);
}
}
#[test]
fn test_pillar_event_structure() {
let workspace_id = Some(uuid::Uuid::new_v4().to_string());
let org_id = Some(uuid::Uuid::new_v4().to_string());
let timestamp = chrono::Utc::now();
let event = PillarUsageEvent {
workspace_id: workspace_id.clone(),
org_id: org_id.clone(),
pillar: CorePillar::Reality,
metric_name: "test".to_string(),
metric_value: serde_json::json!(42.0),
timestamp,
};
assert_eq!(event.workspace_id, workspace_id);
assert_eq!(event.org_id, org_id);
assert_eq!(event.metric_name, "test");
assert_eq!(event.metric_value, serde_json::json!(42.0));
assert_eq!(event.timestamp, timestamp);
}
#[test]
fn test_pillar_types_match() {
let core_pillars = [
CorePillar::Reality,
CorePillar::Contracts,
CorePillar::DevX,
CorePillar::Cloud,
CorePillar::Ai,
];
let analytics_pillars = [
AnalyticsPillar::Reality,
AnalyticsPillar::Contracts,
AnalyticsPillar::DevX,
AnalyticsPillar::Cloud,
AnalyticsPillar::Ai,
];
assert_eq!(core_pillars.len(), analytics_pillars.len());
}
}