remem-ai 0.6.3

Local-first coding agent memory for Claude Code and OpenAI Codex
Documentation
use rusqlite::params;

use super::*;

fn setup_memory_usage_conn() -> Result<rusqlite::Connection> {
    let conn = rusqlite::Connection::open_in_memory()?;
    crate::memory::tests_helper::setup_memory_schema(&conn);
    Ok(conn)
}

fn insert_usage_memory(conn: &rusqlite::Connection, title: &str) -> Result<i64> {
    crate::memory::insert_memory(
        conn,
        Some("seed-session"),
        "/repo",
        None,
        title,
        "memory body",
        "decision",
        None,
    )
}

fn insert_injected_item(conn: &rusqlite::Connection, memory_id: i64) -> Result<()> {
    conn.execute(
        "INSERT INTO context_injection_items
         (injection_run_id, host, project, session_id, injection_key, output_mode,
          decision, item_kind, item_id, memory_id, channel, render_order, status,
          title, provenance, staleness, injected_at_epoch)
         VALUES ('run-1', 'codex-cli', '/repo', 'sess-1', 'key-1', 'full',
                 'emitted', 'memory', ?1, ?1, 'core', 1, 'injected',
                 'title', 'src=memory', 'current', 100)",
        params![memory_id],
    )?;
    Ok(())
}

#[test]
fn parses_only_dedicated_memory_citation_line() {
    let text = "src=memory:#44 is metadata\nMemory citations: memory:#7, memory:#7 memory:#9\n";
    let parsed = parse_memory_citations(text);

    assert!(parsed.line_present);
    assert_eq!(parsed.ids, vec![7, 9]);
}

#[test]
fn recognizes_none_citation_line_without_memory_ids() {
    let parsed = parse_memory_citations("No memory was needed.\nMemory citations: none");

    assert!(parsed.line_present);
    assert!(parsed.ids.is_empty());
}

#[test]
fn records_only_cited_injected_memories_once() -> Result<()> {
    let conn = setup_memory_usage_conn()?;
    let cited = insert_usage_memory(&conn, "cited")?;
    let not_injected = insert_usage_memory(&conn, "not injected")?;
    insert_injected_item(&conn, cited)?;

    let message = format!(
        "I used the durable context.\nMemory citations: memory:#{cited} memory:#{not_injected}"
    );
    let report =
        record_stop_memory_citations(&conn, "codex-cli", "/repo", "sess-1", "hash-a", &message)?;
    let duplicate =
        record_stop_memory_citations(&conn, "codex-cli", "/repo", "sess-1", "hash-a", &message)?;

    assert_eq!(
        report,
        MemoryUsageReport {
            parsed_count: 2,
            matched_count: 1,
            inserted_count: 1,
            duplicate_event: false,
        }
    );
    assert!(duplicate.duplicate_event);
    let cited_count: i64 = conn.query_row(
        "SELECT access_count FROM memories WHERE id = ?1",
        [cited],
        |row| row.get(0),
    )?;
    let not_injected_count: i64 = conn.query_row(
        "SELECT access_count FROM memories WHERE id = ?1",
        [not_injected],
        |row| row.get(0),
    )?;
    let usage_events: i64 =
        conn.query_row("SELECT COUNT(*) FROM memory_usage_events", [], |row| {
            row.get(0)
        })?;
    assert_eq!(cited_count, 1);
    assert_eq!(not_injected_count, 0);
    assert_eq!(usage_events, 1);
    Ok(())
}

#[test]
fn usage_feedback_stats_expose_no_citation_and_unmatched_categories() -> Result<()> {
    let conn = setup_memory_usage_conn()?;
    let cited = insert_usage_memory(&conn, "cited")?;
    let not_injected = insert_usage_memory(&conn, "not injected")?;
    insert_injected_item(&conn, cited)?;

    record_stop_memory_citations(
        &conn,
        "codex-cli",
        "/repo",
        "sess-1",
        "hash-matched",
        &format!("Used memory.\nMemory citations: memory:#{cited}"),
    )?;
    record_stop_memory_citations(
        &conn,
        "codex-cli",
        "/repo",
        "sess-1",
        "hash-none",
        "No memory was needed.\nMemory citations: none",
    )?;
    record_stop_memory_citations(
        &conn,
        "codex-cli",
        "/repo",
        "sess-1",
        "hash-unmatched",
        &format!("Referenced a missing injection.\nMemory citations: memory:#{not_injected}"),
    )?;

    let stats = query_memory_usage_feedback_stats(&conn)?;

    assert_eq!(stats.total_events, 3);
    assert_eq!(stats.parsed_events, 3);
    assert_eq!(stats.matched_events, 1);
    assert_eq!(stats.inserted_events, 1);
    assert_eq!(stats.no_citation_events, 1);
    assert_eq!(stats.unmatched_events, 1);
    assert_eq!(stats.usage_events, 1);
    Ok(())
}