Files
sasedev-knowledge/crates/knowledge-git-lib/unit_tests/backend.rs

273 lines
10 KiB
Rust

// file: crates/knowledge-git-lib/unit_tests/backend.rs
// version: 2
#[derive(Clone)]
struct RecordingRunner {
calls: std::rc::Rc<std::cell::RefCell<Vec<super::GitCommandSpec>>>,
failure: Option<crate::GitBackendError>,
inspection_stdout: Vec<u8>,
}
impl RecordingRunner {
fn successful() -> RecordingRunner {
return RecordingRunner {
calls: std::rc::Rc::new(std::cell::RefCell::new(Vec::new())),
failure: None,
inspection_stdout: b"true\n".to_vec(),
};
}
fn failing(error: crate::GitBackendError) -> RecordingRunner {
return RecordingRunner {
calls: std::rc::Rc::new(std::cell::RefCell::new(Vec::new())),
failure: Some(error),
inspection_stdout: b"true\n".to_vec(),
};
}
fn with_inspection_stdout(value: &[u8]) -> RecordingRunner {
return RecordingRunner {
calls: std::rc::Rc::new(std::cell::RefCell::new(Vec::new())),
failure: None,
inspection_stdout: value.to_vec(),
};
}
}
impl super::GitCommandRunner for RecordingRunner {
fn run(&self, specification: &super::GitCommandSpec) -> Result<super::GitCommandOutput, crate::GitBackendError> {
self.calls.borrow_mut().push(specification.clone());
if let Some(error) = self.failure {
return Err(error);
}
if specification.operation == crate::GitOperation::InspectMirror {
return Ok(super::GitCommandOutput { stdout: self.inspection_stdout.clone() });
}
return Ok(super::GitCommandOutput { stdout: Vec::new() });
}
}
fn source_id(value: &str) -> Option<knowledge_domain_lib::SourceId> {
let result = knowledge_domain_lib::SourceId::new(value);
match result {
Ok(source_id) => return Some(source_id),
Err(_) => return None,
}
}
fn test_root(label: &str) -> std::path::PathBuf {
let mut path = std::env::temp_dir();
path.push(format!("sasedev-knowledge-git-lib-{label}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&path);
return path;
}
#[test]
fn backend_configuration_requires_non_empty_paths() {
assert_eq!(crate::GitBackendConfig::new(std::path::PathBuf::new()), Err(crate::GitBackendError::EmptyStateRoot));
assert_eq!(
crate::GitBackendConfig::with_git_executable(test_root("empty-executable"), std::path::PathBuf::new()),
Err(crate::GitBackendError::EmptyGitExecutable)
);
return;
}
#[test]
fn source_key_never_interpolates_raw_source_identifier_as_a_path() {
let source_result = source_id("../pgr/credential:value");
assert!(source_result.is_some());
let source_id = match source_result {
Some(value) => value,
None => return,
};
let key = crate::GitSourceKey::from_source_id(&source_id);
assert!(key.as_str().starts_with("source-"));
assert!(key.as_str().chars().all(|character| character.is_ascii_alphanumeric() || character == '-'));
assert!(!key.as_str().contains("pgr"));
assert!(!key.as_str().contains(".."));
return;
}
#[test]
fn local_source_clone_uses_mirror_no_local_and_argument_separator() {
let root = test_root("local-arguments");
let config_result = crate::GitBackendConfig::new(root.clone());
assert!(config_result.is_ok());
let config = match config_result {
Ok(value) => value,
Err(_) => return,
};
let runner = RecordingRunner::successful();
let backend = crate::GitBackend::with_runner(config, Box::new(runner.clone()));
let source_id_result = source_id("pgr");
assert!(source_id_result.is_some());
let source_id = match source_id_result {
Some(value) => value,
None => return,
};
let source_result = crate::GitSourceConfig::local(source_id, std::path::PathBuf::from("/controlled/source"));
assert!(source_result.is_ok());
let source = match source_result {
Ok(value) => value,
Err(_) => return,
};
let mirror_result = backend.ensure_mirror(&source);
assert!(mirror_result.is_ok());
let calls = runner.calls.borrow();
assert_eq!(calls.len(), 2);
assert_eq!(calls[0].operation, crate::GitOperation::CloneMirror);
assert_eq!(
calls[0].arguments,
vec![
std::ffi::OsString::from("clone"),
std::ffi::OsString::from("--mirror"),
std::ffi::OsString::from("--no-local"),
std::ffi::OsString::from("--"),
std::ffi::OsString::from("/controlled/source"),
backend.repository_path(source.source_id()).into_os_string(),
]
);
assert_eq!(calls[1].operation, crate::GitOperation::InspectMirror);
assert!(backend.config().hooks_path().is_dir());
assert!(backend.config().repositories_path().is_dir());
assert!(backend.config().snapshots_path().is_dir());
let _ = std::fs::remove_dir_all(root);
return;
}
#[test]
fn remote_source_clone_does_not_add_no_local() {
let root = test_root("remote-arguments");
let config_result = crate::GitBackendConfig::new(root.clone());
assert!(config_result.is_ok());
let config = match config_result {
Ok(value) => value,
Err(_) => return,
};
let runner = RecordingRunner::successful();
let backend = crate::GitBackend::with_runner(config, Box::new(runner.clone()));
let source_id_result = source_id("howtos");
assert!(source_id_result.is_some());
let source_id = match source_id_result {
Some(value) => value,
None => return,
};
let source_result = crate::GitSourceConfig::remote(source_id, "ssh://git@example.invalid/howtos.git");
assert!(source_result.is_ok());
let source = match source_result {
Ok(value) => value,
Err(_) => return,
};
assert!(backend.ensure_mirror(&source).is_ok());
let calls = runner.calls.borrow();
assert_eq!(calls[0].operation, crate::GitOperation::CloneMirror);
assert_eq!(calls[0].arguments[0], std::ffi::OsString::from("clone"));
assert_eq!(calls[0].arguments[1], std::ffi::OsString::from("--mirror"));
assert_eq!(calls[0].arguments[2], std::ffi::OsString::from("--"));
assert!(!calls[0].arguments.contains(&std::ffi::OsString::from("--no-local")));
let _ = std::fs::remove_dir_all(root);
return;
}
#[test]
fn process_errors_do_not_expose_remote_location_or_credentials() {
let root = test_root("redaction");
let config_result = crate::GitBackendConfig::new(root.clone());
assert!(config_result.is_ok());
let config = match config_result {
Ok(value) => value,
Err(_) => return,
};
let runner = RecordingRunner::failing(crate::GitBackendError::GitCommandFailed { operation: crate::GitOperation::CloneMirror, exit_code: Some(128) });
let backend = crate::GitBackend::with_runner(config, Box::new(runner));
let source_id_result = source_id("snippets");
assert!(source_id_result.is_some());
let source_id = match source_id_result {
Some(value) => value,
None => return,
};
let source_result = crate::GitSourceConfig::remote(source_id, "https://user:secret@example.invalid/snippets.git");
assert!(source_result.is_ok());
let source = match source_result {
Ok(value) => value,
Err(_) => return,
};
let result = backend.ensure_mirror(&source);
assert!(result.is_err());
let error = match result {
Ok(_) => return,
Err(value) => value,
};
let display = error.to_string();
assert!(!display.contains("secret"));
assert!(!display.contains("example.invalid"));
assert_eq!(error, crate::GitBackendError::GitCommandFailed { operation: crate::GitOperation::CloneMirror, exit_code: Some(128) });
let _ = std::fs::remove_dir_all(root);
return;
}
#[test]
fn existing_mirror_is_inspected_without_clone() {
let root = test_root("existing-mirror");
let config_result = crate::GitBackendConfig::new(root.clone());
assert!(config_result.is_ok());
let config = match config_result {
Ok(value) => value,
Err(_) => return,
};
let runner = RecordingRunner::successful();
let backend = crate::GitBackend::with_runner(config, Box::new(runner.clone()));
let source_result = source_id("pgr");
assert!(source_result.is_some());
let source_id = match source_result {
Some(value) => value,
None => return,
};
let repository_path = backend.repository_path(&source_id);
assert!(std::fs::create_dir_all(&repository_path).is_ok());
let source_result = crate::GitSourceConfig::remote(source_id, "https://example.invalid/pgr.git");
assert!(source_result.is_ok());
let source = match source_result {
Ok(value) => value,
Err(_) => return,
};
let result = backend.ensure_mirror(&source);
assert!(result.is_ok());
let mirror = match result {
Ok(value) => value,
Err(_) => return,
};
assert_eq!(mirror.status(), crate::GitMirrorStatus::Existing);
let calls = runner.calls.borrow();
assert_eq!(calls.len(), 1);
assert_eq!(calls[0].operation, crate::GitOperation::InspectMirror);
let _ = std::fs::remove_dir_all(root);
return;
}
#[test]
fn invalid_existing_mirror_is_rejected_without_deleting_it() {
let root = test_root("invalid-mirror");
let config_result = crate::GitBackendConfig::new(root.clone());
assert!(config_result.is_ok());
let config = match config_result {
Ok(value) => value,
Err(_) => return,
};
let runner = RecordingRunner::with_inspection_stdout(b"false\n");
let backend = crate::GitBackend::with_runner(config, Box::new(runner));
let source_result = source_id("pgr");
assert!(source_result.is_some());
let source_id = match source_result {
Some(value) => value,
None => return,
};
let repository_path = backend.repository_path(&source_id);
assert!(std::fs::create_dir_all(&repository_path).is_ok());
let result = backend.inspect_mirror(&source_id);
assert_eq!(result, Err(crate::GitBackendError::InvalidMirror));
assert!(repository_path.is_dir());
let _ = std::fs::remove_dir_all(root);
return;
}