- Updated user and thought models to utilize UUIDs for primary keys. - Modified persistence functions to accommodate UUIDs for user and thought IDs. - Implemented tag functionality with new Tag and ThoughtTag models. - Added migration scripts to create new tables for tags and thought-tag relationships. - Enhanced thought creation to parse hashtags and link them to thoughts. - Updated tests to reflect changes in user and thought ID types.
150 lines
4.8 KiB
Rust
150 lines
4.8 KiB
Rust
use crate::api::main::{create_user_with_password, setup};
|
|
use axum::http::{header, StatusCode};
|
|
use http_body_util::BodyExt;
|
|
use serde_json::{json, Value};
|
|
use utils::testing::{
|
|
make_get_request, make_jwt_request, make_post_request, make_request_with_headers,
|
|
};
|
|
|
|
#[tokio::test]
|
|
async fn test_webfinger_discovery() {
|
|
let app = setup().await;
|
|
create_user_with_password(&app.db, "testuser", "password123").await;
|
|
|
|
// 1. Valid WebFinger lookup for existing user
|
|
let url = "/.well-known/webfinger?resource=acct:testuser@localhost:3000";
|
|
let response = make_get_request(app.router.clone(), url, None).await;
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let v: Value = serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(v["subject"], "acct:testuser@localhost:3000");
|
|
assert_eq!(
|
|
v["links"][0]["href"],
|
|
"http://localhost:3000/users/testuser"
|
|
);
|
|
|
|
// 2. WebFinger lookup for a non-existent user
|
|
let response = make_get_request(
|
|
app.router.clone(),
|
|
"/.well-known/webfinger?resource=acct:nobody@localhost:3000",
|
|
None,
|
|
)
|
|
.await;
|
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_user_actor_endpoint() {
|
|
let app = setup().await;
|
|
create_user_with_password(&app.db, "testuser", "password123").await;
|
|
|
|
let response = make_request_with_headers(
|
|
app.router.clone(),
|
|
"/users/testuser",
|
|
"GET",
|
|
None,
|
|
vec![(
|
|
header::ACCEPT,
|
|
"application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
|
|
)],
|
|
).await;
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let content_type = response.headers().get(header::CONTENT_TYPE).unwrap();
|
|
assert_eq!(content_type, "application/activity+json");
|
|
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let v: Value = serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(v["type"], "Person");
|
|
assert_eq!(v["preferredUsername"], "testuser");
|
|
assert_eq!(v["id"], "http://localhost:3000/users/testuser");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_user_inbox_follow() {
|
|
let app = setup().await;
|
|
// user1 will be followed
|
|
let user1 = create_user_with_password(&app.db, "user1", "password123").await;
|
|
// user2 will be the follower
|
|
let user2 = create_user_with_password(&app.db, "user2", "password123").await;
|
|
|
|
// Construct a follow activity from user2, targeting user1
|
|
let follow_activity = json!({
|
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
"id": "http://localhost:3000/some-unique-id",
|
|
"type": "Follow",
|
|
"actor": "http://localhost:3000/users/user2", // The actor is user2
|
|
"object": "http://localhost:3000/users/user1"
|
|
})
|
|
.to_string();
|
|
|
|
// POST the activity to user1's inbox
|
|
let response = make_post_request(
|
|
app.router.clone(),
|
|
"/users/user1/inbox",
|
|
follow_activity,
|
|
None,
|
|
)
|
|
.await;
|
|
|
|
assert_eq!(response.status(), StatusCode::ACCEPTED);
|
|
|
|
// Verify that user2 is now following user1 in the database
|
|
let followers = app::persistence::follow::get_followed_ids(&app.db, user2.id)
|
|
.await
|
|
.unwrap();
|
|
assert!(
|
|
followers.contains(&user1.id),
|
|
"User2 should be following user1"
|
|
);
|
|
|
|
let following = app::persistence::follow::get_followed_ids(&app.db, user1.id)
|
|
.await
|
|
.unwrap();
|
|
assert!(
|
|
!following.contains(&user2.id),
|
|
"User1 should now be followed by user2"
|
|
);
|
|
assert!(following.is_empty(), "User1 should not be following anyone");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_user_outbox_get() {
|
|
let app = setup().await;
|
|
create_user_with_password(&app.db, "testuser", "password123").await;
|
|
let token = super::main::login_user(app.router.clone(), "testuser", "password123").await;
|
|
|
|
// Create a thought first
|
|
let thought_body = json!({ "content": "This is a federated thought!" }).to_string();
|
|
make_jwt_request(
|
|
app.router.clone(),
|
|
"/thoughts",
|
|
"POST",
|
|
Some(thought_body),
|
|
&token,
|
|
)
|
|
.await;
|
|
|
|
// Now, fetch the outbox
|
|
let response = make_request_with_headers(
|
|
app.router.clone(),
|
|
"/users/testuser/outbox",
|
|
"GET",
|
|
None,
|
|
vec![(header::ACCEPT, "application/activity+json")],
|
|
)
|
|
.await;
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let v: Value = serde_json::from_slice(&body).unwrap();
|
|
|
|
assert_eq!(v["type"], "OrderedCollection");
|
|
assert_eq!(v["totalItems"], 1);
|
|
assert_eq!(v["orderedItems"][0]["type"], "Create");
|
|
assert_eq!(
|
|
v["orderedItems"][0]["object"]["content"],
|
|
"This is a federated thought!"
|
|
);
|
|
}
|