Add more thorough OT test

This commit is contained in:
Eric Zhang 2021-06-02 10:51:42 -05:00
parent 43a743f095
commit 615222108f

View file

@ -45,42 +45,75 @@ fn backend() -> BoxedFilter<(impl Reply,)> {
#[cfg(test)]
mod tests {
use std::time::Duration;
use log::info;
use operational_transform::OperationSeq;
use serde_json::{json, Value};
use tokio::time;
use warp::test::WsClient;
use super::*;
/// A test WebSocket client that sends and receives JSON messages
struct JsonSocket(WsClient);
impl JsonSocket {
async fn send(&mut self, msg: &Value) {
self.0.send_text(msg.to_string()).await
}
async fn recv(&mut self) -> Value {
let msg = self.0.recv().await.expect("recv failure");
let msg = msg.to_str().expect("non-string message");
serde_json::from_str(&msg).expect("non-json message")
}
async fn recv_closed(&mut self) {
self.0.recv_closed().await.unwrap()
}
}
/// Connect a new test client WebSocket
async fn connect(filter: &BoxedFilter<(impl Reply + 'static,)>) -> JsonSocket {
let client = warp::test::ws()
.path("/socket")
.handshake(filter.clone())
.await
.expect("handshake failed");
JsonSocket(client)
}
/// Check the text route
async fn expect_text(filter: &BoxedFilter<(impl Reply + 'static,)>, text: &str) {
let resp = warp::test::request().path("/text").reply(filter).await;
assert_eq!(resp.status(), 200);
assert_eq!(resp.body(), text);
}
#[tokio::test]
async fn test_single_operation() {
pretty_env_logger::try_init().ok();
let filter = backend();
let resp = warp::test::request().path("/text").reply(&filter).await;
assert_eq!(resp.status(), 200);
assert_eq!(resp.body(), "");
expect_text(&filter, "").await;
let mut client = warp::test::ws()
.path("/socket")
.handshake(filter.clone())
.await
.expect("handshake");
let msg = client.recv().await.unwrap();
let msg = msg.to_str().unwrap();
assert_eq!(msg, r#"{"Identity":0}"#);
let mut client = connect(&filter).await;
let msg = client.recv().await;
assert_eq!(msg, json!({ "Identity": 0 }));
let mut operation = OperationSeq::default();
operation.insert("hello");
let serialized = format!(
r#"{{"Edit": {{"revision": 0, "operation": {}}}}}"#,
serde_json::to_string(&operation).unwrap(),
);
info!("sending ClientMsg {}", serialized);
client.send_text(serialized).await;
let msg = json!({
"Edit": {
"revision": 0,
"operation": operation
}
});
info!("sending ClientMsg {}", msg);
client.send(&msg).await;
let msg = client.recv().await.unwrap();
let msg = msg.to_str().unwrap();
let msg: Value = serde_json::from_str(&msg).unwrap();
let msg = client.recv().await;
assert_eq!(
msg,
json!({
@ -93,9 +126,7 @@ mod tests {
})
);
let resp = warp::test::request().path("/text").reply(&filter).await;
assert_eq!(resp.status(), 200);
assert_eq!(resp.body(), "hello");
expect_text(&filter, "hello").await;
}
#[tokio::test]
@ -103,24 +134,141 @@ mod tests {
pretty_env_logger::try_init().ok();
let filter = backend();
let mut client = warp::test::ws()
.path("/socket")
.handshake(filter.clone())
.await
.expect("handshake");
let msg = client.recv().await.unwrap();
let msg = msg.to_str().unwrap();
assert_eq!(msg, r#"{"Identity":0}"#);
let resp = warp::test::request().path("/text").reply(&filter).await;
assert_eq!(resp.status(), 200);
assert_eq!(resp.body(), "");
let mut client = connect(&filter).await;
let msg = client.recv().await;
assert_eq!(msg, json!({ "Identity": 0 }));
let mut operation = OperationSeq::default();
operation.insert("hello");
let serialized = format!(
r#"{{"Edit": {{"revision": 1, "operation": {}}}}}"#,
serde_json::to_string(&operation).unwrap(),
);
info!("sending ClientMsg {}", serialized);
client.send_text(serialized).await;
let msg = json!({
"Edit": {
"revision": 1,
"operation": operation
}
});
info!("sending ClientMsg {}", msg);
client.send(&msg).await;
client.recv_closed().await.expect("socket should be closed");
client.recv_closed().await;
}
#[tokio::test]
async fn test_concurrent_transform() {
pretty_env_logger::try_init().ok();
let filter = backend();
// Connect the first client
let mut client = connect(&filter).await;
let msg = client.recv().await;
assert_eq!(msg, json!({ "Identity": 0 }));
// Insert the first operation
let mut operation = OperationSeq::default();
operation.insert("hello");
let msg = json!({
"Edit": {
"revision": 0,
"operation": operation
}
});
info!("sending ClientMsg {}", msg);
client.send(&msg).await;
let msg = client.recv().await;
assert_eq!(
msg,
json!({
"History": {
"start": 0,
"operations": [
{ "id": 0, "operation": ["hello"] }
]
}
})
);
// Insert the second operation
let mut operation = OperationSeq::default();
operation.retain(2);
operation.delete(1);
operation.insert("n");
operation.retain(2);
let msg = json!({
"Edit": {
"revision": 1,
"operation": operation
}
});
info!("sending ClientMsg {}", msg);
client.send(&msg).await;
let msg = client.recv().await;
assert_eq!(
msg,
json!({
"History": {
"start": 1,
"operations": [
{ "id": 0, "operation": [2, "n", -1, 2] }
]
}
})
);
expect_text(&filter, "henlo").await;
// Connect the second client
let mut client2 = connect(&filter).await;
let msg = client2.recv().await;
assert_eq!(msg, json!({ "Identity": 1 }));
// Insert a concurrent operation before seeing the existing history
time::sleep(Duration::from_millis(50)).await;
let mut operation = OperationSeq::default();
operation.insert("~rust~");
let msg = json!({
"Edit": {
"revision": 0,
"operation": operation
}
});
info!("sending ClientMsg {}", msg);
client2.send(&msg).await;
// Receive the existing history
let msg = client2.recv().await;
assert_eq!(
msg,
json!({
"History": {
"start": 0,
"operations": [
{ "id": 0, "operation": ["hello"] },
{ "id": 0, "operation": [2, "n", -1, 2] }
]
}
})
);
// Expect to receive a transformed operation
let transformed_op = json!({
"History": {
"start": 2,
"operations": [
{ "id": 1, "operation": ["~rust~", 5] },
]
}
});
// ... in the first client
let msg = client.recv().await;
assert_eq!(msg, transformed_op);
// ... and in the second client
let msg = client2.recv().await;
assert_eq!(msg, transformed_op);
}
}