firkin 0.0.3

Facade crate for the firkin Rust containerization library
//! Facade re-export tests.

use firkin::e2b::{
    ConnectRequest, ConnectedSandbox, LocalSandboxRegistry, LocalTemplateRegistry,
    SandboxCreateRequest, SandboxRoutes, SandboxRuntimeConfig, SandboxState as E2bSandboxState,
    TemplateBuildRequest,
};
use firkin::{
    BlankDiskImage, BootLog, Capability, Container, DiskImageFormat, DnsConfig, EmptyDirMedium,
    EmptyDirVolume, ExecConfig, FileMount, GuestFilesystem, GuestPath, Hostname, HostsConfig,
    HostsEntry, KernelImage, LinuxCapabilities, LinuxRlimit, MaterializedRootfs, Mount,
    NetworkPolicyRule, PodBuilder, PodContainerSpec, PodRootfsSource, PodStoreSpec, PodVolumeMount,
    PortSandboxHost, RlimitKind, Rootfs, RuntimeCapability, SandboxNetworkPolicy, Seccomp,
    SeccompAction, Size, UnixSocketConfig, User, VirtiofsTag, VsockPort,
    apple_local_runtime_capabilities, create_blank_disk_image, materialize_rootfs_in_pod_store,
};
use std::net::{IpAddr, Ipv4Addr};
use std::num::NonZeroU32;

#[test]
fn facade_exports_single_node_runtime_surface() {
    let config = firkin::runtime::single_node::SingleNodeConfig::new(
        "/tmp/firkin-single-node",
        "cube.localhost",
    );
    assert_eq!(config.domain(), "cube.localhost");
    assert_eq!(config.minimum_free_disk(), Size::gib(10));
}

#[tokio::test]
async fn facade_exports_sandbox_public_surface() {
    use firkin::sandbox::apple_vz::AppleVzBackend;
    use firkin::sandbox::{DataPlaneSpec, Runtime, SandboxSpec, TemplateSpec};

    let backend = AppleVzBackend::from_config(firkin::sandbox::apple_vz::SingleNodeConfig::new(
        "/tmp/firkin-sandbox-facade",
        "cube.localhost",
    ));
    let runtime = Runtime::build(backend).await.expect("runtime");
    let template = runtime
        .templates()
        .prepare(TemplateSpec::oci("example").data_plane(DataPlaneSpec::none()))
        .await
        .expect("template");
    let spec = SandboxSpec::from_template(&template);

    assert_eq!(template.id().as_str(), "example");
    assert!(matches!(spec, SandboxSpec::Template { .. }));
}

#[test]
fn facade_exports_primary_builder_surface() {
    let builder = Container::builder("worker")
        .expect("builder")
        .memory(Size::mib(512))
        .rootfs(Rootfs::raw_block("/tmp/rootfs.img"));

    assert_eq!(builder.id().as_str(), "worker");
    assert_eq!(builder.memory_size(), Size::mib(512));

    let exec = ExecConfig::builder()
        .command(["/bin/echo", "hello"])
        .build();
    assert!(format!("{exec:?}").contains("/bin/echo"));

    let image = BlankDiskImage::asif("/tmp/pod-store.asif", Size::mib(64));
    assert_eq!(image.format(), DiskImageFormat::Asif);
    let _: fn(&BlankDiskImage) -> firkin::vmm::Result<()> = create_blank_disk_image;

    let guest_path = GuestPath::new("/run/firkin/pod-store").expect("guest path");
    assert_eq!(guest_path.as_str(), "/run/firkin/pod-store");
    let store = PodStoreSpec::ext4(firkin::types::BlockDeviceId::from_slot(
        NonZeroU32::new(1).expect("slot"),
    ));
    assert_eq!(store.filesystem(), GuestFilesystem::Ext4);

    let empty_dir = EmptyDirVolume::disk("work").expect("emptyDir");
    assert_eq!(empty_dir.medium(), EmptyDirMedium::Disk);
    let rootfs = GuestPath::new("/run/firkin/pod-store/pods/p/rootfs/c").expect("rootfs");
    let materialized = MaterializedRootfs::new(rootfs.clone(), Some("sha256:test".to_owned()));
    assert_eq!(materialized.path(), &rootfs);
    let container = PodContainerSpec::new("agent", PodRootfsSource::guest_path(rootfs.clone()))
        .expect("pod container")
        .empty_dir_mount("work", "/work")
        .expect("emptyDir mount");
    assert_eq!(
        container.empty_dir_mounts()[0].container_path(),
        std::path::Path::new("/work")
    );
    let mount = PodVolumeMount::read_write("work", "/work").expect("volume mount");
    assert_eq!(mount.volume_name(), "work");
    let pod = PodBuilder::new(
        "pod",
        firkin::vmm::VmConfig::builder().build().unwrap(),
        store,
    )
    .unwrap()
    .empty_dir(empty_dir)
    .container(container);
    assert_eq!(pod.containers().len(), 1);
    let exported_fn = materialize_rootfs_in_pod_store;
    assert_eq!(std::mem::size_of_val(&exported_fn), 0);
}

