embers-test-support 0.1.0

Shared integration-test harnesses and helpers for Embers crates.
use crate::support::integration_test_lock;
use embers_core::{SplitDirection, new_request_id};
use embers_protocol::{
    BufferRecord, BufferRequest, ClientMessage, NodeRequest, ServerResponse, SessionRequest,
    SessionSnapshot, SessionSnapshotResponse, SplitRecord, TabsRecord,
};
use embers_test_support::{TestConnection, TestServer};

async fn create_session(connection: &mut TestConnection, name: &str) -> SessionSnapshotResponse {
    let response = connection
        .request(&ClientMessage::Session(SessionRequest::Create {
            request_id: new_request_id(),
            name: name.to_owned(),
        }))
        .await
        .expect("create session request succeeds");

    match response {
        ServerResponse::SessionSnapshot(snapshot) => snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    }
}

async fn create_buffer(connection: &mut TestConnection, title: &str) -> BufferRecord {
    let response = connection
        .request(&ClientMessage::Buffer(BufferRequest::Create {
            request_id: new_request_id(),
            title: Some(title.to_owned()),
            command: vec!["/bin/sh".to_owned(), "-lc".to_owned(), "cat".to_owned()],
            cwd: None,
            env: Default::default(),
        }))
        .await
        .expect("create buffer request succeeds");

    match response {
        ServerResponse::Buffer(buffer) => buffer.buffer,
        other => panic!("expected buffer response, got {other:?}"),
    }
}

async fn add_root_tab(
    connection: &mut TestConnection,
    session_id: embers_core::SessionId,
    title: &str,
    buffer_id: embers_core::BufferId,
) -> SessionSnapshot {
    let response = connection
        .request(&ClientMessage::Session(SessionRequest::AddRootTab {
            request_id: new_request_id(),
            session_id,
            title: title.to_owned(),
            buffer_id: Some(buffer_id),
            child_node_id: None,
        }))
        .await
        .expect("add root tab request succeeds");

    match response {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    }
}

async fn get_buffer(
    connection: &mut TestConnection,
    buffer_id: embers_core::BufferId,
) -> BufferRecord {
    let response = connection
        .request(&ClientMessage::Buffer(BufferRequest::Get {
            request_id: new_request_id(),
            buffer_id,
        }))
        .await
        .expect("get buffer request succeeds");

    match response {
        ServerResponse::Buffer(buffer) => buffer.buffer,
        other => panic!("expected buffer response, got {other:?}"),
    }
}

fn root_tabs(snapshot: &SessionSnapshot) -> TabsRecord {
    snapshot
        .nodes
        .iter()
        .find(|node| node.id == snapshot.session.root_node_id)
        .and_then(|node| node.tabs.clone())
        .expect("session root snapshot includes tabs record")
}

fn tabs_record(snapshot: &SessionSnapshot, node_id: embers_core::NodeId) -> TabsRecord {
    snapshot
        .nodes
        .iter()
        .find(|node| node.id == node_id)
        .and_then(|node| node.tabs.clone())
        .expect("snapshot includes tabs node")
}

