agent-migrate-core 0.6.1

Core models and transactional filesystem engine for agent configuration migration
Documentation
use std::path::{Path, PathBuf};

use agent_migrate_core::{PathMapping, PathMappingSet, SecretPolicy, redact_json};
use serde_json::json;

const SENTINEL: &str = "AGENT_MIGRATE_FIXTURE_SECRET_DO_NOT_EXPORT";

#[test]
fn redaction_removes_secret_values_and_credential_paths() -> Result<(), Box<dyn std::error::Error>>
{
    // Given
    let input = json!({
        "api_key": SENTINEL,
        "headers": { "Authorization": format!("Bearer {SENTINEL}") },
        "credential_path": "/Users/ethan/.secrets/provider.json",
        "model": "gpt-5",
        "nested": { "password": SENTINEL }
    });

    // When
    let redacted = redact_json(&input, &SecretPolicy::default())?;
    let encoded = serde_json::to_string(&redacted.value)?;

    // Then
    assert!(!encoded.contains(SENTINEL));
    assert!(!encoded.contains("provider.json"));
    assert!(encoded.contains("gpt-5"));
    assert_eq!(redacted.references.len(), 4);
    Ok(())
}

#[test]
fn path_mapping_uses_component_boundary_and_longest_prefix()
-> Result<(), Box<dyn std::error::Error>> {
    // Given
    let mappings = PathMappingSet::new(vec![
        PathMapping::new(PathBuf::from("/Users/ethan"), PathBuf::from("/home/ethan"))?,
        PathMapping::new(
            PathBuf::from("/Users/ethan/projects"),
            PathBuf::from("/srv/projects"),
        )?,
    ])?;

    // When
    let mapped = mappings.map(Path::new("/Users/ethan/projects/demo/AGENTS.md"))?;
    let boundary = mappings.map(Path::new("/Users/ethan2/demo"));

    // Then
    assert_eq!(mapped, PathBuf::from("/srv/projects/demo/AGENTS.md"));
    assert!(boundary.is_err());
    Ok(())
}