#[test]
fn prelude_exports_primary_builder_surface() {
    use firkin::prelude::*;

    let builder = Container::builder("worker")
        .expect("builder")
        .rootfs(Rootfs::ext4_image("/tmp/rootfs.ext4"));

    assert_eq!(builder.id().as_str(), "worker");

    let _: RuntimeCapabilities = apple_local_runtime_capabilities();
    let image = BlankDiskImage::raw("/tmp/pod-store.raw", Size::mib(64));
    assert_eq!(image.format(), DiskImageFormat::Raw);
    let _: fn(&BlankDiskImage) -> firkin::vmm::Result<()> = create_blank_disk_image;
    let exported_mount_fn = mount_pod_store;
    let exported_materialize_fn = materialize_rootfs_in_pod_store;
    assert_eq!(std::mem::size_of_val(&exported_mount_fn), 0);
    assert_eq!(std::mem::size_of_val(&exported_materialize_fn), 0);
    let empty_dir = EmptyDirVolume::disk("work").expect("emptyDir");
    assert_eq!(empty_dir.name(), "work");
}

#[test]
fn facade_exports_apple_local_runtime_capabilities() {
    let capabilities = apple_local_runtime_capabilities();

    assert_eq!(capabilities.backend(), "apple-vz");
    assert!(
        capabilities
            .supported()
            .contains(&RuntimeCapability::supported(
                "create-linux-container",
                None
            ))
    );
    assert!(capabilities.supports("create-linux-container"));
    assert!(
        capabilities
            .require_supported("create-linux-container")
            .is_ok()
    );

    let unsupported_names = capabilities
        .unsupported()
        .iter()
        .map(|capability| capability.name())
        .collect::<Vec<_>>();
    assert_eq!(
        unsupported_names,
        vec![
            "e2b-envd-compatible-api",
            "snapshot-backed-pause-resume-connect",
            "e2b-network-policy",
            "domain-host-proxy",
            "template-build-service",
        ]
    );

    for capability in capabilities.unsupported() {
        assert!(!capability.is_supported());
        assert!(capability.reason().is_some_and(|reason| !reason.is_empty()));
    }

    let error = capabilities
        .require_supported("domain-host-proxy")
        .expect_err("unsupported capability");
    assert_eq!(error.name(), "domain-host-proxy");
    assert!(error.reason().contains("{port}-{sandboxID}.domain proxy"));
    assert!(error.to_string().contains("domain-host-proxy"));
}

#[test]
fn facade_exports_process_policy_types() {
    let builder = Container::builder("worker")
        .expect("builder")
        .user(User::from(1000))
        .capabilities(LinuxCapabilities::single_set(vec![Capability::Chown]))
        .rlimit(LinuxRlimit::new(RlimitKind::OpenFiles, 2048, 1024))
        .rootfs(Rootfs::ext4_image("/tmp/rootfs.ext4"));

    let process = builder.runtime_spec().unwrap().process.unwrap();

    assert_eq!(
        process.capabilities.unwrap().effective,
        Some(vec!["CAP_CHOWN".to_owned()])
    );
    assert_eq!(process.user.uid, 1000);
    assert_eq!(process.user.gid, 1000);
    assert_eq!(process.rlimits[0].kind, "RLIMIT_NOFILE");
}

#[test]
fn facade_exports_mount_surface() {
    let builder = Container::builder("worker")
        .expect("builder")
        .default_mounts(vec![Mount::proc("/proc")])
        .mount(Mount::tmpfs("/scratch"))
        .rootfs(Rootfs::ext4_image("/tmp/rootfs.ext4"));

    let spec = builder.runtime_spec().unwrap();

    assert_eq!(
        spec.mounts
            .iter()
            .map(|mount| mount.destination.as_str())
            .collect::<Vec<_>>(),
        vec!["/proc", "/scratch"]
    );
}

#[test]
fn facade_exports_use_init_surface() {
    let builder = Container::builder("worker")
        .expect("builder")
        .command(["/bin/true"])
        .use_init(true)
        .rootfs(Rootfs::ext4_image("/tmp/rootfs.ext4"));

    assert_eq!(
        builder.runtime_spec().unwrap().process.unwrap().args,
        vec!["/.cz-init", "--", "/bin/true"]
    );
}

#[test]
fn facade_exports_seccomp_surface() {
    let builder = Container::builder("worker")
        .expect("builder")
        .seccomp(Seccomp {
            default_action: SeccompAction::Allow,
            default_errno_ret: None,
            architectures: Vec::new(),
            flags: Vec::new(),
            listener_path: String::new(),
            listener_metadata: String::new(),
            syscalls: Vec::new(),
        })
        .rootfs(Rootfs::ext4_image("/tmp/rootfs.ext4"));

    assert!(
        builder
            .runtime_spec()
            .unwrap()
            .linux
            .unwrap()
            .seccomp
            .is_some()
    );
}

