1pub mod collections {
5 use flix_model::id::CollectionId as FlixId;
6 use flix_tmdb::model::id::CollectionId;
7
8 use chrono::{DateTime, Utc};
9 use sea_orm::entity::prelude::*;
10
11 use crate::entity;
12
13 #[sea_orm::model]
15 #[derive(Debug, Clone, DeriveEntityModel)]
16 #[sea_orm(table_name = "flix_tmdb_collections")]
17 pub struct Model {
18 #[sea_orm(primary_key, auto_increment = false)]
20 pub tmdb_id: CollectionId,
21 #[sea_orm(unique)]
23 pub flix_id: FlixId,
24 pub last_update: DateTime<Utc>,
26 pub movie_count: u16,
28
29 #[sea_orm(
31 belongs_to,
32 from = "flix_id",
33 to = "id",
34 on_update = "Cascade",
35 on_delete = "Cascade"
36 )]
37 pub info: HasOne<entity::info::collections::Entity>,
38
39 #[sea_orm(has_many)]
41 pub movies: HasMany<super::movies::Entity>,
42 }
43
44 impl ActiveModelBehavior for ActiveModel {}
45}
46
47pub mod movies {
49 use flix_model::id::MovieId as FlixId;
50 use flix_tmdb::model::id::{CollectionId, MovieId};
51
52 use seamantic::model::duration::Seconds;
53
54 use chrono::{DateTime, Utc};
55 use sea_orm::entity::prelude::*;
56
57 use crate::entity;
58
59 #[sea_orm::model]
61 #[derive(Debug, Clone, DeriveEntityModel)]
62 #[sea_orm(table_name = "flix_tmdb_movies")]
63 pub struct Model {
64 #[sea_orm(primary_key, auto_increment = false)]
66 pub tmdb_id: MovieId,
67 #[sea_orm(unique)]
69 pub flix_id: FlixId,
70 pub last_update: DateTime<Utc>,
72 pub runtime: Seconds,
74 #[sea_orm(indexed)]
76 pub collection_id: Option<CollectionId>,
77
78 #[sea_orm(
80 belongs_to,
81 from = "collection_id",
82 to = "tmdb_id",
83 on_update = "Cascade",
84 on_delete = "Cascade"
85 )]
86 pub collection: HasOne<super::collections::Entity>,
87 #[sea_orm(
89 belongs_to,
90 from = "flix_id",
91 to = "id",
92 on_update = "Cascade",
93 on_delete = "Cascade"
94 )]
95 pub info: HasOne<entity::info::movies::Entity>,
96 }
97
98 impl ActiveModelBehavior for ActiveModel {}
99}
100
101pub mod shows {
103 use flix_model::id::ShowId as FlixId;
104 use flix_tmdb::model::id::ShowId;
105
106 use chrono::{DateTime, Utc};
107 use sea_orm::entity::prelude::*;
108
109 use crate::entity;
110
111 #[sea_orm::model]
113 #[derive(Debug, Clone, DeriveEntityModel)]
114 #[sea_orm(table_name = "flix_tmdb_shows")]
115 pub struct Model {
116 #[sea_orm(primary_key, auto_increment = false)]
118 pub tmdb_id: ShowId,
119 #[sea_orm(unique)]
121 pub flix_id: FlixId,
122 pub last_update: DateTime<Utc>,
124 pub number_of_seasons: u32,
126
127 #[sea_orm(
129 belongs_to,
130 from = "flix_id",
131 to = "id",
132 on_update = "Cascade",
133 on_delete = "Cascade"
134 )]
135 pub info: HasOne<entity::info::shows::Entity>,
136
137 #[sea_orm(has_many)]
139 pub seasons: HasMany<super::seasons::Entity>,
140 #[sea_orm(has_many)]
142 pub episodes: HasMany<super::episodes::Entity>,
143 }
144
145 impl ActiveModelBehavior for ActiveModel {}
146}
147
148pub mod seasons {
150 use flix_model::id::ShowId as FlixId;
151 use flix_model::numbers::SeasonNumber;
152 use flix_tmdb::model::id::ShowId;
153
154 use chrono::{DateTime, Utc};
155 use sea_orm::entity::prelude::*;
156
157 use crate::entity;
158
159 #[sea_orm::model]
161 #[derive(Debug, Clone, DeriveEntityModel)]
162 #[sea_orm(table_name = "flix_tmdb_seasons")]
163 pub struct Model {
164 #[sea_orm(primary_key, auto_increment = false)]
166 pub tmdb_show: ShowId,
167 #[sea_orm(primary_key, auto_increment = false)]
169 pub tmdb_season: SeasonNumber,
170 #[sea_orm(unique_key = "flix")]
172 pub flix_show: FlixId,
173 #[sea_orm(unique_key = "flix")]
175 pub flix_season: SeasonNumber,
176 pub last_update: DateTime<Utc>,
178
179 #[sea_orm(
181 belongs_to,
182 from = "tmdb_show",
183 to = "tmdb_id",
184 on_update = "Cascade",
185 on_delete = "Cascade"
186 )]
187 pub show: HasOne<super::shows::Entity>,
188 #[sea_orm(
190 belongs_to,
191 from = "(flix_show, flix_season)",
192 to = "(show_id, season_number)",
193 on_update = "Cascade",
194 on_delete = "Cascade"
195 )]
196 pub info: HasOne<entity::info::seasons::Entity>,
197
198 #[sea_orm(has_many)]
200 pub episodes: HasMany<super::episodes::Entity>,
201 }
202
203 impl ActiveModelBehavior for ActiveModel {}
204}
205
206pub mod episodes {
208 use flix_model::id::ShowId as FlixId;
209 use flix_model::numbers::{EpisodeNumber, SeasonNumber};
210 use flix_tmdb::model::id::ShowId;
211 use seamantic::model::duration::Seconds;
212
213 use chrono::{DateTime, Utc};
214 use sea_orm::entity::prelude::*;
215
216 use crate::entity;
217
218 #[sea_orm::model]
220 #[derive(Debug, Clone, DeriveEntityModel)]
221 #[sea_orm(table_name = "flix_tmdb_episodes")]
222 pub struct Model {
223 #[sea_orm(primary_key, auto_increment = false)]
225 pub tmdb_show: ShowId,
226 #[sea_orm(primary_key, auto_increment = false)]
228 pub tmdb_season: SeasonNumber,
229 #[sea_orm(primary_key, auto_increment = false)]
231 pub tmdb_episode: EpisodeNumber,
232 #[sea_orm(unique_key = "flix")]
234 pub flix_show: FlixId,
235 #[sea_orm(unique_key = "flix")]
237 pub flix_season: SeasonNumber,
238 #[sea_orm(unique_key = "flix")]
240 pub flix_episode: EpisodeNumber,
241 pub last_update: DateTime<Utc>,
243 pub runtime: Seconds,
245
246 #[sea_orm(
248 belongs_to,
249 from = "tmdb_show",
250 to = "tmdb_id",
251 on_update = "Cascade",
252 on_delete = "Cascade"
253 )]
254 pub show: HasOne<super::shows::Entity>,
255 #[sea_orm(
257 belongs_to,
258 from = "(tmdb_show, tmdb_season)",
259 to = "(tmdb_show, tmdb_season)",
260 on_update = "Cascade",
261 on_delete = "Cascade"
262 )]
263 pub season: HasOne<super::seasons::Entity>,
264 #[sea_orm(
266 belongs_to,
267 from = "(flix_show, flix_season, flix_episode)",
268 to = "(show_id, season_number, episode_number)",
269 on_update = "Cascade",
270 on_delete = "Cascade"
271 )]
272 pub info: HasOne<entity::info::episodes::Entity>,
273 }
274
275 impl ActiveModelBehavior for ActiveModel {}
276}
277
278#[cfg(test)]
280pub mod test {
281 macro_rules! make_tmdb_collection {
282 ($db:expr, $id:expr, $flix_id:expr) => {
283 $crate::entity::tmdb::collections::ActiveModel {
284 tmdb_id: Set(::flix_tmdb::model::id::CollectionId::from_raw($id)),
285 flix_id: Set(::flix_model::id::CollectionId::from_raw($flix_id)),
286 last_update: Set(::chrono::Utc::now()),
287 movie_count: Set(::core::default::Default::default()),
288 }
289 .insert($db)
290 .await
291 .expect("insert");
292 };
293 }
294 pub(crate) use make_tmdb_collection;
295
296 macro_rules! make_tmdb_movie {
297 ($db:expr, $id:expr, $flix_id:expr) => {
298 $crate::entity::tmdb::movies::ActiveModel {
299 tmdb_id: Set(::flix_tmdb::model::id::MovieId::from_raw($id)),
300 flix_id: Set(::flix_model::id::MovieId::from_raw($flix_id)),
301 last_update: Set(::chrono::Utc::now()),
302 runtime: Set(::core::default::Default::default()),
303 collection_id: Set(None),
304 }
305 .insert($db)
306 .await
307 .expect("insert");
308 };
309 }
310 pub(crate) use make_tmdb_movie;
311
312 macro_rules! make_tmdb_show {
313 ($db:expr, $id:expr, $flix_id:expr) => {
314 $crate::entity::tmdb::shows::ActiveModel {
315 tmdb_id: Set(::flix_tmdb::model::id::ShowId::from_raw($id)),
316 flix_id: Set(::flix_model::id::ShowId::from_raw($flix_id)),
317 last_update: Set(::chrono::Utc::now()),
318 number_of_seasons: Set(::core::default::Default::default()),
319 }
320 .insert($db)
321 .await
322 .expect("insert");
323 };
324 }
325 pub(crate) use make_tmdb_show;
326
327 macro_rules! make_tmdb_season {
328 ($db:expr, $show:expr, $season:expr, $flix_show:expr, $flix_season:expr) => {
329 $crate::entity::tmdb::seasons::ActiveModel {
330 tmdb_show: Set(::flix_tmdb::model::id::ShowId::from_raw($show)),
331 tmdb_season: Set(::flix_model::numbers::SeasonNumber::new($season)),
332 flix_show: Set(::flix_model::id::ShowId::from_raw($flix_show)),
333 flix_season: Set(::flix_model::numbers::SeasonNumber::new($flix_season)),
334 last_update: Set(::chrono::Utc::now()),
335 }
336 .insert($db)
337 .await
338 .expect("insert");
339 };
340 }
341 pub(crate) use make_tmdb_season;
342
343 macro_rules! make_tmdb_episode {
344 ($db:expr, $show:expr, $season:expr, $episode:expr, $flix_show:expr, $flix_season:expr, $flix_episode:expr) => {
345 $crate::entity::tmdb::episodes::ActiveModel {
346 tmdb_show: Set(::flix_tmdb::model::id::ShowId::from_raw($show)),
347 tmdb_season: Set(::flix_model::numbers::SeasonNumber::new($season)),
348 tmdb_episode: Set(::flix_model::numbers::EpisodeNumber::new($episode)),
349 flix_show: Set(::flix_model::id::ShowId::from_raw($flix_show)),
350 flix_season: Set(::flix_model::numbers::SeasonNumber::new($flix_season)),
351 flix_episode: Set(::flix_model::numbers::EpisodeNumber::new($flix_episode)),
352 last_update: Set(::chrono::Utc::now()),
353 runtime: Set(::core::default::Default::default()),
354 }
355 .insert($db)
356 .await
357 .expect("insert");
358 };
359 }
360 pub(crate) use make_tmdb_episode;
361}
362
363#[cfg(test)]
364mod tests {
365 use core::time::Duration;
366
367 use flix_model::id::{CollectionId, MovieId, ShowId};
368 use flix_tmdb::model::id::{
369 CollectionId as TmdbCollectionId, MovieId as TmdbMovieId, ShowId as TmdbShowId,
370 };
371
372 use chrono::NaiveDate;
373 use sea_orm::ActiveValue::{NotSet, Set};
374 use sea_orm::entity::prelude::*;
375 use sea_orm::sqlx::error::ErrorKind;
376
377 use crate::entity::info::test::{
378 make_info_collection, make_info_episode, make_info_movie, make_info_season, make_info_show,
379 };
380 use crate::tests::new_initialized_memory_db;
381
382 use super::super::tests::get_error_kind;
383 use super::super::tests::notsettable;
384 use super::test::{
385 make_tmdb_collection, make_tmdb_episode, make_tmdb_movie, make_tmdb_season, make_tmdb_show,
386 };
387
388 #[tokio::test]
389 async fn use_test_macros() {
390 let db = new_initialized_memory_db().await;
391
392 make_info_collection!(&db, 1);
393 make_info_movie!(&db, 1);
394 make_info_show!(&db, 1);
395 make_info_season!(&db, 1, 1);
396 make_info_episode!(&db, 1, 1, 1);
397
398 make_tmdb_collection!(&db, 1, 1);
399 make_tmdb_movie!(&db, 1, 1);
400 make_tmdb_show!(&db, 1, 1);
401 make_tmdb_season!(&db, 1, 1, 1, 1);
402 make_tmdb_episode!(&db, 1, 1, 1, 1, 1, 1);
403 }
404
405 #[tokio::test]
406 async fn test_round_trip_collections() {
407 let db = new_initialized_memory_db().await;
408
409 macro_rules! assert_collection {
410 ($db:expr, $id:literal, $tid:literal, Success $(; $($skip:ident),+)?) => {
411 let model = assert_collection!(@insert, $db, $id, $tid $(; $($skip),+)?)
412 .expect("insert");
413
414 assert_eq!(model.tmdb_id, TmdbCollectionId::from_raw($tid));
415 assert_eq!(model.flix_id, CollectionId::from_raw($id));
416 assert_eq!(model.last_update, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt").and_hms_opt(0, 0, 0).expect("and_hms_opt").and_utc());
417 assert_eq!(model.movie_count, $id);
418 };
419 ($db:expr, $id:literal, $tid:literal, $error:ident $(; $($skip:ident),+)?) => {
420 let model = assert_collection!(@insert, $db, $id, $tid $(; $($skip),+)?)
421 .expect_err("insert");
422
423 assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
424 };
425 (@insert, $db:expr, $id:literal, $tid:literal $(; $($skip:ident),+)?) => {
426 super::collections::ActiveModel {
427 tmdb_id: notsettable!(tmdb_id, TmdbCollectionId::from_raw($tid) $(, $($skip),+)?),
428 flix_id: notsettable!(flix_id, CollectionId::from_raw($id) $(, $($skip),+)?),
429 last_update: notsettable!(last_update, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt").and_hms_opt(0, 0, 0).expect("and_hms_opt").and_utc() $(, $($skip),+)?),
430 movie_count: notsettable!(movie_count, $id $(, $($skip),+)?),
431 }.insert($db).await
432 };
433 }
434
435 assert_collection!(&db, 1, 1, ForeignKeyViolation);
436 make_info_collection!(&db, 1);
437 assert_collection!(&db, 1, 1, Success);
438 assert_collection!(&db, 1, 1, UniqueViolation);
439
440 assert_collection!(&db, 1, 2, UniqueViolation);
441 assert_collection!(&db, 2, 1, UniqueViolation);
442 make_info_collection!(&db, 2);
443 assert_collection!(&db, 2, 2, Success);
444
445 make_info_collection!(&db, 3);
446 assert_collection!(&db, 3, 3, Success; tmdb_id);
447 assert_collection!(&db, 4, 4, NotNullViolation; flix_id);
448 assert_collection!(&db, 5, 5, NotNullViolation; last_update);
449 assert_collection!(&db, 6, 6, NotNullViolation; movie_count);
450 }
451
452 #[tokio::test]
453 async fn test_round_trip_movies() {
454 let db = new_initialized_memory_db().await;
455
456 macro_rules! assert_movie {
457 ($db:expr, $id:literal, $tid:literal, $cid:expr, Success $(; $($skip:ident),+)?) => {
458 let model = assert_movie!(@insert, $db, $id, $tid, $cid $(; $($skip),+)?)
459 .expect("insert");
460
461 assert_eq!(model.tmdb_id, TmdbMovieId::from_raw($tid));
462 assert_eq!(model.flix_id, MovieId::from_raw($id));
463 assert_eq!(model.last_update, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt").and_hms_opt(0, 0, 0).expect("and_hms_opt").and_utc());
464 assert_eq!(model.runtime, Duration::from_secs($tid).into());
465 assert_eq!(model.collection_id, $cid.map(TmdbCollectionId::from_raw));
466 };
467 ($db:expr, $id:literal, $tid:literal, $cid:expr, $error:ident $(; $($skip:ident),+)?) => {
468 let model = assert_movie!(@insert, $db, $id, $tid, $cid $(; $($skip),+)?)
469 .expect_err("insert");
470
471 assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
472 };
473 (@insert, $db:expr, $id:literal, $tid:literal, $cid:expr $(; $($skip:ident),+)?) => {
474 super::movies::ActiveModel {
475 tmdb_id: notsettable!(tmdb_id, TmdbMovieId::from_raw($tid) $(, $($skip),+)?),
476 flix_id: notsettable!(flix_id, MovieId::from_raw($id) $(, $($skip),+)?),
477 last_update: notsettable!(last_update, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt").and_hms_opt(0, 0, 0).expect("and_hms_opt").and_utc() $(, $($skip),+)?),
478 runtime: notsettable!(runtime, Duration::from_secs($tid).into() $(, $($skip),+)?),
479 collection_id: notsettable!(collection_id, $cid.map(TmdbCollectionId::from_raw) $(, $($skip),+)?),
480 }.insert($db).await
481 };
482 }
483
484 assert_movie!(&db, 1, 1, None, ForeignKeyViolation);
485 make_info_movie!(&db, 1);
486 assert_movie!(&db, 1, 1, None, Success);
487 assert_movie!(&db, 1, 1, None, UniqueViolation);
488
489 make_info_movie!(&db, 2);
490 assert_movie!(&db, 2, 2, Some(2), ForeignKeyViolation);
491 make_info_collection!(&db, 2);
492 make_tmdb_collection!(&db, 2, 2);
493 assert_movie!(&db, 2, 2, Some(2), Success);
494 assert_movie!(&db, 1, 2, None, UniqueViolation);
495 assert_movie!(&db, 2, 1, None, UniqueViolation);
496
497 make_info_movie!(&db, 3);
498 assert_movie!(&db, 3, 3, None, Success; tmdb_id);
499 assert_movie!(&db, 4, 4, None, NotNullViolation; flix_id);
500 assert_movie!(&db, 5, 5, None, NotNullViolation; last_update);
501 assert_movie!(&db, 6, 6, None, NotNullViolation; runtime);
502 assert_movie!(&db, 7, 7, None, ForeignKeyViolation; collection_id);
503 }
504
505 #[tokio::test]
506 async fn test_round_trip_shows() {
507 let db = new_initialized_memory_db().await;
508
509 macro_rules! assert_show {
510 ($db:expr, $id:literal, $tid:literal, Success $(; $($skip:ident),+)?) => {
511 let model = assert_show!(@insert, $db, $id, $tid $(; $($skip),+)?)
512 .expect("insert");
513
514 assert_eq!(model.tmdb_id, TmdbShowId::from_raw($tid));
515 assert_eq!(model.flix_id, ShowId::from_raw($id));
516 assert_eq!(model.last_update, NaiveDate::from_yo_opt($tid, 1).expect("from_yo_opt").and_hms_opt(0, 0, 0).expect("and_hms_opt").and_utc());
517 assert_eq!(model.number_of_seasons, $id);
518 };
519 ($db:expr, $id:literal, $tid:literal, $error:ident $(; $($skip:ident),+)?) => {
520 let model = assert_show!(@insert, $db, $id, $tid $(; $($skip),+)?)
521 .expect_err("insert");
522
523 assert_eq!(
524 get_error_kind(model).expect("get_error_kind"),
525 ErrorKind::$error
526 );
527 };
528 (@insert, $db:expr, $id:literal, $tid:literal $(; $($skip:ident),+)?) => {
529 super::shows::ActiveModel {
530 tmdb_id: notsettable!(tmdb_id, TmdbShowId::from_raw($tid) $(, $($skip),+)?),
531 flix_id: notsettable!(flix_id, ShowId::from_raw($id) $(, $($skip),+)?),
532 last_update: notsettable!(last_update, NaiveDate::from_yo_opt($tid, 1).expect("from_yo_opt").and_hms_opt(0, 0, 0).expect("and_hms_opt").and_utc() $(, $($skip),+)?),
533 number_of_seasons: notsettable!(number_of_seasons, $id $(, $($skip),+)?),
534 }.insert($db).await
535 };
536 }
537
538 assert_show!(&db, 1, 1, ForeignKeyViolation);
539 make_info_show!(&db, 1);
540 assert_show!(&db, 1, 1, Success);
541 assert_show!(&db, 1, 1, UniqueViolation);
542
543 assert_show!(&db, 1, 2, UniqueViolation);
544 assert_show!(&db, 2, 1, UniqueViolation);
545 make_info_show!(&db, 2);
546 assert_show!(&db, 2, 2, Success);
547
548 make_info_show!(&db, 3);
549 assert_show!(&db, 3, 3, Success; tmdb_id);
550 assert_show!(&db, 4, 4, NotNullViolation; flix_id);
551 assert_show!(&db, 5, 5, NotNullViolation; last_update);
552 assert_show!(&db, 6, 6, NotNullViolation; number_of_seasons);
553 }
554
555 #[tokio::test]
556 async fn test_round_trip_seasons() {
557 let db = new_initialized_memory_db().await;
558
559 macro_rules! assert_season {
560 ($db:expr, $show:literal, $season:literal, $tshow:literal, $tseason:literal, Success $(; $($skip:ident),+)?) => {
561 let model = assert_season!(@insert, $db, $show, $season, $tshow, $tseason $(; $($skip),+)?)
562 .expect("insert");
563
564 assert_eq!(model.tmdb_show, TmdbShowId::from_raw($tshow));
565 assert_eq!(model.tmdb_season, ::flix_model::numbers::SeasonNumber::new($tseason));
566 assert_eq!(model.flix_show, ShowId::from_raw($show));
567 assert_eq!(model.flix_season, ::flix_model::numbers::SeasonNumber::new($season));
568 assert_eq!(model.last_update, NaiveDate::from_yo_opt($tshow, 1).expect("from_yo_opt").and_hms_opt(0, 0, 0).expect("and_hms_opt").and_utc());
569 };
570 ($db:expr, $show:literal, $season:literal, $tshow:literal, $tseason:literal, $error:ident $(; $($skip:ident),+)?) => {
571 let model = assert_season!(@insert, $db, $show, $season, $tshow, $tseason $(; $($skip),+)?)
572 .expect_err("insert");
573
574 assert_eq!(
575 get_error_kind(model).expect("get_error_kind"),
576 ErrorKind::$error
577 );
578 };
579 (@insert, $db:expr, $show:literal, $season:literal, $tshow:literal, $tseason:literal $(; $($skip:ident),+)?) => {
580 super::seasons::ActiveModel {
581 tmdb_show: notsettable!(tmdb_show, TmdbShowId::from_raw($tshow) $(, $($skip),+)?),
582 tmdb_season: notsettable!(tmdb_season, ::flix_model::numbers::SeasonNumber::new($tseason) $(, $($skip),+)?),
583 flix_show: notsettable!(flix_show, ShowId::from_raw($show) $(, $($skip),+)?),
584 flix_season: notsettable!(flix_season, ::flix_model::numbers::SeasonNumber::new($season) $(, $($skip),+)?),
585 last_update: notsettable!(last_update, NaiveDate::from_yo_opt($tshow, 1).expect("from_yo_opt").and_hms_opt(0, 0, 0).expect("and_hms_opt").and_utc() $(, $($skip),+)?),
586 }.insert($db).await
587 };
588 }
589
590 make_info_show!(&db, 1);
591 make_tmdb_show!(&db, 1, 1);
592
593 assert_season!(&db, 1, 1, 1, 1, ForeignKeyViolation);
594 make_info_season!(&db, 1, 1);
595 assert_season!(&db, 1, 1, 1, 1, Success);
596
597 assert_season!(&db, 1, 1, 1, 1, UniqueViolation);
598 assert_season!(&db, 1, 1, 2, 1, UniqueViolation);
599 assert_season!(&db, 2, 1, 1, 1, UniqueViolation);
600 make_info_season!(&db, 1, 2);
601 assert_season!(&db, 1, 2, 1, 2, Success);
602
603 assert_season!(&db, 1, 3, 1, 3, NotNullViolation; tmdb_show);
604 assert_season!(&db, 1, 4, 1, 4, NotNullViolation; tmdb_season);
605 assert_season!(&db, 1, 5, 1, 5, NotNullViolation; flix_show);
606 assert_season!(&db, 1, 6, 1, 6, NotNullViolation; flix_season);
607 assert_season!(&db, 1, 7, 1, 7, NotNullViolation; last_update);
608 }
609
610 #[tokio::test]
611 async fn test_round_trip_episodes() {
612 let db = new_initialized_memory_db().await;
613
614 macro_rules! assert_episode {
615 ($db:expr, $show:literal, $season:literal, $episode:literal, $tshow:literal, $tseason:literal, $tepisode:literal, Success $(; $($skip:ident),+)?) => {
616 let model = assert_episode!(@insert, $db, $show, $season, $episode, $tshow, $tseason, $tepisode $(; $($skip),+)?)
617 .expect("insert");
618
619 assert_eq!(model.tmdb_show, TmdbShowId::from_raw($tshow));
620 assert_eq!(model.tmdb_season, ::flix_model::numbers::SeasonNumber::new($tseason));
621 assert_eq!(model.tmdb_episode, ::flix_model::numbers::EpisodeNumber::new($tepisode));
622 assert_eq!(model.flix_show, ShowId::from_raw($show));
623 assert_eq!(model.flix_season, ::flix_model::numbers::SeasonNumber::new($season));
624 assert_eq!(model.flix_episode, ::flix_model::numbers::EpisodeNumber::new($episode));
625 assert_eq!(model.last_update, NaiveDate::from_yo_opt($tshow, 1).expect("from_yo_opt").and_hms_opt(0, 0, 0).expect("and_hms_opt").and_utc());
626 assert_eq!(model.runtime, Duration::from_secs($tshow).into());
627 };
628 ($db:expr, $show:literal, $season:literal, $episode:literal, $tshow:literal, $tseason:literal, $tepisode:literal, $error:ident $(; $($skip:ident),+)?) => {
629 let model = assert_episode!(@insert, $db, $show, $season, $episode, $tshow, $tseason, $tepisode $(; $($skip),+)?)
630 .expect_err("insert");
631
632 assert_eq!(
633 get_error_kind(model).expect("get_error_kind"),
634 ErrorKind::$error
635 );
636 };
637 (@insert, $db:expr, $show:literal, $season:literal, $episode:literal, $tshow:literal, $tseason:literal, $tepisode:literal $(; $($skip:ident),+)?) => {
638 super::episodes::ActiveModel {
639 tmdb_show: notsettable!(tmdb_show, TmdbShowId::from_raw($tshow) $(, $($skip),+)?),
640 tmdb_season: notsettable!(tmdb_season, ::flix_model::numbers::SeasonNumber::new($tseason) $(, $($skip),+)?),
641 tmdb_episode: notsettable!(tmdb_episode, ::flix_model::numbers::EpisodeNumber::new($tepisode) $(, $($skip),+)?),
642 flix_show: notsettable!(flix_show, ShowId::from_raw($show) $(, $($skip),+)?),
643 flix_season: notsettable!(flix_season, ::flix_model::numbers::SeasonNumber::new($season) $(, $($skip),+)?),
644 flix_episode: notsettable!(flix_episode, ::flix_model::numbers::EpisodeNumber::new($episode) $(, $($skip),+)?),
645 last_update: notsettable!(last_update, NaiveDate::from_yo_opt($tshow, 1).expect("from_yo_opt").and_hms_opt(0, 0, 0).expect("and_hms_opt").and_utc() $(, $($skip),+)?),
646 runtime: notsettable!(runtime, Duration::from_secs($tshow).into() $(, $($skip),+)?),
647 }.insert($db).await
648 };
649 }
650
651 make_info_show!(&db, 1);
652 make_info_season!(&db, 1, 1);
653 make_tmdb_show!(&db, 1, 1);
654 make_tmdb_season!(&db, 1, 1, 1, 1);
655
656 assert_episode!(&db, 1, 1, 1, 1, 1, 1, ForeignKeyViolation);
657 make_info_episode!(&db, 1, 1, 1);
658 assert_episode!(&db, 1, 1, 1, 1, 1, 1, Success);
659
660 assert_episode!(&db, 1, 1, 1, 1, 1, 1, UniqueViolation);
661 assert_episode!(&db, 1, 1, 1, 1, 2, 1, UniqueViolation);
662 assert_episode!(&db, 1, 1, 1, 2, 1, 1, UniqueViolation);
663 assert_episode!(&db, 1, 2, 1, 1, 1, 1, UniqueViolation);
664 assert_episode!(&db, 2, 1, 1, 1, 1, 1, UniqueViolation);
665 make_info_episode!(&db, 1, 1, 2);
666 assert_episode!(&db, 1, 1, 2, 1, 1, 2, Success);
667
668 assert_episode!(&db, 1, 1, 3, 1, 1, 3, NotNullViolation; tmdb_show);
669 assert_episode!(&db, 1, 1, 3, 1, 1, 4, NotNullViolation; tmdb_season);
670 assert_episode!(&db, 1, 1, 3, 1, 1, 5, NotNullViolation; tmdb_episode);
671 assert_episode!(&db, 1, 1, 3, 1, 1, 6, NotNullViolation; flix_show);
672 assert_episode!(&db, 1, 1, 3, 1, 1, 7, NotNullViolation; flix_season);
673 assert_episode!(&db, 1, 1, 3, 1, 1, 8, NotNullViolation; flix_episode);
674 assert_episode!(&db, 1, 1, 3, 1, 1, 9, NotNullViolation; last_update);
675 assert_episode!(&db, 1, 1, 3, 1, 1, 10, NotNullViolation; runtime);
676 }
677}