Codex Handbook
state/src/runtime/recovery_tests.rs 105 lines
use super::*;use crate::runtime::test_support::unique_temp_dir;use pretty_assertions::assert_eq;#[tokio::test]async fn backup_moves_only_requested_runtime_db_files_to_backup_folder() -> std::io::Result<()> {    let sqlite_home = unique_temp_dir();    tokio::fs::create_dir_all(sqlite_home.as_path()).await?;    let runtime_paths = super::super::runtime_db_paths(sqlite_home.as_path());    let mut expected_paths = Vec::new();    for db_path in runtime_paths.iter().map(|db| db.path.as_path()) {        for path in sqlite_paths(db_path) {            tokio::fs::write(path.as_path(), path.display().to_string()).await?;            expected_paths.push(path);        }    }    let failed_db_path = super::super::logs_db_path(sqlite_home.as_path());    let failed_paths = sqlite_paths(failed_db_path.as_path());    let backups = backup_runtime_db_for_fresh_start(failed_db_path.as_path()).await?;    assert_eq!(backups.len(), failed_paths.len());    for path in &failed_paths {        assert!(!tokio::fs::try_exists(path.as_path()).await?);    }    for path in expected_paths        .iter()        .filter(|path| !failed_paths.contains(path))    {        assert!(tokio::fs::try_exists(path.as_path()).await?);    }    for backup in backups {        assert!(            backup                .backup_path                .starts_with(sqlite_home.join(BACKUP_DIR_NAME))        );        assert!(tokio::fs::try_exists(backup.backup_path.as_path()).await?);    }    Ok(())}#[tokio::test]async fn backup_replaces_blocking_sqlite_home_file() -> std::io::Result<()> {    let temp_dir = unique_temp_dir();    tokio::fs::create_dir_all(temp_dir.as_path()).await?;    let sqlite_home = temp_dir.join("sqlite-home");    tokio::fs::write(sqlite_home.as_path(), b"not-a-directory").await?;    let backups = backup_runtime_db_for_fresh_start(        super::super::state_db_path(sqlite_home.as_path()).as_path(),    )    .await?;    assert_eq!(backups.len(), 1);    assert!(tokio::fs::metadata(sqlite_home.as_path()).await?.is_dir());    assert!(        backups[0]            .backup_path            .starts_with(temp_dir.join(format!("sqlite-home.{BACKUP_DIR_NAME}")))    );    assert!(tokio::fs::try_exists(backups[0].backup_path.as_path()).await?);    Ok(())}#[test]fn sqlite_error_detail_classifies_corruption_and_lock_errors() {    assert!(sqlite_error_detail_is_corruption("file is not a database"));    assert!(sqlite_error_detail_is_corruption(        "error returned from database: (code: 11) database disk image is malformed"    ));    assert!(!sqlite_error_detail_is_corruption("database is locked"));    assert!(sqlite_error_detail_is_lock("database is locked"));    assert!(sqlite_error_detail_is_lock("database is busy"));}#[tokio::test]async fn runtime_db_path_for_corruption_error_returns_failed_database_path() -> std::io::Result<()>{    let sqlite_home = unique_temp_dir();    tokio::fs::create_dir_all(sqlite_home.as_path()).await?;    let path = super::super::state_db_path(sqlite_home.as_path());    tokio::fs::write(path.as_path(), b"not sqlite").await?;    let err = match super::super::StateRuntime::init(sqlite_home, "openai".to_string()).await {        Ok(_) => panic!("malformed sqlite should fail to initialize"),        Err(err) => err,    };    assert_eq!(runtime_db_path_for_corruption_error(&err), Some(path));    Ok(())}#[test]fn runtime_db_path_for_corruption_error_ignores_corrupt_word_in_path() {    let path = PathBuf::from("/tmp/sqlite_corrupt/state_5.sqlite");    let err = anyhow::Error::new(RuntimeDbInitError::new(        "state DB",        "open",        path.as_path(),        anyhow::anyhow!("permission denied"),    ));    assert_eq!(runtime_db_path_for_corruption_error(&err), None);}