init
Some checks failed
CI / ci (push) Failing after 1m48s

This commit is contained in:
2026-08-25 23:24:36 +02:00
commit 95739892de
466 changed files with 33918 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
use domain::entry::Content;
use domain::errors::DomainError;
#[test]
fn valid_content_is_created() {
let content = Content::new("went for a walk").unwrap();
assert_eq!(content.value(), "went for a walk");
}
#[test]
fn content_trims_whitespace() {
let content = Content::new(" hello world ").unwrap();
assert_eq!(content.value(), "hello world");
}
#[test]
fn empty_content_is_rejected() {
let result = Content::new("");
assert!(matches!(result, Err(DomainError::InvalidInput(_))));
}
#[test]
fn whitespace_only_content_is_rejected() {
let result = Content::new(" ");
assert!(matches!(result, Err(DomainError::InvalidInput(_))));
}
#[test]
fn from_persistence_bypasses_validation() {
let content = Content::from_persistence(String::new());
assert_eq!(content.value(), "");
}

View File

@@ -0,0 +1,36 @@
use chrono::{FixedOffset, TimeZone};
use domain::entry::DateRange;
use domain::errors::DomainError;
fn utc_dt(year: i32, month: u32, day: u32) -> chrono::DateTime<FixedOffset> {
FixedOffset::east_opt(0)
.unwrap()
.with_ymd_and_hms(year, month, day, 0, 0, 0)
.unwrap()
}
#[test]
fn valid_range_is_created() {
let start = utc_dt(2025, 1, 1);
let end = utc_dt(2025, 1, 31);
let range = DateRange::new(start, end).unwrap();
assert_eq!(*range.start(), start);
assert_eq!(*range.end(), end);
}
#[test]
fn start_equal_to_end_is_valid() {
let dt = utc_dt(2025, 6, 15);
let range = DateRange::new(dt, dt);
assert!(range.is_ok());
}
#[test]
fn start_after_end_is_rejected() {
let start = utc_dt(2025, 3, 15);
let end = utc_dt(2025, 3, 1);
let result = DateRange::new(start, end);
assert!(matches!(result, Err(DomainError::InvalidInput(_))));
}

View File

