0.0.2-alpha.2
This commit is contained in:
283
crates/knowledge-git-lib/unit_tests/backend.rs
Normal file
283
crates/knowledge-git-lib/unit_tests/backend.rs
Normal file
@@ -0,0 +1,283 @@
|
||||
// file: crates/knowledge-git-lib/unit_tests/backend.rs
|
||||
// version: 1
|
||||
|
||||
#[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;
|
||||
}
|
||||
Reference in New Issue
Block a user