Modrinth/tests/common/environment.rs
Jackson Kruger 6cfd4637db
OAuth 2.0 Authorization Server [MOD-559] (#733)
* WIP end-of-day push

* Authorize endpoint, accept endpoints, DB stuff for oauth clients, their redirects, and client authorizations

* OAuth Client create route

* Get user clients

* Client delete

* Edit oauth client

* Include redirects in edit client route

* Database stuff for tokens

* Reorg oauth stuff out of auth/flows and into its own module

* Impl OAuth get access token endpoint

* Accept oauth access tokens as auth and update through AuthQueue

* User OAuth authorization management routes

* Forgot to actually add the routes lol

* Bit o cleanup

* Happy path test for OAuth and minor fixes for things it found

* Add dummy data oauth client (and detect/handle dummy data version changes)

* More tests

* Another test

* More tests and reject endpoint

* Test oauth client and authorization management routes

* cargo sqlx prepare

* dead code warning

* Auto clippy fixes

* Uri refactoring

* minor name improvement

* Don't compile-time check the test sqlx queries

* Trying to fix db concurrency problem to get tests to pass

* Try fix from test PR

* Fixes for updated sqlx

* Prevent restricted scopes from being requested or issued

* Get OAuth client(s)

* Remove joined oauth client info from authorization returns

* Add default conversion to OAuthError::error so we can use ?

* Rework routes

* Consolidate scopes into SESSION_ACCESS

* Cargo sqlx prepare

* Parse to OAuthClientId automatically through serde and actix

* Cargo clippy

* Remove validation requiring 1 redirect URI on oauth client creation

* Use serde(flatten) on OAuthClientCreationResult
2023-10-30 09:14:38 -07:00

139 lines
3.7 KiB
Rust

#![allow(dead_code)]
use std::{rc::Rc, sync::Arc};
use super::{
api_v2::ApiV2,
api_v3::ApiV3,
asserts::assert_status,
database::{TemporaryDatabase, FRIEND_USER_ID, USER_USER_PAT},
dummy_data,
};
use crate::common::setup;
use actix_http::StatusCode;
use actix_web::{dev::ServiceResponse, test, App};
use futures::Future;
pub async fn with_test_environment<Fut>(f: impl FnOnce(TestEnvironment) -> Fut)
where
Fut: Future<Output = ()>,
{
let test_env = TestEnvironment::build(None).await;
let db = test_env.db.clone();
f(test_env).await;
db.cleanup().await;
}
// A complete test environment, with a test actix app and a database.
// Must be called in an #[actix_rt::test] context. It also simulates a
// temporary sqlx db like #[sqlx::test] would.
// Use .call(req) on it directly to make a test call as if test::call_service(req) were being used.
#[derive(Clone)]
pub struct TestEnvironment {
test_app: Rc<dyn LocalService>, // Rc as it's not Send
pub db: TemporaryDatabase,
pub v2: ApiV2,
pub v3: ApiV3,
pub dummy: Option<Arc<dummy_data::DummyData>>,
}
impl TestEnvironment {
pub async fn build(max_connections: Option<u32>) -> Self {
let db = TemporaryDatabase::create(max_connections).await;
let mut test_env = Self::build_with_db(db).await;
let dummy = dummy_data::get_dummy_data(&test_env).await;
test_env.dummy = Some(Arc::new(dummy));
test_env
}
pub async fn build_with_db(db: TemporaryDatabase) -> Self {
let labrinth_config = setup(&db).await;
let app = App::new().configure(|cfg| labrinth::app_config(cfg, labrinth_config.clone()));
let test_app: Rc<dyn LocalService> = Rc::new(test::init_service(app).await);
Self {
v2: ApiV2 {
test_app: test_app.clone(),
},
v3: ApiV3 {
test_app: test_app.clone(),
},
test_app,
db,
dummy: None,
}
}
pub async fn cleanup(self) {
self.db.cleanup().await;
}
pub async fn call(&self, req: actix_http::Request) -> ServiceResponse {
self.test_app.call(req).await.unwrap()
}
pub async fn generate_friend_user_notification(&self) {
let resp = self
.v2
.add_user_to_team(
&self.dummy.as_ref().unwrap().project_alpha.team_id,
FRIEND_USER_ID,
None,
None,
USER_USER_PAT,
)
.await;
assert_status(&resp, StatusCode::NO_CONTENT);
}
pub async fn assert_read_notifications_status(
&self,
user_id: &str,
pat: &str,
status_code: StatusCode,
) {
let resp = self.v2.get_user_notifications(user_id, pat).await;
assert_status(&resp, status_code);
}
pub async fn assert_read_user_projects_status(
&self,
user_id: &str,
pat: &str,
status_code: StatusCode,
) {
let resp = self.v2.get_user_projects(user_id, pat).await;
assert_status(&resp, status_code);
}
}
pub trait LocalService {
fn call(
&self,
req: actix_http::Request,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = Result<ServiceResponse, actix_web::Error>>>,
>;
}
impl<S> LocalService for S
where
S: actix_web::dev::Service<
actix_http::Request,
Response = ServiceResponse,
Error = actix_web::Error,
>,
S::Future: 'static,
{
fn call(
&self,
req: actix_http::Request,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = Result<ServiceResponse, actix_web::Error>>>,
> {
Box::pin(self.call(req))
}
}