@@ -0,0 +1,136 @@
use chrono::{FixedOffset, TimeZone, Utc};
use domain::activity::ActivityId;
use domain::attachment::{PhotoId, VoiceMemoId};
use domain::entry::{Content, Mood, MoodEntry, MoodEntryData};
use domain::user::UserId;
fn sample_logged_at() -> chrono::DateTime<FixedOffset> {
FixedOffset::east_opt(3600)
.unwrap()
.with_ymd_and_hms(2025, 3, 15, 20, 0, 0)
.unwrap()
}
#[test]
fn new_entry_has_required_fields() {
let user_id = UserId::generate();
let entry = MoodEntry::new(user_id.clone(), Mood::Good, sample_logged_at());
assert_eq!(*entry.user_id(), user_id);
assert_eq!(entry.mood(), Mood::Good);
assert!(entry.activities().is_empty());
assert!(entry.content().is_none());
assert!(entry.photos().is_empty());
assert!(entry.voice_memos().is_empty());
}
#[test]
fn update_mood_changes_value_and_touches_updated_at() {
let mut entry = MoodEntry::new(UserId::generate(), Mood::Good, sample_logged_at());
let before = *entry.updated_at();
entry.update_mood(Mood::Rad);
assert_eq!(entry.mood(), Mood::Rad);
assert!(*entry.updated_at() >= before);
}
#[test]
fn set_activities_deduplicates_and_sorts() {
let mut entry = MoodEntry::new(UserId::generate(), Mood::Meh, sample_logged_at());
let a = ActivityId::generate();
let b = ActivityId::generate();
entry.set_activities(vec![b.clone(), a.clone(), b.clone()]);
assert_eq!(entry.activities().len(), 2);
let ids = entry.activities();
assert!(ids[0] <= ids[1]);
}
#[test]
fn set_content_updates_and_clears() {
let mut entry = MoodEntry::new(UserId::generate(), Mood::Good, sample_logged_at());
let content = Content::new("great day").unwrap();
entry.set_content(Some(content));
assert!(entry.content().is_some());
entry.set_content(None);
assert!(entry.content().is_none());
}
#[test]
fn from_persistence_reconstructs_all_fields() {
let id = domain::entry::MoodEntryId::generate();
let user_id = UserId::generate();
let activity_id = ActivityId::generate();
let photo_id = PhotoId::generate();
let voice_memo_id = VoiceMemoId::generate();
let content = Content::new("test").unwrap();
let now = Utc::now();
let entry = MoodEntry::from_persistence(MoodEntryData {
id: id.clone(),
user_id: user_id.clone(),
mood: Mood::Bad,
logged_at: sample_logged_at(),
activities: vec![activity_id.clone()],
content: Some(content),
photos: vec![photo_id.clone()],
voice_memos: vec![voice_memo_id.clone()],
created_at: now,
updated_at: now,
});
assert_eq!(*entry.id(), id);
assert_eq!(*entry.user_id(), user_id);
assert_eq!(entry.mood(), Mood::Bad);
assert_eq!(entry.activities().len(), 1);
assert!(entry.content().is_some());
assert_eq!(entry.photos().len(), 1);
assert_eq!(entry.voice_memos().len(), 1);
}
#[test]
fn update_logged_at_changes_timestamp() {
let mut entry = MoodEntry::new(UserId::generate(), Mood::Good, sample_logged_at());
let new_time = FixedOffset::east_opt(3600)
.unwrap()
.with_ymd_and_hms(2025, 6, 1, 12, 0, 0)
.unwrap();
entry.update_logged_at(new_time);
assert_eq!(*entry.logged_at(), new_time);
}
#[test]
fn set_photos_replaces_list() {
let mut entry = MoodEntry::new(UserId::generate(), Mood::Good, sample_logged_at());
let p1 = PhotoId::generate();
let p2 = PhotoId::generate();
entry.set_photos(vec![p1.clone(), p2.clone()]);
assert_eq!(entry.photos().len(), 2);
entry.set_photos(vec![p1.clone()]);
assert_eq!(entry.photos().len(), 1);
entry.set_photos(vec![]);
assert!(entry.photos().is_empty());
}
#[test]
fn set_voice_memos_replaces_list() {
let mut entry = MoodEntry::new(UserId::generate(), Mood::Good, sample_logged_at());
let m1 = VoiceMemoId::generate();
let m2 = VoiceMemoId::generate();
entry.set_voice_memos(vec![m1.clone(), m2.clone()]);
assert_eq!(entry.voice_memos().len(), 2);
entry.set_voice_memos(vec![]);
assert!(entry.voice_memos().is_empty());
}

View File

@@ -0,0 +1,40 @@
use domain::entry::Mood;
use domain::errors::DomainError;
#[test]
fn mood_values_map_to_expected_scale() {
assert_eq!(Mood::Awful.value(), 1);
assert_eq!(Mood::Bad.value(), 2);
assert_eq!(Mood::Meh.value(), 3);
assert_eq!(Mood::Good.value(), 4);
assert_eq!(Mood::Rad.value(), 5);
}
#[test]
fn mood_ordering_reflects_scale() {
assert!(Mood::Awful < Mood::Bad);
assert!(Mood::Bad < Mood::Meh);
assert!(Mood::Meh < Mood::Good);
assert!(Mood::Good < Mood::Rad);
}
#[test]
fn valid_values_convert_to_mood() {
for value in 1..=5u8 {
let mood = Mood::try_from(value);
assert!(mood.is_ok());
assert_eq!(mood.unwrap().value(), value);
}
}
#[test]
fn zero_is_invalid_mood_value() {
let result = Mood::try_from(0u8);
assert!(matches!(result, Err(DomainError::InvalidInput(_))));
}
#[test]
fn six_is_invalid_mood_value() {
let result = Mood::try_from(6u8);
assert!(matches!(result, Err(DomainError::InvalidInput(_))));
}