mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-19 00:34:15 +00:00
More work on the pds notifs
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
mod common;
|
||||
use common::{base_url, client, create_account_and_login, get_db_connection_string};
|
||||
use bspds::notifications::{NewNotification, NotificationType, enqueue_notification};
|
||||
use serde_json::{Value, json};
|
||||
use sqlx::PgPool;
|
||||
|
||||
async fn get_pool() -> PgPool {
|
||||
let conn_str = get_db_connection_string().await;
|
||||
sqlx::postgres::PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect(&conn_str)
|
||||
.await
|
||||
.expect("Failed to connect to test database")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_notification_history() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let pool = get_pool().await;
|
||||
let (token, did) = create_account_and_login(&client).await;
|
||||
|
||||
let user_id: uuid::Uuid = sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", did)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("User not found");
|
||||
|
||||
for i in 0..3 {
|
||||
let notification = NewNotification::email(
|
||||
user_id,
|
||||
NotificationType::Welcome,
|
||||
"test@example.com".to_string(),
|
||||
format!("Subject {}", i),
|
||||
format!("Body {}", i),
|
||||
);
|
||||
enqueue_notification(&pool, notification).await.expect("Failed to enqueue");
|
||||
}
|
||||
|
||||
let resp = client
|
||||
.get(format!("{}/xrpc/com.bspds.account.getNotificationHistory", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(resp.status(), 200);
|
||||
let body: Value = resp.json().await.unwrap();
|
||||
let notifications = body["notifications"].as_array().unwrap();
|
||||
assert_eq!(notifications.len(), 5);
|
||||
|
||||
assert_eq!(notifications[0]["subject"], "Subject 2");
|
||||
assert_eq!(notifications[1]["subject"], "Subject 1");
|
||||
assert_eq!(notifications[2]["subject"], "Subject 0");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_verify_channel_discord() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (token, did) = create_account_and_login(&client).await;
|
||||
|
||||
let prefs = json!({
|
||||
"discordId": "123456789"
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.updateNotificationPrefs", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&prefs)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), 200);
|
||||
let body: Value = resp.json().await.unwrap();
|
||||
assert!(body["verificationRequired"].as_array().unwrap().contains(&json!("discord")));
|
||||
|
||||
let pool = get_pool().await;
|
||||
let user_id: uuid::Uuid = sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", did)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("User not found");
|
||||
|
||||
let code: String = sqlx::query_scalar!(
|
||||
"SELECT code FROM channel_verifications WHERE user_id = $1 AND channel = 'discord'",
|
||||
user_id
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("Verification code not found");
|
||||
|
||||
let input = json!({
|
||||
"channel": "discord",
|
||||
"code": code
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&input)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let resp = client
|
||||
.get(format!("{}/xrpc/com.bspds.account.getNotificationPrefs", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let body: Value = resp.json().await.unwrap();
|
||||
assert_eq!(body["discordVerified"], true);
|
||||
assert_eq!(body["discordId"], "123456789");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_verify_channel_invalid_code() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (token, _did) = create_account_and_login(&client).await;
|
||||
|
||||
let prefs = json!({
|
||||
"telegramUsername": "testuser"
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.updateNotificationPrefs", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&prefs)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let input = json!({
|
||||
"channel": "telegram",
|
||||
"code": "000000"
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&input)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), 400);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_verify_channel_not_set() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (token, _did) = create_account_and_login(&client).await;
|
||||
|
||||
let input = json!({
|
||||
"channel": "signal",
|
||||
"code": "123456"
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&input)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), 400);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_email_via_notification_prefs() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let pool = get_pool().await;
|
||||
let (token, did) = create_account_and_login(&client).await;
|
||||
|
||||
let prefs = json!({
|
||||
"email": "newemail@example.com"
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.updateNotificationPrefs", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&prefs)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), 200);
|
||||
let body: Value = resp.json().await.unwrap();
|
||||
assert!(body["verificationRequired"].as_array().unwrap().contains(&json!("email")));
|
||||
|
||||
let user_id: uuid::Uuid = sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", did)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("User not found");
|
||||
|
||||
let code: String = sqlx::query_scalar!(
|
||||
"SELECT code FROM channel_verifications WHERE user_id = $1 AND channel = 'email'",
|
||||
user_id
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("Verification code not found");
|
||||
|
||||
let input = json!({
|
||||
"channel": "email",
|
||||
"code": code
|
||||
});
|
||||
let resp = client
|
||||
.post(format!("{}/xrpc/com.bspds.account.confirmChannelVerification", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.json(&input)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let resp = client
|
||||
.get(format!("{}/xrpc/com.bspds.account.getNotificationPrefs", base))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let body: Value = resp.json().await.unwrap();
|
||||
assert_eq!(body["email"], "newemail@example.com");
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
mod common;
|
||||
use common::{base_url, client, create_account_and_login};
|
||||
use serde_json::Value;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_server_stats() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let (token1, _) = create_account_and_login(&client).await;
|
||||
|
||||
let (_, _) = create_account_and_login(&client).await;
|
||||
|
||||
let resp = client
|
||||
.get(format!("{}/xrpc/com.bspds.admin.getServerStats", base))
|
||||
.header("Authorization", format!("Bearer {}", token1))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(resp.status(), 200);
|
||||
let body: Value = resp.json().await.unwrap();
|
||||
|
||||
let user_count = body["userCount"].as_i64().unwrap();
|
||||
assert!(user_count >= 2);
|
||||
|
||||
assert!(body["repoCount"].is_number());
|
||||
assert!(body["recordCount"].is_number());
|
||||
assert!(body["blobStorageBytes"].is_number());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_server_stats_no_auth() {
|
||||
let client = client();
|
||||
let base = base_url().await;
|
||||
let resp = client
|
||||
.get(format!("{}/xrpc/com.bspds.admin.getServerStats", base))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), 401);
|
||||
}
|
||||
+9
-6
@@ -79,6 +79,9 @@ pub async fn base_url() -> &'static str {
|
||||
SERVER_URL.get_or_init(|| {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
std::thread::spawn(move || {
|
||||
unsafe {
|
||||
std::env::set_var("BSPDS_ALLOW_INSECURE_SECRETS", "1");
|
||||
}
|
||||
if std::env::var("DOCKER_HOST").is_err() {
|
||||
if let Ok(runtime_dir) = std::env::var("XDG_RUNTIME_DIR") {
|
||||
let podman_sock = std::path::Path::new(&runtime_dir).join("podman/podman.sock");
|
||||
@@ -406,13 +409,13 @@ pub async fn verify_new_account(client: &Client, did: &str) -> String {
|
||||
.await
|
||||
.expect("Failed to connect to test database");
|
||||
let verification_code: String = sqlx::query_scalar!(
|
||||
"SELECT email_confirmation_code FROM users WHERE did = $1",
|
||||
"SELECT code FROM channel_verifications WHERE user_id = (SELECT id FROM users WHERE did = $1) AND channel = 'email'",
|
||||
did
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("Failed to get verification code")
|
||||
.expect("No verification code found");
|
||||
.expect("Failed to get verification code");
|
||||
|
||||
let confirm_payload = json!({
|
||||
"did": did,
|
||||
"verificationCode": verification_code
|
||||
@@ -548,13 +551,13 @@ pub async fn create_account_and_login(client: &Client) -> (String, String) {
|
||||
.await
|
||||
.expect("Failed to connect to test database");
|
||||
let verification_code: String = sqlx::query_scalar!(
|
||||
"SELECT email_confirmation_code FROM users WHERE did = $1",
|
||||
"SELECT code FROM channel_verifications WHERE user_id = (SELECT id FROM users WHERE did = $1) AND channel = 'email'",
|
||||
&did
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("Failed to get verification code")
|
||||
.expect("No verification code found");
|
||||
.expect("Failed to get verification code");
|
||||
|
||||
let confirm_payload = json!({
|
||||
"did": did,
|
||||
"verificationCode": verification_code
|
||||
|
||||
+34
-19
@@ -59,19 +59,20 @@ async fn test_email_update_flow_success() {
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let body: Value = res.json().await.expect("Invalid JSON");
|
||||
assert_eq!(body["tokenRequired"], true);
|
||||
let user = sqlx::query!(
|
||||
"SELECT email_pending_verification, email_confirmation_code, email FROM users WHERE handle = $1",
|
||||
|
||||
let verification = sqlx::query!(
|
||||
"SELECT pending_identifier, code FROM channel_verifications WHERE user_id = (SELECT id FROM users WHERE handle = $1) AND channel = 'email'",
|
||||
handle
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("User not found");
|
||||
.expect("Verification not found");
|
||||
|
||||
assert_eq!(
|
||||
user.email_pending_verification.as_deref(),
|
||||
verification.pending_identifier.as_deref(),
|
||||
Some(new_email.as_str())
|
||||
);
|
||||
assert!(user.email_confirmation_code.is_some());
|
||||
let code = user.email_confirmation_code.unwrap();
|
||||
let code = verification.code;
|
||||
let res = client
|
||||
.post(format!("{}/xrpc/com.atproto.server.confirmEmail", base_url))
|
||||
.bearer_auth(&access_jwt)
|
||||
@@ -84,15 +85,22 @@ async fn test_email_update_flow_success() {
|
||||
.expect("Failed to confirm email");
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let user = sqlx::query!(
|
||||
"SELECT email, email_pending_verification, email_confirmation_code FROM users WHERE handle = $1",
|
||||
"SELECT email FROM users WHERE handle = $1",
|
||||
handle
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("User not found");
|
||||
assert_eq!(user.email, Some(new_email));
|
||||
assert!(user.email_pending_verification.is_none());
|
||||
assert!(user.email_confirmation_code.is_none());
|
||||
|
||||
let verification = sqlx::query!(
|
||||
"SELECT code FROM channel_verifications WHERE user_id = (SELECT id FROM users WHERE handle = $1) AND channel = 'email'",
|
||||
handle
|
||||
)
|
||||
.fetch_optional(&pool)
|
||||
.await
|
||||
.expect("DB error");
|
||||
assert!(verification.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -174,14 +182,14 @@ async fn test_confirm_email_wrong_email() {
|
||||
.await
|
||||
.expect("Failed to request email update");
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let user = sqlx::query!(
|
||||
"SELECT email_confirmation_code FROM users WHERE handle = $1",
|
||||
let verification = sqlx::query!(
|
||||
"SELECT code FROM channel_verifications WHERE user_id = (SELECT id FROM users WHERE handle = $1) AND channel = 'email'",
|
||||
handle
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("User not found");
|
||||
let code = user.email_confirmation_code.unwrap();
|
||||
.expect("Verification not found");
|
||||
let code = verification.code;
|
||||
let res = client
|
||||
.post(format!("{}/xrpc/com.atproto.server.confirmEmail", base_url))
|
||||
.bearer_auth(&access_jwt)
|
||||
@@ -293,14 +301,14 @@ async fn test_update_email_with_valid_token() {
|
||||
.await
|
||||
.expect("Failed to request email update");
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let user = sqlx::query!(
|
||||
"SELECT email_confirmation_code FROM users WHERE handle = $1",
|
||||
let verification = sqlx::query!(
|
||||
"SELECT code FROM channel_verifications WHERE user_id = (SELECT id FROM users WHERE handle = $1) AND channel = 'email'",
|
||||
handle
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("User not found");
|
||||
let code = user.email_confirmation_code.unwrap();
|
||||
.expect("Verification not found");
|
||||
let code = verification.code;
|
||||
let res = client
|
||||
.post(format!("{}/xrpc/com.atproto.server.updateEmail", base_url))
|
||||
.bearer_auth(&access_jwt)
|
||||
@@ -313,14 +321,21 @@ async fn test_update_email_with_valid_token() {
|
||||
.expect("Failed to update email");
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let user = sqlx::query!(
|
||||
"SELECT email, email_pending_verification FROM users WHERE handle = $1",
|
||||
"SELECT email FROM users WHERE handle = $1",
|
||||
handle
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("User not found");
|
||||
assert_eq!(user.email, Some(new_email));
|
||||
assert!(user.email_pending_verification.is_none());
|
||||
let verification = sqlx::query!(
|
||||
"SELECT code FROM channel_verifications WHERE user_id = (SELECT id FROM users WHERE handle = $1) AND channel = 'email'",
|
||||
handle
|
||||
)
|
||||
.fetch_optional(&pool)
|
||||
.await
|
||||
.expect("DB error");
|
||||
assert!(verification.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -872,13 +872,12 @@ async fn test_jwt_security_refresh_token_replay_protection() {
|
||||
.await
|
||||
.expect("Failed to connect to test database");
|
||||
let verification_code: String = sqlx::query_scalar!(
|
||||
"SELECT email_confirmation_code FROM users WHERE did = $1",
|
||||
"SELECT code FROM channel_verifications WHERE user_id = (SELECT id FROM users WHERE did = $1) AND channel = 'email'",
|
||||
did
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.expect("Failed to get verification code")
|
||||
.expect("No verification code found");
|
||||
.expect("Failed to get verification code");
|
||||
let confirm_res = http_client
|
||||
.post(format!("{}/xrpc/com.atproto.server.confirmSignup", url))
|
||||
.json(&json!({
|
||||
|
||||
Reference in New Issue
Block a user