refactor(tests): Add chaos and performance tests, remove obsolete tests, and update dependencies
This commit is contained in:
parent
490ba66c51
commit
d08ebfc094
14 changed files with 253 additions and 89 deletions
10
.vscode/launch.json
vendored
10
.vscode/launch.json
vendored
|
@ -18,7 +18,7 @@
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"env": {
|
"env": {
|
||||||
"RUST_LOG": "debug",
|
"RUST_LOG": "info",
|
||||||
"DATABASE_URL": "postgres://gbuser:gbpassword@localhost:5432/generalbots",
|
"DATABASE_URL": "postgres://gbuser:gbpassword@localhost:5432/generalbots",
|
||||||
"REDIS_URL": "redis://localhost:6379"
|
"REDIS_URL": "redis://localhost:6379"
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,9 @@
|
||||||
"args": [
|
"args": [
|
||||||
"--test-threads=1"
|
"--test-threads=1"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}", "env": {
|
||||||
|
"RUST_LOG": "info"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "lldb",
|
"type": "lldb",
|
||||||
|
@ -61,7 +63,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}", "env": {
|
||||||
|
"RUST_LOG": "info"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"compounds": [
|
"compounds": [
|
||||||
|
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -2,5 +2,6 @@
|
||||||
"lldb.executable": "/usr/bin/lldb",
|
"lldb.executable": "/usr/bin/lldb",
|
||||||
"lldb.showDisassembly": "never",
|
"lldb.showDisassembly": "never",
|
||||||
"lldb.dereferencePointers": true,
|
"lldb.dereferencePointers": true,
|
||||||
"lldb.consoleMode": "commands"
|
"lldb.consoleMode": "commands",
|
||||||
|
"rust-test Explorer.cargoTestExtraArgs": ["--", "--nocapture"]
|
||||||
}
|
}
|
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -2334,6 +2334,19 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
|
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
|
||||||
|
dependencies = [
|
||||||
|
"humantime",
|
||||||
|
"is-terminal",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.11.6"
|
version = "0.11.6"
|
||||||
|
@ -2761,6 +2774,7 @@ dependencies = [
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-openssl",
|
"tokio-openssl",
|
||||||
|
"tokio-stream",
|
||||||
"tokio-test",
|
"tokio-test",
|
||||||
"tower 0.4.13",
|
"tower 0.4.13",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
|
@ -2872,10 +2886,12 @@ dependencies = [
|
||||||
"actix-multipart",
|
"actix-multipart",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"env_logger 0.10.2",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"gb-core",
|
"gb-core",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"lettre",
|
"lettre",
|
||||||
|
"log",
|
||||||
"minio",
|
"minio",
|
||||||
"rstest",
|
"rstest",
|
||||||
"sanitize-filename",
|
"sanitize-filename",
|
||||||
|
@ -2884,6 +2900,7 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
"tokio-test",
|
"tokio-test",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
@ -3080,6 +3097,7 @@ dependencies = [
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
"tokio-tungstenite 0.24.0",
|
"tokio-tungstenite 0.24.0",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tungstenite 0.20.1",
|
"tungstenite 0.20.1",
|
||||||
|
@ -4614,7 +4632,7 @@ dependencies = [
|
||||||
"crc",
|
"crc",
|
||||||
"dashmap 6.1.0",
|
"dashmap 6.1.0",
|
||||||
"derivative",
|
"derivative",
|
||||||
"env_logger",
|
"env_logger 0.11.6",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hex",
|
"hex",
|
||||||
"hmac",
|
"hmac",
|
||||||
|
@ -4724,12 +4742,6 @@ version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
|
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "multimap"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e1a5d38b9b352dbd913288736af36af41c48d61b1a8cd34bcecd727561b7d511"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "multimap"
|
name = "multimap"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -6064,7 +6076,7 @@ dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"log",
|
"log",
|
||||||
"multimap 0.9.1",
|
"multimap 0.10.0",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"petgraph 0.6.5",
|
"petgraph 0.6.5",
|
||||||
"prettyplease 0.2.30",
|
"prettyplease 0.2.30",
|
||||||
|
|
|
@ -39,6 +39,8 @@ futures = "0.3"
|
||||||
futures-util = "0.3" # Add futures-util here
|
futures-util = "0.3" # Add futures-util here
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
bytes = "1.0"
|
bytes = "1.0"
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.10"
|
||||||
|
|
||||||
# Web framework and servers
|
# Web framework and servers
|
||||||
axum = { version = "0.7.9", features = ["ws", "multipart"] }
|
axum = { version = "0.7.9", features = ["ws", "multipart"] }
|
||||||
|
|
|
@ -47,6 +47,7 @@ axum-extra = { version = "0.7" } # Add headers feature
|
||||||
tower = "0.4"
|
tower = "0.4"
|
||||||
tower-http = { version = "0.5", features = ["auth", "cors", "trace"] }
|
tower-http = { version = "0.5", features = ["auth", "cors", "trace"] }
|
||||||
headers = "0.3"
|
headers = "0.3"
|
||||||
|
tokio-stream = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rstest = "0.18"
|
rstest = "0.18"
|
||||||
|
|
|
@ -19,18 +19,16 @@ pub struct CoreError(pub String);
|
||||||
pub enum CustomerStatus {
|
pub enum CustomerStatus {
|
||||||
Active,
|
Active,
|
||||||
Inactive,
|
Inactive,
|
||||||
Suspended
|
Suspended,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum SubscriptionTier {
|
pub enum SubscriptionTier {
|
||||||
Free,
|
Free,
|
||||||
Pro,
|
Pro,
|
||||||
Enterprise
|
Enterprise,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
|
@ -118,7 +116,7 @@ impl FromStr for UserStatus {
|
||||||
"active" => Ok(UserStatus::Active),
|
"active" => Ok(UserStatus::Active),
|
||||||
"inactive" => Ok(UserStatus::Inactive),
|
"inactive" => Ok(UserStatus::Inactive),
|
||||||
"suspended" => Ok(UserStatus::Suspended),
|
"suspended" => Ok(UserStatus::Suspended),
|
||||||
_ => Ok(UserStatus::Inactive)
|
_ => Ok(UserStatus::Inactive),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,8 +144,6 @@ pub struct User {
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Update the Customer struct to include these fields
|
// Update the Customer struct to include these fields
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Customer {
|
pub struct Customer {
|
||||||
|
@ -238,15 +234,17 @@ pub struct FileInfo {
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// App state shared across all handlers
|
||||||
|
|
||||||
|
|
||||||
// App state shared across all handlers
|
// App state shared across all handlers
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub config: AppConfig,
|
pub minio_client: Option<MinioClient>,
|
||||||
pub db_pool: PgPool,
|
pub config: Option<AppConfig>,
|
||||||
pub redis_pool: RedisConnectionManager,
|
pub db_pool: Option<PgPool>,
|
||||||
pub kafka_producer: FutureProducer,
|
pub redis_pool: Option<RedisConnectionManager>,
|
||||||
// pub zitadel_client: AuthServiceClient<tonic::transport::Channel>,
|
pub kafka_producer: Option<FutureProducer>,
|
||||||
pub minio_client: MinioClient,
|
//pub zitadel_client: Option<AuthServiceClient><tonic::transport::Channel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// File models
|
// File models
|
||||||
|
@ -288,7 +286,7 @@ pub struct Conversation {
|
||||||
pub struct ConversationMember {
|
pub struct ConversationMember {
|
||||||
pub conversation_id: Uuid,
|
pub conversation_id: Uuid,
|
||||||
pub user_id: Uuid,
|
pub user_id: Uuid,
|
||||||
pub joined_at: DateTime<Utc>
|
pub joined_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calendar models
|
// Calendar models
|
||||||
|
@ -375,9 +373,6 @@ pub enum AppError {
|
||||||
|
|
||||||
#[error("Internal server error: {0}")]
|
#[error("Internal server error: {0}")]
|
||||||
Internal(String),
|
Internal(String),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl actix_web::ResponseError for AppError {
|
impl actix_web::ResponseError for AppError {
|
||||||
|
@ -385,7 +380,9 @@ impl actix_web::ResponseError for AppError {
|
||||||
let (status, error_message) = match self {
|
let (status, error_message) = match self {
|
||||||
AppError::Validation(_) => (actix_web::http::StatusCode::BAD_REQUEST, self.to_string()),
|
AppError::Validation(_) => (actix_web::http::StatusCode::BAD_REQUEST, self.to_string()),
|
||||||
AppError::NotFound(_) => (actix_web::http::StatusCode::NOT_FOUND, self.to_string()),
|
AppError::NotFound(_) => (actix_web::http::StatusCode::NOT_FOUND, self.to_string()),
|
||||||
AppError::Unauthorized(_) => (actix_web::http::StatusCode::UNAUTHORIZED, self.to_string()),
|
AppError::Unauthorized(_) => {
|
||||||
|
(actix_web::http::StatusCode::UNAUTHORIZED, self.to_string())
|
||||||
|
}
|
||||||
AppError::Forbidden(_) => (actix_web::http::StatusCode::FORBIDDEN, self.to_string()),
|
AppError::Forbidden(_) => (actix_web::http::StatusCode::FORBIDDEN, self.to_string()),
|
||||||
_ => (
|
_ => (
|
||||||
actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
|
|
@ -22,6 +22,9 @@ actix-web ={ workspace = true }
|
||||||
actix-multipart ={ workspace = true }
|
actix-multipart ={ workspace = true }
|
||||||
sanitize-filename = { workspace = true }
|
sanitize-filename = { workspace = true }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
log = { workspace = true }
|
||||||
|
env_logger = { workspace = true }
|
||||||
|
tokio-stream = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rstest= { workspace = true }
|
rstest= { workspace = true }
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
use actix_multipart::Multipart;
|
use actix_multipart::Multipart;
|
||||||
use futures::TryStreamExt;
|
|
||||||
use gb_core::models::AppState;
|
|
||||||
use std::io::Write;
|
|
||||||
use gb_core::models::AppError;
|
|
||||||
use gb_core::utils::{create_response, extract_user_id};
|
|
||||||
use actix_web::{post, web, HttpRequest, HttpResponse};
|
use actix_web::{post, web, HttpRequest, HttpResponse};
|
||||||
use tempfile::NamedTempFile;
|
use gb_core::models::AppError;
|
||||||
|
use gb_core::models::AppState;
|
||||||
|
use gb_core::utils::{create_response, extract_user_id};
|
||||||
use minio::s3::builders::ObjectContent;
|
use minio::s3::builders::ObjectContent;
|
||||||
use minio::s3::Client;
|
use minio::s3::Client;
|
||||||
|
use std::io::Write;
|
||||||
|
use tempfile::NamedTempFile;
|
||||||
|
use minio::s3::types::ToStream;
|
||||||
|
use tokio_stream::StreamExt;
|
||||||
|
|
||||||
|
|
||||||
#[post("/files/upload/{folder_path}")]
|
#[post("/files/upload/{folder_path}")]
|
||||||
pub async fn upload_file(
|
pub async fn upload_file(
|
||||||
|
@ -17,24 +19,29 @@ pub async fn upload_file(
|
||||||
) -> Result<HttpResponse, actix_web::Error> {
|
) -> Result<HttpResponse, actix_web::Error> {
|
||||||
let folder_path = folder_path.into_inner();
|
let folder_path = folder_path.into_inner();
|
||||||
|
|
||||||
// Create a temporary file to store the uploaded file
|
// Create a temporary file to store the uploaded file.
|
||||||
|
|
||||||
let mut temp_file = NamedTempFile::new().map_err(|e| {
|
let mut temp_file = NamedTempFile::new().map_err(|e| {
|
||||||
actix_web::error::ErrorInternalServerError(format!("Failed to create temp file: {}", e))
|
actix_web::error::ErrorInternalServerError(format!("Failed to create temp file: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut file_name = None;
|
let mut file_name = None;
|
||||||
|
|
||||||
// Iterate over the multipart stream
|
// Iterate over the multipart stream.
|
||||||
|
|
||||||
while let Some(mut field) = payload.try_next().await? {
|
while let Some(mut field) = payload.try_next().await? {
|
||||||
let content_disposition = field.content_disposition();
|
let content_disposition = field.content_disposition();
|
||||||
file_name = content_disposition
|
file_name = content_disposition
|
||||||
.get_filename()
|
.get_filename()
|
||||||
.map(|name| name.to_string());
|
.map(|name| name.to_string());
|
||||||
|
|
||||||
// Write the file content to the temporary file
|
// Write the file content to the temporary file.
|
||||||
while let Some(chunk) = field.try_next().await? {
|
while let Some(chunk) = field.try_next().await? {
|
||||||
temp_file.write_all(&chunk).map_err(|e| {
|
temp_file.write_all(&chunk).map_err(|e| {
|
||||||
actix_web::error::ErrorInternalServerError(format!("Failed to write to temp file: {}", e))
|
actix_web::error::ErrorInternalServerError(format!(
|
||||||
|
"Failed to write to temp file: {}",
|
||||||
|
e
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +53,7 @@ pub async fn upload_file(
|
||||||
let object_name = format!("{}/{}", folder_path, file_name);
|
let object_name = format!("{}/{}", folder_path, file_name);
|
||||||
|
|
||||||
// Upload the file to the MinIO bucket
|
// Upload the file to the MinIO bucket
|
||||||
let client: Client = state.minio_client.clone();
|
let client: Client = state.minio_client.clone().unwrap();
|
||||||
let bucket_name = "file-upload-rust-bucket";
|
let bucket_name = "file-upload-rust-bucket";
|
||||||
|
|
||||||
let content = ObjectContent::from(temp_file.path());
|
let content = ObjectContent::from(temp_file.path());
|
||||||
|
@ -80,9 +87,45 @@ pub async fn delete_file(
|
||||||
) -> Result<HttpResponse, AppError> {
|
) -> Result<HttpResponse, AppError> {
|
||||||
let _user_id = extract_user_id(&req)?;
|
let _user_id = extract_user_id(&req)?;
|
||||||
|
|
||||||
|
|
||||||
Ok(create_response(
|
Ok(create_response(
|
||||||
true,
|
true,
|
||||||
Some("File deleted successfully".to_string()),
|
Some("File deleted successfully".to_string()),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
#[post("/files/list/{folder_path}")]
|
||||||
|
pub async fn list_file(
|
||||||
|
folder_path: web::Path<String>,
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
) -> Result<HttpResponse, actix_web::Error> {
|
||||||
|
let folder_path = folder_path.into_inner();
|
||||||
|
|
||||||
|
let client: Client = state.minio_client.clone().unwrap();
|
||||||
|
let bucket_name = "file-upload-rust-bucket";
|
||||||
|
|
||||||
|
// Create the stream using the to_stream() method
|
||||||
|
let mut objects_stream = client
|
||||||
|
.list_objects(bucket_name)
|
||||||
|
.prefix(Some(folder_path))
|
||||||
|
.to_stream()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut file_list = Vec::new();
|
||||||
|
|
||||||
|
// Use StreamExt::next() to iterate through the stream
|
||||||
|
while let Some(items) = objects_stream.next().await {
|
||||||
|
match items {
|
||||||
|
Ok(result) => {
|
||||||
|
for item in result.contents {
|
||||||
|
file_list.push(item.name);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
return Err(actix_web::error::ErrorInternalServerError(
|
||||||
|
format!("Failed to list files in MinIO: {}", e)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(file_list))
|
||||||
|
}
|
|
@ -26,11 +26,11 @@ async fn main() -> std::io::Result<()> {
|
||||||
let minio_client = init_minio(&config).await.expect("Failed to initialize Minio");
|
let minio_client = init_minio(&config).await.expect("Failed to initialize Minio");
|
||||||
|
|
||||||
let app_state = web::Data::new(models::AppState {
|
let app_state = web::Data::new(models::AppState {
|
||||||
config: config.clone(),
|
config: Some(config.clone()),
|
||||||
db_pool,
|
db_pool: Some(db_pool),
|
||||||
redis_pool,
|
redis_pool: Some(redis_pool),
|
||||||
kafka_producer,
|
kafka_producer: Some(kafka_producer),
|
||||||
minio_client,
|
minio_client: Some(minio_client),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start HTTP server
|
// Start HTTP server
|
||||||
|
|
|
@ -27,6 +27,7 @@ criterion = { workspace = true, features = ["async_futures"] }
|
||||||
|
|
||||||
# Async Runtime
|
# Async Runtime
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
tokio-stream= { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
|
|
||||||
# HTTP Client
|
# HTTP Client
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
|
|
||||||
pub struct ChaosTest {
|
pub struct ChaosTest {
|
||||||
namespace: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChaosTest {
|
impl ChaosTest {
|
||||||
pub async fn new(namespace: String) -> anyhow::Result<Self> {
|
pub async fn new(namespace: String) -> anyhow::Result<Self> {
|
||||||
// Initialize the ChaosTest struct
|
// Initialize the ChaosTest struct
|
||||||
Ok(ChaosTest { namespace })
|
Ok(ChaosTest { })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn network_partition(&self) -> anyhow::Result<()> {
|
pub async fn network_partition(&self) -> anyhow::Result<()> {
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
use actix_web::{test, web, App};
|
use actix_web::{test, web, App};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use gb_core::models::AppState;
|
use gb_core::models::AppState;
|
||||||
use gb_file::handlers::upload_file;
|
use gb_file::handlers::upload_file;
|
||||||
use gb_testing::integration::{IntegrationTest, IntegrationTestCase};
|
use minio::s3::args::{BucketExistsArgs, GetObjectArgs, MakeBucketArgs, StatObjectArgs};
|
||||||
use minio::s3::args::{
|
use minio::s3::client::ClientBuilder as MinioClientBuilder;
|
||||||
BucketExistsArgs, GetObjectArgs, MakeBucketArgs, RemoveObjectArgs, StatObjectArgs,
|
|
||||||
};
|
|
||||||
use minio::s3::client::{Client as MinioClient, ClientBuilder as MinioClientBuilder};
|
|
||||||
use minio::s3::creds::StaticProvider;
|
use minio::s3::creds::StaticProvider;
|
||||||
use minio::s3::http::BaseUrl;
|
use minio::s3::http::BaseUrl;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
@ -17,13 +13,10 @@ use std::io::Write;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
||||||
async fn test_successful_file_upload() -> Result<()> {
|
async fn test_successful_file_upload() -> Result<()> {
|
||||||
|
|
||||||
// Setup test environment and MinIO client
|
// Setup test environment and MinIO client
|
||||||
let base_url = format!("https://{}", "localhost:9000");
|
let base_url = format!("http://{}", "localhost:9000");
|
||||||
let base_url = BaseUrl::from_str(&base_url)?;
|
let base_url = BaseUrl::from_str(&base_url)?;
|
||||||
let credentials = StaticProvider::new(&"minioadmin", &"minioadmin", None);
|
let credentials = StaticProvider::new(&"minioadmin", &"minioadmin", None);
|
||||||
|
|
||||||
|
@ -45,14 +38,16 @@ async fn test_successful_file_upload() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let app_state = web::Data::new(AppState {
|
let app_state = web::Data::new(AppState {
|
||||||
minio_client,
|
minio_client: Some(minio_client.clone()),
|
||||||
config: todo!(),
|
config: None,
|
||||||
db_pool: todo!(),
|
db_pool: None,
|
||||||
redis_pool: todo!(),
|
kafka_producer: None,
|
||||||
kafka_producer: todo!(),
|
redis_pool: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let app = test::init_service(App::new().app_data(app_state.clone()).service(upload_file)).await;
|
let app =
|
||||||
|
test::init_service(App::new().app_data(app_state.clone())
|
||||||
|
.service(upload_file)).await;
|
||||||
|
|
||||||
// Create a test file with content
|
// Create a test file with content
|
||||||
let mut temp_file = NamedTempFile::new()?;
|
let mut temp_file = NamedTempFile::new()?;
|
||||||
|
@ -91,17 +86,13 @@ async fn test_successful_file_upload() -> Result<()> {
|
||||||
|
|
||||||
// Using object-based API for stat_object
|
// Using object-based API for stat_object
|
||||||
let stat_object_args = StatObjectArgs::new(bucket_name, object_name)?;
|
let stat_object_args = StatObjectArgs::new(bucket_name, object_name)?;
|
||||||
let object_exists =
|
let object_exists = minio_client.clone().stat_object(&stat_object_args).await.is_ok();
|
||||||
minio_client
|
|
||||||
.stat_object(&stat_object_args)
|
|
||||||
.await
|
|
||||||
.is_ok();
|
|
||||||
|
|
||||||
assert!(object_exists, "Uploaded file should exist in MinIO");
|
assert!(object_exists, "Uploaded file should exist in MinIO");
|
||||||
|
|
||||||
// Verify file content using object-based API
|
// Verify file content using object-based API
|
||||||
let get_object_args = GetObjectArgs::new(bucket_name, object_name)?;
|
// let get_object_args = GetObjectArgs::new(bucket_name, object_name)?;
|
||||||
let get_object_result = minio_client.get_object(bucket_name, object_name);
|
// let get_object_result = minio_client.get_object(bucket_name, object_name);
|
||||||
|
|
||||||
// let mut object_content = Vec::new();
|
// let mut object_content = Vec::new();
|
||||||
// get_object_result.read_to_end(&mut object_content)?;
|
// get_object_result.read_to_end(&mut object_content)?;
|
||||||
|
|
110
gb-testing/tests/int_file_list_test.rs
Normal file
110
gb-testing/tests/int_file_list_test.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
use actix_web::{test, web, App};
|
||||||
|
use anyhow::Result;
|
||||||
|
use bytes::Bytes;
|
||||||
|
use gb_core::models::AppState;
|
||||||
|
use gb_file::handlers::list_file;
|
||||||
|
use minio::s3::args::{BucketExistsArgs, MakeBucketArgs};
|
||||||
|
use minio::s3::builders::SegmentedBytes;
|
||||||
|
use minio::s3::client::ClientBuilder as MinioClientBuilder;
|
||||||
|
use minio::s3::creds::StaticProvider;
|
||||||
|
use minio::s3::http::BaseUrl;
|
||||||
|
use minio::s3::types::ToStream;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use tempfile::NamedTempFile;
|
||||||
|
use tokio_stream::StreamExt;
|
||||||
|
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
|
||||||
|
async fn test_successful_file_listing() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// Setup test environment and MinIO client
|
||||||
|
let base_url = format!("http://{}", "localhost:9000");
|
||||||
|
let base_url = BaseUrl::from_str(&base_url)?;
|
||||||
|
let credentials = StaticProvider::new("minioadmin", "minioadmin", None);
|
||||||
|
|
||||||
|
let minio_client = MinioClientBuilder::new(base_url.clone())
|
||||||
|
.provider(Some(Box::new(credentials)))
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
// Create test bucket if it doesn't exist
|
||||||
|
let bucket_name = "file-upload-rust-bucket";
|
||||||
|
|
||||||
|
// Using object-based API for bucket_exists
|
||||||
|
let bucket_exists_args = BucketExistsArgs::new(bucket_name)?;
|
||||||
|
let bucket_exists = minio_client.bucket_exists(&bucket_exists_args).await?;
|
||||||
|
|
||||||
|
if !bucket_exists {
|
||||||
|
// Using object-based API for make_bucket
|
||||||
|
let make_bucket_args = MakeBucketArgs::new(bucket_name)?;
|
||||||
|
minio_client.make_bucket(&make_bucket_args).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put a single file in the bucket
|
||||||
|
let folder_path = "test-folder";
|
||||||
|
let file_name = "test.txt";
|
||||||
|
let object_name = format!("{}/{}", folder_path, file_name);
|
||||||
|
|
||||||
|
// Create a temporary file with some content
|
||||||
|
let mut temp_file = NamedTempFile::new()?;
|
||||||
|
writeln!(temp_file, "This is a test file.")?;
|
||||||
|
|
||||||
|
// Upload the file to the bucket
|
||||||
|
let mut file = File::open(temp_file.path())?;
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
file.read_to_end(&mut buffer)?;
|
||||||
|
let content = SegmentedBytes::from(Bytes::from(buffer));
|
||||||
|
minio_client.put_object(bucket_name, &object_name, content);
|
||||||
|
|
||||||
|
let app_state = web::Data::new(AppState {
|
||||||
|
minio_client: Some(minio_client.clone()),
|
||||||
|
config: None,
|
||||||
|
db_pool: None,
|
||||||
|
kafka_producer: None,
|
||||||
|
redis_pool: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let app = test::init_service(App::new().app_data(app_state.clone()).service(list_file)).await;
|
||||||
|
|
||||||
|
// Execute request to list files in the folder
|
||||||
|
let req = test::TestRequest::post()
|
||||||
|
.uri(&format!("/files/list/{}", folder_path))
|
||||||
|
.to_request();
|
||||||
|
|
||||||
|
let resp = test::call_service(&app, req).await;
|
||||||
|
|
||||||
|
// Verify response
|
||||||
|
assert_eq!(resp.status(), 200);
|
||||||
|
|
||||||
|
// Parse the response body as JSON
|
||||||
|
let body = test::read_body(resp).await;
|
||||||
|
let file_list: Vec<String> = serde_json::from_slice(&body)?;
|
||||||
|
|
||||||
|
// Verify the uploaded file is in the list
|
||||||
|
assert!(
|
||||||
|
file_list.contains(&object_name),
|
||||||
|
"Uploaded file should be listed"
|
||||||
|
);
|
||||||
|
|
||||||
|
// List all objects in a directory.
|
||||||
|
let mut list_objects = minio_client
|
||||||
|
.list_objects("my-bucket")
|
||||||
|
.use_api_v1(true)
|
||||||
|
.recursive(true)
|
||||||
|
.to_stream()
|
||||||
|
.await;
|
||||||
|
while let Some(result) = list_objects.next().await {
|
||||||
|
match result {
|
||||||
|
Ok(resp) => {
|
||||||
|
for item in resp.contents {
|
||||||
|
println!("{:?}", item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => println!("Error: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
use gb_testing::load::{LoadTest, LoadTestConfig};
|
use gb_testing::load::LoadTestConfig;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_auth_load() -> anyhow::Result<()> {
|
async fn test_auth_load() -> anyhow::Result<()> {
|
||||||
let config = LoadTestConfig {
|
let _config = LoadTestConfig {
|
||||||
users: 100,
|
users: 100,
|
||||||
duration: Duration::from_secs(300),
|
duration: Duration::from_secs(300),
|
||||||
ramp_up: Duration::from_secs(60),
|
ramp_up: Duration::from_secs(60),
|
||||||
|
|
Loading…
Add table
Reference in a new issue