fn split_record(snapshot: &SessionSnapshot, node_id: embers_core::NodeId) -> SplitRecord {
    snapshot
        .nodes
        .iter()
        .find(|node| node.id == node_id)
        .and_then(|node| node.split.clone())
        .expect("snapshot includes split node")
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn nested_tab_mutations_round_trip_through_socket() {
    let _guard = integration_test_lock().lock().await;
    let server = TestServer::start().await.expect("start server");
    let mut connection = TestConnection::connect(server.socket_path())
        .await
        .expect("connect protocol client");

    let session = create_session(&mut connection, "main").await;
    let session_id = session.snapshot.session.id;

    let first_buffer = create_buffer(&mut connection, "one").await;
    let first_snapshot = add_root_tab(&mut connection, session_id, "one", first_buffer.id).await;
    let first_leaf = root_tabs(&first_snapshot).tabs[0].child_id;

    let second_buffer = create_buffer(&mut connection, "two").await;
    let split_snapshot = connection
        .request(&ClientMessage::Node(NodeRequest::Split {
            request_id: new_request_id(),
            leaf_node_id: first_leaf,
            direction: SplitDirection::Horizontal,
            new_buffer_id: second_buffer.id,
        }))
        .await
        .expect("split request succeeds");
    let split_snapshot = match split_snapshot {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    };
    let outer_split_id = root_tabs(&split_snapshot).tabs[0].child_id;
    let second_leaf = split_snapshot
        .session
        .focused_leaf_id
        .expect("split focuses second leaf");

    let wrapped = connection
        .request(&ClientMessage::Node(NodeRequest::WrapInTabs {
            request_id: new_request_id(),
            node_id: second_leaf,
            title: "nested".to_owned(),
        }))
        .await
        .expect("wrap request succeeds");
    let wrapped = match wrapped {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    };
    let tabs_id = split_record(&wrapped, outer_split_id).child_ids[1];

    let fourth_buffer = create_buffer(&mut connection, "four").await;
    let split_inner = connection
        .request(&ClientMessage::Node(NodeRequest::Split {
            request_id: new_request_id(),
            leaf_node_id: second_leaf,
            direction: SplitDirection::Vertical,
            new_buffer_id: fourth_buffer.id,
        }))
        .await
        .expect("inner split request succeeds");
    let split_inner = match split_inner {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    };
    let inner_split_id = tabs_record(&split_inner, tabs_id).tabs[0].child_id;
    let fourth_leaf = split_inner
        .session
        .focused_leaf_id
        .expect("inner split focuses newest leaf");

    let third_buffer = create_buffer(&mut connection, "three").await;
    let added = connection
        .request(&ClientMessage::Node(NodeRequest::AddTab {
            request_id: new_request_id(),
            tabs_node_id: tabs_id,
            title: "other".to_owned(),
            buffer_id: Some(third_buffer.id),
            child_node_id: None,
            index: 1,
        }))
        .await
        .expect("add nested tab request succeeds");
    let added = match added {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    };
    let third_leaf = added
        .session
        .focused_leaf_id
        .expect("new nested tab focuses new leaf");

    let selected_first = connection
        .request(&ClientMessage::Node(NodeRequest::SelectTab {
            request_id: new_request_id(),
            tabs_node_id: tabs_id,
            index: 0,
        }))
        .await
        .expect("select first nested tab request succeeds");
    let selected_first = match selected_first {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    };
    assert_eq!(selected_first.session.focused_leaf_id, Some(fourth_leaf));

    let selected_second = connection
        .request(&ClientMessage::Node(NodeRequest::SelectTab {
            request_id: new_request_id(),
            tabs_node_id: tabs_id,
            index: 1,
        }))
        .await
        .expect("select second nested tab request succeeds");
    let selected_second = match selected_second {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    };
    assert_eq!(selected_second.session.focused_leaf_id, Some(third_leaf));

    let closed = connection
        .request(&ClientMessage::Node(NodeRequest::Close {
            request_id: new_request_id(),
            node_id: third_leaf,
        }))
        .await
        .expect("close nested tab node request succeeds");
    let closed = match closed {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    };
    assert_eq!(closed.session.focused_leaf_id, Some(fourth_leaf));
    assert_eq!(
        split_record(&closed, outer_split_id).child_ids[1],
        inner_split_id
    );
    assert_eq!(
        get_buffer(&mut connection, third_buffer.id)
            .await
            .attachment_node_id,
        None
    );

    server.shutdown().await.expect("shutdown server");
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_tree_returns_nested_tab_structure() {
    let _guard = integration_test_lock().lock().await;
    let server = TestServer::start().await.expect("start server");
    let mut connection = TestConnection::connect(server.socket_path())
        .await
        .expect("connect protocol client");

    let session = create_session(&mut connection, "main").await;
    let session_id = session.snapshot.session.id;

    let first_buffer = create_buffer(&mut connection, "one").await;
    let first_snapshot = add_root_tab(&mut connection, session_id, "one", first_buffer.id).await;
    let first_leaf = root_tabs(&first_snapshot).tabs[0].child_id;

    let second_buffer = create_buffer(&mut connection, "two").await;
    let split_snapshot = connection
        .request(&ClientMessage::Node(NodeRequest::Split {
            request_id: new_request_id(),
            leaf_node_id: first_leaf,
            direction: SplitDirection::Vertical,
            new_buffer_id: second_buffer.id,
        }))
        .await
        .expect("split request succeeds");
    let split_snapshot = match split_snapshot {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    };
    let outer_split_id = root_tabs(&split_snapshot).tabs[0].child_id;
    let second_leaf = split_snapshot
        .session
        .focused_leaf_id
        .expect("split focuses second leaf");

    let wrapped = connection
        .request(&ClientMessage::Node(NodeRequest::WrapInTabs {
            request_id: new_request_id(),
            node_id: second_leaf,
            title: "nested".to_owned(),
        }))
        .await
        .expect("wrap request succeeds");
    let wrapped = match wrapped {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    };
    let tabs_id = split_record(&wrapped, outer_split_id).child_ids[1];

    let third_buffer = create_buffer(&mut connection, "three").await;
    connection
        .request(&ClientMessage::Node(NodeRequest::AddTab {
            request_id: new_request_id(),
            tabs_node_id: tabs_id,
            title: "other".to_owned(),
            buffer_id: Some(third_buffer.id),
            child_node_id: None,
            index: 1,
        }))
        .await
        .expect("add nested tab request succeeds");

    let tree = connection
        .request(&ClientMessage::Node(NodeRequest::GetTree {
            request_id: new_request_id(),
            session_id,
        }))
        .await
        .expect("get tree request succeeds");
    let tree = match tree {
        ServerResponse::SessionSnapshot(snapshot) => snapshot.snapshot,
        other => panic!("expected session snapshot response, got {other:?}"),
    };

    let outer_split = split_record(&tree, outer_split_id);
    assert_eq!(outer_split.child_ids[1], tabs_id);
    let nested_tabs = tabs_record(&tree, tabs_id);
    assert_eq!(nested_tabs.tabs.len(), 2);
    assert_eq!(nested_tabs.tabs[0].title, "nested");
    assert_eq!(nested_tabs.tabs[1].title, "other");

    server.shutdown().await.expect("shutdown server");
}