mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
new tests
This commit is contained in:
+203
@@ -107,3 +107,206 @@ pub async fn read_upload_session(
|
||||
maybe_cookie_session,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//! DATABASE TESTS: these connect to the guarded MySQL test database (see the `mysql_testing`
|
||||
//! crate — never production, never the local dev database) and drive the real helper with
|
||||
//! dummy Actix HTTP requests against a test `ServerState`. They RUN BY DEFAULT under
|
||||
//! `cargo test`; on machines without a local MySQL, skip them with:
|
||||
//!
|
||||
//! ```bash
|
||||
//! SQLX_OFFLINE=true cargo test -p storyteller-web --features skip_database_tests
|
||||
//! ```
|
||||
//!
|
||||
//! They run in PARALLEL: every test creates its own users, API keys, and MCP sessions.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use actix_web::cookie::Cookie;
|
||||
use actix_web::test::TestRequest;
|
||||
use actix_web::HttpRequest;
|
||||
|
||||
use mysql_queries::queries::mcp_sessions::revoke_mcp_session::{
|
||||
revoke_mcp_session, RevokeMcpSessionArgs,
|
||||
};
|
||||
use mysql_testing::fixtures::users::{create_test_user, TestUser};
|
||||
|
||||
use crate::http_server::endpoints::omni_gen::generate::video::tests::support::TestHarness;
|
||||
use crate::http_server::user_lookup::api_or_web_session::require_any_session_or_key::AnySessionType;
|
||||
|
||||
use super::*;
|
||||
|
||||
mod anonymous_and_cookie_lookups {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn no_credentials_is_an_anonymous_upload() {
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
let request = TestRequest::default().to_http_request();
|
||||
let session_auth = read(&harness, &request).await.expect("anonymous should be allowed");
|
||||
|
||||
assert!(!session_auth.is_logged_in());
|
||||
assert!(session_auth.maybe_user_token().is_none());
|
||||
assert!(session_auth.maybe_header_session.is_none());
|
||||
assert!(session_auth.maybe_cookie_session.is_none());
|
||||
assert!(session_auth.maybe_preferred_visibility().is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn web_session_cookie_identifies_the_user() {
|
||||
let harness = TestHarness::create().await;
|
||||
let user = create_user(&harness).await;
|
||||
|
||||
let request = request_with_session_cookie(&harness, &user);
|
||||
let session_auth = read(&harness, &request).await.expect("cookie session should be read");
|
||||
|
||||
assert!(session_auth.is_logged_in());
|
||||
assert_eq!(session_auth.maybe_user_token(), Some(&user.user_token));
|
||||
assert!(session_auth.maybe_header_session.is_none());
|
||||
// Cookie sessions carry the user's preferences.
|
||||
assert!(session_auth.maybe_preferred_visibility().is_some());
|
||||
}
|
||||
}
|
||||
|
||||
mod header_credential_lookups {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn api_key_identifies_the_user() {
|
||||
let harness = TestHarness::create().await;
|
||||
let user = create_user(&harness).await;
|
||||
let api_key = harness.create_api_key(&user).await;
|
||||
|
||||
let request = request_with_bearer(api_key.as_str_be_careful());
|
||||
let session_auth = read(&harness, &request).await.expect("API key should authenticate");
|
||||
|
||||
assert!(session_auth.is_logged_in());
|
||||
assert_eq!(session_auth.maybe_user_token(), Some(&user.user_token));
|
||||
let header_session = session_auth.maybe_header_session.as_ref().expect("header session");
|
||||
assert_eq!(header_session.session_type, AnySessionType::Api);
|
||||
assert!(header_session.maybe_api_key_token.is_some());
|
||||
// Header lookups don't carry user preferences.
|
||||
assert!(session_auth.maybe_preferred_visibility().is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn mcp_session_identifies_the_user() {
|
||||
let harness = TestHarness::create().await;
|
||||
let user = create_user(&harness).await;
|
||||
let credential = harness.create_mcp_session(&user).await;
|
||||
|
||||
let request = request_with_bearer(credential.as_str());
|
||||
let session_auth = read(&harness, &request).await.expect("MCP session should authenticate");
|
||||
|
||||
assert!(session_auth.is_logged_in());
|
||||
assert_eq!(session_auth.maybe_user_token(), Some(&user.user_token));
|
||||
let header_session = session_auth.maybe_header_session.as_ref().expect("header session");
|
||||
assert_eq!(header_session.session_type, AnySessionType::McpSession);
|
||||
assert!(header_session.maybe_mcp_session_token.is_some());
|
||||
assert!(session_auth.maybe_preferred_visibility().is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn header_credential_wins_over_a_session_cookie() {
|
||||
let harness = TestHarness::create().await;
|
||||
let cookie_user = create_user(&harness).await;
|
||||
let api_key_user = create_user(&harness).await;
|
||||
let api_key = harness.create_api_key(&api_key_user).await;
|
||||
|
||||
let cookie = session_cookie(&harness, &cookie_user);
|
||||
let request = TestRequest::default()
|
||||
.insert_header(("Authorization", format!("Bearer {}", api_key.as_str_be_careful())))
|
||||
.cookie(cookie)
|
||||
.to_http_request();
|
||||
let session_auth = read(&harness, &request).await.expect("header credential should win");
|
||||
|
||||
assert_eq!(session_auth.maybe_user_token(), Some(&api_key_user.user_token));
|
||||
assert!(session_auth.maybe_cookie_session.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn unknown_header_credentials_are_rejected_not_anonymous() {
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
for credential in [
|
||||
"artcraft_api_no_such_key_0000000000000000",
|
||||
"mcp_session_no_such_session_00000000000000000000000000000000",
|
||||
] {
|
||||
let request = request_with_bearer(credential);
|
||||
let result = read(&harness, &request).await;
|
||||
assert!(
|
||||
matches!(result, Err(MediaFileUploadError::NotAuthorized)),
|
||||
"bad credential {credential:?} must be a 401, not an anonymous fallthrough",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn revoked_mcp_session_is_rejected() {
|
||||
let harness = TestHarness::create().await;
|
||||
let user = create_user(&harness).await;
|
||||
let credential = harness.create_mcp_session(&user).await;
|
||||
|
||||
let mut connection = harness.pool.acquire().await.expect("acquire for revoke");
|
||||
let revoked = revoke_mcp_session(RevokeMcpSessionArgs {
|
||||
private_session_token: &credential,
|
||||
mysql_executor: &mut *connection,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
.await
|
||||
.expect("revoke mcp session");
|
||||
assert_eq!(revoked, 1);
|
||||
|
||||
let request = request_with_bearer(credential.as_str());
|
||||
let result = read(&harness, &request).await;
|
||||
|
||||
assert!(matches!(result, Err(MediaFileUploadError::NotAuthorized)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the helper under test against a fresh connection from the test pool.
|
||||
async fn read(
|
||||
harness: &TestHarness,
|
||||
request: &HttpRequest,
|
||||
) -> Result<UploadSessionAuth, MediaFileUploadError> {
|
||||
let mut connection = harness.pool.acquire().await.expect("acquire for lookup");
|
||||
read_upload_session(request, &harness.server_state, &mut connection).await
|
||||
}
|
||||
|
||||
async fn create_user(harness: &TestHarness) -> TestUser {
|
||||
create_test_user(&harness.pool).await.expect("create test user")
|
||||
}
|
||||
|
||||
/// A dummy HTTP request presenting `credential` as a Bearer Authorization header.
|
||||
fn request_with_bearer(credential: &str) -> HttpRequest {
|
||||
TestRequest::default()
|
||||
.insert_header(("Authorization", format!("Bearer {credential}")))
|
||||
.to_http_request()
|
||||
}
|
||||
|
||||
/// A dummy HTTP request carrying the user's session cookie and no Authorization header.
|
||||
fn request_with_session_cookie(harness: &TestHarness, user: &TestUser) -> HttpRequest {
|
||||
TestRequest::default()
|
||||
.cookie(session_cookie(harness, user))
|
||||
.to_http_request()
|
||||
}
|
||||
|
||||
/// The user's session cookie, owned so TestRequest can take it.
|
||||
fn session_cookie(harness: &TestHarness, user: &TestUser) -> Cookie<'static> {
|
||||
let cookie = harness
|
||||
.server_state
|
||||
.session_cookie_manager
|
||||
.create_cookie(&user.session_token, &user.user_token)
|
||||
.expect("create session cookie");
|
||||
Cookie::new("session", cookie.value().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,5 +8,6 @@ pub mod insert_db_job;
|
||||
// Database fixture tests. Always compiled with `cfg(test)` so IDEs discover
|
||||
// them and they can't bit-rot, but each test is `ignore`d unless the
|
||||
// `database_tests` feature is enabled (see tests/mod.rs).
|
||||
// `pub(crate)` so other modules' database tests can reuse `support::TestHarness`.
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub(crate) mod tests;
|
||||
|
||||
Reference in New Issue
Block a user