use axum::{ extract::{Json, State}, http::StatusCode, }; use crate::{login::routes, test::fixtures, token::app}; #[tokio::test] async fn new_identity() { // Set up the environment let app = fixtures::scratch_app().await; // Call the endpoint let identity = fixtures::identity::not_logged_in(); let logged_in_at = fixtures::now(); let (name, password) = fixtures::login::propose(); let request = routes::LoginRequest { name: name.clone(), password, }; let (identity, status) = routes::on_login(State(app.clone()), logged_in_at, identity, Json(request)) .await .expect("logged in with valid credentials"); // Verify the return value's basic structure assert_eq!(StatusCode::NO_CONTENT, status); let secret = identity.secret().expect("logged in with valid credentials"); // Verify the semantics let validated_at = fixtures::now(); let (_, validated) = app .tokens() .validate(&secret, &validated_at) .await .expect("identity secret is valid"); assert_eq!(name, validated.name); } #[tokio::test] async fn existing_identity() { // Set up the environment let app = fixtures::scratch_app().await; let (name, password) = fixtures::login::create_with_password(&app, &fixtures::now()).await; // Call the endpoint let identity = fixtures::identity::not_logged_in(); let logged_in_at = fixtures::now(); let request = routes::LoginRequest { name: name.clone(), password, }; let (identity, status) = routes::on_login(State(app.clone()), logged_in_at, identity, Json(request)) .await .expect("logged in with valid credentials"); // Verify the return value's basic structure assert_eq!(StatusCode::NO_CONTENT, status); let secret = identity.secret().expect("logged in with valid credentials"); // Verify the semantics let validated_at = fixtures::now(); let (_, validated_login) = app .tokens() .validate(&secret, &validated_at) .await .expect("identity secret is valid"); assert_eq!(name, validated_login.name); } #[tokio::test] async fn authentication_failed() { // Set up the environment let app = fixtures::scratch_app().await; let login = fixtures::login::create(&app, &fixtures::now()).await; // Call the endpoint let logged_in_at = fixtures::now(); let identity = fixtures::identity::not_logged_in(); let request = routes::LoginRequest { name: login.name, password: fixtures::login::propose_password(), }; let routes::LoginError(error) = routes::on_login(State(app.clone()), logged_in_at, identity, Json(request)) .await .expect_err("logged in with an incorrect password"); // Verify the return value's basic structure assert!(matches!(error, app::LoginError::Rejected)); } #[tokio::test] async fn token_expires() { // Set up the environment let app = fixtures::scratch_app().await; let (name, password) = fixtures::login::create_with_password(&app, &fixtures::now()).await; // Call the endpoint let logged_in_at = fixtures::ancient(); let identity = fixtures::identity::not_logged_in(); let request = routes::LoginRequest { name, password }; let (identity, _) = routes::on_login(State(app.clone()), logged_in_at, identity, Json(request)) .await .expect("logged in with valid credentials"); let secret = identity.secret().expect("logged in with valid credentials"); // Verify the semantics let expired_at = fixtures::now(); app.tokens() .expire(&expired_at) .await .expect("expiring tokens never fails"); let verified_at = fixtures::now(); let error = app .tokens() .validate(&secret, &verified_at) .await .expect_err("validating an expired token"); assert!(matches!(error, app::ValidateError::InvalidToken)); }