1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-24 00:21:08 +01:00

added support for websocket testing

This commit is contained in:
Nikolay Kim 2018-01-30 15:13:33 -08:00
parent 76f9542df7
commit 577f91206c
6 changed files with 134 additions and 10 deletions

View File

@ -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"

View File

@ -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())));
}
```

View File

@ -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 {

View File

@ -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 {

View File

@ -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
View 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())));
}