use super::*;
use crate::hooks::{HookError, Hooks, LabView};
use crate::{BoxFuture, ErrorCode, Park};
#[tokio::test]
async fn a_run_whose_lock_cant_be_taken_never_starts() {
let f = fixture();
let lab = f.runner.open(&f.manifest, None).unwrap();
std::fs::create_dir_all(f.runner.store.lock_path(lab.name())).unwrap();
let events = Recorder::new();
let err = lab.up(&opts(), &events).await.unwrap_err();
assert_eq!(err.code(), ErrorCode::Io);
assert!(f.fake.calls().is_empty(), "no tool ran: {:?}", f.fake.lines());
assert!(matches!(kinds(&events).as_slice(), [EventKind::Error { code: ErrorCode::Io, .. }]));
}
#[tokio::test]
async fn a_down_replays_the_params_the_run_was_started_with() {
let f = fixture();
let lab = f.runner.open(&f.manifest, None).unwrap();
lab.up(&opts(), &Recorder::new()).await.unwrap();
let mut now = RunOptions::default();
now.params.insert("ISOLOOM_PUBLISH_ADDRESS".into(), "0.0.0.0".into());
lab.down(&now, &Recorder::new()).await.unwrap();
let down = f.fake.last(" down --volumes").unwrap();
assert_eq!(down.env("ISOLOOM_PUBLISH_ADDRESS"), Some("127.0.0.1"));
}
#[tokio::test]
async fn a_pinned_port_another_run_is_picking_is_never_handed_out_twice() {
let f = fixture();
let other = crate::RunName::new("other-lab").unwrap();
assert!(
f.runner.reserve(&other, 41873, isoloom_manifest::Protocol::Tcp),
"another run starting meanwhile"
);
let lab = f.runner.open(&f.manifest, None).unwrap();
let run = lab.up(&opts(), &Recorder::new()).await.unwrap();
assert_eq!(run.outputs[0].value.as_deref(), Some("http://127.0.0.1:41874/"));
}
#[tokio::test]
async fn a_port_held_on_the_host_is_refused_before_anything_starts() {
let f = fixture();
f.ports.take(8080, isoloom_manifest::Protocol::Tcp);
let lab = f.runner.open(&f.manifest, None).unwrap();
let mut fixed = opts();
fixed.params.insert("ISOLOOM_PUBLISH_FIXED".into(), "1".into());
let err = lab.up(&fixed, &Recorder::new()).await.unwrap_err();
assert_eq!(err.to_string(), "host port 8080/tcp is in use");
assert_eq!(f.called(" up --detach "), 0);
}
#[tokio::test]
async fn a_run_recorded_elsewhere_is_refused() {
let f = fixture();
let lab = f.runner.open(&f.manifest, None).unwrap();
let mut here = opts();
here.host_label = Some("pve-lab".into());
lab.up(&here, &Recorder::new()).await.unwrap();
here.host_label = Some("other-server".into());
assert_eq!(lab.down(&here, &Recorder::new()).await.unwrap_err().code(), ErrorCode::RunningElsewhere);
}
#[derive(Default)]
struct Sidecar(std::sync::Mutex<Vec<String>>);
impl Sidecar {
fn note(&self, what: &str, lab: &LabView<'_>) {
self.0
.lock()
.unwrap()
.push(format!("{what} {} {}", lab.name, lab.networks[0].native.clone().unwrap_or_default()));
}
}
impl Hooks for Sidecar {
fn after_up<'a>(&'a self, lab: &'a LabView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
self.note("after_up", lab);
Box::pin(async { Err(HookError::from("the attack box didn't start")) })
}
fn before_park<'a>(&'a self, lab: &'a LabView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
self.note("before_park", lab);
Box::pin(async { Ok(()) })
}
fn after_resume<'a>(&'a self, lab: &'a LabView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
self.note("after_resume", lab);
Box::pin(async { Ok(()) })
}
fn before_down<'a>(&'a self, lab: &'a LabView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
self.note("before_down", lab);
Box::pin(async { Ok(()) })
}
}
#[tokio::test]
async fn hooks_go_around_the_lifecycle_and_their_failure_is_a_warning() {
let mut f = fixture();
let sidecar = Arc::new(Sidecar::default());
let hooks: Arc<dyn Hooks> = Arc::<Sidecar>::clone(&sidecar);
f.runner.add_hooks(hooks);
let lab = f.runner.open(&f.manifest, None).unwrap();
let events = Recorder::new();
lab.up(&opts(), &events).await.unwrap();
assert!(
kinds(&events).iter().any(
|k| matches!(k, EventKind::Warning { code, message } if *code == crate::WarningCode::HookFailed && message.contains("after_up: the attack box"))
)
);
lab.park(Park::Shutdown, &Recorder::new()).await.unwrap();
lab.resume(&RunOptions::default(), &Recorder::new()).await.unwrap();
lab.down(&RunOptions::default(), &Recorder::new()).await.unwrap();
let calls = sidecar.0.lock().unwrap().clone();
let expected = ["after_up", "before_park", "after_resume", "before_down"].map(|e| format!("{e} flag-portal flag-portal_lab"));
assert_eq!(calls, expected);
}