mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-23 23:51:06 +01:00
added support for websocket testing
This commit is contained in:
parent
76f9542df7
commit
577f91206c
@ -78,7 +78,8 @@ tokio-openssl = { version="0.2", optional = true }
|
||||
|
||||
[dependencies.actix]
|
||||
#version = "^0.4.6"
|
||||
git = "https://github.com/actix/actix.git"
|
||||
#git = "https://github.com/actix/actix.git"
|
||||
path = "../actix"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.5"
|
||||
|
@ -95,3 +95,54 @@ fn main() {
|
||||
assert!(reqwest::get(&url).unwrap().status().is_success()); // <- make request
|
||||
}
|
||||
```
|
||||
|
||||
## WebSocket server tests
|
||||
|
||||
It is possible to register *handler* with `TestApp::handler()` method that
|
||||
initiate web socket connection. *TestServer* provides `ws()` which connects to
|
||||
websocket server and returns ws reader and writer objects. *TestServer* also
|
||||
provides `execute()` method which runs future object to completion and returns
|
||||
result of the future computation.
|
||||
|
||||
Here is simple example, that shows how to test server websocket handler.
|
||||
|
||||
```rust
|
||||
# extern crate actix;
|
||||
# extern crate actix_web;
|
||||
# extern crate futures;
|
||||
# extern crate http;
|
||||
# extern crate bytes;
|
||||
|
||||
use actix_web::*;
|
||||
use futures::Stream;
|
||||
# use actix::prelude::*;
|
||||
|
||||
struct Ws; // <- WebSocket actor
|
||||
|
||||
impl Actor for Ws {
|
||||
type Context = ws::WebsocketContext<Self>;
|
||||
}
|
||||
|
||||
impl Handler<ws::Message> for Ws {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
|
||||
match msg {
|
||||
ws::Message::Text(text) => ctx.text(&text),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut srv = test::TestServer::new( // <- start our server with ws handler
|
||||
|app| app.handler(|req| ws::start(req, Ws)));
|
||||
|
||||
let (reader, mut writer) = srv.ws().unwrap(); // <- connect to ws server
|
||||
|
||||
writer.text("text"); // <- send message to server
|
||||
|
||||
let (item, reader) = srv.execute(reader.into_future()).unwrap(); // <- wait for one message
|
||||
assert_eq!(item, Some(ws::Message::Text("text".to_owned())));
|
||||
}
|
||||
```
|
||||
|
27
src/test.rs
27
src/test.rs
@ -6,7 +6,7 @@ use std::sync::mpsc;
|
||||
use std::str::FromStr;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use actix::{Arbiter, SyncAddress, System, msgs};
|
||||
use actix::{Arbiter, SyncAddress, System, SystemRunner, msgs};
|
||||
use cookie::Cookie;
|
||||
use http::{Uri, Method, Version, HeaderMap, HttpTryFrom};
|
||||
use http::header::{HeaderName, HeaderValue};
|
||||
@ -25,6 +25,7 @@ use payload::Payload;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use server::{HttpServer, HttpHandler, IntoHttpHandler, ServerSettings};
|
||||
use ws::{WsClient, WsClientError, WsClientReader, WsClientWriter};
|
||||
|
||||
/// The `TestServer` type.
|
||||
///
|
||||
@ -54,7 +55,8 @@ use server::{HttpServer, HttpHandler, IntoHttpHandler, ServerSettings};
|
||||
pub struct TestServer {
|
||||
addr: net::SocketAddr,
|
||||
thread: Option<thread::JoinHandle<()>>,
|
||||
sys: SyncAddress<System>,
|
||||
system: SystemRunner,
|
||||
server_sys: SyncAddress<System>,
|
||||
}
|
||||
|
||||
impl TestServer {
|
||||
@ -95,7 +97,8 @@ impl TestServer {
|
||||
TestServer {
|
||||
addr: addr,
|
||||
thread: Some(join),
|
||||
sys: sys,
|
||||
system: System::new("actix-test"),
|
||||
server_sys: sys,
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +134,8 @@ impl TestServer {
|
||||
TestServer {
|
||||
addr: addr,
|
||||
thread: Some(join),
|
||||
sys: sys,
|
||||
system: System::new("actix-test"),
|
||||
server_sys: sys,
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,10 +166,23 @@ impl TestServer {
|
||||
/// Stop http server
|
||||
fn stop(&mut self) {
|
||||
if let Some(handle) = self.thread.take() {
|
||||
self.sys.send(msgs::SystemExit(0));
|
||||
self.server_sys.send(msgs::SystemExit(0));
|
||||
let _ = handle.join();
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute future on current core
|
||||
pub fn execute<F, I, E>(&mut self, fut: F) -> Result<I, E>
|
||||
where F: Future<Item=I, Error=E>
|
||||
{
|
||||
self.system.run_until_complete(fut)
|
||||
}
|
||||
|
||||
/// Connect to websocket server
|
||||
pub fn ws(&mut self) -> Result<(WsClientReader, WsClientWriter), WsClientError> {
|
||||
let url = self.url("/");
|
||||
self.system.run_until_complete(WsClient::new(url).connect().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestServer {
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Http client request
|
||||
#![allow(unused_imports, dead_code)]
|
||||
use std::{io, str};
|
||||
use std::{fmt, io, str};
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
use std::cell::UnsafeCell;
|
||||
@ -299,8 +299,8 @@ impl Future for WsHandshake {
|
||||
let match_key = if let Some(key) = resp.headers().get(
|
||||
HeaderName::try_from("SEC-WEBSOCKET-ACCEPT").unwrap())
|
||||
{
|
||||
// ... field is constructed by concatenating /key/ ...
|
||||
// ... with the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" (RFC 6455)
|
||||
// field is constructed by concatenating /key/
|
||||
// with the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" (RFC 6455)
|
||||
const WS_GUID: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
let mut sha1 = Sha1::new();
|
||||
sha1.update(self.key.as_ref());
|
||||
@ -336,6 +336,12 @@ pub struct WsClientReader {
|
||||
inner: Rc<UnsafeCell<Inner>>
|
||||
}
|
||||
|
||||
impl fmt::Debug for WsClientReader {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "WsClientReader()")
|
||||
}
|
||||
}
|
||||
|
||||
impl WsClientReader {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut Inner {
|
||||
|
@ -74,7 +74,7 @@ const SEC_WEBSOCKET_VERSION: &str = "SEC-WEBSOCKET-VERSION";
|
||||
|
||||
|
||||
/// `WebSocket` Message
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Message {
|
||||
Text(String),
|
||||
Binary(Binary),
|
||||
|
49
tests/test_ws.rs
Normal file
49
tests/test_ws.rs
Normal file
@ -0,0 +1,49 @@
|
||||
extern crate actix;
|
||||
extern crate actix_web;
|
||||
extern crate futures;
|
||||
extern crate http;
|
||||
extern crate bytes;
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::Stream;
|
||||
|
||||
use actix_web::*;
|
||||
use actix::prelude::*;
|
||||
|
||||
struct Ws;
|
||||
|
||||
impl Actor for Ws {
|
||||
type Context = ws::WebsocketContext<Self>;
|
||||
}
|
||||
|
||||
impl Handler<ws::Message> for Ws {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
|
||||
match msg {
|
||||
ws::Message::Ping(msg) => ctx.pong(&msg),
|
||||
ws::Message::Text(text) => ctx.text(&text),
|
||||
ws::Message::Binary(bin) => ctx.binary(bin),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
let mut srv = test::TestServer::new(
|
||||
|app| app.handler(|req| ws::start(req, Ws)));
|
||||
let (reader, mut writer) = srv.ws().unwrap();
|
||||
|
||||
writer.text("text");
|
||||
let (item, reader) = srv.execute(reader.into_future()).unwrap();
|
||||
assert_eq!(item, Some(ws::Message::Text("text".to_owned())));
|
||||
|
||||
writer.binary(b"text".as_ref());
|
||||
let (item, reader) = srv.execute(reader.into_future()).unwrap();
|
||||
assert_eq!(item, Some(ws::Message::Binary(Bytes::from_static(b"text").into())));
|
||||
|
||||
writer.ping("ping");
|
||||
let (item, _) = srv.execute(reader.into_future()).unwrap();
|
||||
assert_eq!(item, Some(ws::Message::Pong("ping".to_owned())));
|
||||
}
|
Loading…
Reference in New Issue
Block a user