mod common;

use common::{
    cleanup_container,
    conn::TtyChunk,
    create_base_container, get_container_full_id, init_runtime,
    opts::{ContainerCreateOpts, ExecCreateOpts},
    StreamExt, TryStreamExt, DEFAULT_CMD, DEFAULT_CMD_ARRAY, DEFAULT_IMAGE,
};

#[tokio::test]
async fn container_create_exists_remove() {
    let podman = init_runtime();
    let container_name = "test-create-exist-container";

    let container = create_base_container(&podman, container_name, None).await;
    let exists_result = container.exists().await;
    assert!(exists_result.unwrap());

    let remove_result = container.remove().await;
    assert!(remove_result.is_ok());

    let exists_result = container.exists().await;
    assert!(!exists_result.unwrap());
}

#[tokio::test]
async fn container_inspect() {
    let podman = init_runtime();

    let container_name = "test-inspect-container";
    let container = create_base_container(&podman, container_name, None).await;
    let inspect_result = container.inspect().await;
    assert!(inspect_result.is_ok());
    let inspect_data = inspect_result.unwrap();
    assert!(inspect_data.image_name.unwrap().contains("ubuntu:latest"));
    assert_eq!(inspect_data.name.unwrap(), "test-inspect-container");
    assert_eq!(inspect_data.config.unwrap().cmd.unwrap(), DEFAULT_CMD_ARRAY);

    cleanup_container(&podman, container_name).await;
    // check that the container got correctly removed
    let inspect_result = container.inspect().await;
    assert!(inspect_result.is_err());
}

#[tokio::test]
async fn container_rename() {
    let podman = init_runtime();
    cleanup_container(&podman, "new-container-name").await;

    let container_name = "test-rename-container";
    let new_container_name = "test-rename-container-new";
    let container = create_base_container(&podman, container_name, None).await;

    let exists_result = podman.containers().get(new_container_name).exists().await;
    assert!(!exists_result.unwrap());

    let rename_result = container.rename(new_container_name).await;
    assert!(rename_result.is_ok());

    let exists_result = podman.containers().get(new_container_name).exists().await;
    assert!(exists_result.unwrap());

    cleanup_container(&podman, container_name).await;
    cleanup_container(&podman, new_container_name).await;
}

