#![allow(clippy::unwrap_used)]
use std::{collections::HashMap, sync::Arc};
use axum::{Router, body::Body};
use fraiseql_core::{cache::CachedDatabaseAdapter, schema::CompiledSchema};
use fraiseql_test_utils::failing_adapter::FailingAdapter;
use http::{Request, StatusCode};
use tower::ServiceExt;
use crate::{server::Server, server_config::ServerConfig};
async fn server_with(config: ServerConfig) -> Server<CachedDatabaseAdapter<FailingAdapter>> {
Server::new(config, CompiledSchema::new(), Arc::new(FailingAdapter::new()), None)
.await
.expect("Server::new should succeed for an empty schema + default config")
}
#[tokio::test]
async fn legacy_storage_backend_not_mounted_without_token() {
let backend = Arc::new(crate::storage::LocalStorageBackend::new("/tmp/fraiseql-c6-test-unused"))
as Arc<dyn crate::storage::StorageBackend>;
let server = server_with(ServerConfig::default()).await.with_storage(backend);
let state = server.build_app_state();
let app: Router = server.mount_extensions(Router::new(), &state);
let response = app
.oneshot(
Request::builder()
.method("PATCH")
.uri("/storage/v1/object/secret.txt")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(
response.status(),
StatusCode::NOT_FOUND,
"legacy storage backend (no RLS) must fail closed when storage_token is unset",
);
}
fn minimal_storage_state() -> fraiseql_storage::StorageState {
fraiseql_storage::StorageState {
backend: Arc::new(fraiseql_storage::StorageBackend::Local(
fraiseql_storage::LocalBackend::new("/tmp/fraiseql-c6-test-unused"),
)),
metadata: Arc::new(fraiseql_storage::StorageMetadataRepo::new(
sqlx::PgPool::connect_lazy("postgres://test:test@localhost/test").unwrap(),
)),
rls: fraiseql_storage::StorageRlsEvaluator::new(),
buckets: Arc::new(HashMap::new()),
}
}
#[tokio::test]
async fn hardened_storage_state_not_mounted_without_any_auth() {
let server = server_with(ServerConfig::default())
.await
.with_storage_state(minimal_storage_state());
let state = server.build_app_state();
let app: Router = server.mount_extensions(Router::new(), &state);
let response = app
.oneshot(
Request::builder()
.method("PATCH")
.uri("/storage/v1/object/secret.txt")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(
response.status(),
StatusCode::NOT_FOUND,
"hardened storage API must fail closed when neither storage_token nor an OIDC validator is set",
);
}
#[tokio::test]
async fn legacy_storage_backend_is_mounted_with_token() {
let backend = Arc::new(crate::storage::LocalStorageBackend::new("/tmp/fraiseql-c6-test-unused"))
as Arc<dyn crate::storage::StorageBackend>;
let config = ServerConfig {
storage_token: Some("storage-admin-token-32chars-minimum".to_string()),
..ServerConfig::default()
};
let server = server_with(config).await.with_storage(backend);
let state = server.build_app_state();
let app: Router = server.mount_extensions(Router::new(), &state);
let response = app
.oneshot(
Request::builder()
.uri("/storage/v1/object/secret.txt")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(
response.status(),
StatusCode::UNAUTHORIZED,
"with a storage_token the route must be mounted and reject the anonymous request (401, not 404)",
);
}