// file: crates/knowledge-git-lib/tests/git_read_path.rs // version: 2 //! Integration tests for the real generic Git read path using local temporary repositories. fn test_root(label: &str) -> std::path::PathBuf { let nanos_result = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH); let nanos = match nanos_result { Ok(value) => value.as_nanos(), Err(_) => 0, }; let mut path = std::env::temp_dir(); path.push(format!("sasedev-knowledge-git-integration-{label}-{}-{nanos}", std::process::id())); let _ = std::fs::remove_dir_all(&path); return path; } fn run_git(repository: &std::path::Path, arguments: &[&str]) -> bool { let mut command = std::process::Command::new("git"); command.current_dir(repository); command.args(arguments); command.env("GIT_TERMINAL_PROMPT", "0"); let status_result = command.status(); match status_result { Ok(status) => return status.success(), Err(_) => return false, } } fn prepare_source(root: &std::path::Path) -> Option { let source = root.join("source"); if std::fs::create_dir_all(&source).is_err() { return None; } if !run_git(&source, &["init", "-b", "main"]) { return None; } if !run_git(&source, &["config", "user.name", "Knowledge Test"]) { return None; } if !run_git(&source, &["config", "user.email", "knowledge@example.invalid"]) { return None; } if !run_git(&source, &["config", "commit.gpgsign", "false"]) { return None; } if std::fs::write(source.join("content.txt"), "first\n").is_err() { return None; } if !run_git(&source, &["add", "content.txt"]) { return None; } if !run_git(&source, &["commit", "-m", "first"]) { return None; } return Some(source); } fn source_id() -> Option { let result = knowledge_domain_lib::SourceId::new("integration-source"); match result { Ok(value) => return Some(value), Err(_) => return None, } } fn configured_backend(root: &std::path::Path, source: &std::path::Path) -> Option<(knowledge_git_lib::GitBackend, knowledge_domain_lib::SourceId)> { let source_id_result = source_id(); let source_id = match source_id_result { Some(value) => value, None => return None, }; let config_result = knowledge_git_lib::GitBackendConfig::new(root.join("state")); let config = match config_result { Ok(value) => value, Err(_) => return None, }; let source_result = knowledge_git_lib::GitSourceConfig::local(source_id.clone(), source.to_path_buf()); let source_config = match source_result { Ok(value) => value, Err(_) => return None, }; let mut backend = knowledge_git_lib::GitBackend::new(config); if backend.register_source(source_config).is_err() { return None; } return Some((backend, source_id)); } #[test] fn real_local_repository_refreshes_resolves_and_materializes_revision_snapshots() { let root = test_root("refresh-resolve-snapshot"); let source_result = prepare_source(&root); assert!(source_result.is_some()); let source = match source_result { Some(value) => value, None => return, }; let backend_result = configured_backend(&root, &source); assert!(backend_result.is_some()); let (mut backend, source_id) = match backend_result { Some(value) => value, None => return, }; let reference = knowledge_git_api::GitReference::new("refs/heads/main"); assert!(knowledge_git_api::GitRepository::refresh(&mut backend, &source_id).is_ok()); let first_revision_result = knowledge_git_api::GitRepository::resolve_revision(&backend, &source_id, &reference); assert!(first_revision_result.is_ok()); let first_revision = match first_revision_result { Ok(value) => value, Err(_) => return, }; let first_snapshot_result = knowledge_git_api::GitRepository::prepare_snapshot(&backend, &source_id, &first_revision); assert!(first_snapshot_result.is_ok()); let first_snapshot = match first_snapshot_result { Ok(value) => value, Err(_) => return, }; assert!(first_snapshot.path().is_dir()); let first_content_result = std::fs::read_to_string(first_snapshot.path().join("content.txt")); assert!(first_content_result.is_ok()); let first_content = match first_content_result { Ok(value) => value, Err(_) => return, }; assert_eq!(first_content, "first\n"); let repeated_snapshot_result = knowledge_git_api::GitRepository::prepare_snapshot(&backend, &source_id, &first_revision); assert!(repeated_snapshot_result.is_ok()); let repeated_snapshot = match repeated_snapshot_result { Ok(value) => value, Err(_) => return, }; assert_eq!(first_snapshot.path(), repeated_snapshot.path()); assert!(std::fs::write(source.join("content.txt"), "second\n").is_ok()); assert!(run_git(&source, &["add", "content.txt"])); assert!(run_git(&source, &["commit", "-m", "second"])); assert!(knowledge_git_api::GitRepository::refresh(&mut backend, &source_id).is_ok()); let second_revision_result = knowledge_git_api::GitRepository::resolve_revision(&backend, &source_id, &reference); assert!(second_revision_result.is_ok()); let second_revision = match second_revision_result { Ok(value) => value, Err(_) => return, }; assert_ne!(first_revision, second_revision); let second_snapshot_result = knowledge_git_api::GitRepository::prepare_snapshot(&backend, &source_id, &second_revision); assert!(second_snapshot_result.is_ok()); let second_snapshot = match second_snapshot_result { Ok(value) => value, Err(_) => return, }; assert_ne!(first_snapshot.path(), second_snapshot.path()); let second_content_result = std::fs::read_to_string(second_snapshot.path().join("content.txt")); assert!(second_content_result.is_ok()); let second_content = match second_content_result { Ok(value) => value, Err(_) => return, }; assert_eq!(second_content, "second\n"); let _ = std::fs::remove_dir_all(&root); return; } #[test] fn invalid_reference_and_dirty_existing_snapshot_are_rejected() { let root = test_root("invalid-and-dirty"); let source_result = prepare_source(&root); assert!(source_result.is_some()); let source = match source_result { Some(value) => value, None => return, }; let backend_result = configured_backend(&root, &source); assert!(backend_result.is_some()); let (mut backend, source_id) = match backend_result { Some(value) => value, None => return, }; assert!(knowledge_git_api::GitRepository::refresh(&mut backend, &source_id).is_ok()); let missing_reference = knowledge_git_api::GitReference::new("refs/heads/does-not-exist"); let missing_result = knowledge_git_api::GitRepository::resolve_revision(&backend, &source_id, &missing_reference); assert!(missing_result.is_err()); let reference = knowledge_git_api::GitReference::new("refs/heads/main"); let revision_result = knowledge_git_api::GitRepository::resolve_revision(&backend, &source_id, &reference); assert!(revision_result.is_ok()); let revision = match revision_result { Ok(value) => value, Err(_) => return, }; let snapshot_result = knowledge_git_api::GitRepository::prepare_snapshot(&backend, &source_id, &revision); assert!(snapshot_result.is_ok()); let snapshot = match snapshot_result { Ok(value) => value, Err(_) => return, }; assert!(std::fs::write(snapshot.path().join("content.txt"), "dirty\n").is_ok()); let repeated_result = knowledge_git_api::GitRepository::prepare_snapshot(&backend, &source_id, &revision); assert_eq!(repeated_result, Err(knowledge_git_lib::GitBackendError::InvalidSnapshot)); let _ = std::fs::remove_dir_all(&root); return; }