GitBaby/tests/context.rs
zhenyi 680411c4fb chore: initialize gitbaby repository
gitbaby is an asynchronous Rust library wrapping git operations, built on
gix + tokio + async-trait.

Module layout:
- archive, blame, blob, branch, cleanup, commit, compare, config,
  conflict, diff, merge, refs, remote, setup, share, submodule, tags,
  tree (feature modules)
- command: command scheduler (cmd/env/pipe/context/error)
- repo: RepositoryFacade trait (consumers implement, exposing a
  gix::Repository)

Features:
- async API on tokio 1.53
- order-preserving Env (Vec<(String, String)>)
- hand-written BabyError / CmdError, not using thiserror derive
- integration tests covering cmd_run / context / env / pipe (env keys
  prefixed for isolation)

CI: none, standard cargo build / test / clippy / fmt only
2026-08-14 18:10:04 +08:00

161 lines
3.7 KiB
Rust

use gitbaby::command::context::GitPipeContext;
struct MinimalCtx;
impl GitPipeContext for MinimalCtx {
fn cancel_pipe(&mut self) {}
}
struct StatefulCtx {
cancelled: bool,
current: Option<usize>,
finished: Vec<usize>,
progress: Vec<(usize, u64)>,
}
impl GitPipeContext for StatefulCtx {
fn cancel_pipe(&mut self) {
self.cancelled = true;
}
fn current_index(&self) -> Option<usize> {
self.current
}
fn on_command_finished(&mut self, index: usize) {
self.finished.push(index);
}
fn on_progress(&mut self, index: usize, bytes: u64) {
self.progress.push((index, bytes));
}
}
#[test]
fn default_current_index_is_none() {
let ctx = MinimalCtx;
assert_eq!(ctx.current_index(), None);
}
#[test]
fn default_hooks_are_noops() {
let mut ctx = MinimalCtx;
ctx.on_command_finished(0);
ctx.on_command_finished(7);
ctx.on_progress(2, 1024);
ctx.on_progress(5, 9999);
assert_eq!(ctx.current_index(), None);
}
#[test]
fn cancel_pipe_marks_cancelled_state() {
let mut ctx = StatefulCtx {
cancelled: false,
current: Some(0),
finished: vec![],
progress: vec![],
};
assert!(!ctx.cancelled);
ctx.cancel_pipe();
assert!(ctx.cancelled);
}
#[test]
fn on_command_finished_records_indices_in_order() {
let mut ctx = StatefulCtx {
cancelled: false,
current: None,
finished: vec![],
progress: vec![],
};
for i in [0_usize, 1, 2, 3] {
ctx.on_command_finished(i);
}
assert_eq!(ctx.finished, vec![0, 1, 2, 3]);
}
#[test]
fn on_progress_records_index_and_bytes_pairs() {
let mut ctx = StatefulCtx {
cancelled: false,
current: None,
finished: vec![],
progress: vec![],
};
ctx.on_progress(0, 0);
ctx.on_progress(0, 1024);
ctx.on_progress(1, 2048);
assert_eq!(ctx.progress, vec![(0, 0), (0, 1024), (1, 2048)]);
}
#[test]
fn current_index_returns_impl_specific_value() {
let ctx = StatefulCtx {
cancelled: false,
current: Some(3),
finished: vec![],
progress: vec![],
};
assert_eq!(ctx.current_index(), Some(3));
}
#[test]
fn current_index_can_be_none_even_when_active() {
let ctx = StatefulCtx {
cancelled: false,
current: None,
finished: vec![],
progress: vec![],
};
assert_eq!(ctx.current_index(), None);
}
#[test]
fn trait_object_dispatch_invokes_impl() {
let mut ctx = StatefulCtx {
cancelled: false,
current: Some(2),
finished: vec![],
progress: vec![],
};
{
let trait_obj: &mut dyn GitPipeContext = &mut ctx;
trait_obj.cancel_pipe();
trait_obj.on_command_finished(1);
trait_obj.on_progress(1, 42);
assert_eq!(trait_obj.current_index(), Some(2));
}
assert!(ctx.cancelled);
assert_eq!(ctx.finished, vec![1]);
assert_eq!(ctx.progress, vec![(1, 42)]);
}
#[test]
fn multiple_impls_are_independent_via_trait_objects() {
let mut a = StatefulCtx {
cancelled: false,
current: Some(0),
finished: vec![],
progress: vec![],
};
let mut b = StatefulCtx {
cancelled: false,
current: Some(1),
finished: vec![],
progress: vec![],
};
{
let trait_obj: &mut dyn GitPipeContext = &mut a;
trait_obj.cancel_pipe();
}
{
let trait_obj: &mut dyn GitPipeContext = &mut b;
trait_obj.on_command_finished(7);
}
assert!(a.cancelled);
assert!(!b.cancelled);
assert_eq!(b.finished, vec![7]);
assert_eq!(a.current_index(), Some(0));
assert_eq!(b.current_index(), Some(1));
}