diff options
Diffstat (limited to 'src/channel/routes')
| -rw-r--r-- | src/channel/routes/test/list.rs | 64 | ||||
| -rw-r--r-- | src/channel/routes/test/mod.rs | 3 | ||||
| -rw-r--r-- | src/channel/routes/test/on_create.rs | 58 | ||||
| -rw-r--r-- | src/channel/routes/test/on_send.rs | 148 |
4 files changed, 273 insertions, 0 deletions
diff --git a/src/channel/routes/test/list.rs b/src/channel/routes/test/list.rs new file mode 100644 index 0000000..f7f7b44 --- /dev/null +++ b/src/channel/routes/test/list.rs @@ -0,0 +1,64 @@ +use axum::extract::State; + +use crate::{channel::routes, test::fixtures}; + +#[tokio::test] +async fn empty_list() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let viewer = fixtures::login::create(&app).await; + + // Call the endpoint + + let routes::Channels(channels) = routes::list(State(app), viewer) + .await + .expect("always succeeds"); + + // Verify the semantics + + assert!(channels.is_empty()); +} + +#[tokio::test] +async fn one_channel() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let viewer = fixtures::login::create(&app).await; + let channel = fixtures::channel::create(&app).await; + + // Call the endpoint + + let routes::Channels(channels) = routes::list(State(app), viewer) + .await + .expect("always succeeds"); + + // Verify the semantics + + assert!(channels.contains(&channel)); +} + +#[tokio::test] +async fn multiple_channels() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let viewer = fixtures::login::create(&app).await; + let channels = vec![ + fixtures::channel::create(&app).await, + fixtures::channel::create(&app).await, + ]; + + // Call the endpoint + + let routes::Channels(response_channels) = routes::list(State(app), viewer) + .await + .expect("always succeeds"); + + // Verify the semantics + + assert!(channels + .into_iter() + .all(|channel| response_channels.contains(&channel))); +} diff --git a/src/channel/routes/test/mod.rs b/src/channel/routes/test/mod.rs new file mode 100644 index 0000000..ab663eb --- /dev/null +++ b/src/channel/routes/test/mod.rs @@ -0,0 +1,3 @@ +mod list; +mod on_create; +mod on_send; diff --git a/src/channel/routes/test/on_create.rs b/src/channel/routes/test/on_create.rs new file mode 100644 index 0000000..df23deb --- /dev/null +++ b/src/channel/routes/test/on_create.rs @@ -0,0 +1,58 @@ +use axum::extract::{Json, State}; + +use crate::{ + channel::{app, routes}, + test::fixtures, +}; + +#[tokio::test] +async fn new_channel() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let creator = fixtures::login::create(&app).await; + + // Call the endpoint + + let name = fixtures::channel::propose(); + let request = routes::CreateRequest { name }; + let Json(response_channel) = + routes::on_create(State(app.clone()), creator, Json(request.clone())) + .await + .expect("new channel in an empty app"); + + // Verify the structure of the response + + assert_eq!(request.name, response_channel.name); + + // Verify the semantics + + let channels = app.channels().all().await.expect("always succeeds"); + + assert!(channels.contains(&response_channel)); +} + +#[tokio::test] +async fn duplicate_name() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let creator = fixtures::login::create(&app).await; + let channel = fixtures::channel::create(&app).await; + + // Call the endpoint + + let request = routes::CreateRequest { name: channel.name }; + let routes::CreateError(error) = + routes::on_create(State(app.clone()), creator, Json(request.clone())) + .await + .expect_err("duplicate channel name"); + + // Verify the structure of the response + + fixtures::error::expected!( + error, + app::CreateError::DuplicateName(name), + assert_eq!(request.name, name), + ); +} diff --git a/src/channel/routes/test/on_send.rs b/src/channel/routes/test/on_send.rs new file mode 100644 index 0000000..eab7c32 --- /dev/null +++ b/src/channel/routes/test/on_send.rs @@ -0,0 +1,148 @@ +use axum::{ + extract::{Json, Path, State}, + http::StatusCode, +}; +use futures::stream::StreamExt; + +use crate::{ + channel::{app, routes}, + repo::channel, + test::fixtures::{self, future::Immediately as _}, +}; + +#[tokio::test] +async fn channel_exists() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let sender = fixtures::login::create(&app).await; + let channel = fixtures::channel::create(&app).await; + + // Call the endpoint + + let sent_at = fixtures::now(); + let request = routes::SendRequest { + message: fixtures::message::propose(), + }; + let status = routes::on_send( + State(app.clone()), + Path(channel.id.clone()), + sent_at.clone(), + sender.clone(), + Json(request.clone()), + ) + .await + .expect("sending to a valid channel"); + + // Verify the structure of the response + + assert_eq!(StatusCode::ACCEPTED, status); + + // Verify the semantics + + let subscribed_at = fixtures::now(); + let mut events = app + .channels() + .events(&channel.id, &subscribed_at, None) + .await + .expect("subscribing to a valid channel"); + + let event = events + .next() + .immediately() + .await + .expect("event received by subscribers"); + + assert_eq!(request.message, event.body); + assert_eq!(sender, event.sender); + assert_eq!(*sent_at, event.sent_at); +} + +#[tokio::test] +async fn messages_in_order() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let sender = fixtures::login::create(&app).await; + let channel = fixtures::channel::create(&app).await; + + // Call the endpoint (twice) + + let requests = vec![ + ( + fixtures::now(), + routes::SendRequest { + message: fixtures::message::propose(), + }, + ), + ( + fixtures::now(), + routes::SendRequest { + message: fixtures::message::propose(), + }, + ), + ]; + + for (sent_at, request) in &requests { + routes::on_send( + State(app.clone()), + Path(channel.id.clone()), + sent_at.clone(), + sender.clone(), + Json(request.clone()), + ) + .await + .expect("sending to a valid channel"); + } + + // Verify the semantics + + let subscribed_at = fixtures::now(); + let events = app + .channels() + .events(&channel.id, &subscribed_at, None) + .await + .expect("subscribing to a valid channel") + .take(requests.len()); + + let events = events.collect::<Vec<_>>().immediately().await; + + for ((sent_at, request), event) in requests.into_iter().zip(events) { + assert_eq!(request.message, event.body); + assert_eq!(sender, event.sender); + assert_eq!(*sent_at, event.sent_at); + } +} + +#[tokio::test] +async fn nonexistent_channel() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let login = fixtures::login::create(&app).await; + + // Call the endpoint + + let sent_at = fixtures::now(); + let channel = channel::Id::generate(); + let request = routes::SendRequest { + message: fixtures::message::propose(), + }; + let routes::ErrorResponse(error) = routes::on_send( + State(app), + Path(channel.clone()), + sent_at, + login, + Json(request), + ) + .await + .expect_err("sending to a nonexistent channel"); + + // Verify the structure of the response + + fixtures::error::expected!( + error, + app::EventsError::ChannelNotFound(error_channel), + assert_eq!(channel, error_channel) + ); +} |
