feat: add WatchMedium field, general review editing, configurable deploy
Some checks failed
CI / Check / Test (push) Failing after 27m0s

- WatchMedium enum (cinema/streaming/tv/physical_media/download/media_server/other)
- PATCH /api/v1/reviews/:id partial update (rating, comment, watched_at, watch_medium)
- edit_review use case w/ ownership + remote review guard, best-effort AP Update broadcast
- SPA: icon picker, edit sheet (long-press mobile / pencil desktop), watch medium badge
- shared ReviewFormFields, EditableContextMenu, parse_watched_at/format_watched_at
- deploy.sh parameterized (--features, --tag), CORS allows PATCH
- CONTEXT.md glossary, ADR-0001 general review editing
This commit is contained in:
2026-07-10 00:01:14 +02:00
parent 9794babe06
commit 29cc68b07c
72 changed files with 1298 additions and 124 deletions

View File

@@ -0,0 +1,81 @@
use chrono::NaiveDateTime;
use crate::value_objects::{Comment, MovieId, Rating, UserId, WatchMedium};
use super::*;
fn make_review(watch_medium: Option<WatchMedium>) -> Review {
Review::new(
MovieId::generate(),
UserId::generate(),
Rating::new(4).unwrap(),
Some(Comment::new("great film".into()).unwrap()),
NaiveDateTime::parse_from_str("2024-06-15 20:00:00", "%Y-%m-%d %H:%M:%S").unwrap(),
watch_medium,
)
.unwrap()
}
#[test]
fn new_review_stores_watch_medium() {
let review = make_review(Some(WatchMedium::Cinema));
assert_eq!(review.watch_medium(), Some(&WatchMedium::Cinema));
}
#[test]
fn new_review_without_medium() {
let review = make_review(None);
assert_eq!(review.watch_medium(), None);
}
#[test]
fn apply_edit_updates_rating_only() {
let original = make_review(Some(WatchMedium::Streaming));
let edited = original.apply_edit(ReviewEdit {
rating: Some(Rating::new(2).unwrap()),
..Default::default()
});
assert_eq!(edited.rating().value(), 2);
assert_eq!(edited.comment().unwrap().value(), "great film");
assert_eq!(edited.watch_medium(), Some(&WatchMedium::Streaming));
}
#[test]
fn apply_edit_clears_comment() {
let original = make_review(None);
let edited = original.apply_edit(ReviewEdit {
comment: Some(None),
..Default::default()
});
assert!(edited.comment().is_none());
assert_eq!(edited.rating().value(), 4);
}
#[test]
fn apply_edit_sets_watch_medium() {
let original = make_review(None);
let edited = original.apply_edit(ReviewEdit {
watch_medium: Some(Some(WatchMedium::Cinema)),
..Default::default()
});
assert_eq!(edited.watch_medium(), Some(&WatchMedium::Cinema));
}
#[test]
fn apply_edit_clears_watch_medium() {
let original = make_review(Some(WatchMedium::Cinema));
let edited = original.apply_edit(ReviewEdit {
watch_medium: Some(None),
..Default::default()
});
assert_eq!(edited.watch_medium(), None);
}
#[test]
fn apply_edit_no_changes_preserves_all() {
let original = make_review(Some(WatchMedium::TV));
let edited = original.apply_edit(ReviewEdit::default());
assert_eq!(edited.rating().value(), 4);
assert_eq!(edited.comment().unwrap().value(), "great film");
assert_eq!(edited.watch_medium(), Some(&WatchMedium::TV));
}