ito-core 0.1.33

Core functionality and business logic for Ito
Documentation
use ito_core::audit::{
    AuditEventStore, AuditStorageLocation, EventFilter, read_audit_events_filtered_from_store,
    read_audit_events_from_store,
};
use ito_domain::audit::event::{AuditEvent, EventContext, SCHEMA_VERSION};
use ito_domain::audit::writer::AuditWriter;
use std::sync::Mutex;

#[derive(Default)]
struct MemoryAuditStore {
    events: Mutex<Vec<AuditEvent>>,
}

impl AuditWriter for MemoryAuditStore {
    fn append(&self, event: &AuditEvent) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.events
            .lock()
            .expect("memory audit store lock")
            .push(event.clone());
        Ok(())
    }
}

impl AuditEventStore for MemoryAuditStore {
    fn read_all(&self) -> Vec<AuditEvent> {
        self.events.lock().expect("memory audit store lock").clone()
    }

    fn location(&self) -> AuditStorageLocation {
        AuditStorageLocation::Other("memory".to_string())
    }
}

fn memory_store(events: Vec<AuditEvent>) -> MemoryAuditStore {
    MemoryAuditStore {
        events: Mutex::new(events),
    }
}

fn event(entity: &str, entity_id: &str, scope: Option<&str>, op: &str) -> AuditEvent {
    AuditEvent {
        v: SCHEMA_VERSION,
        ts: "2026-03-16T12:00:00.000Z".to_string(),
        entity: entity.to_string(),
        entity_id: entity_id.to_string(),
        scope: scope.map(str::to_string),
        op: op.to_string(),
        from: None,
        to: Some("pending".to_string()),
        actor: "cli".to_string(),
        by: "@test".to_string(),
        meta: None,
        count: 1,
        ctx: EventContext {
            session_id: "s1".to_string(),
            harness_session_id: None,
            branch: None,
            worktree: None,
            commit: None,
        },
    }
}

#[test]
fn reads_events_from_injected_store_without_filesystem_path() {
    let store = memory_store(vec![event("task", "1.1", Some("009-03"), "create")]);

    let events = read_audit_events_from_store(&store);

    assert_eq!(events.len(), 1);
    assert_eq!(events[0].entity_id, "1.1");
}

#[test]
fn filters_events_from_injected_store() {
    let store = memory_store(vec![
        event("task", "1.1", Some("009-03"), "create"),
        event("change", "009-03", None, "create"),
    ]);
    let filter = EventFilter {
        entity: Some("task".to_string()),
        ..Default::default()
    };

    let events = read_audit_events_filtered_from_store(&store, &filter);

    assert_eq!(events.len(), 1);
    assert_eq!(events[0].entity, "task");
}

#[test]
fn memory_store_append_persists_events() {
    let store = MemoryAuditStore::default();

    store
        .append(&event("task", "1.1", Some("009-03"), "create"))
        .expect("append to memory store");

    let events = read_audit_events_from_store(&store);
    assert_eq!(events.len(), 1);
    assert_eq!(events[0].entity_id, "1.1");
}