rmux-server 0.1.1

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use super::*;

#[tokio::test]
async fn move_window_across_sessions_migrates_the_terminal_ownership_map() {
    let handler = RequestHandler::new();
    let alpha = session_name("alpha");
    let beta = session_name("beta");
    create_session(&handler, "alpha").await;
    create_session(&handler, "beta").await;
    insert_window(&handler, &alpha, 1).await;

    let moved_pane_id = {
        let state = handler.state.lock().await;
        state
            .sessions
            .session(&alpha)
            .expect("alpha should exist")
            .window_at(1)
            .expect("window 1 should exist")
            .pane(0)
            .expect("pane 0 should exist")
            .id()
    };

    let response = handler
        .handle(Request::MoveWindow(MoveWindowRequest {
            source: Some(WindowTarget::with_window(alpha.clone(), 1)),
            target: MoveWindowTarget::Window(WindowTarget::with_window(beta.clone(), 4)),
            renumber: false,
            kill_destination: false,
            detached: true,
        }))
        .await;

    assert_eq!(
        response,
        Response::MoveWindow(rmux_proto::MoveWindowResponse {
            session_name: beta.clone(),
            target: Some(WindowTarget::with_window(beta.clone(), 4)),
        })
    );

    let state = handler.state.lock().await;
    let alpha_session = state.sessions.session(&alpha).expect("alpha should exist");
    let beta_session = state.sessions.session(&beta).expect("beta should exist");
    assert_eq!(
        alpha_session.windows().keys().copied().collect::<Vec<_>>(),
        vec![0]
    );
    assert_eq!(
        beta_session.windows().keys().copied().collect::<Vec<_>>(),
        vec![0, 4]
    );
    assert_eq!(
        beta_session
            .window_at(4)
            .and_then(|window| window.pane(0))
            .map(|pane| pane.id()),
        Some(moved_pane_id)
    );
    state
        .pane_profile_in_window(&beta, 4, 0)
        .expect("moved pane terminal should exist in the destination session");
    assert_eq!(
        state.pane_profile_in_window(&alpha, 1, 0).unwrap_err(),
        rmux_proto::RmuxError::invalid_target("alpha:1", "window index does not exist in session")
    );
}

#[tokio::test]
async fn move_window_within_session_restores_the_killed_destination_when_resize_fails() {
    let handler = RequestHandler::new();
    let alpha = session_name("alpha");
    create_session(&handler, "alpha").await;
    insert_window(&handler, &alpha, 1).await;

    let (source_pane_id, destination_pane_id) = {
        let state = handler.state.lock().await;
        let session = state.sessions.session(&alpha).expect("alpha should exist");
        (
            session
                .window_at(0)
                .and_then(|window| window.pane(0))
                .map(|pane| pane.id())
                .expect("window 0 pane should exist"),
            session
                .window_at(1)
                .and_then(|window| window.pane(0))
                .map(|pane| pane.id())
                .expect("window 1 pane should exist"),
        )
    };

    {
        let mut state = handler.state.lock().await;
        state.fail_next_resize_for_test();
    }

    let response = handler
        .handle(Request::MoveWindow(MoveWindowRequest {
            source: Some(WindowTarget::with_window(alpha.clone(), 0)),
            target: MoveWindowTarget::Window(WindowTarget::with_window(alpha.clone(), 1)),
            renumber: false,
            kill_destination: true,
            detached: true,
        }))
        .await;

    assert_eq!(
        response,
        Response::Error(rmux_proto::ErrorResponse {
            error: rmux_proto::RmuxError::Server(
                "injected pane terminal resize failure".to_owned()
            ),
        })
    );

    let state = handler.state.lock().await;
    let session = state.sessions.session(&alpha).expect("alpha should exist");
    assert_eq!(
        session.windows().keys().copied().collect::<Vec<_>>(),
        vec![0, 1]
    );
    assert_eq!(session.pane_id_in_window(0, 0), Some(source_pane_id));
    assert_eq!(session.pane_id_in_window(1, 0), Some(destination_pane_id));
    state
        .pane_profile_in_window(&alpha, 0, 0)
        .expect("source pane terminal should be restored");
    state
        .pane_profile_in_window(&alpha, 1, 0)
        .expect("destination pane terminal should be restored");
}

