diff --git a/tests/admin_plugins_official.rs b/tests/admin_plugins_official.rs
--- a/tests/admin_plugins_official.rs
+++ b/tests/admin_plugins_official.rs
@@ -41,8 +41,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn official_plugins_endpoint_returns_cached_list() {
+ common::require_db!();
let app = TestApp::new().await;
let gh = MockServer::start().await;
@@ -111,8 +111,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn plugins_list_populates_update_available_when_behind() {
+ common::require_db!();
let app = TestApp::new().await;
let gh = MockServer::start().await;
@@ -187,8 +187,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn check_update_endpoint_refreshes_cache_on_demand() {
+ common::require_db!();
// Start the mock server BEFORE building the app so we can wire its URL
// into the registry config.
let gh = MockServer::start().await;
diff --git a/tests/common/app.rs b/tests/common/app.rs
--- a/tests/common/app.rs
+++ b/tests/common/app.rs
@@ -118,12 +118,23 @@ },
})
.expect("Failed to create test OAuth client");
+ let domain_cache = happyview::domain::DomainCache::new();
+ domain_cache
+ .insert(happyview::domain::Domain {
+ id: uuid::Uuid::new_v4().to_string(),
+ url: "http://127.0.0.1:0".to_string(),
+ is_primary: true,
+ created_at: now_rfc3339(),
+ updated_at: now_rfc3339(),
+ })
+ .await;
+
let state = AppState {
config,
http: reqwest::Client::new(),
db: pool.clone(),
db_backend: backend,
- domain_cache: happyview::domain::DomainCache::new(),
+ domain_cache,
lexicons,
collections_tx,
labeler_subscriptions_tx,
@@ -158,7 +169,15 @@ happyview::proxy_config::ProxyConfig::default(),
))),
};
- let router = server::router(state.clone());
+ let router = server::router(state.clone()).layer(axum::middleware::from_fn(
+ |mut req: axum::extract::Request, next: axum::middleware::Next| async move {
+ if !req.headers().contains_key("host") {
+ req.headers_mut()
+ .insert("host", axum::http::HeaderValue::from_static("127.0.0.1"));
+ }
+ next.run(req).await
+ },
+ ));
Self {
router,
@@ -178,20 +197,7 @@ }
pub async fn new_with_encryption() -> Self {
let mut app = Self::new().await;
- // Set a test encryption key (32 bytes)
app.state.config.token_encryption_key = Some([0x42u8; 32]);
- // Seed a domain so the domain middleware doesn't reject requests with 421
- app.state
- .domain_cache
- .insert(happyview::domain::Domain {
- id: uuid::Uuid::new_v4().to_string(),
- url: "http://127.0.0.1:0".to_string(),
- is_primary: true,
- created_at: now_rfc3339(),
- updated_at: now_rfc3339(),
- })
- .await;
- // Rebuild the router with the updated state
app.router = server::router(app.state.clone());
app
}
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
--- a/tests/common/mod.rs
+++ b/tests/common/mod.rs
@@ -6,3 +6,14 @@ #[allow(dead_code, unused_imports)]
pub mod db;
#[allow(dead_code, unused_imports)]
pub mod fixtures;
+
+macro_rules! require_db {
+ () => {
+ if std::env::var("TEST_DATABASE_URL").is_err() {
+ eprintln!("skipped (TEST_DATABASE_URL not set)");
+ return;
+ }
+ };
+}
+
+pub(crate) use require_db;
diff --git a/tests/dev_happyview.rs b/tests/dev_happyview.rs
--- a/tests/dev_happyview.rs
+++ b/tests/dev_happyview.rs
@@ -88,6 +88,7 @@ /// Unauthenticated request (no Authorization header) should be rejected.
#[tokio::test]
#[serial]
async fn list_api_clients_unauthenticated_returns_non_200() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let req = Request::builder()
@@ -111,6 +112,7 @@ /// DPoP-authenticated request returns 200 with a `clients` array.
#[tokio::test]
#[serial]
async fn list_api_clients_authenticated_returns_200_with_clients_array() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let user_did = "did:plc:testowner";
let (client_key, dpop_key, access_token) = setup_dpop_session(&app, user_did).await;
@@ -147,6 +149,7 @@ /// Authenticated request for a nonexistent client ID returns 404.
#[tokio::test]
#[serial]
async fn get_api_client_not_found() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let user_did = "did:plc:testowner404";
let (client_key, dpop_key, access_token) = setup_dpop_session(&app, user_did).await;
@@ -178,6 +181,7 @@ /// clientKey (hvc_) and clientSecret (hvs_) in the response.
#[tokio::test]
#[serial]
async fn create_api_client_via_xrpc() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let user_did = "did:plc:testcreator";
let (client_key, dpop_key, access_token) = setup_dpop_session(&app, user_did).await;
@@ -230,6 +234,7 @@ /// Creating a public client returns no clientSecret in the response.
#[tokio::test]
#[serial]
async fn create_api_client_public_no_secret() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let user_did = "did:plc:testcreatorpublic";
let (client_key, dpop_key, access_token) = setup_dpop_session(&app, user_did).await;
@@ -286,6 +291,7 @@ /// Create a client, delete it, then verify a GET returns 404.
#[tokio::test]
#[serial]
async fn delete_api_client_success() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let user_did = "did:plc:testownerdelete";
let (client_key, dpop_key, access_token) = setup_dpop_session(&app, user_did).await;
@@ -367,6 +373,7 @@ /// Attempting to delete a nonexistent client returns 404.
#[tokio::test]
#[serial]
async fn delete_api_client_not_found() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let user_did = "did:plc:testownerdel404";
let (client_key, dpop_key, access_token) = setup_dpop_session(&app, user_did).await;
@@ -397,6 +404,7 @@ /// Authenticated request returns 200 with the matching client.
#[tokio::test]
#[serial]
async fn get_api_client_returns_client() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let user_did = "did:plc:testownerget";
let (client_key, dpop_key, access_token) = setup_dpop_session(&app, user_did).await;
diff --git a/tests/dpop_auth.rs b/tests/dpop_auth.rs
--- a/tests/dpop_auth.rs
+++ b/tests/dpop_auth.rs
@@ -47,6 +47,7 @@
#[tokio::test]
#[serial]
async fn test_provision_dpop_key_confidential_client() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let (client_key, client_secret, _id) = app.create_api_client("confidential", None).await;
@@ -72,6 +73,7 @@
#[tokio::test]
#[serial]
async fn test_provision_dpop_key_public_client_requires_pkce() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let (client_key, _secret, _id) = app
.create_api_client("public", Some(vec!["http://localhost:3000".to_string()]))
@@ -94,6 +96,7 @@
#[tokio::test]
#[serial]
async fn test_provision_dpop_key_public_client_with_pkce() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let (client_key, _secret, _id) = app
.create_api_client("public", Some(vec!["http://localhost:3000".to_string()]))
@@ -127,6 +130,7 @@
#[tokio::test]
#[serial]
async fn test_register_session_validates_scopes() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let (client_key, client_secret, _id) = app.create_api_client("confidential", None).await;
@@ -165,6 +169,7 @@
#[tokio::test]
#[serial]
async fn test_register_session_requires_atproto_scope() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let (client_key, client_secret, _id) = app.create_api_client("confidential", None).await;
@@ -201,6 +206,7 @@
#[tokio::test]
#[serial]
async fn test_full_flow_provision_register_delete() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let (client_key, client_secret, _id) = app.create_api_client("confidential", None).await;
@@ -265,6 +271,7 @@
#[tokio::test]
#[serial]
async fn test_xrpc_rejects_bearer_auth() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
// Bearer auth should be explicitly rejected on XRPC routes
@@ -290,6 +297,7 @@
#[tokio::test]
#[serial]
async fn test_xrpc_allows_anonymous_queries() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
// Anonymous access (no auth header) should pass through to lexicon lookup.
@@ -314,6 +322,7 @@
#[tokio::test]
#[serial]
async fn test_xrpc_procedure_requires_dpop_auth() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
// POST to an XRPC procedure without DPoP auth should be rejected
@@ -339,6 +348,7 @@
#[tokio::test]
#[serial]
async fn test_xrpc_dpop_auth_accepted() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let (client_key, client_secret, _id) = app.create_api_client("confidential", None).await;
diff --git a/tests/e2e_admin.rs b/tests/e2e_admin.rs
--- a/tests/e2e_admin.rs
+++ b/tests/e2e_admin.rs
@@ -63,8 +63,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_no_auth_returns_401() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -84,8 +84,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_wrong_token_returns_401() {
+ common::require_db!();
let app = TestApp::new().await;
// No valid session cookie — the request will be rejected.
@@ -107,8 +107,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_valid_token_returns_200() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -123,8 +123,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_non_admin_did_returns_403() {
+ common::require_db!();
let app = TestApp::new().await;
// Use a DID that is NOT in the admins table.
@@ -143,8 +143,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_auto_bootstrap_first_user() {
+ common::require_db!();
let app = TestApp::new().await;
let backend = app.state.db_backend;
@@ -186,8 +186,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn lexicon_create_returns_201() {
+ common::require_db!();
let app = TestApp::new().await;
let body = json!({
"lexicon_json": fixtures::game_record_lexicon(),
@@ -209,8 +209,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn lexicon_upsert_returns_200_with_incremented_revision() {
+ common::require_db!();
let app = TestApp::new().await;
let body = json!({
"lexicon_json": fixtures::game_record_lexicon(),
@@ -241,8 +241,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn lexicon_invalid_version_returns_400() {
+ common::require_db!();
let app = TestApp::new().await;
let body = json!({
"lexicon_json": { "lexicon": 99, "id": "test.bad" },
@@ -260,8 +260,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn lexicon_missing_id_returns_400() {
+ common::require_db!();
let app = TestApp::new().await;
let body = json!({
"lexicon_json": { "lexicon": 1 },
@@ -279,8 +279,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn lexicon_list_all() {
+ common::require_db!();
let app = TestApp::new().await;
// Seed a lexicon
@@ -311,8 +311,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn lexicon_get_by_id() {
+ common::require_db!();
let app = TestApp::new().await;
app.router
@@ -343,8 +343,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn lexicon_get_not_found() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -362,8 +362,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn lexicon_delete() {
+ common::require_db!();
let app = TestApp::new().await;
app.router
@@ -392,8 +392,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn lexicon_delete_not_found() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -415,8 +415,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn stats_empty_db() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -434,8 +434,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn stats_with_seeded_records() {
+ common::require_db!();
let app = TestApp::new().await;
let backend = app.state.db_backend;
@@ -496,8 +496,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn backfill_create_job() {
+ common::require_db!();
let app = TestApp::new().await;
// Register a record-type lexicon first (required by backfill validation).
@@ -527,14 +527,14 @@ .unwrap();
assert_eq!(resp.status(), StatusCode::CREATED);
let json = json_body(resp).await;
- assert_eq!(json["status"], "completed");
+ assert_eq!(json["status"], "running");
assert!(json.get("id").is_some());
}
#[tokio::test]
#[serial]
-#[ignore]
async fn backfill_list_jobs() {
+ common::require_db!();
let app = TestApp::new().await;
// Create a job first
@@ -567,8 +567,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_create_returns_did() {
+ common::require_db!();
let app = TestApp::new().await;
let body = json!({ "did": "did:plc:newadmin" });
@@ -587,8 +587,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_created_did_authenticates() {
+ common::require_db!();
let app = TestApp::new().await;
let new_did = "did:plc:newadmin2";
@@ -618,8 +618,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_list_returns_dids() {
+ common::require_db!();
let app = TestApp::new().await;
let cookie = app.admin_cookie();
@@ -643,8 +643,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_delete_returns_204() {
+ common::require_db!();
let app = TestApp::new().await;
// Create an admin to delete
@@ -675,8 +675,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_delete_not_found() {
+ common::require_db!();
let app = TestApp::new().await;
let cookie = app.admin_cookie();
diff --git a/tests/e2e_api_clients.rs b/tests/e2e_api_clients.rs
--- a/tests/e2e_api_clients.rs
+++ b/tests/e2e_api_clients.rs
@@ -3,7 +3,6 @@
use axum::body::Body;
use axum::http::{Request, StatusCode};
use happyview::db::{adapt_sql, now_rfc3339};
-use happyview::oauth::pds_write::generate_dpop_proof;
use http_body_util::BodyExt;
use serde_json::{Value, json};
use serial_test::serial;
@@ -88,8 +87,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn create_api_client_returns_201() {
+ common::require_db!();
let app = TestApp::new().await;
let body = sample_api_client_body();
@@ -115,8 +114,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn create_api_client_duplicate_client_id_url_fails() {
+ common::require_db!();
let app = TestApp::new().await;
let body = sample_api_client_body();
@@ -141,8 +140,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn create_api_client_registers_in_oauth_registry() {
+ common::require_db!();
let app = TestApp::new().await;
let body = sample_api_client_body();
@@ -168,8 +167,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn list_api_clients_empty() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -186,8 +185,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn list_api_clients_returns_created_clients() {
+ common::require_db!();
let app = TestApp::new().await;
// Create two clients
@@ -246,8 +245,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn get_api_client_returns_details() {
+ common::require_db!();
let app = TestApp::new().await;
let body = sample_api_client_body();
@@ -286,8 +285,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn get_api_client_not_found() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -309,8 +308,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn update_api_client_changes_fields() {
+ common::require_db!();
let app = TestApp::new().await;
// Create
@@ -363,8 +362,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn update_api_client_not_found() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -383,8 +382,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn update_api_client_deactivate_removes_from_registry() {
+ common::require_db!();
let app = TestApp::new().await;
let create_resp = app
@@ -429,8 +428,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn delete_api_client_returns_204() {
+ common::require_db!();
let app = TestApp::new().await;
let create_resp = app
@@ -471,8 +470,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn delete_api_client_removes_from_oauth_registry() {
+ common::require_db!();
let app = TestApp::new().await;
let create_resp = app
@@ -508,8 +507,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn delete_api_client_not_found() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -531,8 +530,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn api_clients_no_auth_returns_401() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -552,8 +551,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn api_clients_non_admin_returns_403() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -575,8 +574,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn oauth_registry_get_or_default_returns_default_for_unknown() {
+ common::require_db!();
let app = TestApp::new().await;
let client = app
@@ -591,8 +590,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn oauth_registry_get_or_default_returns_default_for_none() {
+ common::require_db!();
let app = TestApp::new().await;
let client = app.state.oauth.get_or_default(None);
@@ -607,8 +606,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn create_api_client_with_rate_limit_overrides() {
+ common::require_db!();
let app = TestApp::new().await;
let body = json!({
"name": "Rate Limited App",
@@ -645,492 +644,22 @@ assert_eq!(json["rate_limit_capacity"], 50);
assert_eq!(json["rate_limit_refill_rate"], 1.5);
}
-// ---------------------------------------------------------------------------
-// Self-service API client creation (POST /oauth/api-clients)
-// ---------------------------------------------------------------------------
-
-/// Helper to make a POST request with JSON body and extra headers.
-fn post_json_with_headers(
- uri: &str,
- body: &serde_json::Value,
- headers: Vec<(&str, &str)>,
-) -> Request
{
- let mut builder = Request::builder()
- .method("POST")
- .uri(uri)
- .header("content-type", "application/json")
- .header("host", "127.0.0.1:0");
- for (name, value) in headers {
- builder = builder.header(name, value);
- }
- builder
- .body(Body::from(serde_json::to_vec(body).unwrap()))
- .unwrap()
-}
-
-/// Parse a response body as JSON, returning `null` on empty/invalid bodies.
-async fn response_json(resp: axum::response::Response) -> Value {
- let body = resp.into_body().collect().await.unwrap().to_bytes();
- serde_json::from_slice(&body).unwrap_or(json!(null))
-}
-
-/// Runs the full DPoP provisioning flow and returns the values needed to call
-/// the self-service endpoint:
-/// `(client_key, dpop_key_json, access_token)`
-///
-/// `user_did` is the DID that will be associated with the DPoP session.
-async fn setup_dpop_session(app: &TestApp, user_did: &str) -> (String, Value, String) {
- let (client_key, client_secret, _id) = app.create_api_client("confidential", None).await;
-
- // 1. Provision DPoP key
- let key_req = post_json_with_headers(
- "/oauth/dpop-keys",
- &json!({}),
- vec![
- ("x-client-key", &client_key),
- ("x-client-secret", &client_secret),
- ],
- );
- let key_resp = app.router.clone().oneshot(key_req).await.unwrap();
- assert_eq!(
- key_resp.status(),
- StatusCode::CREATED,
- "dpop key provisioning failed"
- );
- let key_body = response_json(key_resp).await;
- let provision_id = key_body["provision_id"].as_str().unwrap().to_string();
- let dpop_key = key_body["dpop_key"].clone();
-
- // 2. Register session
- let access_token = format!("test-access-{}", uuid::Uuid::new_v4());
- let session_req = post_json_with_headers(
- "/oauth/sessions",
- &json!({
- "provision_id": provision_id,
- "did": user_did,
- "access_token": &access_token,
- "scopes": "atproto",
- "pds_url": "https://pds.example.com",
- }),
- vec![
- ("x-client-key", &client_key),
- ("x-client-secret", &client_secret),
- ],
- );
- let session_resp = app.router.clone().oneshot(session_req).await.unwrap();
- assert_eq!(
- session_resp.status(),
- StatusCode::CREATED,
- "session registration failed"
- );
-
- (client_key, dpop_key, access_token)
-}
-
-/// Build a self-service POST /oauth/api-clients request with full DPoP auth.
-fn self_service_request(
- client_key: &str,
- access_token: &str,
- dpop_proof: &str,
- body: &Value,
-) -> Request {
- Request::builder()
- .method("POST")
- .uri("/oauth/api-clients")
- .header("host", "127.0.0.1:0")
- .header("content-type", "application/json")
- .header("x-client-key", client_key)
- .header("authorization", format!("DPoP {}", access_token))
- .header("dpop", dpop_proof)
- .body(Body::from(serde_json::to_vec(body).unwrap()))
- .unwrap()
-}
+// Self-service client creation tests removed — the /oauth/api-clients route
+// no longer exists. The XRPC equivalent (dev.happyview.createApiClient) is
+// tested in tests/dev_happyview.rs.
// ---------------------------------------------------------------------------
-// Happy path
+// Cascade: deactivate / delete parent cascades to children
// ---------------------------------------------------------------------------
-#[tokio::test]
-#[serial]
-#[ignore]
-async fn test_self_service_create_confidential_child_client() {
- let app = TestApp::new_with_encryption().await;
- let (client_key, dpop_key, access_token) = setup_dpop_session(&app, "did:plc:testadmin").await;
-
- let request_url = "http://127.0.0.1:0/oauth/api-clients";
- let proof = generate_dpop_proof(&dpop_key, "POST", request_url, &access_token, None)
- .expect("failed to generate DPoP proof");
-
- let body = json!({
- "name": "My Confidential Child",
- "client_id_url": "https://child-confidential.example.com/oauth-client-metadata.json",
- "client_uri": "https://child-confidential.example.com",
- "redirect_uris": ["https://child-confidential.example.com/callback"],
- "scopes": "atproto",
- "client_type": "confidential"
- });
-
- let req = self_service_request(&client_key, &access_token, &proof, &body);
- let resp = app.router.clone().oneshot(req).await.unwrap();
-
- assert_eq!(resp.status(), StatusCode::CREATED);
- let json = response_json(resp).await;
-
- // Verify all expected fields
- assert!(json["id"].as_str().is_some(), "response should have id");
- let key = json["client_key"].as_str().unwrap();
- assert!(key.starts_with("hvc_"), "client_key should start with hvc_");
- let secret = json["client_secret"].as_str().unwrap();
- assert!(
- secret.starts_with("hvs_"),
- "client_secret should start with hvs_"
- );
- assert_eq!(json["name"], "My Confidential Child");
- assert_eq!(
- json["client_id_url"],
- "https://child-confidential.example.com/oauth-client-metadata.json"
- );
- assert_eq!(json["client_type"], "confidential");
-}
-
-#[tokio::test]
-#[serial]
-#[ignore]
-async fn test_self_service_create_public_child_client() {
- let app = TestApp::new_with_encryption().await;
- let (client_key, dpop_key, access_token) = setup_dpop_session(&app, "did:plc:testadmin").await;
-
- let request_url = "http://127.0.0.1:0/oauth/api-clients";
- let proof = generate_dpop_proof(&dpop_key, "POST", request_url, &access_token, None)
- .expect("failed to generate DPoP proof");
-
- let body = json!({
- "name": "My Public Child",
- "client_id_url": "https://child-public.example.com/oauth-client-metadata.json",
- "client_uri": "https://child-public.example.com",
- "redirect_uris": ["https://child-public.example.com/callback"],
- "scopes": "atproto",
- "client_type": "public"
- });
-
- let req = self_service_request(&client_key, &access_token, &proof, &body);
- let resp = app.router.clone().oneshot(req).await.unwrap();
-
- assert_eq!(resp.status(), StatusCode::CREATED);
- let json = response_json(resp).await;
-
- assert!(json["id"].as_str().is_some(), "response should have id");
- let key = json["client_key"].as_str().unwrap();
- assert!(key.starts_with("hvc_"), "client_key should start with hvc_");
- assert_eq!(json["name"], "My Public Child");
- assert_eq!(
- json["client_id_url"],
- "https://child-public.example.com/oauth-client-metadata.json"
- );
- assert_eq!(json["client_type"], "public");
- // Public clients should NOT have a client_secret
- assert!(
- json["client_secret"].is_null(),
- "public client should not have a client_secret"
- );
-}
-
-// ---------------------------------------------------------------------------
-// Error cases
-// ---------------------------------------------------------------------------
-
-#[tokio::test]
-#[serial]
-#[ignore]
-async fn test_self_service_child_cannot_create_children() {
- let app = TestApp::new_with_encryption().await;
-
- // Create a parent client via the helper (top-level, no parent_client_id).
- let (_parent_key, _parent_secret, parent_id) =
- app.create_api_client("confidential", None).await;
-
- // Insert a child client directly in the DB with parent_client_id set.
- let child_key = format!("hvc_{}", hex::encode([0xAAu8; 16]));
- let child_secret = format!("hvs_{}", hex::encode([0xBBu8; 32]));
- let child_secret_hash = hex::encode(sha2::Sha256::digest(child_secret.as_bytes()));
- let child_id = uuid::Uuid::new_v4().to_string();
- let now = now_rfc3339();
-
- let sql = adapt_sql(
- "INSERT INTO api_clients (id, client_key, client_secret_hash, name, client_id_url, client_uri, redirect_uris, scopes, client_type, is_active, created_by, created_at, updated_at, parent_client_id, owner_did) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?)",
- app.state.db_backend,
- );
- sqlx::query(&sql)
- .bind(&child_id)
- .bind(&child_key)
- .bind(&child_secret_hash)
- .bind("child-client")
- .bind("https://child-no-nest.example.com/oauth-client-metadata.json")
- .bind("https://child-no-nest.example.com")
- .bind("[]")
- .bind("atproto")
- .bind("confidential")
- .bind("did:plc:testadmin")
- .bind(&now)
- .bind(&now)
- .bind(&parent_id)
- .bind("did:plc:testadmin")
- .execute(&app.state.db)
- .await
- .expect("failed to insert child client");
-
- // The endpoint checks parent_client_id IS NULL at step 6, BEFORE DPoP
- // validation. So we just need the DPoP headers to exist — they do not
- // need to be cryptographically valid.
- let body = json!({
- "name": "Grandchild",
- "client_id_url": "https://grandchild.example.com/oauth-client-metadata.json",
- "client_uri": "https://grandchild.example.com",
- "redirect_uris": ["https://grandchild.example.com/callback"],
- "scopes": "atproto",
- "client_type": "confidential"
- });
-
- let req = Request::builder()
- .method("POST")
- .uri("/oauth/api-clients")
- .header("host", "127.0.0.1:0")
- .header("content-type", "application/json")
- .header("x-client-key", &child_key)
- .header("authorization", "DPoP fake-token")
- .header("dpop", "fake-proof")
- .body(Body::from(serde_json::to_vec(&body).unwrap()))
- .unwrap();
-
- let resp = app.router.clone().oneshot(req).await.unwrap();
- assert_eq!(resp.status(), StatusCode::FORBIDDEN);
-}
-
-#[tokio::test]
-#[serial]
-#[ignore]
-async fn test_self_service_duplicate_client_id_url() {
- let app = TestApp::new_with_encryption().await;
- let (client_key, dpop_key, access_token) = setup_dpop_session(&app, "did:plc:testadmin").await;
-
- let shared_url = "https://dup-test.example.com/oauth-client-metadata.json";
-
- // First creation should succeed.
- let request_url = "http://127.0.0.1:0/oauth/api-clients";
- let proof1 =
- generate_dpop_proof(&dpop_key, "POST", request_url, &access_token, None).expect("proof 1");
-
- let body = json!({
- "name": "First Child",
- "client_id_url": shared_url,
- "client_uri": "https://dup-test.example.com",
- "redirect_uris": ["https://dup-test.example.com/callback"],
- "scopes": "atproto",
- "client_type": "confidential"
- });
-
- let req1 = self_service_request(&client_key, &access_token, &proof1, &body);
- let resp1 = app.router.clone().oneshot(req1).await.unwrap();
- assert_eq!(resp1.status(), StatusCode::CREATED);
-
- // Second creation with the same client_id_url should fail with 409.
- let proof2 =
- generate_dpop_proof(&dpop_key, "POST", request_url, &access_token, None).expect("proof 2");
-
- let body2 = json!({
- "name": "Second Child",
- "client_id_url": shared_url,
- "client_uri": "https://dup-test2.example.com",
- "redirect_uris": ["https://dup-test2.example.com/callback"],
- "scopes": "atproto",
- "client_type": "confidential"
- });
-
- let req2 = self_service_request(&client_key, &access_token, &proof2, &body2);
- let resp2 = app.router.clone().oneshot(req2).await.unwrap();
- assert_eq!(resp2.status(), StatusCode::CONFLICT);
-}
-
-#[tokio::test]
-#[serial]
-#[ignore]
-async fn test_self_service_missing_client_key() {
- let app = TestApp::new_with_encryption().await;
-
- let body = json!({
- "name": "No Key Client",
- "client_id_url": "https://nokey.example.com/oauth-client-metadata.json",
- "client_uri": "https://nokey.example.com",
- "redirect_uris": ["https://nokey.example.com/callback"],
- "scopes": "atproto",
- "client_type": "confidential"
- });
-
- // No x-client-key header at all.
- let req = Request::builder()
- .method("POST")
- .uri("/oauth/api-clients")
- .header("host", "127.0.0.1:0")
- .header("content-type", "application/json")
- .header("authorization", "DPoP fake-token")
- .header("dpop", "fake-proof")
- .body(Body::from(serde_json::to_vec(&body).unwrap()))
- .unwrap();
-
- let resp = app.router.clone().oneshot(req).await.unwrap();
- assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
-}
-
-#[tokio::test]
-#[serial]
-#[ignore]
-async fn test_self_service_invalid_client_type() {
- let app = TestApp::new_with_encryption().await;
-
- // Step 3 (client_type validation) happens before step 5 (client resolution).
- // The request just needs the required headers to exist.
- let body = json!({
- "name": "Invalid Type Client",
- "client_id_url": "https://badtype.example.com/oauth-client-metadata.json",
- "client_uri": "https://badtype.example.com",
- "redirect_uris": ["https://badtype.example.com/callback"],
- "scopes": "atproto",
- "client_type": "invalid"
- });
-
- let req = Request::builder()
- .method("POST")
- .uri("/oauth/api-clients")
- .header("host", "127.0.0.1:0")
- .header("content-type", "application/json")
- .header("x-client-key", "hvc_doesnotmatter")
- .header("authorization", "DPoP fake-token")
- .header("dpop", "fake-proof")
- .body(Body::from(serde_json::to_vec(&body).unwrap()))
- .unwrap();
-
- let resp = app.router.clone().oneshot(req).await.unwrap();
- assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
-}
-
-#[tokio::test]
-#[serial]
-#[ignore]
-async fn test_self_service_parent_owner_not_in_users() {
- let app = TestApp::new_with_encryption().await;
-
- // Create a parent API client whose created_by DID is NOT in the users table.
- let orphan_did = "did:plc:orphan";
- let (client_key, client_secret, _id) = {
- use happyview::db::{adapt_sql, now_rfc3339};
- use rand::RngCore;
- use sha2::{Digest, Sha256};
-
- let mut key_bytes = [0u8; 16];
- rand::rng().fill_bytes(&mut key_bytes);
- let client_key = format!("hvc_{}", hex::encode(key_bytes));
-
- let mut secret_bytes = [0u8; 32];
- rand::rng().fill_bytes(&mut secret_bytes);
- let client_secret = format!("hvs_{}", hex::encode(secret_bytes));
- let secret_hash = hex::encode(Sha256::digest(client_secret.as_bytes()));
-
- let id = uuid::Uuid::new_v4().to_string();
- let now = now_rfc3339();
-
- let sql = adapt_sql(
- "INSERT INTO api_clients (id, client_key, client_secret_hash, name, client_id_url, client_uri, redirect_uris, scopes, client_type, allowed_origins, is_active, created_by, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?)",
- app.state.db_backend,
- );
-
- sqlx::query(&sql)
- .bind(&id)
- .bind(&client_key)
- .bind(&secret_hash)
- .bind("orphan-client")
- .bind(format!("https://orphan.example.com/oauth/{}", &id[..8]))
- .bind("https://orphan.example.com")
- .bind("[]")
- .bind("atproto")
- .bind("confidential")
- .bind(None::)
- .bind(orphan_did)
- .bind(&now)
- .bind(&now)
- .execute(&app.state.db)
- .await
- .expect("failed to create orphan API client");
-
- app.state.rate_limiter.register_client_identity(
- client_key.clone(),
- happyview::rate_limit::ClientIdentity {
- secret_hash,
- client_uri: "https://orphan.example.com".to_string(),
- },
- );
-
- (client_key, client_secret, id)
- };
-
- // Provision a DPoP key and session using this orphan parent client.
- let key_req = post_json_with_headers(
- "/oauth/dpop-keys",
- &json!({}),
- vec![
- ("x-client-key", &client_key),
- ("x-client-secret", &client_secret),
- ],
- );
- let key_resp = app.router.clone().oneshot(key_req).await.unwrap();
- assert_eq!(key_resp.status(), StatusCode::CREATED);
- let key_body = response_json(key_resp).await;
- let provision_id = key_body["provision_id"].as_str().unwrap().to_string();
- let dpop_key = key_body["dpop_key"].clone();
-
- let access_token = format!("test-access-{}", uuid::Uuid::new_v4());
- let session_req = post_json_with_headers(
- "/oauth/sessions",
- &json!({
- "provision_id": provision_id,
- "did": "did:plc:sessionuser",
- "access_token": &access_token,
- "scopes": "atproto",
- "pds_url": "https://pds.example.com",
- }),
- vec![
- ("x-client-key", &client_key),
- ("x-client-secret", &client_secret),
- ],
- );
- let session_resp = app.router.clone().oneshot(session_req).await.unwrap();
- assert_eq!(session_resp.status(), StatusCode::CREATED);
-
- let request_url = "http://127.0.0.1:0/oauth/api-clients";
- let proof = generate_dpop_proof(&dpop_key, "POST", request_url, &access_token, None)
- .expect("failed to generate DPoP proof");
-
- let body = json!({
- "name": "Orphan Owner Child",
- "client_id_url": "https://orphan-child.example.com/oauth-client-metadata.json",
- "client_uri": "https://orphan-child.example.com",
- "redirect_uris": ["https://orphan-child.example.com/callback"],
- "scopes": "atproto",
- "client_type": "confidential"
- });
-
- let req = self_service_request(&client_key, &access_token, &proof, &body);
- let resp = app.router.clone().oneshot(req).await.unwrap();
- assert_eq!(resp.status(), StatusCode::FORBIDDEN);
-}
-
// ---------------------------------------------------------------------------
// Cascade: deactivate / delete parent cascades to children
// ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn test_deactivate_parent_cascades_to_children() {
+ common::require_db!();
let app = TestApp::new().await;
// Create parent via admin API
@@ -1234,8 +763,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn test_delete_parent_cascades_to_children() {
+ common::require_db!();
let app = TestApp::new().await;
// Create parent via admin API
@@ -1332,8 +861,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn test_list_api_clients_filter_by_parent() {
+ common::require_db!();
let app = TestApp::new().await;
// Create two parents via admin API
@@ -1482,8 +1011,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn test_list_api_clients_includes_parent_and_owner_fields() {
+ common::require_db!();
let app = TestApp::new().await;
// Create a top-level parent via admin API
diff --git a/tests/e2e_base_path.rs b/tests/e2e_base_path.rs
--- a/tests/e2e_base_path.rs
+++ b/tests/e2e_base_path.rs
@@ -8,8 +8,8 @@ use tower::ServiceExt;
#[tokio::test]
#[serial]
-#[ignore]
async fn health_at_root_when_base_path_set() {
+ common::require_db!();
let app = common::app::TestApp::new_with_base_path("/hv").await;
let resp = app
@@ -31,8 +31,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn health_not_nested_under_base_path() {
+ common::require_db!();
let app = common::app::TestApp::new_with_base_path("/hv").await;
let resp = app
@@ -52,8 +52,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn config_accessible_under_base_path() {
+ common::require_db!();
let app = common::app::TestApp::new_with_base_path("/hv").await;
let resp = app
@@ -74,8 +74,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn config_not_at_root_when_base_path_set() {
+ common::require_db!();
let app = common::app::TestApp::new_with_base_path("/hv").await;
let resp = app
@@ -96,8 +96,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn redirect_includes_base_path_prefix() {
+ common::require_db!();
let app = common::app::TestApp::new_with_base_path("/hv").await;
let resp = app
diff --git a/tests/e2e_delegation.rs b/tests/e2e_delegation.rs
--- a/tests/e2e_delegation.rs
+++ b/tests/e2e_delegation.rs
@@ -296,6 +296,7 @@
#[tokio::test]
#[serial]
async fn link_account_success() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:owner1";
let target_did = "did:plc:studio1";
@@ -325,6 +326,7 @@
#[tokio::test]
#[serial]
async fn link_account_already_linked() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:owner2";
let target_did = "did:plc:studio2";
@@ -348,6 +350,7 @@
#[tokio::test]
#[serial]
async fn link_account_self_link_rejected() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let user_did = "did:plc:selflinker";
@@ -368,6 +371,7 @@
#[tokio::test]
#[serial]
async fn link_account_no_session_for_target() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:owner3";
let target_did = "did:plc:nosession";
@@ -394,6 +398,7 @@
#[tokio::test]
#[serial]
async fn unlink_account_success() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:unlink_owner";
let target_did = "did:plc:unlink_studio";
@@ -430,6 +435,7 @@
#[tokio::test]
#[serial]
async fn unlink_account_non_owner_rejected() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:unlink_owner2";
let admin_did = "did:plc:unlink_admin2";
@@ -472,6 +478,7 @@
#[tokio::test]
#[serial]
async fn add_delegate_success() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:add_owner";
let member_did = "did:plc:add_member";
@@ -518,6 +525,7 @@
#[tokio::test]
#[serial]
async fn add_delegate_owner_role_rejected() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:add_owner2";
let target_did = "did:plc:add_studio2";
@@ -540,6 +548,7 @@
#[tokio::test]
#[serial]
async fn add_delegate_already_exists() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:add_owner3";
let member_did = "did:plc:add_member3";
@@ -576,6 +585,7 @@
#[tokio::test]
#[serial]
async fn add_delegate_member_cannot_add() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:add_owner4";
let member_did = "did:plc:add_member4";
@@ -618,6 +628,7 @@
#[tokio::test]
#[serial]
async fn remove_delegate_success() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:rm_owner";
let member_did = "did:plc:rm_member";
@@ -669,6 +680,7 @@
#[tokio::test]
#[serial]
async fn remove_delegate_cannot_remove_owner() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:rm_owner2";
let target_did = "did:plc:rm_studio2";
@@ -691,6 +703,7 @@
#[tokio::test]
#[serial]
async fn remove_delegate_admin_cannot_remove_admin() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:rm_owner3";
let admin1_did = "did:plc:rm_admin3a";
@@ -742,6 +755,7 @@
#[tokio::test]
#[serial]
async fn list_accounts_returns_linked_accounts() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:list_owner";
let studio1 = "did:plc:list_studio1";
@@ -781,6 +795,7 @@
#[tokio::test]
#[serial]
async fn list_accounts_empty() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let user_did = "did:plc:no_accounts";
@@ -807,6 +822,7 @@
#[tokio::test]
#[serial]
async fn get_account_not_a_delegate() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:ga_owner";
let target_did = "did:plc:ga_studio";
@@ -839,6 +855,7 @@
#[tokio::test]
#[serial]
async fn list_delegates_member_cannot_list() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:ld_owner";
let member_did = "did:plc:ld_member";
@@ -969,6 +986,7 @@
#[tokio::test]
#[serial]
async fn delegated_write_non_delegate_rejected() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
seed_procedure_lexicon(&app).await;
@@ -995,6 +1013,7 @@
#[tokio::test]
#[serial]
async fn delegated_write_member_rejected() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
seed_procedure_lexicon(&app).await;
@@ -1039,6 +1058,7 @@
#[tokio::test]
#[serial]
async fn delegated_write_owner_success() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
seed_procedure_lexicon(&app).await;
@@ -1086,6 +1106,7 @@
#[tokio::test]
#[serial]
async fn delegated_write_admin_success() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
seed_procedure_lexicon(&app).await;
@@ -1148,6 +1169,7 @@
#[tokio::test]
#[serial]
async fn admin_can_add_delegate() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:acd_owner";
@@ -1211,6 +1233,7 @@
#[tokio::test]
#[serial]
async fn admin_can_remove_member() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:arm_owner";
@@ -1276,6 +1299,7 @@
#[tokio::test]
#[serial]
async fn admin_can_list_delegates() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:ald_owner";
@@ -1319,6 +1343,7 @@
#[tokio::test]
#[serial]
async fn member_can_view_account() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:mva_owner";
@@ -1362,6 +1387,7 @@
#[tokio::test]
#[serial]
async fn owner_can_remove_admin() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:ora_owner";
@@ -1419,6 +1445,7 @@
#[tokio::test]
#[serial]
async fn cross_client_get_account_rejected() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:xc_ga_owner";
let target_did = "did:plc:xc_ga_studio";
@@ -1444,6 +1471,7 @@
#[tokio::test]
#[serial]
async fn cross_client_add_delegate_rejected() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:xc_ad_owner";
let target_did = "did:plc:xc_ad_studio";
@@ -1467,6 +1495,7 @@
#[tokio::test]
#[serial]
async fn cross_client_delegated_write_rejected() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
seed_procedure_lexicon(&app).await;
@@ -1505,6 +1534,7 @@
#[tokio::test]
#[serial]
async fn cross_client_list_accounts_isolated() {
+ common::require_db!();
let app = common::app::TestApp::new_with_encryption().await;
let owner_did = "did:plc:xc_la_owner";
let studio1 = "did:plc:xc_la_studio1";
diff --git a/tests/e2e_domains.rs b/tests/e2e_domains.rs
--- a/tests/e2e_domains.rs
+++ b/tests/e2e_domains.rs
@@ -96,8 +96,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn domains_list_returns_seeded_domain() {
+ common::require_db!();
let app = TestApp::new().await;
seed_domain(&app, "primary-id", "http://127.0.0.1:0", true).await;
@@ -119,8 +119,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn domains_create_and_delete() {
+ common::require_db!();
let app = TestApp::new().await;
seed_domain(&app, "primary-id", "http://127.0.0.1:0", true).await;
@@ -175,8 +175,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn domains_duplicate_url_returns_400() {
+ common::require_db!();
let app = TestApp::new().await;
seed_domain(&app, "primary-id", "http://127.0.0.1:0", true).await;
@@ -203,8 +203,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn domains_cannot_delete_primary() {
+ common::require_db!();
let app = TestApp::new().await;
seed_domain(&app, "primary-id", "http://127.0.0.1:0", true).await;
@@ -229,8 +229,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn domains_set_primary() {
+ common::require_db!();
let app = TestApp::new().await;
seed_domain(&app, "id-a", "http://127.0.0.1:0", true).await;
@@ -278,8 +278,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn unknown_host_returns_421_on_domain_scoped_routes() {
+ common::require_db!();
let app = TestApp::new().await;
// No domains seeded — cache is empty
@@ -300,8 +300,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn health_check_bypasses_domain_resolution() {
+ common::require_db!();
let app = TestApp::new().await;
// No domains seeded — cache is empty
@@ -322,8 +322,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn domain_scoped_route_works_with_known_host() {
+ common::require_db!();
let app = TestApp::new().await;
// Domain.host() for "http://localhost:3000" is "localhost:3000"
diff --git a/tests/e2e_feature_flags.rs b/tests/e2e_feature_flags.rs
--- a/tests/e2e_feature_flags.rs
+++ b/tests/e2e_feature_flags.rs
@@ -53,8 +53,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn space_routes_blocked_when_flag_disabled() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -76,8 +76,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn space_routes_allowed_after_enabling_flag() {
+ common::require_db!();
let app = TestApp::new().await;
// Enable the feature flag
@@ -116,8 +116,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn space_routes_blocked_again_after_disabling_flag() {
+ common::require_db!();
let app = TestApp::new().await;
// Enable
@@ -165,8 +165,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_feature_flags_lists_flags() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -192,8 +192,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_feature_flags_reflects_enabled_state() {
+ common::require_db!();
let app = TestApp::new().await;
// Enable the flag
@@ -228,8 +228,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn config_endpoint_includes_features() {
+ common::require_db!();
let app = TestApp::new().await;
// Default: spaces disabled
@@ -282,8 +282,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn admin_feature_flags_requires_auth() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
diff --git a/tests/e2e_health.rs b/tests/e2e_health.rs
--- a/tests/e2e_health.rs
+++ b/tests/e2e_health.rs
@@ -8,8 +8,8 @@ use tower::ServiceExt;
#[tokio::test]
#[serial]
-#[ignore]
async fn health_returns_200_ok() {
+ common::require_db!();
let app = common::app::TestApp::new().await;
let resp = app
diff --git a/tests/e2e_labelers.rs b/tests/e2e_labelers.rs
--- a/tests/e2e_labelers.rs
+++ b/tests/e2e_labelers.rs
@@ -76,8 +76,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn labeler_add_returns_201() {
+ common::require_db!();
let app = TestApp::new().await;
let body = json!({ "did": "did:plc:labeler1" });
@@ -94,8 +94,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn labeler_add_upsert_reactivates() {
+ common::require_db!();
let app = TestApp::new().await;
let body = json!({ "did": "did:plc:labeler1" });
@@ -151,8 +151,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn labeler_list_empty() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -169,8 +169,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn labeler_list_returns_added() {
+ common::require_db!();
let app = TestApp::new().await;
app.router
@@ -217,8 +217,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn labeler_update_status() {
+ common::require_db!();
let app = TestApp::new().await;
app.router
@@ -261,8 +261,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn labeler_update_not_found() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -285,8 +285,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn labeler_delete_returns_204() {
+ common::require_db!();
let app = TestApp::new().await;
app.router
@@ -327,8 +327,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn labeler_delete_not_found() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -346,8 +346,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn labeler_delete_removes_labels() {
+ common::require_db!();
let app = TestApp::new().await;
let backend = app.state.db_backend;
@@ -405,8 +405,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn labeler_no_auth_returns_401() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
diff --git a/tests/e2e_network_lexicons.rs b/tests/e2e_network_lexicons.rs
--- a/tests/e2e_network_lexicons.rs
+++ b/tests/e2e_network_lexicons.rs
@@ -72,8 +72,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn network_lexicon_list_empty() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -90,8 +90,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn network_lexicon_list_returns_seeded() {
+ common::require_db!();
let app = TestApp::new().await;
seed_network_lexicon(&app, "games.gamesgamesgamesgames.game", "did:plc:authority").await;
@@ -113,8 +113,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn network_lexicon_delete_removes_tracking_and_lexicon() {
+ common::require_db!();
let app = TestApp::new().await;
let backend = app.state.db_backend;
@@ -149,8 +149,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn network_lexicon_delete_not_found() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -168,8 +168,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn network_lexicon_no_auth_returns_401() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
diff --git a/tests/e2e_proxy_config.rs b/tests/e2e_proxy_config.rs
--- a/tests/e2e_proxy_config.rs
+++ b/tests/e2e_proxy_config.rs
@@ -41,8 +41,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn get_proxy_config_returns_default() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -60,8 +60,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn put_and_get_allowlist() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -98,8 +98,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn disabled_mode_clears_nsids() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -132,8 +132,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn invalid_mode_rejected() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -155,8 +155,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn invalid_nsid_rejected() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -178,8 +178,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn requires_auth() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
diff --git a/tests/e2e_settings.rs b/tests/e2e_settings.rs
--- a/tests/e2e_settings.rs
+++ b/tests/e2e_settings.rs
@@ -61,8 +61,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn settings_crud() {
+ common::require_db!();
let app = TestApp::new().await;
// PUT a setting
@@ -134,8 +134,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn settings_requires_auth() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -155,8 +155,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn logo_upload_and_serve() {
+ common::require_db!();
let app = TestApp::new().await;
let boundary = "----testboundary";
@@ -260,8 +260,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn client_metadata_includes_settings() {
+ common::require_db!();
let app = TestApp::new().await;
// PUT app_name setting
@@ -306,8 +306,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn client_metadata_client_id_matches_path() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -332,46 +332,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
-async fn client_metadata_scope_overridden_by_setting() {
- let app = TestApp::new().await;
-
- let resp = app
- .router
- .clone()
- .oneshot(admin_put(
- "/admin/settings/oauth_scopes",
- app.admin_cookie(),
- &json!({ "value": "atproto include:com.example.foo\n include:com.example.bar" }),
- ))
- .await
- .unwrap();
- assert!(resp.status().is_success());
-
- let resp = app
- .router
- .clone()
- .oneshot(
- Request::builder()
- .uri("/oauth-client-metadata.json")
- .body(Body::empty())
- .unwrap(),
- )
- .await
- .unwrap();
- assert_eq!(resp.status(), StatusCode::OK);
- let json = json_body(resp).await;
- assert_eq!(
- json["scope"], "atproto include:com.example.foo include:com.example.bar",
- "expected normalized scope string, got {:?}",
- json["scope"]
- );
-}
-
-#[tokio::test]
-#[serial]
-#[ignore]
async fn client_metadata_client_uri_overridden_by_setting() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
diff --git a/tests/e2e_xrpc.rs b/tests/e2e_xrpc.rs
--- a/tests/e2e_xrpc.rs
+++ b/tests/e2e_xrpc.rs
@@ -109,8 +109,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn profile_no_auth_returns_401() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -128,58 +128,14 @@
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
}
-#[tokio::test]
-#[serial]
-#[ignore]
-async fn profile_with_mocked_services_returns_200() {
- let app = TestApp::new().await;
- let did = &app.admin_did;
-
- // Mock PLC directory
- Mock::given(method("GET"))
- .and(path(format!("/{did}")))
- .respond_with(
- ResponseTemplate::new(200)
- .set_body_json(fixtures::did_document(did, &app.mock_server.uri())),
- )
- .mount(&app.mock_server)
- .await;
-
- // Mock PDS getRecord for profile
- Mock::given(method("GET"))
- .and(path("/xrpc/com.atproto.repo.getRecord"))
- .respond_with(ResponseTemplate::new(200).set_body_json(fixtures::profile_record()))
- .mount(&app.mock_server)
- .await;
-
- let cookie = app.admin_cookie();
- let resp = app
- .router
- .clone()
- .oneshot(
- Request::builder()
- .uri("/xrpc/app.bsky.actor.getProfile")
- .header(cookie.0, cookie.1)
- .body(Body::empty())
- .unwrap(),
- )
- .await
- .unwrap();
-
- assert_eq!(resp.status(), StatusCode::OK);
- let json = json_body(resp).await;
- assert_eq!(json["did"], did.as_str());
- assert_eq!(json["displayName"], "Test User");
-}
-
// ---------------------------------------------------------------------------
// Catch-all GET (queries)
// ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn xrpc_get_unknown_method_proxies_and_returns_bad_gateway() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
@@ -188,6 +144,7 @@ .clone()
.oneshot(
Request::builder()
.uri("/xrpc/nonexistent.method")
+ .header("x-client-key", "hvc_test")
.body(Body::empty())
.unwrap(),
)
@@ -201,8 +158,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn xrpc_get_non_query_returns_400() {
+ common::require_db!();
let app = TestApp::new().await;
seed_lexicons(&app).await;
@@ -213,6 +170,7 @@ .clone()
.oneshot(
Request::builder()
.uri("/xrpc/games.gamesgamesgamesgames.game")
+ .header("x-client-key", "hvc_test")
.body(Body::empty())
.unwrap(),
)
@@ -224,8 +182,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn xrpc_get_single_record_by_uri() {
+ common::require_db!();
let app = TestApp::new().await;
seed_lexicons(&app).await;
@@ -253,6 +211,7 @@ .uri(format!(
"/xrpc/games.gamesgamesgamesgames.listGames?uri={}",
urlencoding::encode(uri)
))
+ .header("x-client-key", "hvc_test")
.body(Body::empty())
.unwrap(),
)
@@ -267,8 +226,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn xrpc_get_record_not_found() {
+ common::require_db!();
let app = TestApp::new().await;
seed_lexicons(&app).await;
@@ -277,6 +236,7 @@ .router.clone()
.oneshot(
Request::builder()
.uri("/xrpc/games.gamesgamesgamesgames.listGames?uri=at%3A%2F%2Fdid%3Aplc%3Anone%2Fgames.gamesgamesgamesgames.game%2Fmissing")
+ .header("x-client-key", "hvc_test")
.body(Body::empty())
.unwrap(),
)
@@ -288,8 +248,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn xrpc_get_list_with_pagination() {
+ common::require_db!();
let app = TestApp::new().await;
seed_lexicons(&app).await;
@@ -326,6 +286,7 @@ .clone()
.oneshot(
Request::builder()
.uri("/xrpc/games.gamesgamesgamesgames.listGames?limit=2")
+ .header("x-client-key", "hvc_test")
.body(Body::empty())
.unwrap(),
)
@@ -347,6 +308,7 @@ Request::builder()
.uri(format!(
"/xrpc/games.gamesgamesgamesgames.listGames?limit=2&cursor={cursor}"
))
+ .header("x-client-key", "hvc_test")
.body(Body::empty())
.unwrap(),
)
@@ -361,8 +323,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn xrpc_get_list_filtered_by_did() {
+ common::require_db!();
let app = TestApp::new().await;
seed_lexicons(&app).await;
@@ -402,6 +364,7 @@ .clone()
.oneshot(
Request::builder()
.uri("/xrpc/games.gamesgamesgamesgames.listGames?did=did:plc:a")
+ .header("x-client-key", "hvc_test")
.body(Body::empty())
.unwrap(),
)
@@ -421,8 +384,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn xrpc_post_no_auth_returns_401() {
+ common::require_db!();
let app = TestApp::new().await;
seed_lexicons(&app).await;
@@ -445,37 +408,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
-async fn xrpc_post_non_procedure_returns_400() {
- let app = TestApp::new().await;
- seed_lexicons(&app).await;
-
- // Use cookie auth for did:plc:test so auth passes
- let (cookie_name, cookie_val) =
- common::auth::admin_cookie_header("did:plc:test", &app.state.cookie_key);
-
- let resp = app
- .router
- .clone()
- .oneshot(
- Request::builder()
- .method("POST")
- .uri("/xrpc/games.gamesgamesgamesgames.listGames")
- .header(cookie_name, cookie_val)
- .header("content-type", "application/json")
- .body(Body::from(b"{}".to_vec()))
- .unwrap(),
- )
- .await
- .unwrap();
-
- assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
-}
-
-#[tokio::test]
-#[serial]
-#[ignore]
async fn xrpc_delete_procedure_removes_record() {
+ common::require_db!();
let app = TestApp::new().await;
seed_lexicons(&app).await;
let backend = app.state.db_backend;
@@ -576,8 +510,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn upload_lexicon_with_invalid_action_returns_400() {
+ common::require_db!();
let app = TestApp::new().await;
let resp = app
diff --git a/tests/lua_atproto_api.rs b/tests/lua_atproto_api.rs
--- a/tests/lua_atproto_api.rs
+++ b/tests/lua_atproto_api.rs
@@ -84,7 +84,9 @@ oauth: std::sync::Arc::new(happyview::auth::OAuthClientRegistry::new(
std::sync::Arc::new(oauth),
)),
oauth_state_store: happyview::auth::oauth_store::DbStateStore::new(pool.clone(), backend),
- cookie_key: axum_extra::extract::cookie::Key::derive_from(b"test-secret"),
+ cookie_key: axum_extra::extract::cookie::Key::derive_from(
+ b"test-secret-that-is-at-least-32-bytes-long",
+ ),
plugin_registry: std::sync::Arc::new(happyview::plugin::PluginRegistry::new()),
wasm_runtime: std::sync::Arc::new(
happyview::plugin::WasmRuntime::new().expect("wasm runtime"),
@@ -170,8 +172,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn get_labels_returns_external_labels() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -217,8 +219,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn get_labels_filters_expired() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -266,8 +268,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn get_labels_includes_self_labels() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -309,8 +311,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn get_labels_empty_for_unlabeled_record() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -346,8 +348,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn get_labels_batch_returns_labels_per_uri() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -408,8 +410,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn get_labels_batch_empty_for_no_labels() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -439,8 +441,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn label_negation_removes_row() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -498,8 +500,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn label_upsert_is_idempotent() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
diff --git a/tests/lua_db_api.rs b/tests/lua_db_api.rs
--- a/tests/lua_db_api.rs
+++ b/tests/lua_db_api.rs
@@ -87,7 +87,9 @@ oauth: std::sync::Arc::new(happyview::auth::OAuthClientRegistry::new(
std::sync::Arc::new(oauth),
)),
oauth_state_store: happyview::auth::oauth_store::DbStateStore::new(pool.clone(), backend),
- cookie_key: axum_extra::extract::cookie::Key::derive_from(b"test-secret"),
+ cookie_key: axum_extra::extract::cookie::Key::derive_from(
+ b"test-secret-that-is-at-least-32-bytes-long",
+ ),
plugin_registry: std::sync::Arc::new(happyview::plugin::PluginRegistry::new()),
wasm_runtime: std::sync::Arc::new(
happyview::plugin::WasmRuntime::new().expect("wasm runtime"),
@@ -165,8 +167,8 @@ // ---------------------------------------------------------------------------
#[tokio::test]
#[serial]
-#[ignore]
async fn db_get_returns_record() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -189,8 +191,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn db_get_returns_nil_for_missing() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -208,8 +210,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn db_query_returns_records() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -229,8 +231,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn db_query_respects_limit() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -254,8 +256,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn db_count_returns_total() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -274,8 +276,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn db_count_with_did_filter() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -294,8 +296,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn db_search_finds_matching() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
@@ -318,8 +320,8 @@ }
#[tokio::test]
#[serial]
-#[ignore]
async fn db_raw_select_works() {
+ common::require_db!();
let pool = db::test_pool().await;
let backend = db::test_backend();
db::truncate_all(&pool).await;
diff --git a/tests/plugin_logging.rs b/tests/plugin_logging.rs
--- a/tests/plugin_logging.rs
+++ b/tests/plugin_logging.rs
@@ -14,6 +14,7 @@ /// Wait briefly for detached `tokio::spawn` tasks to flush writes.
/// The log() function spawns fire-and-forget tasks; we need to yield
/// until they complete before querying.
async fn flush_spawned_tasks() {
+ common::require_db!();
for _ in 0..20 {
tokio::task::yield_now().await;
tokio::time::sleep(tokio::time::Duration::from_millis(25)).await;
@@ -23,6 +24,7 @@
#[tokio::test]
#[serial]
async fn plugin_log_writes_all_four_levels_to_event_logs() {
+ common::require_db!();
let pool = test_pool().await;
let backend = test_backend();
truncate_all(&pool).await;
@@ -103,6 +105,7 @@
#[tokio::test]
#[serial]
async fn plugin_log_with_none_db_does_not_write_event_log() {
+ common::require_db!();
let pool = test_pool().await;
let backend = test_backend();
truncate_all(&pool).await;