#[tokio::test]
async fn container_start() {
    let podman = init_runtime();

    let container_name = "test-start-container";
    let container = create_base_container(&podman, container_name, None).await;

    let start_result = container.start(None).await;
    assert!(start_result.is_ok());

    let inspect_data = container
        .inspect()
        .await
        .expect("started container inspect data");
    assert!(inspect_data.state.unwrap().running.unwrap());

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_restart() {
    let podman = init_runtime();

    let container_name = "test-restart-container";
    let container = create_base_container(&podman, container_name, None).await;
    let _ = container.start(None).await;

    let timestamp = container
        .inspect()
        .await
        .expect("restarted container inspect data before")
        .state
        .unwrap()
        .started_at
        .unwrap();

    let restart_result = container.restart().await;
    assert!(restart_result.is_ok());

    let timestamp_after = container
        .inspect()
        .await
        .expect("restarted container inspect data")
        .state
        .unwrap()
        .started_at
        .unwrap();
    assert_ne!(timestamp, timestamp_after);

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_pause_unpause() {
    let podman = init_runtime();

    let container_name = "test-pause-container";
    let container = create_base_container(&podman, container_name, None).await;

    let _ = container.start(None).await;

    let pause_result = container.pause().await;
    assert!(pause_result.is_ok());

    let inspect_data = container
        .inspect()
        .await
        .expect("paused container inspect data");
    assert!(inspect_data.state.unwrap().paused.unwrap());

    let unpause_result = container.unpause().await;
    assert!(unpause_result.is_ok());

    let inspect_data = container
        .inspect()
        .await
        .expect("paused container inspect data");
    assert!(!inspect_data.state.unwrap().paused.unwrap());

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_kill() {
    let podman = init_runtime();

    let container_name = "test-kill-container";
    let container = create_base_container(&podman, container_name, None).await;

    let _ = container.start(None).await;

    let inspect_data = container
        .inspect()
        .await
        .expect("killed container inspect data");
    let state = inspect_data.state.unwrap();
    assert!(state.running.unwrap());

    let kill_result = container.kill().await;
    assert!(kill_result.is_ok());

    let inspect_data = container
        .inspect()
        .await
        .expect("killed container inspect data");
    let state = inspect_data.state.unwrap();
    assert!(!state.running.unwrap());

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_stop() {
    let podman = init_runtime();

    let container_name = "test-stop-container";
    let container = create_base_container(&podman, container_name, None).await;

    let _ = container.start(None).await;

    let inspect_data = container
        .inspect()
        .await
        .expect("stopped container inspect data");
    assert!(inspect_data.state.unwrap().running.unwrap());

    let stop_result = container.stop(&Default::default()).await;
    assert!(stop_result.is_ok());

    let inspect_data = container
        .inspect()
        .await
        .expect("stopped container inspect data");
    assert!(!inspect_data.state.unwrap().running.unwrap());

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_init() {
    let podman = init_runtime();

    let container_name = "test-init-container";
    let container = create_base_container(&podman, container_name, None).await;

    let inspect_data = container
        .inspect()
        .await
        .expect("init container inspect data before");
    assert!(["created", "configured"].contains(
        &inspect_data
            .state
            .unwrap()
            .status
            .unwrap()
            .to_lowercase()
            .as_str()
    ));

    let init_result = container.init().await;
    assert!(init_result.is_ok());

    let inspect_data = container
        .inspect()
        .await
        .expect("init container inspect data");
    assert_eq!(
        inspect_data.state.unwrap().status.unwrap().to_lowercase(),
        "initialized"
    );

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_mount_unmount() {
    let podman = init_runtime();

    let container_name = "test-mount-container";
    let container = create_base_container(&podman, container_name, None).await;

    let inspect_data = container
        .inspect()
        .await
        .expect("mount container inspect data before");
    assert!(["created", "configured"].contains(
        &inspect_data
            .state
            .unwrap()
            .status
            .unwrap()
            .to_lowercase()
            .as_str()
    ));

    let mount_result = container.mount().await;
    assert!(mount_result.is_ok());
    let mount_path = mount_result.unwrap();
    assert!(mount_path.exists());

    let unmount_result = container.unmount().await;
    assert!(unmount_result.is_ok());
    assert!(!mount_path.exists());

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_commit() {
    use podman_api::opts::ContainerCommitOpts;
    let podman = init_runtime();

    let container_name = "test-commit-container";
    let container = create_base_container(&podman, container_name, None).await;

    let author = "podman api rs";
    let commit_result = container
        .commit(
            &ContainerCommitOpts::builder()
                .author(author)
                .repo("test-commit-image")
                .tag("test")
                .build(),
        )
        .await;
    assert!(commit_result.is_ok());
    let image_name = "localhost/test-commit-image:test";

    let inspect_data = podman
        .images()
        .get(image_name)
        .inspect()
        .await
        .expect("commited image inspect data");
    assert_eq!(inspect_data.author.unwrap(), author);

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
#[ignore] // don't run by default
async fn container_checkpoint() {
    let podman = init_runtime();

    // must be run as root
    #[cfg(unix)]
    {
        assert!(nix::unistd::Uid::effective().is_root());
    }
    #[cfg(not(unix))]
    {
        return;
    }

    let container_name = "test-checkpoint-container";
    let container = create_base_container(&podman, container_name, None).await;
    let _ = container.start(None).await;

    let inspect_data = podman
        .containers()
        .get(container_name)
        .inspect()
        .await
        .expect("checkpointed container inspect data before");
    assert!(!inspect_data.state.unwrap().checkpointed.unwrap_or_default());

    let checkpoint_result = container.checkpoint(&Default::default()).await;
    assert!(checkpoint_result.is_ok());

    let inspect_data = podman
        .containers()
        .get(container_name)
        .inspect()
        .await
        .expect("checkpointed container inspect data");
    let state = inspect_data.state.unwrap();
    assert!(state.checkpointed.unwrap());
    assert!(!state.restored.unwrap_or_default());

    let restore_result = container.restore(&Default::default()).await;
    assert!(restore_result.is_ok());

    let inspect_data = podman
        .containers()
        .get(container_name)
        .inspect()
        .await
        .expect("checkpointed container inspect data after restore");
    let state = inspect_data.state.unwrap();
    assert!(!state.checkpointed.unwrap_or_default());
    assert!(state.restored.unwrap_or_default());

    // TODO: test checkpoint_export and restore_import

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_exec() {
    let podman = init_runtime();

    let container_name = "test-exec-container";
    let container = create_base_container(&podman, container_name, None).await;

    let _ = container.start(None).await;

    let exec_result = container
        .create_exec(
            &ExecCreateOpts::builder()
                .attach_stderr(true)
                .attach_stdout(true)
                .command([
                    "bash",
                    "-c",
                    "mkdir /tmp/test123 && echo 1234 >> /tmp/test123/testfile",
                ])
                .build(),
        )
        .await;
    assert!(exec_result.is_ok());
    let exec = exec_result.unwrap();
    let opts = Default::default();
    let mut exec_stream = exec.start(&opts);
    while exec_stream.next().await.is_some() {}

    let exec_inspect_result = exec.inspect().await;
    assert!(exec_inspect_result.is_ok());
    let exec_inspect_data = exec_inspect_result.unwrap();
    assert_eq!(exec_inspect_data.get("ExitCode").unwrap(), 0);

    let exec_result = container
        .create_exec(
            &ExecCreateOpts::builder()
                .attach_stderr(true)
                .attach_stdout(true)
                .command(["cat", "test123/testfile"])
                .working_dir("/tmp")
                .build(),
        )
        .await;
    assert!(exec_result.is_ok());
    let exec = exec_result.unwrap();
    let mut exec_stream = exec.start(&opts);
    let chunk = exec_stream.next().await;
    assert!(chunk.is_some());
    match chunk.unwrap() {
        Ok(TtyChunk::StdOut(chunk)) => {
            let testfile_content = String::from_utf8_lossy(&chunk);
            assert_eq!(testfile_content, "1234\n");
        }
        Ok(chunk) => {
            let fd = match chunk {
                TtyChunk::StdIn(_) => "stdin",
                TtyChunk::StdOut(_) => "stdOut",
                TtyChunk::StdErr(_) => "stderr",
            };
            let chunk = String::from_utf8_lossy(&chunk);
            eprintln!("invalid chunk, fd: {fd}, content: `{chunk:?}`");
            std::process::exit(1);
        }
        chunk => {
            eprintln!("invalid chunk {chunk:?}");
            std::process::exit(1);
        }
    }

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_copy_from() {
    let podman = init_runtime();

    let container_name = "test-copy-from-container";
    let container = create_base_container(&podman, container_name, None).await;

    let _ = container.start(None).await;

    let exec = container
        .create_exec(
            &ExecCreateOpts::builder()
                .attach_stderr(true)
                .attach_stdout(true)
                .command([
                    "bash",
                    "-c",
                    "mkdir /tmp/test123 && echo 1234 >> /tmp/test123/test-copy-from",
                ])
                .build(),
        )
        .await
        .expect("valid exec instance");
    let opts = Default::default();
    let mut exec_stream = exec.start(&opts);
    while exec_stream.next().await.is_some() {}

    let tar_stream = container.copy_from("/tmp/test123");
    let bytes = tar_stream.try_concat().await.expect("joined tarball bytes");
    let mut archive = tar::Archive::new(&bytes[..]);
    let tmp = tempdir::TempDir::new("test-copy-from").expect("temporary dir");
    archive.unpack(tmp.path()).unwrap();

    let local_path = tmp.path().join("test123").join("test-copy-from");
    assert!(local_path.exists());
    assert_eq!(std::fs::read_to_string(&local_path).unwrap(), "1234\n");

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_copy_file_into() {
    let podman = init_runtime();

    let container_name = "test-copy-file-into-container";
    let container = create_base_container(&podman, container_name, None).await;

    let _ = container.start(None).await;

    let data = b"12345";
    let copy_result = container.copy_file_into("/tmp/test-file", data).await;
    assert!(copy_result.is_ok());

    let exec = container
        .create_exec(
            &ExecCreateOpts::builder()
                .attach_stderr(true)
                .attach_stdout(true)
                .command(["cat", "/tmp/test-file"])
                .build(),
        )
        .await
        .expect("valid exec instance");
    let opts = Default::default();
    let mut exec_stream = exec.start(&opts);
    let chunk = exec_stream.next().await;
    assert!(chunk.is_some());
    match chunk.unwrap() {
        Ok(TtyChunk::StdOut(chunk)) => {
            assert_eq!(chunk, data);
        }
        chunk => {
            eprintln!("invalid chunk {chunk:?}");
            std::process::exit(1);
        }
    }

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_changes() {
    let podman = init_runtime();

    let container_name = "test-changes-container";
    let container = create_base_container(&podman, container_name, None).await;

    let _ = container.start(None).await;

    let exec = container
        .create_exec(
            &ExecCreateOpts::builder()
                .attach_stderr(true)
                .attach_stdout(true)
                .command([
                    "bash",
                    "-c",
                    "rm /etc/xattr.conf && echo 12345 >> /tmp/test-changes",
                ])
                .build(),
        )
        .await
        .expect("valid exec instance");

    let opts = Default::default();
    let mut exec_stream = exec.start(&opts);
    while exec_stream.next().await.is_some() {}

    use podman_api::models::ContainerChangeResponseItem;

    let changes = container
        .changes(&Default::default())
        .await
        .expect("container changes");
    assert!(changes.contains(&ContainerChangeResponseItem {
        kind: 0,
        path: "/tmp".into()
    }));
    assert!(changes.contains(&ContainerChangeResponseItem {
        kind: 1,
        path: "/tmp/test-changes".into()
    }));
    assert!(changes.contains(&ContainerChangeResponseItem {
        kind: 2,
        path: "/etc/xattr.conf".into()
    }));

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_logs() {
    let podman = init_runtime();

    let container_name = "test-logs-container";
    let container = create_base_container(
        &podman,
        container_name,
        Some(
            ContainerCreateOpts::builder()
                .name(container_name)
                .command(["bash", "-c", "echo 123456 && sleep inf"])
                .image(DEFAULT_IMAGE)
                .build(),
        ),
    )
    .await;

    let _ = container.start(None).await;

    use podman_api::opts::ContainerLogsOpts;

    let mut logs_stream = container.logs(
        &ContainerLogsOpts::builder()
            .stdout(true)
            .stderr(true)
            .build(),
    );
    let chunk = logs_stream.next().await;
    match chunk {
        Some(Ok(TtyChunk::StdOut(chunk))) => {
            let logs = String::from_utf8_lossy(&chunk);
            assert_eq!(logs, "123456\n");
        }
        chunk => {
            eprintln!("invalid chunk {chunk:?}");
            std::process::exit(1);
        }
    }

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_stats() {
    let podman = init_runtime();

    let container_name = "test-stats-container";
    let container = create_base_container(&podman, container_name, None).await;

    let _ = container.start(None).await;

    let stats_result = container.stats().await;
    assert!(stats_result.is_ok());

    const WANT: i32 = 5;
    let mut got = 0;
    let mut stats_stream = container.stats_stream(Some(1));

    for _ in 0..WANT {
        if let Some(chunk) = stats_stream.next().await {
            assert!(chunk.is_ok());
            got += 1;
        }
    }

    assert_eq!(got, WANT);

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_top() {
    use podman_api::opts::ContainerTopOpts;

    let podman = init_runtime();

    let container_name = "test-top-container";
    let container = create_base_container(&podman, container_name, None).await;

    let _ = container.start(None).await;

    let top_result = container.top(&Default::default()).await;
    assert!(top_result.is_ok());
    assert!(top_result.unwrap().processes[0].contains(&DEFAULT_CMD.to_string()));

    const WANT: i32 = 5;
    let mut got = 0;
    let mut top_stream = container.top_stream(&ContainerTopOpts::builder().delay(1).build());

    for _ in 0..WANT {
        if let Some(chunk) = top_stream.next().await {
            assert!(chunk.is_ok());
            assert!(chunk.unwrap().processes[0].contains(&DEFAULT_CMD.to_string()));

            got += 1;
        }
    }

    assert_eq!(got, WANT);

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_generate_kube_yaml() {
    let podman = init_runtime();

    let container_name = "test-kube-yaml-container";
    let container = create_base_container(&podman, container_name, None).await;

    let yaml = container.generate_kube_yaml(false).await;
    assert!(yaml.is_ok());
    let yaml = yaml.unwrap();
    assert!(yaml.contains("kind: Pod"));
    assert!(!yaml.contains("kind: Service"));

    let yaml_service = container.generate_kube_yaml(true).await;
    assert!(yaml_service.is_ok());
    let yaml_service = yaml_service.unwrap();
    assert!(yaml_service.contains("kind: Pod"));
    assert!(yaml_service.contains("kind: Service"));

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_generate_systemd_units() {
    let podman = init_runtime();

    let container_name = "test-systemd-units-container";
    let container = create_base_container(&podman, container_name, None).await;

    let full_id = get_container_full_id(&podman, container_name).await;

    let units = container.generate_systemd_units(&Default::default()).await;
    assert!(units.is_ok());
    let units = units.unwrap();

    let unit = units.get(format!("container-{full_id}"));
    assert!(unit.is_some());

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn container_healthcheck() {
    use podman_api::models::Schema2HealthConfig;
    let podman = init_runtime();

    let container_name = "test-healthcheck-container";
    let opts = ContainerCreateOpts::builder()
        .name(container_name)
        .image(DEFAULT_IMAGE)
        .command(DEFAULT_CMD_ARRAY)
        .health_config(Schema2HealthConfig {
            interval: Some(1),
            retries: None,
            start_period: Some(1),
            test: Some(vec![
                "CMD-SHELL".into(),
                "cat".into(),
                "/etc/xattr.conf".into(),
            ]),
            timeout: Some(1000000000),
        })
        .build();
    let container = create_base_container(&podman, container_name, Some(opts)).await;
    let _ = container.start(None).await;

    let healthcheck_result = container.healthcheck().await;
    assert!(healthcheck_result.is_ok());
    assert_eq!(
        healthcheck_result.unwrap().status.unwrap_or_default(),
        "healthy"
    );

    let exec = container
        .create_exec(
            &ExecCreateOpts::builder()
                .command(["rm", "/etc/xattr.conf"])
                .build(),
        )
        .await
        .expect("valid exec instance");

    let opts = Default::default();
    let mut exec_stream = exec.start(&opts);
    while exec_stream.next().await.is_some() {}

    tokio::time::sleep(std::time::Duration::from_secs(2)).await;

    // This is what I'd expect after removing the file but it doesn't work
    //let healthcheck_result = container.healthcheck().await;
    //assert!(healthcheck_result.is_ok());
    //assert_eq!(
    //healthcheck_result.unwrap().status.unwrap_or_default(),
    //"unhealthy"
    //);

    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn containers_list() {
    use podman_api::opts::{ContainerListFilter, ContainerListOpts};
    let podman = init_runtime();

    let container_name = "test-list-container";
    let opts = ContainerCreateOpts::builder()
        .command(DEFAULT_CMD_ARRAY)
        .image(DEFAULT_IMAGE)
        .labels([("test-podman-list", "value")])
        .name(container_name)
        .build();

    let second_name = "test-list-second-container";
    let second_opts = ContainerCreateOpts::builder()
        .command(DEFAULT_CMD_ARRAY)
        .image(DEFAULT_IMAGE)
        .labels([("test-podman-list", "value2")])
        .name(second_name)
        .build();
    create_base_container(&podman, container_name, Some(opts)).await;
    create_base_container(&podman, second_name, Some(second_opts)).await;

    let filter = ContainerListFilter::Name(container_name.to_string());
    let opts = ContainerListOpts::builder()
        .all(true)
        .filter([filter])
        .build();
    let list_result = podman.containers().list(&opts).await;
    assert!(list_result.is_ok());
    let list_data = list_result.unwrap();
    assert_eq!(list_data.len(), 1);
    assert!(list_data[0]
        .names
        .as_ref()
        .unwrap()
        .contains(&container_name.to_string()));

    let filter = ContainerListFilter::LabelKey("test-podman-list".into());
    let opts = ContainerListOpts::builder()
        .all(true)
        .filter([filter])
        .build();
    let list_result = podman.containers().list(&opts).await;
    assert!(list_result.is_ok());
    let list_data = list_result.unwrap();
    assert_eq!(list_data.len(), 2);

    let filter = ContainerListFilter::LabelKeyVal("test-podman-list".into(), "value".into());
    let opts = ContainerListOpts::builder()
        .all(true)
        .filter([filter])
        .build();
    let list_result = podman.containers().list(&opts).await;
    assert!(list_result.is_ok());
    let list_data = list_result.unwrap();
    assert_eq!(list_data.len(), 1);
    assert!(list_data[0]
        .names
        .as_ref()
        .unwrap()
        .contains(&container_name.to_string()));

    let filter = ContainerListFilter::LabelKeyVal("test-podman-list".into(), "value2".into());
    let opts = ContainerListOpts::builder()
        .all(true)
        .filter([filter])
        .build();
    let list_result = podman.containers().list(&opts).await;
    assert!(list_result.is_ok());
    let list_data = list_result.unwrap();
    assert_eq!(list_data.len(), 1);
    assert!(list_data[0]
        .names
        .as_ref()
        .unwrap()
        .contains(&second_name.to_string()));

    let container = podman.containers().get(container_name);
    let _ = container.start(None).await;
    let full_id = get_container_full_id(&podman, container_name).await;

    let mount_result = container.mount().await;
    assert!(mount_result.is_ok());
    let mount_path = mount_result.unwrap();
    assert!(mount_path.exists());

    let list_mounted_result = podman.containers().list_mounted().await;
    assert!(list_mounted_result.is_ok());
    let list_mounted_data = list_mounted_result.unwrap();
    assert_eq!(
        list_mounted_data.get(full_id).unwrap().as_str().unwrap(),
        mount_path.to_string_lossy()
    );
    container.unmount().await.unwrap();

    cleanup_container(&podman, container_name).await;
    cleanup_container(&podman, second_name).await;
}

#[tokio::test]
async fn containers_prune() {
    use podman_api::opts::{ContainerPruneFilter, ContainerPruneOpts};
    let podman = init_runtime();

    let container_name = "test-prune-container";
    let opts = ContainerCreateOpts::builder()
        .command(DEFAULT_CMD_ARRAY)
        .image(DEFAULT_IMAGE)
        .labels([("test-podman-prune", "value")])
        .name(container_name)
        .build();

    let second_name = "test-prune-second-container";
    let second_opts = ContainerCreateOpts::builder()
        .command(DEFAULT_CMD_ARRAY)
        .image(DEFAULT_IMAGE)
        .labels([("test-podman-prune", "value2")])
        .name(second_name)
        .build();

    create_base_container(&podman, container_name, Some(opts.clone())).await;
    create_base_container(&podman, second_name, Some(second_opts.clone())).await;
    let full_id = get_container_full_id(&podman, container_name).await;
    let second_full_id = get_container_full_id(&podman, second_name).await;

    let filter = ContainerPruneFilter::LabelKey("test-podman-prune".into());
    let prune_opts = ContainerPruneOpts::builder().filter([filter]).build();
    let prune_result = podman.containers().prune(&prune_opts).await;
    assert!(prune_result.is_ok());
    let prune_data = prune_result.unwrap();
    assert_eq!(prune_data.len(), 2);
    assert!(prune_data
        .iter()
        .any(|report| report.id.as_deref().unwrap_or_default() == full_id));
    assert!(prune_data
        .iter()
        .any(|report| report.id.as_deref().unwrap_or_default() == second_full_id));

    create_base_container(&podman, container_name, Some(opts.clone())).await;
    create_base_container(&podman, second_name, Some(second_opts.clone())).await;
    let full_id = get_container_full_id(&podman, container_name).await;
    let second_full_id = get_container_full_id(&podman, second_name).await;

    let filter = ContainerPruneFilter::LabelKeyVal("test-podman-prune".into(), "value".into());
    let prune_opts = ContainerPruneOpts::builder().filter([filter]).build();
    let prune_result = podman.containers().prune(&prune_opts).await;
    assert!(prune_result.is_ok());
    let prune_data = prune_result.unwrap();
    assert_eq!(prune_data.len(), 1);
    assert!(prune_data
        .iter()
        .any(|report| report.id.as_deref().unwrap_or_default() == full_id));

    let filter = ContainerPruneFilter::LabelKeyVal("test-podman-prune".into(), "value2".into());
    let prune_opts = ContainerPruneOpts::builder().filter([filter]).build();
    let prune_result = podman.containers().prune(&prune_opts).await;
    assert!(prune_result.is_ok());
    let prune_data = prune_result.unwrap();
    assert_eq!(prune_data.len(), 1);
    assert!(prune_data
        .iter()
        .any(|report| report.id.as_deref().unwrap_or_default() == second_full_id));
}

#[tokio::test]
async fn containers_stats() {
    use podman_api::opts::ContainerStatsOpts;
    let podman = init_runtime();
    let containers = podman.containers();

    let container_name = "test-stats-multiple-container";
    let container = create_base_container(&podman, container_name, None).await;
    let second_name = "test-stats-multiple-container2";
    let second_container = create_base_container(&podman, second_name, None).await;

    let _ = container.start(None).await;
    let _ = second_container.start(None).await;

    let opts = ContainerStatsOpts::builder()
        .containers([container_name, second_name])
        .interval(1)
        .build();
    let stats_result = containers.stats(&opts).await;
    assert!(stats_result.is_ok());

    const WANT: i32 = 5;
    let mut got = 0;
    let mut stats_stream = containers.stats_stream(&opts);

    for _ in 0..WANT {
        if let Some(chunk) = stats_stream.next().await {
            assert!(chunk.is_ok());
            got += 1;
        }
    }

    assert_eq!(got, WANT);

    cleanup_container(&podman, container_name).await;
    cleanup_container(&podman, second_name).await;
}
