Set up a function to update the tabletop image and a test for that
All checks were successful
Monorepo build / build-flake (push) Successful in 3s
All checks were successful
Monorepo build / build-flake (push) Successful in 3s
This commit is contained in:
parent
7cb38bff77
commit
39b4bda954
2
.config/nextest.toml
Normal file
2
.config/nextest.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[profile.default]
|
||||
slow-timeout = { period = "5s", terminate-after = 2 }
|
||||
@ -16,9 +16,11 @@ pub async fn set_tabletop_image(
|
||||
) -> (StatusCode, Json<Option<()>>) {
|
||||
auth_required(state.clone(), headers, move |user| {
|
||||
clone!((state, request), async move {
|
||||
let Json(request) = request;
|
||||
let state = state.write().await;
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, ())
|
||||
let Json(SetTabletopImageRequest { game_id, url }) = request;
|
||||
match state.write().await.set_tabletop_image(&game_id, url) {
|
||||
Ok(()) => (StatusCode::OK, ()),
|
||||
Err(_) => (StatusCode::INTERNAL_SERVER_ERROR, ()),
|
||||
}
|
||||
})
|
||||
})
|
||||
.await
|
||||
@ -28,24 +30,73 @@ pub async fn set_tabletop_image(
|
||||
mod test {
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_channel::Sender;
|
||||
use axum::http::StatusCode;
|
||||
use axum_test::TestServer;
|
||||
use cool_asserts::assert_matches;
|
||||
use tokio::sync::RwLock;
|
||||
use visions_types::{GameId, SetTabletopImageRequest};
|
||||
use visions_types::{
|
||||
AuthRequest, AuthResponse, GameId, GameMessage, SessionId, SetTabletopImageRequest,
|
||||
};
|
||||
|
||||
use crate::{router::api_routes, state::AppState};
|
||||
use crate::{dispatcher::Dispatcher, router::api_routes, state::AppState};
|
||||
|
||||
fn test_server() -> TestServer {
|
||||
let app_state = AppState::new();
|
||||
let my_app = api_routes(Arc::new(RwLock::new(app_state)));
|
||||
TestServer::new(my_app).expect("test server to succeed")
|
||||
fn test_server() -> (TestServer, Dispatcher) {
|
||||
let app_state = Arc::new(RwLock::new(AppState::new()));
|
||||
let (dispatcher, _) = Dispatcher::new(app_state.clone());
|
||||
|
||||
tokio::spawn({
|
||||
let dispatcher = dispatcher.clone();
|
||||
async move {
|
||||
dispatcher.run().await;
|
||||
}
|
||||
});
|
||||
|
||||
let my_app = api_routes(app_state);
|
||||
(
|
||||
TestServer::new(my_app).expect("test server to succeed"),
|
||||
dispatcher,
|
||||
)
|
||||
}
|
||||
|
||||
async fn authenticate(server: &TestServer, username: String, password: String) -> SessionId {
|
||||
let response = server
|
||||
.post("/auth")
|
||||
.json(&AuthRequest { username, password })
|
||||
.await;
|
||||
response.assert_status(StatusCode::OK);
|
||||
let auth: AuthResponse = response.json();
|
||||
match auth {
|
||||
AuthResponse::Success((session_id, _)) => return session_id,
|
||||
AuthResponse::PasswordReset(_) => panic!("Did not expect password reset"),
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn gm_can_set_tabletop_image_on_their_game() {
|
||||
todo!();
|
||||
let (server, dispatcher) = test_server();
|
||||
let session_id = authenticate(&server, "vakarian".to_owned(), "aoeu".to_owned()).await;
|
||||
let (sender, receive_from_dispatch) = async_channel::unbounded();
|
||||
|
||||
dispatcher.register(session_id.clone(), "missing-librarians-id".into(), sender);
|
||||
|
||||
let response = server
|
||||
.post("/tabletop/image")
|
||||
.add_header("AUTHORIZATION", format!("Bearer {}", session_id.as_str()))
|
||||
.json(&SetTabletopImageRequest {
|
||||
game_id: "missing-librarians-id".into(),
|
||||
url: "http://nowhere.com/nothing.png".to_owned(),
|
||||
})
|
||||
.await;
|
||||
response.assert_status(StatusCode::OK);
|
||||
|
||||
let response = receive_from_dispatch.recv().await.unwrap();
|
||||
assert_matches!(response, GameMessage::Tabletop(url) => {
|
||||
assert_eq!(url, "http://nowhere.com/nothing.png".to_owned())
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
#[tokio::test]
|
||||
async fn gm_cannot_set_tabletop_image_on_another_game() {
|
||||
todo!();
|
||||
@ -75,10 +126,12 @@ mod test {
|
||||
async fn player_in_another_game_does_not_receive_update_on_tabletop_image_change() {
|
||||
todo!();
|
||||
}
|
||||
*/
|
||||
|
||||
#[tokio::test]
|
||||
async fn unauthenticated_sessions_get_rejected() {
|
||||
let server = test_server();
|
||||
let (server, dispatcher) = test_server();
|
||||
|
||||
let response = server
|
||||
.post("/tabletop/image")
|
||||
.json(&SetTabletopImageRequest {
|
||||
@ -89,8 +142,10 @@ mod test {
|
||||
response.assert_status(StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
/*
|
||||
#[tokio::test]
|
||||
async fn invalid_auth_tokens_get_rejected() {
|
||||
todo!();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@ -1,24 +1,23 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use async_channel::Sender;
|
||||
use axum::{
|
||||
extract::Path,
|
||||
extract::{
|
||||
ws::{Message, Utf8Bytes, WebSocket},
|
||||
Path, WebSocketUpgrade,
|
||||
},
|
||||
http::{
|
||||
header::{AUTHORIZATION, CONTENT_TYPE},
|
||||
HeaderMap, HeaderValue, Method, StatusCode,
|
||||
},
|
||||
routing::{get, post},
|
||||
routing::{any, get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use visions_types::{
|
||||
AuthRequest, CharacterId, Charsheet, GameId, GameOverview, SetTabletopImageRequest, UserId,
|
||||
UserOverview,
|
||||
};
|
||||
use visions_types::{AuthRequest, CharacterId, GameId, SetTabletopImageRequest};
|
||||
|
||||
use crate::{handlers::*, state::AppState, types::User};
|
||||
|
||||
use super::{auth_required, check_password};
|
||||
use crate::{check_password, handlers::*, state::AppState};
|
||||
|
||||
pub fn api_routes(state: Arc<RwLock<AppState>>) -> Router {
|
||||
Router::new()
|
||||
@ -112,7 +111,9 @@ pub fn api_routes(state: Arc<RwLock<AppState>>) -> Router {
|
||||
mod test {
|
||||
use axum::http::StatusCode;
|
||||
use axum_test::TestServer;
|
||||
use visions_types::{AccountStatus, AuthResponse, SessionId};
|
||||
use visions_types::{AccountStatus, AuthResponse, SessionId, UserId, UserOverview};
|
||||
|
||||
use crate::dispatcher::Dispatcher;
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -126,10 +127,14 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
fn test_server() -> TestServer {
|
||||
let app_state = AppState::new();
|
||||
let my_app = api_routes(Arc::new(RwLock::new(app_state)));
|
||||
TestServer::new(my_app).expect("test server to succeed")
|
||||
fn test_server() -> (TestServer, Dispatcher) {
|
||||
let app_state = Arc::new(RwLock::new(AppState::new()));
|
||||
let (dispatcher, _) = Dispatcher::new(app_state.clone());
|
||||
let my_app = api_routes(app_state);
|
||||
(
|
||||
TestServer::new(my_app).expect("test server to succeed"),
|
||||
dispatcher,
|
||||
)
|
||||
}
|
||||
|
||||
async fn authenticate(
|
||||
@ -165,14 +170,14 @@ mod test {
|
||||
|
||||
#[tokio::test]
|
||||
async fn app_construction() {
|
||||
let server = test_server();
|
||||
let (server, _) = test_server();
|
||||
let response = server.get("/health").await;
|
||||
response.assert_status(StatusCode::OK);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn retrieve_all_users() {
|
||||
let server = test_server();
|
||||
let (server, _) = test_server();
|
||||
let response = server.get("/users").await;
|
||||
response.assert_status(StatusCode::OK);
|
||||
|
||||
@ -183,7 +188,7 @@ mod test {
|
||||
|
||||
#[tokio::test]
|
||||
async fn retrieve_self() {
|
||||
let server = test_server();
|
||||
let (server, _) = test_server();
|
||||
let response = server.get("/self").await;
|
||||
response.assert_status(StatusCode::UNAUTHORIZED);
|
||||
|
||||
|
||||
@ -54,12 +54,13 @@ pub async fn auth_required<B, F, Fut>(
|
||||
f: F,
|
||||
) -> (StatusCode, Json<Option<B>>)
|
||||
where
|
||||
F: Fn(&User) -> Fut,
|
||||
F: Fn(User) -> Fut,
|
||||
Fut: Future<Output = (StatusCode, B)>,
|
||||
{
|
||||
match parse_session_header(headers) {
|
||||
ResultExt::Ok(Some(session_id)) => {
|
||||
match state.read().await.user_from_session(&session_id) {
|
||||
let user = state.read().await.user_from_session(&session_id).cloned();
|
||||
match user {
|
||||
Some(user) => {
|
||||
let (code, result) = f(user).await;
|
||||
(code, Json(Some(result)))
|
||||
|
||||
@ -1,9 +1,16 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use thiserror::Error;
|
||||
use visions_types::*;
|
||||
|
||||
use crate::types::*;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SetTabletopError {
|
||||
#[error("Game was not found")]
|
||||
GameNotFound,
|
||||
}
|
||||
|
||||
fn users() -> Vec<User> {
|
||||
vec![
|
||||
User {
|
||||
@ -476,4 +483,18 @@ impl AppState {
|
||||
.values()
|
||||
.filter(|sheet| sheet.game_id == *game_id)
|
||||
}
|
||||
|
||||
pub fn set_tabletop_image(
|
||||
&mut self,
|
||||
game_id: &GameId,
|
||||
image_url: String,
|
||||
) -> Result<(), SetTabletopError> {
|
||||
match self.games.get_mut(game_id) {
|
||||
Some(game) => {
|
||||
game.tabletop.image = Some(image_url);
|
||||
Ok(())
|
||||
}
|
||||
None => Err(SetTabletopError::GameNotFound),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,15 +15,16 @@ pub enum GameRequest {
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "content", rename_all = "kebab-case")]
|
||||
pub enum GameMessage {
|
||||
Title(String),
|
||||
Background(String),
|
||||
AvailableScenes(Vec<(SceneId, String)>),
|
||||
AvailableImages(Vec<String>),
|
||||
AvailableScenes(Vec<(SceneId, String)>),
|
||||
Background(String),
|
||||
CardSidebar,
|
||||
CharacterOverviews(Vec<Charsheet>),
|
||||
Charsheet(Charsheet),
|
||||
PlaceCard(Card, ScreenPosition),
|
||||
MoveCard(CardId, ScreenPosition),
|
||||
PlaceCard(Card, ScreenPosition),
|
||||
Tabletop(String),
|
||||
Title(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user