added integration tests and added message with attachments
This commit is contained in:
+720
@@ -0,0 +1,720 @@
|
||||
use axum::{
|
||||
body::Body,
|
||||
http::{Request, StatusCode, header},
|
||||
};
|
||||
use frangipane::{
|
||||
AppConfig,
|
||||
auth::{create_jwt, hash_password, verify_jwt_string, verify_password},
|
||||
create_app, realtime,
|
||||
routes::{messages::Message, rooms::Room},
|
||||
users::LoginResponse,
|
||||
};
|
||||
use serde_json::json;
|
||||
use sha2::{Digest, Sha256};
|
||||
use sqlx::PgPool;
|
||||
use std::sync::Arc;
|
||||
use tower::ServiceExt;
|
||||
use uuid::Uuid;
|
||||
|
||||
// --- HELPERS ---
|
||||
|
||||
async fn get_test_pool() -> sqlx::PgPool {
|
||||
let database_url = format!("postgres://frangipane:secret@127.0.0.1/frangipane");
|
||||
PgPool::connect(database_url.as_str()).await.unwrap()
|
||||
}
|
||||
|
||||
fn get_test_config() -> Arc<AppConfig> {
|
||||
let mut path = std::env::current_dir().unwrap();
|
||||
path.push("target");
|
||||
path.push("test_uploads");
|
||||
std::fs::create_dir_all(&path).ok();
|
||||
|
||||
Arc::new(AppConfig {
|
||||
avatar_dir: path.clone(),
|
||||
prohibit_registration: false,
|
||||
max_file_size: 1024 * 1024 * 10,
|
||||
uploads_dir: path,
|
||||
})
|
||||
}
|
||||
|
||||
async fn setup_app() -> (axum::Router, sqlx::PgPool) {
|
||||
let _ = tracing_subscriber::fmt()
|
||||
.with_env_filter("debug")
|
||||
.with_test_writer()
|
||||
.try_init();
|
||||
|
||||
let pool = get_test_pool().await;
|
||||
let app = create_app(
|
||||
pool.clone(),
|
||||
get_test_config(),
|
||||
realtime::RealtimeMessages::new(),
|
||||
realtime::RealTimeVoices::new(),
|
||||
false,
|
||||
);
|
||||
(app, pool)
|
||||
}
|
||||
|
||||
/// Helper to register a user and return credentials.
|
||||
async fn register_test_user(app: &mut axum::Router, username: &str, email: &str) -> (String, Uuid) {
|
||||
let payload = json!({
|
||||
"username": username,
|
||||
"email": email,
|
||||
"password": "secure_password_123"
|
||||
});
|
||||
|
||||
let response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/register")
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(serde_json::to_vec(&payload).unwrap()))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let status = response.status();
|
||||
let body_bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if !status.is_success() {
|
||||
panic!(
|
||||
"Registration failed for {} with status {}. Body: {:?}",
|
||||
username,
|
||||
status,
|
||||
String::from_utf8_lossy(&body_bytes)
|
||||
);
|
||||
}
|
||||
|
||||
let data: LoginResponse =
|
||||
serde_json::from_slice(&body_bytes).expect("Failed to parse LoginResponse");
|
||||
(data.token, data.uuid)
|
||||
}
|
||||
|
||||
// --- AUTH TESTS ---
|
||||
|
||||
#[test]
|
||||
fn test_password_hashing_logic() {
|
||||
let pass = "hunter2_extra_safe";
|
||||
let hash = hash_password(pass).unwrap();
|
||||
assert!(verify_password(&hash, pass));
|
||||
assert!(!verify_password(&hash, "wrong_pass"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_jwt_logic() {
|
||||
let id = Uuid::now_v7();
|
||||
let token = create_jwt(id).unwrap();
|
||||
let claims = verify_jwt_string(&token).unwrap();
|
||||
assert_eq!(claims.sub, id);
|
||||
}
|
||||
|
||||
// --- USER / REGISTRATION TESTS ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_api_register_success() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let username = format!("u{}", Uuid::now_v7().simple());
|
||||
let email = format!("{}@example.com", username);
|
||||
|
||||
let (token, _) = register_test_user(&mut app, &username, &email).await;
|
||||
assert!(!token.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_api_register_duplicate_username() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let name = format!("d{}", Uuid::now_v7().simple());
|
||||
let email1 = format!("{}1@test.com", name);
|
||||
let email2 = format!("{}2@test.com", name);
|
||||
|
||||
register_test_user(&mut app, &name, &email1).await;
|
||||
|
||||
let payload = json!({
|
||||
"username": name,
|
||||
"email": email2,
|
||||
"password": "password123"
|
||||
});
|
||||
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/register")
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(serde_json::to_vec(&payload).unwrap()))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::CONFLICT);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_api_register_invalid_email() {
|
||||
let (app, _) = setup_app().await;
|
||||
let payload = json!({
|
||||
"username": "bademail",
|
||||
"email": "not-an-email",
|
||||
"password": "password123"
|
||||
});
|
||||
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/register")
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(serde_json::to_vec(&payload).unwrap()))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_api_login_success() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let id = Uuid::now_v7().simple();
|
||||
let name = format!("l{}", id);
|
||||
let email = format!("{}@test.com", id);
|
||||
register_test_user(&mut app, &name, &email).await;
|
||||
|
||||
let payload = json!({ "email": email, "password": "secure_password_123" });
|
||||
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/login")
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(serde_json::to_vec(&payload).unwrap()))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
// --- ROOM TESTS ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_room_and_list() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let id = Uuid::now_v7().simple();
|
||||
let name = format!("o{}", id);
|
||||
let (token, _) = register_test_user(&mut app, &name, &format!("{}@t.com", id)).await;
|
||||
|
||||
let room_name = "Testing Room";
|
||||
let _ = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/rooms")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_vec(&json!({"name": room_name, "global": false})).unwrap(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("GET")
|
||||
.uri("/rooms")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let body = axum::body::to_bytes(res.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap();
|
||||
let rooms: Vec<Room> = serde_json::from_slice(&body).unwrap();
|
||||
assert!(rooms.iter().any(|r| r.name == room_name));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_room_permission() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let id1 = Uuid::now_v7().simple();
|
||||
let id2 = Uuid::now_v7().simple();
|
||||
let (t1, _) =
|
||||
register_test_user(&mut app, &format!("u1{}", id1), &format!("{}@t.com", id1)).await;
|
||||
let (t2, _) =
|
||||
register_test_user(&mut app, &format!("u2{}", id2), &format!("{}@t.com", id2)).await;
|
||||
|
||||
// u1 creates room
|
||||
let res = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/rooms")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {t1}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_vec(&json!({"name": "KillMe", "global": false})).unwrap(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let room: Room = serde_json::from_slice(
|
||||
&axum::body::to_bytes(res.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// u2 tries to delete it
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("DELETE")
|
||||
.uri(format!("/rooms/{}/delete", room.uuid))
|
||||
.header(header::AUTHORIZATION, format!("Bearer {t2}"))
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
// --- FRIEND TESTS ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_friend_request_self_error() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let id = Uuid::now_v7().simple();
|
||||
let name = format!("s{}", id);
|
||||
let (token, _) = register_test_user(&mut app, &name, &format!("{}@t.com", id)).await;
|
||||
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/friends/request")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_vec(&json!({"receiver_username": name})).unwrap(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_accept_friend_request() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let id1 = Uuid::now_v7().simple();
|
||||
let id2 = Uuid::now_v7().simple();
|
||||
let n1 = format!("f1{}", id1);
|
||||
let n2 = format!("f2{}", id2);
|
||||
let (t1, u1_uuid) = register_test_user(&mut app, &n1, &format!("{}@t.com", id1)).await;
|
||||
let (t2, _) = register_test_user(&mut app, &n2, &format!("{}@t.com", id2)).await;
|
||||
|
||||
// t1 sends to t2
|
||||
let _ = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/friends/request")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {t1}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_vec(&json!({"receiver_username": n2})).unwrap(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// t2 accepts
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/friends/accept")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {t2}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_vec(&json!({"sender_uuid": u1_uuid})).unwrap(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::CREATED);
|
||||
}
|
||||
|
||||
// --- MESSAGE TESTS ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_send_message_not_member() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let id1 = Uuid::now_v7().simple();
|
||||
let id2 = Uuid::now_v7().simple();
|
||||
let n1 = format!("ma{}", id1);
|
||||
let n2 = format!("mb{}", id2);
|
||||
let (t1, _) = register_test_user(&mut app, &n1, &format!("{}@t.com", id1)).await;
|
||||
let (t2, _) = register_test_user(&mut app, &n2, &format!("{}@t.com", id2)).await;
|
||||
|
||||
// t1 creates room
|
||||
let res = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/rooms")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {t1}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_vec(&json!({"name": "Private", "global": false})).unwrap(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let room: Room = serde_json::from_slice(
|
||||
&axum::body::to_bytes(res.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// t2 (non-member) tries to send
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri(format!("/messages/{}", room.uuid))
|
||||
.header(header::AUTHORIZATION, format!("Bearer {t2}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_vec(&json!({"message_type": "text", "content": "spy"})).unwrap(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_send_and_list_messages() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let id = Uuid::now_v7().simple();
|
||||
let n = format!("m{}", id);
|
||||
let (token, _) = register_test_user(&mut app, &n, &format!("{}@t.com", id)).await;
|
||||
|
||||
let res = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/rooms")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_vec(&json!({"name": "Chat", "global": false})).unwrap(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let room: Room = serde_json::from_slice(
|
||||
&axum::body::to_bytes(res.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Send
|
||||
let _ = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri(format!("/messages/{}", room.uuid))
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_vec(&json!({"message_type": "text", "content": "hello"}))
|
||||
.unwrap(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// List
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("GET")
|
||||
.uri(format!("/messages/{}", room.uuid))
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let messages: Vec<Message> = serde_json::from_slice(
|
||||
&axum::body::to_bytes(res.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(messages.len(), 1);
|
||||
}
|
||||
|
||||
// --- MISC / SYSTEM TESTS ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_settings_and_validate() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let id = Uuid::now_v7().simple();
|
||||
let n = format!("st{}", id);
|
||||
let (token, _) = register_test_user(&mut app, &n, &format!("{}@t.com", id)).await;
|
||||
|
||||
let new_id = Uuid::now_v7().simple();
|
||||
let new_name = format!("nw{}", new_id);
|
||||
let new_email = format!("{}@new.com", new_id);
|
||||
let res = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("PUT")
|
||||
.uri("/account/settings")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::to_vec(&json!({
|
||||
"username": new_name,
|
||||
"email": new_email,
|
||||
"password": "new_secure_pass_123"
|
||||
}))
|
||||
.unwrap(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::CREATED);
|
||||
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("GET")
|
||||
.uri("/validate-token")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_version_route() {
|
||||
let (app, _) = setup_app().await;
|
||||
let res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/version")
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_unauthorized_access() {
|
||||
let (app, _) = setup_app().await;
|
||||
let paths = vec![
|
||||
("GET", "/rooms", Body::empty()),
|
||||
("GET", "/friends", Body::empty()),
|
||||
("GET", "/friends/requests", Body::empty()),
|
||||
(
|
||||
"PUT",
|
||||
"/account/settings",
|
||||
Body::from(
|
||||
json!({
|
||||
"username": "a",
|
||||
"email": "a@a.com",
|
||||
"password": "p"
|
||||
})
|
||||
.to_string(),
|
||||
),
|
||||
),
|
||||
];
|
||||
for (method, path, body) in paths {
|
||||
let res = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method(method)
|
||||
.uri(path)
|
||||
.header("content-type", "application/json")
|
||||
.body(body)
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.status(),
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"Path {} should be protected",
|
||||
path
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_upload_large_file_and_verify_hash() {
|
||||
let (mut app, _) = setup_app().await;
|
||||
let id = Uuid::now_v7().simple();
|
||||
let username = format!("u{}", id);
|
||||
let (token, _) = register_test_user(&mut app, &username, &format!("{}@t.com", id)).await;
|
||||
|
||||
// Create a room to upload into
|
||||
let res = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/rooms")
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(
|
||||
json!({"name": "UploadRoom", "global": false}).to_string(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let room: Room = serde_json::from_slice(
|
||||
&axum::body::to_bytes(res.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Prepare file
|
||||
let file_size = 1024 * 1024;
|
||||
let file_bytes: Vec<u8> = (0..file_size).map(|i| (i % 255) as u8).collect();
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&file_bytes);
|
||||
let original_hash = hasher.finalize();
|
||||
|
||||
let boundary = "boundary123";
|
||||
let mut body_content = Vec::new();
|
||||
|
||||
// First boundary
|
||||
body_content.extend_from_slice(format!("--{boundary}\r\n").as_bytes());
|
||||
|
||||
body_content.extend_from_slice(b"Content-Disposition: form-data; name=\"content\"\r\n\r\n");
|
||||
body_content.extend_from_slice(b"Check out this file");
|
||||
body_content.extend_from_slice(b"\r\n"); // CRLF denoting end of field data
|
||||
|
||||
body_content.extend_from_slice(format!("--{boundary}\r\n").as_bytes());
|
||||
body_content.extend_from_slice(
|
||||
b"Content-Disposition: form-data; name=\"file\"; filename=\"test_file.bin\"\r\n",
|
||||
);
|
||||
body_content.extend_from_slice(b"Content-Type: application/octet-stream\r\n\r\n");
|
||||
body_content.extend_from_slice(&file_bytes);
|
||||
body_content.extend_from_slice(b"\r\n"); // CRLF denoting end of field data
|
||||
|
||||
// Final boundary
|
||||
body_content.extend_from_slice(format!("--{boundary}--\r\n").as_bytes());
|
||||
|
||||
let upload_res = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri(format!("/messages/{}/upload", room.uuid))
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.header(
|
||||
header::CONTENT_TYPE,
|
||||
format!("multipart/form-data; boundary={boundary}"),
|
||||
)
|
||||
.body(Body::from(body_content))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(upload_res.status(), StatusCode::CREATED);
|
||||
|
||||
let message_resp: Message = serde_json::from_slice(
|
||||
&axum::body::to_bytes(upload_res.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let file_uuid = message_resp.attachments[0].uuid;
|
||||
|
||||
// Download the file back
|
||||
let download_res = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("GET")
|
||||
.uri(format!("/uploads/{}", file_uuid))
|
||||
.header(header::AUTHORIZATION, format!("Bearer {token}"))
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(download_res.status(), StatusCode::OK);
|
||||
|
||||
let downloaded_bytes = axum::body::to_bytes(download_res.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Verify integrity
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&downloaded_bytes);
|
||||
let downloaded_hash = hasher.finalize();
|
||||
|
||||
assert_eq!(
|
||||
file_bytes.len(),
|
||||
downloaded_bytes.len(),
|
||||
"File sizes must match"
|
||||
);
|
||||
assert_eq!(original_hash, downloaded_hash, "File hashes must match");
|
||||
}
|
||||
Reference in New Issue
Block a user