159 lines
4.9 KiB
Rust
159 lines
4.9 KiB
Rust
use application::storage::{
|
|
IngestAssetCommand, IngestAssetHandler, RegisterLibraryPathCommand, RegisterLibraryPathHandler,
|
|
RegisterVolumeCommand, RegisterVolumeHandler,
|
|
};
|
|
use application::testing::{
|
|
InMemoryFileStorage, InMemoryIngestTransaction, InMemoryLibraryPathRepository,
|
|
InMemoryStorageVolumeRepository, StubEventPublisher,
|
|
};
|
|
use bytes::Bytes;
|
|
use domain::entities::{IngestStatus, QuotaDefinition, TimePeriod, UsageType};
|
|
use domain::errors::DomainError;
|
|
use domain::value_objects::SystemId;
|
|
use std::sync::Arc;
|
|
|
|
struct Harness {
|
|
tx: Arc<InMemoryIngestTransaction>,
|
|
path_repo: Arc<InMemoryLibraryPathRepository>,
|
|
file_storage: Arc<InMemoryFileStorage>,
|
|
event_pub: Arc<StubEventPublisher>,
|
|
vol_repo: Arc<InMemoryStorageVolumeRepository>,
|
|
}
|
|
|
|
impl Harness {
|
|
fn new() -> Self {
|
|
Self {
|
|
tx: Arc::new(InMemoryIngestTransaction::new()),
|
|
path_repo: Arc::new(InMemoryLibraryPathRepository::new()),
|
|
file_storage: Arc::new(InMemoryFileStorage::new()),
|
|
event_pub: Arc::new(StubEventPublisher::new()),
|
|
vol_repo: Arc::new(InMemoryStorageVolumeRepository::new()),
|
|
}
|
|
}
|
|
|
|
fn ingest_handler(&self) -> IngestAssetHandler {
|
|
IngestAssetHandler::new(
|
|
self.tx.clone(),
|
|
self.path_repo.clone(),
|
|
self.file_storage.clone(),
|
|
self.event_pub.clone(),
|
|
)
|
|
}
|
|
|
|
async fn setup_volume_and_path(&self, owner: SystemId) -> SystemId {
|
|
let vol_handler = RegisterVolumeHandler::new(self.vol_repo.clone());
|
|
let vol = vol_handler
|
|
.execute(RegisterVolumeCommand {
|
|
volume_name: "main".into(),
|
|
uri_prefix: "file:///data".into(),
|
|
is_writable: true,
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
let path_handler =
|
|
RegisterLibraryPathHandler::new(self.vol_repo.clone(), self.path_repo.clone());
|
|
let path = path_handler
|
|
.execute(RegisterLibraryPathCommand {
|
|
volume_id: vol.volume_id,
|
|
relative_path: "photos/inbox".into(),
|
|
owner_id: owner,
|
|
is_ingest_destination: true,
|
|
})
|
|
.await
|
|
.unwrap();
|
|
path.path_id
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn ingests_successfully() {
|
|
let h = Harness::new();
|
|
let user = SystemId::new();
|
|
let path_id = h.setup_volume_and_path(user).await;
|
|
|
|
let handler = h.ingest_handler();
|
|
let (asset, session) = handler
|
|
.execute(IngestAssetCommand {
|
|
uploader_id: user,
|
|
client_device_id: "iphone-1".into(),
|
|
filename: "photo.jpg".into(),
|
|
target_path_id: path_id,
|
|
file_size: 1024,
|
|
data: Bytes::from(vec![0u8; 1024]),
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(asset.mime_type, "image/jpeg");
|
|
assert_eq!(asset.file_size, 1024);
|
|
assert_eq!(asset.owner_user_id, user);
|
|
assert_eq!(session.status, IngestStatus::AwaitingProcessing);
|
|
assert!(!h.event_pub.published().await.is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rejects_quota_exceeded() {
|
|
let h = Harness::new();
|
|
let user = SystemId::new();
|
|
let path_id = h.setup_volume_and_path(user).await;
|
|
|
|
let mut quota = QuotaDefinition::new(user);
|
|
quota.add_rule(UsageType::StorageBytes, 500, TimePeriod::Lifetime);
|
|
h.tx.insert_quota("a).await;
|
|
|
|
let handler = h.ingest_handler();
|
|
let result = handler
|
|
.execute(IngestAssetCommand {
|
|
uploader_id: user,
|
|
client_device_id: "iphone-1".into(),
|
|
filename: "big.jpg".into(),
|
|
target_path_id: path_id,
|
|
file_size: 1024,
|
|
data: Bytes::from(vec![0u8; 1024]),
|
|
})
|
|
.await;
|
|
|
|
assert!(matches!(result, Err(DomainError::QuotaExceeded(_))));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rejects_non_ingest_path() {
|
|
let h = Harness::new();
|
|
let user = SystemId::new();
|
|
|
|
// Create volume + non-ingest path directly
|
|
let vol_handler = RegisterVolumeHandler::new(h.vol_repo.clone());
|
|
let vol = vol_handler
|
|
.execute(RegisterVolumeCommand {
|
|
volume_name: "main".into(),
|
|
uri_prefix: "file:///data".into(),
|
|
is_writable: true,
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
let path = domain::entities::LibraryPath::new_user_owned(
|
|
vol.volume_id,
|
|
"photos/archive",
|
|
user,
|
|
false, // not an ingest destination
|
|
);
|
|
use domain::ports::LibraryPathRepository;
|
|
h.path_repo.save(&path).await.unwrap();
|
|
|
|
let handler = h.ingest_handler();
|
|
let result = handler
|
|
.execute(IngestAssetCommand {
|
|
uploader_id: user,
|
|
client_device_id: "iphone-1".into(),
|
|
filename: "photo.jpg".into(),
|
|
target_path_id: path.path_id,
|
|
file_size: 1024,
|
|
data: Bytes::from(vec![0u8; 1024]),
|
|
})
|
|
.await;
|
|
|
|
assert!(matches!(result, Err(DomainError::Validation(_))));
|
|
}
|