domain: add Storage & Sources entities (StorageVolume, LibraryPath, IngestSession, Quota)

This commit is contained in:
2026-05-31 03:23:34 +02:00
parent 04811ff436
commit 3c5c4ed9b1
10 changed files with 335 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
use domain::entities::{IngestSession, IngestStatus};
use domain::errors::DomainError;
use domain::value_objects::{Checksum, SystemId};
fn make_session() -> IngestSession {
let checksum = Checksum::new("a".repeat(64)).unwrap();
IngestSession::new(
SystemId::new(),
"device-1",
"photo.jpg",
checksum,
SystemId::new(),
)
}
#[test]
fn valid_state_transitions() {
let mut s = make_session();
assert_eq!(s.status, IngestStatus::Uploading);
s.advance_to(IngestStatus::AwaitingProcessing).unwrap();
assert_eq!(s.status, IngestStatus::AwaitingProcessing);
s.advance_to(IngestStatus::Processing).unwrap();
assert_eq!(s.status, IngestStatus::Processing);
s.advance_to(IngestStatus::Completed).unwrap();
assert_eq!(s.status, IngestStatus::Completed);
}
#[test]
fn invalid_transition_rejected() {
let mut s = make_session();
let result = s.advance_to(IngestStatus::Completed);
assert!(matches!(result, Err(DomainError::Validation(_))));
}
#[test]
fn can_fail_from_any_non_terminal() {
for target in [IngestStatus::Uploading, IngestStatus::AwaitingProcessing, IngestStatus::Processing] {
let mut s = make_session();
// advance to target state
if target == IngestStatus::AwaitingProcessing || target == IngestStatus::Processing {
s.advance_to(IngestStatus::AwaitingProcessing).unwrap();
}
if target == IngestStatus::Processing {
s.advance_to(IngestStatus::Processing).unwrap();
}
s.advance_to(IngestStatus::Failed).unwrap();
assert_eq!(s.status, IngestStatus::Failed);
}
}

View File

@@ -0,0 +1,13 @@
use domain::entities::{LibraryPath, OwnershipPolicy};
use domain::value_objects::SystemId;
#[test]
fn user_owned_path() {
let vol = SystemId::new();
let owner = SystemId::new();
let lp = LibraryPath::new_user_owned(vol, "/photos", owner, true);
assert_eq!(lp.ownership_policy, OwnershipPolicy::UserOwned);
assert_eq!(lp.designated_owner_id, Some(owner));
assert!(lp.is_ingest_destination);
assert_eq!(lp.volume_id, vol);
}

View File

@@ -2,3 +2,7 @@ mod group;
mod permission;
mod role;
mod user;
mod storage_volume;
mod library_path;
mod ingest_session;
mod quota;

View File

@@ -0,0 +1,17 @@
use domain::entities::{QuotaDefinition, TimePeriod, UsageType};
use domain::value_objects::SystemId;
#[test]
fn quota_with_rules() {
let mut q = QuotaDefinition::new(SystemId::new());
assert!(q.is_enforced);
assert!(q.rules.is_empty());
q.add_rule(UsageType::StorageBytes, 1_000_000, TimePeriod::Monthly);
q.add_unlimited_rule(UsageType::ApiCalls);
assert_eq!(q.rules.len(), 2);
assert!(!q.rules[0].is_unlimited);
assert_eq!(q.rules[0].limit_value, 1_000_000);
assert!(q.rules[1].is_unlimited);
}

View File

@@ -0,0 +1,10 @@
use domain::entities::StorageVolume;
#[test]
fn creates_read_only_volume() {
let vol = StorageVolume::new("archive", "s3://bucket/", false);
assert_eq!(vol.volume_name, "archive");
assert_eq!(vol.uri_prefix, "s3://bucket/");
assert!(!vol.is_writable);
assert_eq!(vol.available_bytes, 0);
}