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::>().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) ); }