use super::drop_recheck::LifecycleGoal;
#[test]
fn a_goal_is_reached_only_by_the_state_that_satisfies_it() {
assert!(LifecycleGoal::Running.reached(Some("running")));
assert!(!LifecycleGoal::Running.reached(Some("exited")));
assert!(!LifecycleGoal::Running.reached(Some("paused")));
assert!(!LifecycleGoal::Running.reached(None));
assert!(LifecycleGoal::NotRunning.reached(None));
assert!(LifecycleGoal::NotRunning.reached(Some("exited")));
assert!(!LifecycleGoal::NotRunning.reached(Some("running")));
}
#[cfg(unix)]
mod over_the_wire {
use super::super::drop_recheck::LifecycleGoal;
use crate::engine::fake_podman::{self, FakeReply};
use crate::engine::Engine;
use crate::libpod::API_PREFIX;
pub(super) fn engine_with(client: crate::libpod::Client, project: &str) -> Engine {
Engine::with_base_dir(client, project.into(), std::env::temp_dir())
}
pub(super) fn fake_dropping_the_op(state: Option<&'static str>) -> fake_podman::FakePodman {
fake_podman::start_replying(move |method, target| {
let touches_it = target.contains("/proj-web-1");
if touches_it && (method == "POST" || method == "DELETE") {
return FakeReply::ClosedWithoutResponse;
}
if method == "GET" && target.contains("/containers/json") {
let body = match state {
Some(s) => format!(
r#"[{{"Id":"abc","Names":["/proj-web-1"],"Image":"i","Status":"","State":"{s}"}}]"#
),
None => "[]".to_string(),
};
return FakeReply::Body(200, body);
}
FakeReply::Body(404, r#"{"message":"not found"}"#.to_string())
})
}
fn start_path() -> String {
format!("{API_PREFIX}/containers/proj-web-1/start")
}
#[tokio::test]
async fn a_lost_response_succeeds_when_the_container_reached_the_goal() {
let fake = fake_dropping_the_op(Some("running"));
let engine = engine_with(fake.client(), "proj");
let acted = engine
.run_lifecycle_op(
&start_path(),
"proj-web-1",
"Started",
LifecycleGoal::Running,
)
.await
.expect("the container is running, so the operation landed");
assert!(acted, "a confirmed operation counts as having acted");
}
#[tokio::test]
async fn a_lost_response_fails_when_the_container_did_not_reach_the_goal() {
let fake = fake_dropping_the_op(Some("exited"));
let engine = engine_with(fake.client(), "proj");
engine
.run_lifecycle_op(
&start_path(),
"proj-web-1",
"Started",
LifecycleGoal::Running,
)
.await
.expect_err("the container is not running, so nothing confirms the start");
}
#[tokio::test]
async fn a_lost_response_fails_when_the_state_cannot_be_re_checked() {
let fake = fake_podman::start_replying(|method, target| {
if method == "POST" && target.contains("/proj-web-1/") {
return FakeReply::ClosedWithoutResponse;
}
FakeReply::Body(500, r#"{"message":"boom"}"#.to_string())
});
let engine = engine_with(fake.client(), "proj");
engine
.run_lifecycle_op(
&start_path(),
"proj-web-1",
"Started",
LifecycleGoal::Running,
)
.await
.expect_err("an unreadable re-check must not be read as success");
}
#[tokio::test]
async fn a_lost_kill_response_succeeds_when_the_container_is_gone() {
let fake = fake_dropping_the_op(None);
let engine = engine_with(fake.client(), "proj");
let acted = engine
.run_lifecycle_op(
&format!("{API_PREFIX}/containers/proj-web-1/kill?signal=SIGKILL"),
"proj-web-1",
"Killed",
LifecycleGoal::NotRunning,
)
.await
.expect("the container is gone, so the kill landed");
assert!(acted);
}
}
#[test]
fn gone_needs_absence_not_merely_a_stopped_container() {
assert!(LifecycleGoal::Gone.reached(None));
assert!(!LifecycleGoal::Gone.reached(Some("exited")));
assert!(!LifecycleGoal::Gone.reached(Some("running")));
assert!(LifecycleGoal::NotRunning.reached(Some("exited")));
}
#[cfg(unix)]
mod stop_and_remove {
use super::over_the_wire::{engine_with, fake_dropping_the_op};
#[tokio::test]
async fn a_lost_stop_response_succeeds_when_the_container_is_not_running() {
let fake = fake_dropping_the_op(Some("exited"));
let engine = engine_with(fake.client(), "proj");
engine
.stop_container("proj-web-1", 10)
.await
.expect("the container is not running, so the stop landed");
}
#[tokio::test]
async fn a_lost_stop_response_fails_while_the_container_still_runs() {
let fake = fake_dropping_the_op(Some("running"));
let engine = engine_with(fake.client(), "proj");
engine
.stop_container("proj-web-1", 10)
.await
.expect_err("still running is not a stop");
}
#[tokio::test]
async fn a_lost_removal_response_fails_when_the_container_is_merely_stopped() {
let fake = fake_dropping_the_op(Some("exited"));
let engine = engine_with(fake.client(), "proj");
engine
.teardown_one_container("proj-web-1", 10, &[], false)
.await
.expect_err("a container that is still there was not removed");
}
#[tokio::test]
async fn a_lost_removal_response_succeeds_when_the_container_is_gone() {
let fake = fake_dropping_the_op(None);
let engine = engine_with(fake.client(), "proj");
engine
.teardown_one_container("proj-web-1", 10, &[], false)
.await
.expect("the container is absent, so the removal landed");
}
}