remem-ai 0.6.81

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

#[tokio::test]
async fn session_activity_routes_project_list_detail_and_report_stats() -> anyhow::Result<()> {
    let _test_dir = ScopedTestDataDir::new("api-session-activity");
    let conn = db::open_db()?;
    conn.execute(
        "INSERT INTO raw_messages
         (id, session_id, project, role, content, content_hash, source, cwd,
          created_at_epoch, source_root, event_time_source)
         VALUES
         (901, 'activity-session', 'activity/project', 'user',
          'Add an evidence-first session view', 'activity-user', 'transcript',
          '/activity', 100, 'local', 'transcript_event'),
         (902, 'activity-session', 'activity/project', 'assistant',
          'The session activity view is implemented and verified.',
          'activity-assistant', 'transcript', '/activity', 120, 'local',
          'transcript_event')",
        [],
    )?;
    drop(conn);

    crate::api::ensure_api_token()?;
    let token = crate::api::load_api_token()?;
    let app = crate::api::build_router(0).with_state(DbState);
    let unauthorized = app
        .clone()
        .oneshot(
            Request::builder()
                .uri("/api/v1/session-stats")
                .body(Body::empty())?,
        )
        .await?;
    assert_eq!(unauthorized.status(), StatusCode::UNAUTHORIZED);

    let projected = app
        .clone()
        .oneshot(authorized_json_request(
            Method::POST,
            "/api/v1/session-activity/project",
            &token,
            r#"{"source_root":"local","project":"activity/project","session_id":"activity-session"}"#,
        ))
        .await?;
    assert_eq!(projected.status(), StatusCode::OK);
    let projected: Value =
        serde_json::from_slice(&to_bytes(projected.into_body(), usize::MAX).await?)?;
    assert_eq!(projected["data"]["changed"], true);
    assert_eq!(projected["data"]["turn_count"], 1);

    let sessions = app
        .clone()
        .oneshot(authorized_request(
            Method::GET,
            "/api/v1/session-activity/sessions?project=activity%2Fproject",
            &token,
            Body::empty(),
        ))
        .await?;
    assert_eq!(sessions.status(), StatusCode::OK);
    let sessions: Value =
        serde_json::from_slice(&to_bytes(sessions.into_body(), usize::MAX).await?)?;
    assert_eq!(sessions["data"][0]["projected_turn_count"], 1);

    let turns = app
        .clone()
        .oneshot(authorized_request(
            Method::GET,
            "/api/v1/session-activity?session_id=activity-session",
            &token,
            Body::empty(),
        ))
        .await?;
    assert_eq!(turns.status(), StatusCode::OK);
    let turns: Value = serde_json::from_slice(&to_bytes(turns.into_body(), usize::MAX).await?)?;
    assert_eq!(
        turns["data"][0]["user_said"],
        "Add an evidence-first session view"
    );
    assert_eq!(turns["data"][0]["capture_health"], "unavailable");
    let id = turns["data"][0]["id"].as_i64().expect("turn id");

    let detail = app
        .clone()
        .oneshot(authorized_request(
            Method::GET,
            &format!("/api/v1/session-activity/{id}"),
            &token,
            Body::empty(),
        ))
        .await?;
    assert_eq!(detail.status(), StatusCode::OK);

    let stats = app
        .oneshot(authorized_request(
            Method::GET,
            "/api/v1/session-stats?project=activity%2Fproject&since_epoch=90&until_epoch=200",
            &token,
            Body::empty(),
        ))
        .await?;
    assert_eq!(stats.status(), StatusCode::OK);
    let stats: Value = serde_json::from_slice(&to_bytes(stats.into_body(), usize::MAX).await?)?;
    assert_eq!(stats["data"]["sessions"], 1);
    assert_eq!(stats["data"]["turns"], 1);
    assert_eq!(stats["data"]["actions"], 0);
    Ok(())
}

#[tokio::test]
async fn session_activity_rejects_invalid_windows_and_ids() -> anyhow::Result<()> {
    let _test_dir = ScopedTestDataDir::new("api-session-activity-invalid");
    db::open_db()?;
    crate::api::ensure_api_token()?;
    let token = crate::api::load_api_token()?;
    let app = crate::api::build_router(0).with_state(DbState);

    let window = app
        .clone()
        .oneshot(authorized_request(
            Method::GET,
            "/api/v1/session-stats?since_epoch=20&until_epoch=10",
            &token,
            Body::empty(),
        ))
        .await?;
    assert_eq!(window.status(), StatusCode::BAD_REQUEST);

    let id = app
        .clone()
        .oneshot(authorized_request(
            Method::GET,
            "/api/v1/session-activity/sessions?cursor=not-a-cursor",
            &token,
            Body::empty(),
        ))
        .await?;
    assert_eq!(id.status(), StatusCode::BAD_REQUEST);

    let id = app
        .oneshot(authorized_request(
            Method::GET,
            "/api/v1/session-activity/not-an-id",
            &token,
            Body::empty(),
        ))
        .await?;
    assert_eq!(id.status(), StatusCode::BAD_REQUEST);
    Ok(())
}