summaryrefslogtreecommitdiff
path: root/src/channel/routes
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-09-19 01:25:31 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-09-20 23:55:22 -0400
commite5f72711c5a17c5db24e209b14f82d426eceb86e (patch)
tree04865172284c86549dd08d700c21a29c36f54005 /src/channel/routes
parent0079624488af334817f58e30dbc676d3adde8de6 (diff)
Write tests.
Diffstat (limited to 'src/channel/routes')
-rw-r--r--src/channel/routes/test/list.rs64
-rw-r--r--src/channel/routes/test/mod.rs3
-rw-r--r--src/channel/routes/test/on_create.rs58
-rw-r--r--src/channel/routes/test/on_send.rs148
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)
+ );
+}