#[tokio::test]
async fn move_window_reindex_compacts_sparse_window_indices() {
    let handler = RequestHandler::new();
    let alpha = session_name("alpha");
    create_session(&handler, "alpha").await;
    insert_window(&handler, &alpha, 3).await;
    insert_window(&handler, &alpha, 7).await;

    let response = handler
        .handle(Request::MoveWindow(MoveWindowRequest {
            source: None,
            target: MoveWindowTarget::Session(alpha.clone()),
            renumber: true,
            kill_destination: false,
            detached: true,
        }))
        .await;

    assert_eq!(
        response,
        Response::MoveWindow(rmux_proto::MoveWindowResponse {
            session_name: alpha.clone(),
            target: None,
        })
    );

    let state = handler.state.lock().await;
    let session = state.sessions.session(&alpha).expect("alpha should exist");
    assert_eq!(
        session.windows().keys().copied().collect::<Vec<_>>(),
        vec![0, 1, 2]
    );
}

#[tokio::test]
async fn move_window_across_sessions_restores_terminal_ownership_when_resize_fails() {
    let handler = RequestHandler::new();
    let alpha = session_name("alpha");
    let beta = session_name("beta");
    create_session(&handler, "alpha").await;
    create_session(&handler, "beta").await;
    insert_window(&handler, &alpha, 1).await;
    insert_window(&handler, &beta, 4).await;

    let (moved_pane_id, replaced_pane_id) = {
        let state = handler.state.lock().await;
        let alpha_session = state.sessions.session(&alpha).expect("alpha should exist");
        let beta_session = state.sessions.session(&beta).expect("beta should exist");
        (
            alpha_session
                .window_at(1)
                .and_then(|window| window.pane(0))
                .map(|pane| pane.id())
                .expect("alpha window 1 pane should exist"),
            beta_session
                .window_at(4)
                .and_then(|window| window.pane(0))
                .map(|pane| pane.id())
                .expect("beta window 4 pane should exist"),
        )
    };

    {
        let mut state = handler.state.lock().await;
        state.fail_next_resize_for_test();
    }

    let response = handler
        .handle(Request::MoveWindow(MoveWindowRequest {
            source: Some(WindowTarget::with_window(alpha.clone(), 1)),
            target: MoveWindowTarget::Window(WindowTarget::with_window(beta.clone(), 4)),
            renumber: false,
            kill_destination: true,
            detached: true,
        }))
        .await;

    assert_eq!(
        response,
        Response::Error(rmux_proto::ErrorResponse {
            error: rmux_proto::RmuxError::Server(
                "injected pane terminal resize failure".to_owned()
            ),
        })
    );

    let state = handler.state.lock().await;
    let alpha_session = state.sessions.session(&alpha).expect("alpha should exist");
    let beta_session = state.sessions.session(&beta).expect("beta should exist");
    assert_eq!(
        alpha_session.windows().keys().copied().collect::<Vec<_>>(),
        vec![0, 1]
    );
    assert_eq!(
        beta_session.windows().keys().copied().collect::<Vec<_>>(),
        vec![0, 4]
    );
    assert_eq!(alpha_session.pane_id_in_window(1, 0), Some(moved_pane_id));
    assert_eq!(beta_session.pane_id_in_window(4, 0), Some(replaced_pane_id));
    state
        .pane_profile_in_window(&alpha, 1, 0)
        .expect("moved pane terminal should return to the source session");
    state
        .pane_profile_in_window(&beta, 4, 0)
        .expect("replaced pane terminal should return to the destination session");
}