diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2025-06-17 02:11:45 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2025-06-18 18:31:40 -0400 |
| commit | 4e3d5ccac99b24934c972e088cd7eb02bb95df06 (patch) | |
| tree | c94f5a42f7e734b81892c1289a1d2b566706ba7c /src/channel/handlers/send/test.rs | |
| parent | 5ed96f8e8b9d9f19ee249f5c73a5a21ef6bca09f (diff) | |
Handlers are _named operations_, which can be exposed via routes.
Each domain module that exposes handlers does so through a `handlers` child module, ideally as a top-level symbol that can be plugged directly into Axum's `MethodRouter`. Modules could make exceptions to this - kill the doctrinaire inside yourself, after all - but none of the API modules that actually exist need such exceptions, and consistency is useful.
The related details of request types, URL types, response types, errors, &c &c are then organized into modules under `handlers`, along with their respective tests.
Diffstat (limited to 'src/channel/handlers/send/test.rs')
| -rw-r--r-- | src/channel/handlers/send/test.rs | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/channel/handlers/send/test.rs b/src/channel/handlers/send/test.rs new file mode 100644 index 0000000..f43f901 --- /dev/null +++ b/src/channel/handlers/send/test.rs @@ -0,0 +1,131 @@ +use axum::extract::{Json, Path, State}; +use futures::stream::{self, StreamExt as _}; + +use crate::{ + channel, + event::Sequenced, + message::app::SendError, + test::fixtures::{self, future::Expect as _}, +}; + +#[tokio::test] +async fn messages_in_order() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let sender = fixtures::identity::create(&app, &fixtures::now()).await; + let channel = fixtures::channel::create(&app, &fixtures::now()).await; + let resume_point = fixtures::boot::resume_point(&app).await; + + // Call the endpoint (twice) + + let requests = vec![ + (fixtures::now(), fixtures::message::propose()), + (fixtures::now(), fixtures::message::propose()), + ]; + + for (sent_at, body) in &requests { + let request = super::Request { body: body.clone() }; + + let _ = super::handler( + State(app.clone()), + Path(channel.id.clone()), + sent_at.clone(), + sender.clone(), + Json(request), + ) + .await + .expect("sending to a valid channel succeeds"); + } + + // Verify the semantics + + let mut events = app + .events() + .subscribe(resume_point) + .await + .expect("subscribing to a valid channel succeeds") + .filter_map(fixtures::event::message) + .filter_map(fixtures::event::message::sent) + .zip(stream::iter(requests)); + + while let Some((event, (sent_at, body))) = events + .next() + .expect_ready("an event should be ready for each message") + .await + { + assert_eq!(*sent_at, event.at()); + assert_eq!(sender.user.id, event.message.sender); + assert_eq!(body, event.message.body); + } +} + +#[tokio::test] +async fn nonexistent_channel() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let sender = fixtures::identity::create(&app, &fixtures::now()).await; + + // Call the endpoint + + let sent_at = fixtures::now(); + let channel = channel::Id::generate(); + let request = super::Request { + body: fixtures::message::propose(), + }; + let super::Error(error) = super::handler( + State(app), + Path(channel.clone()), + sent_at, + sender, + Json(request), + ) + .await + .expect_err("sending to a nonexistent channel fails"); + + // Verify the structure of the response + + assert!(matches!( + error, + SendError::ChannelNotFound(error_channel) if channel == error_channel + )); +} + +#[tokio::test] +async fn deleted_channel() { + // Set up the environment + + let app = fixtures::scratch_app().await; + let sender = fixtures::identity::create(&app, &fixtures::now()).await; + let channel = fixtures::channel::create(&app, &fixtures::now()).await; + + app.channels() + .delete(&channel.id, &fixtures::now()) + .await + .expect("deleting a new channel succeeds"); + + // Call the endpoint + + let sent_at = fixtures::now(); + let channel = channel::Id::generate(); + let request = super::Request { + body: fixtures::message::propose(), + }; + let super::Error(error) = super::handler( + State(app), + Path(channel.clone()), + sent_at, + sender, + Json(request), + ) + .await + .expect_err("sending to a deleted channel fails"); + + // Verify the structure of the response + + assert!(matches!( + error, + SendError::ChannelNotFound(error_channel) if channel == error_channel + )); +} |