#[test]
fn facade_exports_dns_and_hosts_surface() {
    let builder = Container::builder("worker")
        .expect("builder")
        .dns(DnsConfig {
            nameservers: vec![IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1))],
            domain: None,
            search: Vec::new(),
            options: Vec::new(),
        })
        .hosts(HostsConfig {
            entries: vec![HostsEntry {
                ip: IpAddr::V4(Ipv4Addr::LOCALHOST),
                hostnames: vec!["localhost".to_owned()],
                comment: None,
            }],
            comment: None,
        })
        .rootfs(Rootfs::ext4_image("/tmp/rootfs.ext4"));

    assert_eq!(builder.id().as_str(), "worker");
}

#[test]
fn facade_exports_socket_relay_surface() {
    let builder = Container::builder("worker")
        .expect("builder")
        .default_mounts(Vec::new())
        .socket(UnixSocketConfig::into_guest(
            "api",
            "/tmp/api.sock",
            "/run/api.sock",
        ))
        .rootfs(Rootfs::ext4_image("/tmp/rootfs.ext4"));

    let spec = builder.runtime_spec().unwrap();

    assert_eq!(
        spec.mounts[0].source,
        "/run/container/worker/sockets/api.sock"
    );
}

#[test]
fn facade_exports_file_mount_surface() {
    let source_dir = tempfile::tempdir().unwrap();
    let source = source_dir.path().join("config.json");
    std::fs::write(&source, b"{}").unwrap();
    let builder = Container::builder("worker")
        .expect("builder")
        .default_mounts(Vec::new())
        .file_mount(FileMount::read_only(&source, "/etc/app/config.json"))
        .rootfs(Rootfs::ext4_image("/tmp/rootfs.ext4"));

    let spec = builder.runtime_spec().unwrap();

    assert_eq!(spec.mounts[0].destination, "/etc/app/config.json");
}

#[test]
fn facade_exports_implicit_vm_config_types() {
    let builder = Container::builder("worker")
        .expect("builder")
        .cpus(NonZeroU32::new(2).unwrap())
        .virtiofs_share(VirtiofsTag::new("shared").unwrap(), "/tmp")
        .boot_log(BootLog::None)
        .kernel(KernelImage::from_file("/tmp/vmlinux"))
        .rootfs(Rootfs::ext4_image("/tmp/rootfs.ext4"));

    assert_eq!(builder.cpu_count().get(), 2);
}

#[test]
fn facade_exports_container_vsock_surface() {
    async fn calls_dial_vsock(container: &Container) {
        let _ = container.dial_vsock(VsockPort::new(2222)).await;
    }

    let _ = calls_dial_vsock;
}

#[test]
fn facade_exports_proxy_host_route_type() {
    let domain = Hostname::new("cube.localhost").unwrap();
    let route = PortSandboxHost::parse_for_domain("49983-sbx.cube.localhost", &domain).unwrap();

    assert_eq!(route.to_string(), "49983-sbx.cube.localhost");
}

#[test]
fn facade_exports_e2b_network_policy_shape() {
    let rule = NetworkPolicyRule::new("api.example.com").unwrap();
    let policy = SandboxNetworkPolicy::new(Some(true), [rule], [], Some(true), None);

    assert_eq!(policy.allow_internet_access(), Some(true));
    assert_eq!(policy.allow_public_traffic(), Some(true));
    assert!(policy.requires_policy_engine());
}

#[test]
fn facade_exports_e2b_control_plane_contract_types() {
    let create = SandboxCreateRequest::default();
    let connected = ConnectedSandbox {
        sandbox_id: "sbx_123".to_owned(),
        envd_version: "0.5.7".to_owned(),
        envd_access_token: None,
        traffic_access_token: None,
        domain: Some("cube.localhost".to_owned()),
    };

    assert_eq!(create.template_id, "base");
    assert_eq!(connected.sandbox_id, "sbx_123");
    assert_eq!(ConnectRequest { timeout: 300 }.timeout, 300);
    assert_eq!(E2bSandboxState::Running, E2bSandboxState::Running);
    assert_eq!(SandboxRoutes::pause("sbx_123"), "/sandboxes/sbx_123/pause");

    let mut registry = LocalSandboxRegistry::new();
    registry
        .create(
            create,
            SandboxRuntimeConfig {
                sandbox_id: "sbx_123".to_owned(),
                domain: "cube.localhost".to_owned(),
                envd_version: "0.5.7".to_owned(),
                envd_access_token: None,
                traffic_access_token: None,
                started_at: "2026-05-03T12:00:00Z".to_owned(),
                end_at: "2026-05-03T12:05:00Z".to_owned(),
                cpu_count: 2,
                memory_mb: 1024,
            },
        )
        .unwrap();
    assert_eq!(
        registry.get("sbx_123").unwrap().state,
        E2bSandboxState::Running
    );

    let mut templates = LocalTemplateRegistry::new("2026-05-03T12:00:00Z");
    let requested = templates.request_build(TemplateBuildRequest {
        name: Some("agent-template".to_owned()),
        ..TemplateBuildRequest::default()
    });
    assert_eq!(requested.template_id, "tpl_1");
}