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/user/routes/logout | |
| 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/user/routes/logout')
| -rw-r--r-- | src/user/routes/logout/mod.rs | 4 | ||||
| -rw-r--r-- | src/user/routes/logout/post.rs | 50 | ||||
| -rw-r--r-- | src/user/routes/logout/test.rs | 79 |
3 files changed, 0 insertions, 133 deletions
diff --git a/src/user/routes/logout/mod.rs b/src/user/routes/logout/mod.rs deleted file mode 100644 index 36b384e..0000000 --- a/src/user/routes/logout/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod post; - -#[cfg(test)] -mod test; diff --git a/src/user/routes/logout/post.rs b/src/user/routes/logout/post.rs deleted file mode 100644 index 0ac663e..0000000 --- a/src/user/routes/logout/post.rs +++ /dev/null @@ -1,50 +0,0 @@ -use axum::{ - extract::{Json, State}, - http::StatusCode, - response::{IntoResponse, Response}, -}; - -use crate::{ - app::App, - clock::RequestedAt, - error::{Internal, Unauthorized}, - token::{app, extract::IdentityCookie}, -}; - -pub async fn handler( - State(app): State<App>, - RequestedAt(now): RequestedAt, - identity: IdentityCookie, - Json(_): Json<Request>, -) -> Result<(IdentityCookie, StatusCode), Error> { - if let Some(secret) = identity.secret() { - let (token, _) = app.tokens().validate(&secret, &now).await?; - app.tokens().logout(&token).await?; - } - - let identity = identity.clear(); - Ok((identity, StatusCode::NO_CONTENT)) -} - -// This forces the only valid request to be `{}`, and not the infinite -// variation allowed when there's no body extractor. -#[derive(Default, serde::Deserialize)] -pub struct Request {} - -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct Error(#[from] pub app::ValidateError); - -impl IntoResponse for Error { - fn into_response(self) -> Response { - let Self(error) = self; - match error { - app::ValidateError::InvalidToken | app::ValidateError::LoginDeleted => { - Unauthorized.into_response() - } - app::ValidateError::Name(_) | app::ValidateError::Database(_) => { - Internal::from(error).into_response() - } - } - } -} diff --git a/src/user/routes/logout/test.rs b/src/user/routes/logout/test.rs deleted file mode 100644 index ce93760..0000000 --- a/src/user/routes/logout/test.rs +++ /dev/null @@ -1,79 +0,0 @@ -use axum::{ - extract::{Json, State}, - http::StatusCode, -}; - -use super::post; -use crate::{test::fixtures, token::app}; - -#[tokio::test] -async fn successful() { - // Set up the environment - - let app = fixtures::scratch_app().await; - let now = fixtures::now(); - let creds = fixtures::user::create_with_password(&app, &fixtures::now()).await; - let identity = fixtures::cookie::logged_in(&app, &creds, &now).await; - let secret = fixtures::cookie::secret(&identity); - - // Call the endpoint - - let (response_identity, response_status) = post::handler( - State(app.clone()), - fixtures::now(), - identity.clone(), - Json::default(), - ) - .await - .expect("logged out with a valid token"); - - // Verify the return value's basic structure - - assert!(response_identity.secret().is_none()); - assert_eq!(StatusCode::NO_CONTENT, response_status); - - // Verify the semantics - let error = app - .tokens() - .validate(&secret, &now) - .await - .expect_err("secret is invalid"); - assert!(matches!(error, app::ValidateError::InvalidToken)); -} - -#[tokio::test] -async fn no_identity() { - // Set up the environment - - let app = fixtures::scratch_app().await; - - // Call the endpoint - - let identity = fixtures::cookie::not_logged_in(); - let (identity, status) = post::handler(State(app), fixtures::now(), identity, Json::default()) - .await - .expect("logged out with no token succeeds"); - - // Verify the return value's basic structure - - assert!(identity.secret().is_none()); - assert_eq!(StatusCode::NO_CONTENT, status); -} - -#[tokio::test] -async fn invalid_token() { - // Set up the environment - - let app = fixtures::scratch_app().await; - - // Call the endpoint - - let identity = fixtures::cookie::fictitious(); - let post::Error(error) = post::handler(State(app), fixtures::now(), identity, Json::default()) - .await - .expect_err("logged out with an invalid token fails"); - - // Verify the return value's basic structure - - assert!(matches!(error, app::ValidateError::InvalidToken)); -} |
