mobius-gateway 0.15.37

Headless authenticated gateway for möbius frontends
Documentation
use super::*;
use serde_json::json;

#[tokio::test]
async fn desktop_registration_rejects_remote_and_non_macos_clients() {
    use crate::wire::{ClientKind, ClientMessage, FrameReader, ServerFrame, read_frame};

    for (local, kind) in [(false, ClientKind::Macos), (true, ClientKind::Ios)] {
        let control = Arc::new(DesktopControl::default());
        let mut connection = None;
        let (reader, mut writer) = tokio::io::duplex(4096);
        handle_message(
            ClientMessage::SetDesktopRuntime {
                request_id: "register".into(),
                enabled: true,
            },
            &control,
            &mut connection,
            local,
            kind,
            &mut writer,
        )
        .await
        .expect("registration handled");
        let frame = read_frame::<ServerFrame>(&mut FrameReader::new(reader))
            .await
            .expect("response frame")
            .expect("registration response");
        assert!(
            matches!(frame.message, ServerMessage::Rejected { request_id, code, fatal: false, .. }
            if request_id == "register" && code == "desktop_control")
        );
        assert!(connection.is_none());
        assert!(control.connect("session").is_err());
    }
}

#[tokio::test]
async fn desktop_serializes_evaluations_and_rejects_stale_replies() {
    let control = Arc::new(DesktopControl::default());
    assert!(control.connect("session").is_err());
    let mut app = control.attach().expect("local runtime");
    assert!(control.attach().is_err());
    let mut worker = control.connect("session").expect("evaluation");
    assert!(control.connect("other-session").is_err());
    let request = serde_json::to_vec(&json!({"action": "apps"})).expect("request");
    worker.write_u32(request.len() as u32).await.expect("size");
    worker.write_all(&request).await.expect("request");
    let Some(ServerMessage::DesktopControlRequested {
        request_id,
        execution_id,
        session_id,
        request,
    }) = app.outgoing.recv().await
    else {
        panic!("desktop request");
    };
    assert_eq!(session_id, "session");
    assert_eq!(request, json!({"action": "apps"}));
    assert!(app.reply("stale", json!({"result": []})).is_err());
    app.reply(&request_id, json!({"result": []}))
        .expect("reply");
    let size = worker.read_u32().await.expect("reply size") as usize;
    let mut reply = vec![0; size];
    worker.read_exact(&mut reply).await.expect("reply");
    assert_eq!(
        serde_json::from_slice::<Value>(&reply).expect("JSON"),
        json!({"result": []})
    );
    assert!(app.reply(&request_id, json!({"result": []})).is_err());
    drop(worker);
    assert!(
        matches!(app.outgoing.recv().await, Some(ServerMessage::DesktopControlEnded { execution_id: id }) if id == execution_id)
    );
    let worker = control.connect("next").expect("released lease");
    drop(worker);
}

#[tokio::test]
async fn disconnect_cancels_pending_desktop_request() {
    let control = Arc::new(DesktopControl::default());
    let mut app = control.attach().expect("runtime");
    let mut worker = control.connect("session").expect("evaluation");
    let request = br#"{"action":"apps"}"#;
    worker.write_u32(request.len() as u32).await.expect("size");
    worker.write_all(request).await.expect("request");
    assert!(matches!(
        app.outgoing.recv().await,
        Some(ServerMessage::DesktopControlRequested { .. })
    ));
    drop(app);
    assert!(worker.read_u32().await.is_err());
    assert!(control.connect("session").is_err());
}

#[tokio::test]
async fn workspace_policy_cannot_reach_the_native_runtime() {
    use mobius::backend::sandbox::{SandboxBackend, SandboxMode};
    let directory = tempfile::tempdir().expect("directory");
    let workspace = directory.path().join("workspace");
    std::fs::create_dir(&workspace).expect("workspace");
    std::fs::create_dir(directory.path().join("state")).expect("state");
    let sandbox = crate::sandbox::GatewaySandbox::new(
        &workspace,
        &directory.path().join("state"),
        None,
        std::time::Duration::from_secs(5),
    )
    .expect("sandbox")
    .with_desktop(Arc::new(DesktopControl::default()));
    assert!(
        sandbox
            .worker_connection("session", SandboxMode::WorkspaceWrite)
            .await
            .is_err()
    );
}