diff --git a/TODO-LIST.md b/TODO-LIST.md index e88fa587..4bce0f6b 100644 --- a/TODO-LIST.md +++ b/TODO-LIST.md @@ -5,12 +5,12 @@ This document contains the task list for the development of OxiCloud, a minimali ## Phase 1: Basic File Functionalities ### Folder System -- [ ] Implement API for creating folders -- [ ] Add support for hierarchical paths in the backend +- [x] Implement API for creating folders +- [x] Add support for hierarchical paths in the backend - [ ] Update UI to show folder structure (tree) -- [ ] Implement navigation between folders -- [ ] Add functionality to rename folders -- [ ] Add option to move files between folders +- [x] Implement navigation between folders +- [x] Add functionality to rename folders +- [x] Add option to move files between folders ### File Preview - [ ] Implement integrated image viewer @@ -31,7 +31,7 @@ This document contains the task list for the development of OxiCloud, a minimali - [ ] Improve responsive design for mobile devices - [ ] Implement drag & drop between folders - [ ] Add support for multiple file selection -- [ ] Implement multiple file uploads +- [x] Implement multiple file uploads - [ ] Add progress indicators for long operations - [ ] Implement UI notifications for events @@ -53,9 +53,9 @@ This document contains the task list for the development of OxiCloud, a minimali - [ ] Add storage usage monitoring ### Basic Security -- [ ] Implement secure password hashing with Argon2 -- [ ] Add session management -- [ ] Implement JWT authentication token +- [x] Implement secure password hashing with Argon2 +- [x] Add session management +- [x] Implement JWT authentication token - [ ] Add CSRF protection - [ ] Implement login attempt limits - [ ] Create activity logging system @@ -90,8 +90,8 @@ This document contains the task list for the development of OxiCloud, a minimali ### Complete REST API - [ ] Design OpenAPI specification -- [ ] Implement endpoints for file operations -- [ ] Add endpoints for users and authentication +- [x] Implement endpoints for file operations +- [x] Add endpoints for users and authentication - [ ] Implement automatic documentation (Swagger) - [ ] Create API token system - [ ] Implement rate limiting @@ -141,8 +141,8 @@ This document contains the task list for the development of OxiCloud, a minimali ### Backend - [x] Implement file cache with Rust - [x] Enable Link Time Optimization (LTO) for better performance -- [ ] Optimize large file transmission -- [ ] Add adaptive compression by file type +- [x] Optimize large file transmission +- [x] Add adaptive compression by file type - [x] Implement asynchronous processing for heavy tasks - [ ] Optimize database queries - [ ] Implement scaling strategies @@ -158,16 +158,16 @@ This document contains the task list for the development of OxiCloud, a minimali ### Storage - [ ] Research deduplication options - [ ] Implement block storage -- [ ] Add transparent compression by file type +- [x] Add transparent compression by file type - [ ] Implement log rotation and archiving - [ ] Create automated backup system - [ ] Add support for distributed storage ## Infrastructure and Deployment -- [ ] Create Docker configuration +- [x] Create Docker configuration - [ ] Implement CI/CD with GitHub Actions -- [ ] Add automated tests +- [x] Add automated tests - [ ] Create installation documentation - [ ] Implement monitoring and alerts - [ ] Add automatic update system \ No newline at end of file diff --git a/src/application/services/file_service.rs b/src/application/services/file_service.rs index b4122314..88d1e720 100644 --- a/src/application/services/file_service.rs +++ b/src/application/services/file_service.rs @@ -10,25 +10,41 @@ use crate::common::errors::DomainError; use futures::Stream; use bytes::Bytes; -/// Errores específicos del servicio de archivos +/** + * File service-specific error types. + * + * This enum represents the application-level errors that can occur during file operations, + * providing a translation layer between domain/infrastructure errors and application errors. + */ #[derive(Debug, Error)] pub enum FileServiceError { + /// Returned when a requested file cannot be found #[error("Archivo no encontrado: {0}")] NotFound(String), + /// Returned when a file operation conflicts with existing files #[error("Archivo ya existe: {0}")] Conflict(String), + /// Returned when file access fails due to permissions or I/O issues #[error("Error de acceso al archivo: {0}")] AccessError(String), + /// Returned when a file path is invalid #[error("Ruta de archivo inválida: {0}")] InvalidPath(String), + /// Generic internal error for unexpected failures #[error("Error interno: {0}")] InternalError(String), } +/** + * Converts repository errors to service errors. + * + * This implementation maps low-level repository errors to more + * application-appropriate error types, abstracting away the implementation details. + */ impl From for FileServiceError { fn from(err: FileRepositoryError) -> Self { match err { @@ -42,6 +58,12 @@ impl From for FileServiceError { } } +/** + * Converts domain errors to service errors. + * + * This implementation ensures that general domain errors are properly translated + * to file service-specific errors while preserving their semantic meaning. + */ impl From for FileServiceError { fn from(err: DomainError) -> Self { match err.kind { @@ -54,6 +76,12 @@ impl From for FileServiceError { } } +/** + * Converts service errors to domain errors. + * + * This implementation allows service errors to be propagated up the call stack as + * domain errors when crossing architectural boundaries. + */ impl From for DomainError { fn from(err: FileServiceError) -> Self { match err { @@ -66,10 +94,23 @@ impl From for DomainError { } } +/** + * Type alias for results of file service operations. + * + * Provides a convenient way to return either a successful value or a FileServiceError. + */ pub type FileServiceResult = Result; -/// Service for file operations +/** + * Service component for file operations in the application layer. + * + * The FileService implements the application use cases related to files by orchestrating + * domain logic and infrastructure components. It acts as an adapter between the inbound + * ports (interfaces) and outbound ports (repositories), translating between DTOs and + * domain entities. + */ pub struct FileService { + /// Repository responsible for file storage operations file_repository: Arc, } diff --git a/src/application/services/trash_service.rs b/src/application/services/trash_service.rs index 49eddece..11239d50 100644 --- a/src/application/services/trash_service.rs +++ b/src/application/services/trash_service.rs @@ -11,11 +11,30 @@ use crate::domain::repositories::file_repository::FileRepository; use crate::domain::repositories::folder_repository::FolderRepository; use crate::domain::repositories::trash_repository::TrashRepository; -/// Servicio de aplicación para operaciones de papelera +/** + * Application service for trash operations. + * + * The TrashService implements the trash management functionality in the application layer, + * handling movement of files and folders to trash, restoration from trash, and permanent + * deletion. It orchestrates interactions between the domain entities and infrastructure + * repositories while enforcing business rules like retention policies. + * + * This service follows the Clean Architecture pattern by: + * - Depending on domain interfaces rather than concrete implementations + * - Orchestrating domain operations without containing domain logic + * - Exposing its functionality through the TrashUseCase port + */ pub struct TrashService { + /// Repository for trash-specific operations like listing and retrieving trashed items trash_repository: Arc, + + /// Repository for file operations used when trashing, restoring, or deleting files file_repository: Arc, + + /// Repository for folder operations used when trashing, restoring, or deleting folders folder_repository: Arc, + + /// Number of days items should be kept in trash before automatic cleanup retention_days: u32, } diff --git a/src/domain/entities/file.rs b/src/domain/entities/file.rs index a21c7634..694899dd 100644 --- a/src/domain/entities/file.rs +++ b/src/domain/entities/file.rs @@ -1,50 +1,70 @@ use serde::{Serialize, Deserialize}; use crate::domain::services::path_service::StoragePath; -/// Error en la creación o manipulación de entidades de archivo +/** + * Represents errors that can occur during file entity operations. + * + * This enum encapsulates various error conditions that may arise when creating, + * validating, or manipulating file entities in the domain model. + */ #[derive(Debug, thiserror::Error)] pub enum FileError { + /// Occurs when a file name contains invalid characters or is empty. #[error("Nombre de archivo inválido: {0}")] InvalidFileName(String), + /// Occurs when validation fails for any file entity attribute. #[error("Error en la validación: {0}")] #[allow(dead_code)] ValidationError(String), } -/// Tipo de resultado para operaciones con entidades de archivo +/** + * Type alias for results of file entity operations. + * + * Provides a convenient way to return either a successful value or a FileError. + */ pub type FileResult = Result; -/// Represents a file entity in the domain +/** + * Represents a file in the system's domain model. + * + * The File entity is a core domain object that encapsulates all properties and behaviors + * of a file in the system. It implements an immutable design pattern where modification + * operations return new instances rather than modifying the existing one. + * + * This entity maintains both physical storage information and logical metadata about files, + * serving as the bridge between the storage system and the application. + */ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct File { - /// Unique identifier for the file + /// Unique identifier for the file - used throughout the system for file operations id: String, - /// Name of the file + /// Name of the file including extension name: String, - /// Path to the file in the domain model + /// Path to the file in the domain model - not serialized as it contains internal representation #[serde(skip_serializing, skip_deserializing)] storage_path: StoragePath, - /// String representation of the path (for serialization compatibility) + /// String representation of the path for serialization and API compatibility #[serde(rename = "path")] path_string: String, /// Size of the file in bytes size: u64, - /// MIME type of the file + /// MIME type of the file (e.g., "text/plain", "image/jpeg") mime_type: String, - /// Parent folder ID + /// Parent folder ID if the file is within a folder, None if in root folder_id: Option, - /// Creation timestamp + /// Creation timestamp (seconds since UNIX epoch) created_at: u64, - /// Last modification timestamp + /// Last modification timestamp (seconds since UNIX epoch) modified_at: u64, } diff --git a/src/domain/repositories/file_repository.rs b/src/domain/repositories/file_repository.rs index f7012c8c..59819ab1 100644 --- a/src/domain/repositories/file_repository.rs +++ b/src/domain/repositories/file_repository.rs @@ -5,50 +5,89 @@ use crate::common::errors::DomainError; use futures::Stream; use bytes::Bytes; -/// Error types for file repository operations +/** + * Comprehensive error types for file repository operations. + * + * This enum represents all possible error conditions that can occur during file repository + * operations, providing detailed context for error handling across the application. + */ #[derive(Debug, thiserror::Error)] #[allow(dead_code)] pub enum FileRepositoryError { + /// Returned when a requested file cannot be found by ID or path #[error("File not found: {0}")] NotFound(String), + /// Returned when attempting to create a file at a location where one already exists #[error("File already exists: {0}")] AlreadyExists(String), + /// Returned when a provided file path is invalid or malformed #[error("Invalid file path: {0}")] InvalidPath(String), + /// Returned when an operation is not supported by the current implementation #[error("Operation not supported: {0}")] OperationNotSupported(String), + /// Wraps standard I/O errors from the filesystem #[error("IO Error: {0}")] IoError(#[from] std::io::Error), + /// Indicates errors in the path-to-ID mapping system #[error("Mapping error: {0}")] MappingError(String), + /// Specific errors related to ID mapping operations #[error("ID Mapping error: {0}")] IdMappingError(String), + /// Returned when an operation exceeds its timeout threshold #[error("Timeout error: {0}")] Timeout(String), + /// Propagates domain model errors to the repository layer #[error("Domain error: {0}")] DomainError(#[from] DomainError), + /// Catch-all for other unspecified errors #[error("Other error: {0}")] Other(String), } -/// Result type for file repository operations +/** + * Type alias for results of file repository operations. + * + * Provides a consistent return type for all repository methods, containing + * either a successful value or a FileRepositoryError. + */ pub type FileRepositoryResult = Result; -/// Repository interface for file operations (primary port) -/// Esta interfaz define las operaciones de negocio relacionadas con archivos -/// sin exponer detalles de implementación como rutas o sistemas de archivos +/** + * Repository interface defining all file storage operations. + * + * This trait represents the primary port for file operations in the domain model, + * following the hexagonal architecture pattern. It defines the contract that any + * file storage implementation must fulfill, abstracting away implementation details + * like filesystem specifics, cloud storage, or database operations. + * + * All implementations must be thread-safe (Send + Sync) and have a 'static lifetime + * to support the async operations in the system. + */ #[async_trait] pub trait FileRepository: Send + Sync + 'static { - /// Saves a file from bytes + /** + * Creates and saves a new file from binary content. + * + * This method handles new file creation with automatic ID generation, + * content storage, and metadata registration. + * + * @param name The filename with extension + * @param folder_id Optional ID of parent folder, None for root + * @param content_type MIME type of the file + * @param content Binary data of the file + * @return A File entity with generated metadata on success, error otherwise + */ async fn save_file_from_bytes( &self, name: String, @@ -57,7 +96,19 @@ pub trait FileRepository: Send + Sync + 'static { content: Vec, ) -> FileRepositoryResult; - /// Saves a file with a specific ID + /** + * Saves a file with a predetermined ID. + * + * Similar to save_file_from_bytes but allows specifying the ID, + * useful for restoring files or migrations. + * + * @param id Predefined unique ID for the file + * @param name The filename with extension + * @param folder_id Optional ID of parent folder, None for root + * @param content_type MIME type of the file + * @param content Binary data of the file + * @return The created File entity on success, error otherwise + */ #[allow(dead_code)] async fn save_file_with_id( &self, @@ -68,38 +119,109 @@ pub trait FileRepository: Send + Sync + 'static { content: Vec, ) -> FileRepositoryResult; - /// Gets a file by its ID + /** + * Retrieves a file entity by its unique ID. + * + * @param id The unique identifier of the file + * @return The File entity if found, NotFound error otherwise + */ async fn get_file_by_id(&self, id: &str) -> FileRepositoryResult; - /// Lists files in a folder + /** + * Lists all files within a specified folder. + * + * @param folder_id Optional folder ID to list files from, None for root + * @return Vector of File entities in the folder + */ async fn list_files(&self, folder_id: Option<&str>) -> FileRepositoryResult>; - /// Deletes a file + /** + * Deletes a file by ID. + * + * @param id The unique identifier of the file to delete + * @return Success or error + */ async fn delete_file(&self, id: &str) -> FileRepositoryResult<()>; - /// Deletes a file and its entry from mapping systems + /** + * Deletes a file and removes its mapping entries. + * + * More thorough than delete_file as it also purges ID mappings, + * useful for permanent deletions. + * + * @param id The unique identifier of the file to delete + * @return Success or error + */ #[allow(dead_code)] async fn delete_file_entry(&self, id: &str) -> FileRepositoryResult<()>; - /// Gets file content as bytes - use only for small files + /** + * Retrieves the complete file content as a byte vector. + * + * This method loads the entire file into memory, so it should + * only be used for reasonably sized files. + * + * @param id The unique identifier of the file + * @return The file's binary content + */ async fn get_file_content(&self, id: &str) -> FileRepositoryResult>; - /// Gets file content as a stream - better for large files + /** + * Retrieves file content as an asynchronous stream of bytes. + * + * Preferred for large files as it avoids loading everything into memory at once. + * + * @param id The unique identifier of the file + * @return A stream that yields chunks of file data + */ #[allow(clippy::type_complexity)] async fn get_file_stream(&self, id: &str) -> FileRepositoryResult> + Send>>; - /// Moves a file to a different folder + /** + * Moves a file to a different folder. + * + * @param id The unique identifier of the file to move + * @param target_folder_id The destination folder ID, None for root + * @return The updated File entity after the move + */ async fn move_file(&self, id: &str, target_folder_id: Option) -> FileRepositoryResult; - /// Gets the storage path for a file + /** + * Retrieves the storage path for a file. + * + * @param id The unique identifier of the file + * @return The StoragePath object representing the file's location + */ async fn get_file_path(&self, id: &str) -> FileRepositoryResult; - /// Moves a file to trash + /** + * Moves a file to the trash system. + * + * Instead of permanent deletion, this marks the file as trashed + * and relocates it to the trash storage area. + * + * @param file_id The unique identifier of the file to trash + * @return Success or error + */ async fn move_to_trash(&self, file_id: &str) -> FileRepositoryResult<()>; - /// Restores a file from trash + /** + * Restores a file from the trash to its original location. + * + * @param file_id The unique identifier of the file to restore + * @param original_path The original path where the file was located before trashing + * @return Success or error + */ async fn restore_from_trash(&self, file_id: &str, original_path: &str) -> FileRepositoryResult<()>; - /// Permanently deletes a file (used for trash cleanup) + /** + * Permanently deletes a file from the trash system. + * + * This operation is not reversible and removes the file completely. + * Used primarily by the trash cleanup service. + * + * @param file_id The unique identifier of the file to permanently delete + * @return Success or error + */ async fn delete_file_permanently(&self, file_id: &str) -> FileRepositoryResult<()>; } \ No newline at end of file diff --git a/src/domain/services/auth_service.rs b/src/domain/services/auth_service.rs index fdf8986c..dbb11cb6 100644 --- a/src/domain/services/auth_service.rs +++ b/src/domain/services/auth_service.rs @@ -6,39 +6,78 @@ use chrono::Utc; use crate::domain::entities::user::User; use crate::common::errors::{DomainError, ErrorKind}; -// Reclamaciones JWT +/** + * JWT claims structure for authentication tokens. + * + * This structure represents the payload of JWT tokens used for authentication + * in the system. It contains all the necessary claims to identify users and + * manage token lifecycle. + */ #[derive(Debug, Serialize, Deserialize)] pub struct TokenClaims { - pub sub: String, // user ID - pub exp: i64, // expiration timestamp - pub iat: i64, // issued at timestamp - pub jti: String, // JWT ID - pub username: String, // username - pub email: String, // email - pub role: String, // role as string + /// Subject identifier - contains the user ID + pub sub: String, + + /// Expiration timestamp (seconds since Unix epoch) + pub exp: i64, + + /// Issued at timestamp (seconds since Unix epoch) + pub iat: i64, + + /// JWT unique ID for token tracking and revocation + pub jti: String, + + /// Username for display and identification purposes + pub username: String, + + /// User email for communication and identification + pub email: String, + + /// User role for authorization checks + pub role: String, } +/** + * Authentication-specific error types. + * + * This enum encapsulates all error scenarios that can occur during + * authentication and authorization processes, providing clear error messages + * and categorization for proper handling. + */ #[derive(Debug, thiserror::Error)] pub enum AuthError { + /// Returned when username/password authentication fails #[error("Credenciales inválidas")] InvalidCredentials, + /// Returned when a JWT token has passed its expiration time #[error("Token expirado")] TokenExpired, + /// Returned when a JWT token is malformed or has invalid signature #[error("Token inválido: {0}")] InvalidToken(String), + /// Returned when a user attempts to access a resource they don't have permission for #[error("Acceso denegado: {0}")] AccessDenied(String), + /// Returned when a requested operation is not allowed for the user #[error("Operación no permitida: {0}")] OperationNotAllowed(String), + /// Returned for unexpected errors in the authentication system #[error("Error interno: {0}")] InternalError(String), } +/** + * Conversion from AuthError to DomainError. + * + * This implementation allows authentication errors to be seamlessly + * transformed into domain errors, making error handling more consistent + * throughout the application. + */ impl From for DomainError { fn from(err: AuthError) -> Self { match err { @@ -64,10 +103,22 @@ impl From for DomainError { } } +/** + * Authentication service for managing user sessions and authorization. + * + * This service provides the core authentication functionality for the system, + * including JWT token generation and validation, refresh token management, + * and session duration control. + */ pub struct AuthService { + /// Secret key used for signing JWT tokens jwt_secret: String, - access_token_expiry: i64, // segundos - refresh_token_expiry: i64, // segundos + + /// Expiration time for access tokens in seconds + access_token_expiry: i64, + + /// Expiration time for refresh tokens in seconds + refresh_token_expiry: i64, } impl AuthService { diff --git a/src/infrastructure/repositories/file_fs_repository.rs b/src/infrastructure/repositories/file_fs_repository.rs index 2ae4b629..5acfbe91 100644 --- a/src/infrastructure/repositories/file_fs_repository.rs +++ b/src/infrastructure/repositories/file_fs_repository.rs @@ -26,6 +26,25 @@ use crate::common::config::AppConfig; use crate::application::ports::outbound::FileStoragePort; use crate::infrastructure::repositories::parallel_file_processor::ParallelFileProcessor; +/** + * Filesystem implementation of the File Repository interface. + * + * This repository provides a concrete implementation of the FileRepository domain interface + * that interacts with a filesystem-based storage backend. It implements: + * + * 1. File creation, retrieval, and deletion operations + * 2. File content reading (both in-memory and streaming) + * 3. Folder organization for files + * 4. ID-to-path mapping persistence + * 5. Optimized handling of large files using parallel I/O + * 6. Metadata caching to reduce filesystem operations + * 7. Trash operations for file lifecycle management + * + * The implementation follows the hexagonal architecture pattern as a secondary adapter, + * implementing domain interfaces and ports while isolating the application core from + * filesystem-specific details. + */ + // Usar constantes de la configuración centralizada en lugar de valores fijos // Esto se reemplaza con self.config.concurrency.max_concurrent_files más adelante diff --git a/src/interfaces/api/handlers/file_handler.rs b/src/interfaces/api/handlers/file_handler.rs index e266cd95..446da24b 100644 --- a/src/interfaces/api/handlers/file_handler.rs +++ b/src/interfaces/api/handlers/file_handler.rs @@ -18,10 +18,32 @@ use crate::infrastructure::services::compression_service::{ }; use crate::common::di::AppState; +/** + * Type aliases for dependency injection state. + * These aliases improve code readability when working with service dependencies. + */ +/// State containing the file service for dependency injection type FileServiceState = Arc; +/// Global application state for dependency injection type GlobalState = AppState; -/// Handler for file-related API endpoints +/** + * API handler for file-related operations. + * + * The FileHandler is responsible for processing HTTP requests related to file operations. + * It handles: + * + * 1. File uploads through multipart form data + * 2. File downloads with optional compression + * 3. Listing files in folders + * 4. Moving files between folders + * 5. Deleting files (with trash integration) + * + * This component acts as an adapter in the hexagonal architecture, translating + * between HTTP requests/responses and application service calls. It handles + * HTTP-specific concerns like status codes, headers, and request parsing while + * delegating business logic to the application services. + */ pub struct FileHandler; // Simpler approach to make streams Unpin - use Pin> directly diff --git a/src/main.rs b/src/main.rs index e7548589..d8e367ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,10 +6,41 @@ use axum::Router; use tower_http::trace::TraceLayer; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +/** + * OxiCloud - Cloud Storage Platform + * + * OxiCloud is a NextCloud-like file storage system built in Rust with a focus on + * performance, security, and clean architecture. The system provides: + * + * - File and folder management with rich metadata + * - User authentication and authorization + * - File trash system with automatic cleanup + * - Efficient handling of large files through parallel processing + * - Compression capabilities for bandwidth optimization + * - RESTful API and web interface + * + * The architecture follows the Clean/Hexagonal Architecture pattern with: + * + * - Domain Layer: Core business entities and repository interfaces (domain/*) + * - Application Layer: Use cases and service orchestration (application/*) + * - Infrastructure Layer: Technical implementations of repositories (infrastructure/*) + * - Interface Layer: API endpoints and web controllers (interfaces/*) + * + * Dependencies are managed through dependency inversion, with high-level modules + * defining interfaces (ports) that low-level modules implement (adapters). + * + * @author OxiCloud Development Team + */ + +/// Common utilities, configuration, and error handling mod common; +/// Core domain model, entities, and business rules mod domain; +/// Application services, use cases, and DTOs mod application; +/// Technical implementations of repositories and services mod infrastructure; +/// External interfaces like API endpoints and web controllers mod interfaces; use application::services::folder_service::FolderService; diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 00000000..26f2c0da --- /dev/null +++ b/tests/README.md @@ -0,0 +1,71 @@ +# OxiCloud Tests + +This directory contains test scripts, utilities and troubleshooting tools for the OxiCloud project. + +## API Tests + +* `test-all-routes.py` - Tests all API routes to verify which ones are implemented +* `test-api.sh` - Bash script to test basic API functionality (list, upload, download) +* `test-api-integration.py` - Python-based integration tests for the API +* `test-upload.py` - Dedicated upload testing script with more options +* `test-upload.sh` - Simple shell script for testing file uploads +* `test-delete-file.py` - Tests the file deletion functionality + +## Folder Tests + +* `test-folder.sh` - Tests folder API operations +* `test-folder-simple.sh` - Simplified folder creation/listing tests +* `test-create-folder.sh` - Specific test for folder creation +* `test-folder.js` - JavaScript based folder tests + +## Trash Tests + +* `test-trash.sh` - Tests trash functionality +* `test-trash-simple.sh` - Basic trash functionality test +* `test-trash-api.py` - Python script for testing trash API +* `test-trash-api-simple.py` - Simplified version of trash API tests +* `test-trash-api.sh` - Bash scripts for trash API testing +* `test-compile-trash.sh` - Tests compilation with trash feature enabled +* `fix-trash-index.sh` - Utility to fix trash indexing issues +* `check-trash-dirs.sh` - Checks trash directories structure +* `debug-trash.py` - Debug tool for trash functionality +* `run-trash-test.sh` - Runner for trash tests + +## Authentication Tests + +* `test-auth-api.sh` - Tests the authentication API endpoints +* `test-auth-env.sh` - Tests authentication with environment variables + +## Utilities + +* `check-db.sh` - Database check utility +* `simulate-id-mapping.py` - Simulates ID mapping for testing +* `direct-upload-test.py` - Tests direct uploads bypassing certain layers + +## Test Files + +* `test-upload.txt` - Sample file for upload testing +* `test-api-file.txt` - Sample file for API testing + +## Running Tests + +Most test scripts can be run directly from this directory. Many accept command-line arguments +to customize their behavior. Check the script headers or run with `--help` for more information. + +Basic usage examples: + +```bash +# Test API endpoints +python test-all-routes.py + +# Test file upload +./test-upload.sh --file sample.txt + +# Test trash API +python test-trash-api.py + +# Run folder tests +./test-folder.sh +``` + +Note that these tests expect a running OxiCloud server, typically on localhost:8086. \ No newline at end of file diff --git a/check-db.sh b/tests/check-db.sh similarity index 100% rename from check-db.sh rename to tests/check-db.sh diff --git a/check-trash-dirs.sh b/tests/check-trash-dirs.sh similarity index 100% rename from check-trash-dirs.sh rename to tests/check-trash-dirs.sh diff --git a/debug-trash.py b/tests/debug-trash.py similarity index 100% rename from debug-trash.py rename to tests/debug-trash.py diff --git a/direct-upload-test.py b/tests/direct-upload-test.py similarity index 100% rename from direct-upload-test.py rename to tests/direct-upload-test.py diff --git a/fix-trash-index.sh b/tests/fix-trash-index.sh old mode 100644 new mode 100755 similarity index 100% rename from fix-trash-index.sh rename to tests/fix-trash-index.sh diff --git a/run-trash-test.sh b/tests/run-trash-test.sh similarity index 100% rename from run-trash-test.sh rename to tests/run-trash-test.sh diff --git a/simulate-id-mapping.py b/tests/simulate-id-mapping.py similarity index 100% rename from simulate-id-mapping.py rename to tests/simulate-id-mapping.py diff --git a/test-all-routes.py b/tests/test-all-routes.py similarity index 95% rename from test-all-routes.py rename to tests/test-all-routes.py index 98ca6e2f..24bd23b9 100755 --- a/test-all-routes.py +++ b/tests/test-all-routes.py @@ -72,7 +72,7 @@ for route in routes: # Test POST upload print("\nTesting file upload...") -with open("test-all-routes.py", "rb") as f: +with open(__file__, "rb") as f: # Use __file__ to reference the current script regardless of location files = {"file": f} data = {"folder_id": "folder-storage:1"} test_route(f"{SERVER_URL}/api/files/upload", method="POST", data=data, files=files) diff --git a/test-api-file.txt b/tests/test-api-file.txt similarity index 100% rename from test-api-file.txt rename to tests/test-api-file.txt diff --git a/test-api-integration.py b/tests/test-api-integration.py similarity index 100% rename from test-api-integration.py rename to tests/test-api-integration.py diff --git a/test-api.sh b/tests/test-api.sh similarity index 100% rename from test-api.sh rename to tests/test-api.sh diff --git a/test-auth-api.sh b/tests/test-auth-api.sh similarity index 100% rename from test-auth-api.sh rename to tests/test-auth-api.sh diff --git a/test-auth-env.sh b/tests/test-auth-env.sh similarity index 100% rename from test-auth-env.sh rename to tests/test-auth-env.sh diff --git a/test-compile-trash.sh b/tests/test-compile-trash.sh similarity index 100% rename from test-compile-trash.sh rename to tests/test-compile-trash.sh diff --git a/test-create-folder.sh b/tests/test-create-folder.sh similarity index 100% rename from test-create-folder.sh rename to tests/test-create-folder.sh diff --git a/test-delete-file.py b/tests/test-delete-file.py similarity index 100% rename from test-delete-file.py rename to tests/test-delete-file.py diff --git a/test-folder-simple.sh b/tests/test-folder-simple.sh similarity index 100% rename from test-folder-simple.sh rename to tests/test-folder-simple.sh diff --git a/test-folder.js b/tests/test-folder.js similarity index 100% rename from test-folder.js rename to tests/test-folder.js diff --git a/test-folder.sh b/tests/test-folder.sh similarity index 100% rename from test-folder.sh rename to tests/test-folder.sh diff --git a/test-trash-api-simple.py b/tests/test-trash-api-simple.py similarity index 100% rename from test-trash-api-simple.py rename to tests/test-trash-api-simple.py diff --git a/test-trash-api.py b/tests/test-trash-api.py old mode 100644 new mode 100755 similarity index 100% rename from test-trash-api.py rename to tests/test-trash-api.py diff --git a/test-trash-api.sh b/tests/test-trash-api.sh similarity index 100% rename from test-trash-api.sh rename to tests/test-trash-api.sh diff --git a/test-trash-simple.sh b/tests/test-trash-simple.sh similarity index 100% rename from test-trash-simple.sh rename to tests/test-trash-simple.sh diff --git a/test-trash.sh b/tests/test-trash.sh similarity index 100% rename from test-trash.sh rename to tests/test-trash.sh diff --git a/test-upload.py b/tests/test-upload.py similarity index 100% rename from test-upload.py rename to tests/test-upload.py diff --git a/test-upload.sh b/tests/test-upload.sh similarity index 100% rename from test-upload.sh rename to tests/test-upload.sh diff --git a/test-upload.txt b/tests/test-upload.txt similarity index 100% rename from test-upload.txt rename to tests/test-upload.txt