remem-ai 0.6.38

Local-first coding agent memory for Claude Code and OpenAI Codex
Documentation
use axum::http::StatusCode;
use rusqlite::params;
use serde::Serialize;
use serde_json::json;

use crate::api::mutation::{
    mutation_request_hash, validate_idempotency_key, CredentialFreeMutationBody,
};
use crate::db;
use crate::db::test_support::ScopedTestDataDir;

use super::insert_safe_review_candidate;
use super::{candidate_version, response_json, send_safe_review};

#[tokio::test]
async fn safe_review_distinguishes_nonreviewable_and_unknown_replay_schema() -> anyhow::Result<()> {
    let _test_dir = ScopedTestDataDir::new("api-safe-review-stable-errors");
    crate::api::ensure_api_token()?;
    let token = crate::api::load_api_token()?;
    let (candidate_id, _) = insert_safe_review_candidate(
        "safe-review-stable-errors",
        "file_edit",
        "safe evidence",
        "Candidate with stable error responses.",
    )?;
    let conn = db::open_db()?;
    conn.execute(
        "UPDATE memory_candidates SET review_status = 'discarded' WHERE id = ?1",
        params![candidate_id],
    )?;
    drop(conn);
    let version = candidate_version(candidate_id)?;
    let response = send_safe_review(
        candidate_id,
        "reject",
        &token,
        json!({
            "reason": "cannot review again",
            "expected_version": version,
            "idempotency_key": "nonreviewable-fresh-key"
        }),
    )
    .await?;
    assert_eq!(response.status(), StatusCode::CONFLICT);
    assert_eq!(
        response_json(response).await?["error"]["code"],
        "candidate_not_reviewable"
    );

    let (schema_id, _) = insert_safe_review_candidate(
        "safe-review-unknown-schema",
        "file_edit",
        "safe evidence",
        "Candidate whose replay schema is unknown.",
    )?;
    let schema_version = candidate_version(schema_id)?;
    let identity = validate_idempotency_key("unknown-schema-key")?;
    let request_hash = mutation_request_hash(
        "candidate",
        schema_id,
        "reject",
        &RejectHashFixture {
            reason: "schema replay",
            expected_version: schema_version,
        },
    )?;
    let conn = db::open_db()?;
    conn.execute(
        "INSERT INTO api_mutation_requests(
             idempotency_key_hash, request_hash, operation_id, resource_kind,
             resource_id, action, response_schema_version, response_json,
             audit_id, created_at_epoch)
         VALUES (?1, ?2, ?3, 'candidate', ?4, 'reject', 99, '{}', 1, 1)",
        params![
            identity.idempotency_key_hash,
            request_hash,
            identity.operation_id,
            schema_id
        ],
    )?;
    drop(conn);
    let response = send_safe_review(
        schema_id,
        "reject",
        &token,
        json!({
            "reason": "schema replay",
            "expected_version": schema_version,
            "idempotency_key": "unknown-schema-key"
        }),
    )
    .await?;
    assert_eq!(response.status(), StatusCode::CONFLICT);
    assert_eq!(
        response_json(response).await?["error"]["code"],
        "idempotency_schema_unsupported"
    );
    Ok(())
}

#[derive(Serialize)]
struct RejectHashFixture<'a> {
    reason: &'a str,
    expected_version: i64,
}

impl CredentialFreeMutationBody for RejectHashFixture<'_> {}