durable-actors 0.12.2

Standalone regional durable-actors control plane, host, and durability runtime
use super::*;
use crate::host::HostId;

#[tokio::test]
async fn assignment_is_authenticated_single_use_and_waits_for_readiness() -> anyhow::Result<()> {
    for with_operation in [false, true] {
        let (send, receive) = tokio::sync::oneshot::channel();
        let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await?;
        let url = format!("http://{}/assign", listener.local_addr()?);
        let server = tokio::spawn(async move {
            axum::serve(listener, router("secret".into(), send))
                .await
                .unwrap();
        });
        let client = reqwest::Client::new();
        let health = client
            .get(url.replace("/assign", "/healthz"))
            .send()
            .await?;
        assert_eq!(health.status(), 200);
        let response = client
            .post(&url)
            .json(&serde_json::json!({}))
            .send()
            .await?;
        assert_eq!(response.status(), 401);
        let actor =
            serde_json::json!({"project_id":"project", "actor_name":"Counter", "actor_id":"one"});
        let environment =
            serde_json::json!({"actor":"one", "DURABLE_ACTORS_ACTOR":actor.to_string()});
        let body = if with_operation {
            let mut invalid = serde_json::json!({"environment":environment, "operation":{
                "routingMs":1.0, "request":{"type":"invoke", "invocation":{
                    "actor":actor, "request_id":"first", "method":"read", "args":[]
                }, "grant":null}
            }});
            invalid["operation"]["request"]["invocation"]["actor"]["actor_id"] = "other".into();
            assert_eq!(
                client
                    .post(&url)
                    .bearer_auth("secret")
                    .json(&invalid)
                    .send()
                    .await?
                    .status(),
                400
            );
            invalid["operation"]["request"]["invocation"]["actor"]["actor_id"] = "one".into();
            invalid
        } else {
            environment
        };
        let request = client.post(&url).bearer_auth("secret").json(&body);
        let call = tokio::spawn(async move { request.send().await });
        let assigned = tokio::time::timeout(std::time::Duration::from_secs(1), receive).await??;
        assert_eq!(assigned.environment["actor"], "one");
        assert_eq!(assigned.operation.is_some(), with_operation);
        assert!(
            !call.is_finished(),
            "assignment must wait for hydration and ownership"
        );
        let duplicate = client
            .post(&url)
            .bearer_auth("secret")
            .json(&serde_json::json!({}))
            .send()
            .await?;
        assert_eq!(duplicate.status(), 409);
        assert!(
            assigned
                .ready
                .send(Ok(super::super::process::HostReadiness {
                    initial_reply: with_operation
                        .then(|| serde_json::json!({"type":"completed", "result":7})),
                    host_id: HostId::new("host.v3.test.one"),
                    session_id: "session".into(),
                    route: "https://host.test".into(),
                    canonical_region: "north-america-east".into(),
                    owner_epoch: 42,
                    lease: crate::host_leases::HostLease {
                        id: HostId::new("host.v3.test.one"),
                        session_id: "session".into(),
                        route: "https://host.test".into(),
                        expires_at_ms: 60_000,
                    },
                }))
                .is_ok()
        );
        let reply = call.await??;
        assert_eq!(reply.status(), 200);
        let reply = reply.json::<serde_json::Value>().await?;
        assert_eq!(reply["ownerEpoch"], 42);
        if with_operation {
            assert_eq!(reply["initialReply"]["result"], 7);
        }
        server.abort();
    }
    Ok(())
}

#[tokio::test]
async fn failed_initialization_never_reports_ready() -> anyhow::Result<()> {
    let (send, receive) = tokio::sync::oneshot::channel();
    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await?;
    let url = format!("http://{}/assign", listener.local_addr()?);
    let server = tokio::spawn(async move {
        axum::serve(listener, router("secret".into(), send))
            .await
            .unwrap();
    });
    let request = reqwest::Client::new()
        .post(url)
        .bearer_auth("secret")
        .json(&serde_json::json!({}));
    let call = tokio::spawn(async move { request.send().await });
    drop(receive.await?);
    assert_eq!(call.await??.status(), 503);
    server.abort();
    Ok(())
}