flix_db/entity/
info.rs

1//! This module contains entities for storing media information such as
2//! titles and overviews
3
4/// Collection entity
5pub mod collections {
6	use flix_model::id::CollectionId;
7
8	use sea_orm::entity::prelude::*;
9
10	/// The database representation of a flix collection
11	#[sea_orm::model]
12	#[derive(Debug, Clone, DeriveEntityModel)]
13	#[sea_orm(table_name = "flix_info_collections")]
14	pub struct Model {
15		/// The collection's ID
16		#[sea_orm(primary_key, auto_increment = false)]
17		pub id: CollectionId,
18		/// The collection's title
19		pub title: String,
20		/// The collection's overview
21		pub overview: String,
22
23		/// The sortable title
24		#[sea_orm(indexed)]
25		pub sort_title: String,
26		/// The filesystem-safe slug
27		#[sea_orm(indexed, unique)]
28		pub fs_slug: String,
29		/// The url-safe slug
30		#[sea_orm(indexed, unique)]
31		pub web_slug: String,
32	}
33
34	impl ActiveModelBehavior for ActiveModel {}
35}
36
37/// Movie entity
38pub mod movies {
39	use flix_model::id::MovieId;
40
41	use chrono::NaiveDate;
42	use sea_orm::entity::prelude::*;
43
44	/// The database representation of a flix movie
45	#[sea_orm::model]
46	#[derive(Debug, Clone, DeriveEntityModel)]
47	#[sea_orm(table_name = "flix_info_movies")]
48	pub struct Model {
49		/// The movie's ID
50		#[sea_orm(primary_key, auto_increment = false)]
51		pub id: MovieId,
52		/// The movie's title
53		pub title: String,
54		/// The movie's tagline
55		pub tagline: String,
56		/// The movie's overview
57		pub overview: String,
58		/// The movie's release date
59		#[sea_orm(indexed)]
60		pub date: NaiveDate,
61
62		/// The sortable title
63		#[sea_orm(indexed)]
64		pub sort_title: String,
65		/// The filesystem-safe slug
66		#[sea_orm(indexed, unique)]
67		pub fs_slug: String,
68		/// The url-safe slug
69		#[sea_orm(indexed, unique)]
70		pub web_slug: String,
71	}
72
73	impl ActiveModelBehavior for ActiveModel {}
74}
75
76/// Show entity
77pub mod shows {
78	use flix_model::id::ShowId;
79
80	use chrono::NaiveDate;
81	use sea_orm::entity::prelude::*;
82
83	/// The database representation of a flix show
84	#[sea_orm::model]
85	#[derive(Debug, Clone, DeriveEntityModel)]
86	#[sea_orm(table_name = "flix_info_shows")]
87	pub struct Model {
88		/// The show's ID
89		#[sea_orm(primary_key, auto_increment = false)]
90		pub id: ShowId,
91		/// The show's title
92		pub title: String,
93		/// The show's tagline
94		pub tagline: String,
95		/// The show's overview
96		pub overview: String,
97		/// The show's air date
98		#[sea_orm(indexed)]
99		pub date: NaiveDate,
100
101		/// The sortable title
102		#[sea_orm(indexed)]
103		pub sort_title: String,
104		/// The filesystem-safe slug
105		#[sea_orm(indexed, unique)]
106		pub fs_slug: String,
107		/// The url-safe slug
108		#[sea_orm(indexed, unique)]
109		pub web_slug: String,
110
111		/// Seasons that are part of this show
112		#[sea_orm(has_many)]
113		pub seasons: HasMany<super::seasons::Entity>,
114		/// Episodes that are part of this show
115		#[sea_orm(has_many)]
116		pub episodes: HasMany<super::episodes::Entity>,
117	}
118
119	impl ActiveModelBehavior for ActiveModel {}
120}
121
122/// Season entity
123pub mod seasons {
124	use flix_model::id::ShowId;
125	use flix_model::numbers::SeasonNumber;
126
127	use chrono::NaiveDate;
128	use sea_orm::entity::prelude::*;
129
130	/// The database representation of a flix season
131	#[sea_orm::model]
132	#[derive(Debug, Clone, DeriveEntityModel)]
133	#[sea_orm(table_name = "flix_info_seasons")]
134	pub struct Model {
135		/// The season's show's ID
136		#[sea_orm(primary_key, auto_increment = false)]
137		pub show_id: ShowId,
138		/// The season's number
139		#[sea_orm(primary_key, auto_increment = false)]
140		pub season_number: SeasonNumber,
141		/// The season's title
142		pub title: String,
143		/// The season's overview
144		pub overview: String,
145		/// The season's air date
146		#[sea_orm(indexed)]
147		pub date: NaiveDate,
148
149		/// The show this season belongs to
150		#[sea_orm(
151			belongs_to,
152			from = "show_id",
153			to = "id",
154			on_update = "Cascade",
155			on_delete = "Cascade"
156		)]
157		pub show: HasOne<super::shows::Entity>,
158		/// Episodes that are part of this season
159		#[sea_orm(has_many)]
160		pub episodes: HasMany<super::episodes::Entity>,
161	}
162
163	impl ActiveModelBehavior for ActiveModel {}
164}
165
166/// Episode entity
167pub mod episodes {
168	use flix_model::id::ShowId;
169	use flix_model::numbers::{EpisodeNumber, SeasonNumber};
170
171	use chrono::NaiveDate;
172	use sea_orm::entity::prelude::*;
173
174	/// The database representation of a flix episode
175	#[sea_orm::model]
176	#[derive(Debug, Clone, DeriveEntityModel)]
177	#[sea_orm(table_name = "flix_info_episodes")]
178	pub struct Model {
179		/// The episode's show's ID
180		#[sea_orm(primary_key, auto_increment = false)]
181		pub show_id: ShowId,
182		/// The episode's season's number
183		#[sea_orm(primary_key, auto_increment = false)]
184		pub season_number: SeasonNumber,
185		/// The episode's number
186		#[sea_orm(primary_key, auto_increment = false)]
187		pub episode_number: EpisodeNumber,
188		/// The episode's title
189		pub title: String,
190		/// The episode's overview
191		pub overview: String,
192		/// The episode's air date
193		#[sea_orm(indexed)]
194		pub date: NaiveDate,
195
196		/// The show this episode belongs to
197		#[sea_orm(
198			belongs_to,
199			from = "show_id",
200			to = "id",
201			on_update = "Cascade",
202			on_delete = "Cascade"
203		)]
204		pub show: HasOne<super::shows::Entity>,
205		/// The season this episode belongs to
206		#[sea_orm(
207			belongs_to,
208			from = "(show_id, season_number)",
209			to = "(show_id, season_number)",
210			on_update = "Cascade",
211			on_delete = "Cascade"
212		)]
213		pub season: HasOne<super::seasons::Entity>,
214	}
215
216	impl ActiveModelBehavior for ActiveModel {}
217}
218
219/// Macros for creating info entities
220#[cfg(test)]
221pub mod test {
222	macro_rules! make_info_collection {
223		($db:expr, $id:expr) => {
224			$crate::entity::info::collections::ActiveModel {
225				id: Set(::flix_model::id::CollectionId::from_raw($id)),
226				title: Set(::std::string::String::new()),
227				overview: Set(::std::string::String::new()),
228				sort_title: Set(::std::string::String::new()),
229				fs_slug: Set(format!("C FS {}", $id)),
230				web_slug: Set(format!("C Web {}", $id)),
231			}
232			.insert($db)
233			.await
234			.expect("insert");
235		};
236	}
237	pub(crate) use make_info_collection;
238
239	macro_rules! make_info_movie {
240		($db:expr, $id:expr) => {
241			$crate::entity::info::movies::ActiveModel {
242				id: Set(::flix_model::id::MovieId::from_raw($id)),
243				title: Set(::std::string::String::new()),
244				tagline: Set(::std::string::String::new()),
245				overview: Set(::std::string::String::new()),
246				date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
247				sort_title: Set(::std::string::String::new()),
248				fs_slug: Set(format!("M FS {}", $id)),
249				web_slug: Set(format!("M Web {}", $id)),
250			}
251			.insert($db)
252			.await
253			.expect("insert");
254		};
255	}
256	pub(crate) use make_info_movie;
257
258	macro_rules! make_info_show {
259		($db:expr, $id:expr) => {
260			$crate::entity::info::shows::ActiveModel {
261				id: Set(::flix_model::id::ShowId::from_raw($id)),
262				title: Set(::std::string::String::new()),
263				tagline: Set(::std::string::String::new()),
264				overview: Set(::std::string::String::new()),
265				date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
266				sort_title: Set(::std::string::String::new()),
267				fs_slug: Set(format!("S FS {}", $id)),
268				web_slug: Set(format!("S Web {}", $id)),
269			}
270			.insert($db)
271			.await
272			.expect("insert");
273		};
274	}
275	pub(crate) use make_info_show;
276
277	macro_rules! make_info_season {
278		($db:expr, $show:expr, $season:expr) => {
279			$crate::entity::info::seasons::ActiveModel {
280				show_id: Set(::flix_model::id::ShowId::from_raw($show)),
281				season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
282				title: Set(::std::string::String::new()),
283				overview: Set(::std::string::String::new()),
284				date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
285			}
286			.insert($db)
287			.await
288			.expect("insert");
289		};
290	}
291	pub(crate) use make_info_season;
292
293	macro_rules! make_info_episode {
294		($db:expr, $show:expr, $season:expr, $episode:expr) => {
295			$crate::entity::info::episodes::ActiveModel {
296				show_id: Set(::flix_model::id::ShowId::from_raw($show)),
297				season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
298				episode_number: Set(::flix_model::numbers::EpisodeNumber::new($episode)),
299				title: Set(::std::string::String::new()),
300				overview: Set(::std::string::String::new()),
301				date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
302			}
303			.insert($db)
304			.await
305			.expect("insert");
306		};
307	}
308	pub(crate) use make_info_episode;
309}
310
311#[cfg(test)]
312mod tests {
313	use flix_model::id::{CollectionId, MovieId, ShowId};
314
315	use chrono::NaiveDate;
316	use sea_orm::ActiveValue::{NotSet, Set};
317	use sea_orm::entity::prelude::*;
318	use sea_orm::sqlx::error::ErrorKind;
319
320	use crate::tests::new_initialized_memory_db;
321
322	use super::super::tests::get_error_kind;
323	use super::super::tests::notsettable;
324	use super::test::{
325		make_info_collection, make_info_episode, make_info_movie, make_info_season, make_info_show,
326	};
327
328	#[tokio::test]
329	async fn use_test_macros() {
330		let db = new_initialized_memory_db().await;
331
332		make_info_collection!(&db, 1);
333		make_info_movie!(&db, 1);
334		make_info_show!(&db, 1);
335		make_info_season!(&db, 1, 1);
336		make_info_episode!(&db, 1, 1, 1);
337	}
338
339	#[tokio::test]
340	async fn test_round_trip_collections() {
341		let db = new_initialized_memory_db().await;
342
343		macro_rules! assert_collection {
344			($db:expr, $id:literal, Success $(; $($skip:ident),+)?) => {
345				let model = assert_collection!(@insert, $db, $id $(; $($skip),+)?)
346					.expect("insert");
347
348				assert_eq!(model.id, CollectionId::from_raw($id));
349				assert_eq!(model.title, concat!("C Title ", $id));
350				assert_eq!(model.overview, concat!("C Overview ", $id));
351			};
352			($db:expr, $id:literal, $error:ident $(; $($skip:ident),+)?) => {
353				let model = assert_collection!(@insert, $db, $id $(; $($skip),+)?)
354					.expect_err("insert");
355
356				assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
357			};
358			(@insert, $db:expr, $id:literal $(; $($skip:ident),+)?) => {
359				super::collections::ActiveModel {
360					id: notsettable!(id, CollectionId::from_raw($id) $(, $($skip),+)?),
361					title: notsettable!(title, concat!("C Title ", $id).to_string() $(, $($skip),+)?),
362					overview: notsettable!(overview, concat!("C Overview ", $id).to_string() $(, $($skip),+)?),
363					sort_title: notsettable!(sort_title, concat!("C Sort Title ", $id).to_string() $(, $($skip),+)?),
364					fs_slug: notsettable!(fs_slug, concat!("C FS Slug ", $id).to_string() $(, $($skip),+)?),
365					web_slug: notsettable!(web_slug, concat!("C Web Slug ", $id).to_string() $(, $($skip),+)?),
366				}.insert($db).await
367			};
368		}
369
370		assert_collection!(&db, 1, Success);
371		assert_collection!(&db, 1, UniqueViolation);
372		assert_collection!(&db, 2, Success);
373
374		assert_collection!(&db, 3, Success; id);
375		assert_collection!(&db, 4, NotNullViolation; title);
376		assert_collection!(&db, 5, NotNullViolation; overview);
377		assert_collection!(&db, 6, NotNullViolation; sort_title);
378		assert_collection!(&db, 7, NotNullViolation; fs_slug);
379		assert_collection!(&db, 8, NotNullViolation; web_slug);
380	}
381
382	#[tokio::test]
383	async fn test_round_trip_movies() {
384		let db = new_initialized_memory_db().await;
385
386		macro_rules! assert_movie {
387			($db:expr, $id:literal, Success $(; $($skip:ident),+)?) => {
388				let model = assert_movie!(@insert, $db, $id $(; $($skip),+)?)
389					.expect("insert");
390
391				assert_eq!(model.id, MovieId::from_raw($id));
392				assert_eq!(model.title, concat!("M Title ", $id));
393				assert_eq!(model.tagline, concat!("M Tagline ", $id));
394				assert_eq!(model.overview, concat!("M Overview ", $id));
395				assert_eq!(model.date, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt"));
396			};
397			($db:expr, $id:literal, $error:ident $(; $($skip:ident),+)?) => {
398				let model = assert_movie!(@insert, $db, $id $(; $($skip),+)?)
399					.expect_err("insert");
400
401				assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
402			};
403			(@insert, $db:expr, $id:literal $(; $($skip:ident),+)?) => {
404				super::movies::ActiveModel {
405					id: notsettable!(id, MovieId::from_raw($id) $(, $($skip),+)?),
406					title: notsettable!(title, concat!("M Title ", $id).to_string() $(, $($skip),+)?),
407					tagline: notsettable!(tagline, concat!("M Tagline ", $id).to_string() $(, $($skip),+)?),
408					overview: notsettable!(overview, concat!("M Overview ", $id).to_string() $(, $($skip),+)?),
409					date: notsettable!(date, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt") $(, $($skip),+)?),
410					sort_title: notsettable!(sort_title, concat!("M Sort Title ", $id).to_string() $(, $($skip),+)?),
411					fs_slug: notsettable!(fs_slug, concat!("M FS Slug ", $id).to_string() $(, $($skip),+)?),
412					web_slug: notsettable!(web_slug, concat!("M Web Slug ", $id).to_string() $(, $($skip),+)?),
413				}.insert($db).await
414			};
415		}
416
417		assert_movie!(&db, 1, Success);
418		assert_movie!(&db, 1, UniqueViolation);
419		assert_movie!(&db, 2, Success);
420
421		assert_movie!(&db, 3, Success; id);
422		assert_movie!(&db, 4, NotNullViolation; title);
423		assert_movie!(&db, 5, NotNullViolation; tagline);
424		assert_movie!(&db, 6, NotNullViolation; overview);
425		assert_movie!(&db, 7, NotNullViolation; date);
426		assert_movie!(&db, 8, NotNullViolation; sort_title);
427		assert_movie!(&db, 9, NotNullViolation; fs_slug);
428		assert_movie!(&db, 10, NotNullViolation; web_slug);
429	}
430
431	#[tokio::test]
432	async fn test_round_trip_shows() {
433		let db = new_initialized_memory_db().await;
434
435		macro_rules! assert_show {
436			($db:expr, $id:literal, Success $(; $($skip:ident),+)?) => {
437				let model = assert_show!(@insert, $db, $id $(; $($skip),+)?)
438					.expect("insert");
439
440				assert_eq!(model.id, ShowId::from_raw($id));
441				assert_eq!(model.title, concat!("S Title ", $id));
442				assert_eq!(model.tagline, concat!("S Tagline ", $id));
443				assert_eq!(model.overview, concat!("S Overview ", $id));
444				assert_eq!(model.date, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt"));
445			};
446			($db:expr, $id:literal, $error:ident $(; $($skip:ident),+)?) => {
447				let model = assert_show!(@insert, $db, $id $(; $($skip),+)?)
448					.expect_err("insert");
449
450				assert_eq!(
451					get_error_kind(model).expect("get_error_kind"),
452					ErrorKind::$error
453				);
454			};
455			(@insert, $db:expr, $id:literal $(; $($skip:ident),+)?) => {
456				super::shows::ActiveModel {
457					id: notsettable!(id, ShowId::from_raw($id) $(, $($skip),+)?),
458					title: notsettable!(title, concat!("S Title ", $id).to_string() $(, $($skip),+)?),
459					tagline: notsettable!(tagline, concat!("S Tagline ", $id).to_string() $(, $($skip),+)?),
460					overview: notsettable!(overview, concat!("S Overview ", $id).to_string() $(, $($skip),+)?),
461					date: notsettable!(date, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt") $(, $($skip),+)?),
462					sort_title: notsettable!(sort_title, concat!("S Sort Title ", $id).to_string() $(, $($skip),+)?),
463					fs_slug: notsettable!(fs_slug, concat!("S FS Slug ", $id).to_string() $(, $($skip),+)?),
464					web_slug: notsettable!(web_slug, concat!("S Web Slug ", $id).to_string() $(, $($skip),+)?),
465				}.insert($db).await
466			};
467		}
468
469		assert_show!(&db, 1, Success);
470		assert_show!(&db, 1, UniqueViolation);
471		assert_show!(&db, 2, Success);
472
473		assert_show!(&db, 3, Success; id);
474		assert_show!(&db, 4, NotNullViolation; title);
475		assert_show!(&db, 5, NotNullViolation; tagline);
476		assert_show!(&db, 6, NotNullViolation; overview);
477		assert_show!(&db, 7, NotNullViolation; date);
478		assert_show!(&db, 8, NotNullViolation; sort_title);
479		assert_show!(&db, 9, NotNullViolation; fs_slug);
480		assert_show!(&db, 10, NotNullViolation; web_slug);
481	}
482
483	#[tokio::test]
484	async fn test_round_trip_seasons() {
485		let db = new_initialized_memory_db().await;
486
487		macro_rules! assert_season {
488			($db:expr, $show:literal, $season:literal, Success $(; $($skip:ident),+)?) => {
489				let model = assert_season!(@insert, $db, $show, $season $(; $($skip),+)?)
490					.expect("insert");
491
492				assert_eq!(model.show_id, ShowId::from_raw($show));
493				assert_eq!(model.season_number, ::flix_model::numbers::SeasonNumber::new($season));
494				assert_eq!(model.title, concat!("SS Title ", $show, ",", $season));
495				assert_eq!(model.overview, concat!("SS Overview ", $show, ",", $season));
496				assert_eq!(model.date, NaiveDate::from_yo_opt($show + $season, 1).expect("from_yo_opt"));
497			};
498			($db:expr, $show:literal, $season:literal, $error:ident $(; $($skip:ident),+)?) => {
499				let model = assert_season!(@insert, $db, $show, $season $(; $($skip),+)?)
500					.expect_err("insert");
501
502				assert_eq!(
503					get_error_kind(model).expect("get_error_kind"),
504					ErrorKind::$error
505				);
506			};
507			(@insert, $db:expr, $show:literal, $season:literal $(; $($skip:ident),+)?) => {
508				super::seasons::ActiveModel {
509					show_id: notsettable!(show_id, ShowId::from_raw($show) $(, $($skip),+)?),
510					season_number: notsettable!(season_number, ::flix_model::numbers::SeasonNumber::new($season) $(, $($skip),+)?),
511					title: notsettable!(title, concat!("SS Title ", $show, ",", $season).to_string() $(, $($skip),+)?),
512					overview: notsettable!(overview, concat!("SS Overview ", $show, ",", $season).to_string() $(, $($skip),+)?),
513					date: notsettable!(date, NaiveDate::from_yo_opt($show + $season, 1).expect("from_yo_opt") $(, $($skip),+)?),
514				}.insert($db).await
515			};
516		}
517
518		assert_season!(&db, 1, 1, ForeignKeyViolation);
519		make_info_show!(&db, 1);
520		make_info_show!(&db, 2);
521
522		assert_season!(&db, 1, 1, Success);
523		assert_season!(&db, 1, 1, UniqueViolation);
524		assert_season!(&db, 2, 1, Success);
525		assert_season!(&db, 1, 2, Success);
526
527		assert_season!(&db, 1, 3, NotNullViolation; show_id);
528		assert_season!(&db, 1, 4, NotNullViolation; season_number);
529		assert_season!(&db, 1, 5, NotNullViolation; title);
530		assert_season!(&db, 1, 6, NotNullViolation; overview);
531		assert_season!(&db, 1, 7, NotNullViolation; date);
532	}
533
534	#[tokio::test]
535	async fn test_round_trip_episodes() {
536		let db = new_initialized_memory_db().await;
537
538		macro_rules! assert_episode {
539			($db:expr, $show:literal, $season:literal, $episode:literal, Success $(; $($skip:ident),+)?) => {
540				let model = assert_episode!(@insert, $db, $show, $season, $episode $(; $($skip),+)?)
541					.expect("insert");
542
543				assert_eq!(model.show_id, ShowId::from_raw($show));
544				assert_eq!(model.season_number, ::flix_model::numbers::SeasonNumber::new($season));
545				assert_eq!(model.episode_number, ::flix_model::numbers::EpisodeNumber::new($episode));
546				assert_eq!(model.title, concat!("SSE Title ", $show, ",", $season, ",", $episode));
547				assert_eq!(model.overview, concat!("SSE Overview ", $show, ",", $season, ",", $episode));
548				assert_eq!(model.date, NaiveDate::from_yo_opt($show + $season, 1).expect("from_yo_opt"));
549			};
550			($db:expr, $show:literal, $season:literal, $episode:literal, $error:ident $(; $($skip:ident),+)?) => {
551				let model = assert_episode!(@insert, $db, $show, $season, $episode $(; $($skip),+)?)
552					.expect_err("insert");
553
554				assert_eq!(
555					get_error_kind(model).expect("get_error_kind"),
556					ErrorKind::$error
557				);
558			};
559			(@insert, $db:expr, $show:literal, $season:literal, $episode:literal $(; $($skip:ident),+)?) => {
560				super::episodes::ActiveModel {
561					show_id: notsettable!(show_id, ShowId::from_raw($show) $(, $($skip),+)?),
562					season_number: notsettable!(season_number, ::flix_model::numbers::SeasonNumber::new($season) $(, $($skip),+)?),
563					episode_number: notsettable!(episode_number, ::flix_model::numbers::EpisodeNumber::new($episode) $(, $($skip),+)?),
564					title: notsettable!(title, concat!("SSE Title ", $show, ",", $season, ",", $episode).to_string() $(, $($skip),+)?),
565					overview: notsettable!(overview, concat!("SSE Overview ", $show, ",", $season, ",", $episode).to_string() $(, $($skip),+)?),
566					date: notsettable!(date, NaiveDate::from_yo_opt($show + $season, 1).expect("from_yo_opt") $(, $($skip),+)?),
567				}.insert($db).await
568			};
569		}
570
571		assert_episode!(&db, 1, 1, 1, ForeignKeyViolation);
572		make_info_show!(&db, 1);
573		make_info_show!(&db, 2);
574		assert_episode!(&db, 1, 1, 1, ForeignKeyViolation);
575		make_info_season!(&db, 1, 1);
576		make_info_season!(&db, 1, 2);
577		make_info_season!(&db, 2, 1);
578
579		assert_episode!(&db, 1, 1, 1, Success);
580		assert_episode!(&db, 1, 1, 1, UniqueViolation);
581		assert_episode!(&db, 2, 1, 1, Success);
582		assert_episode!(&db, 1, 2, 1, Success);
583		assert_episode!(&db, 1, 1, 2, Success);
584
585		assert_episode!(&db, 1, 1, 3, NotNullViolation; show_id);
586		assert_episode!(&db, 1, 1, 4, NotNullViolation; season_number);
587		assert_episode!(&db, 1, 1, 4, NotNullViolation; episode_number);
588		assert_episode!(&db, 1, 1, 5, NotNullViolation; title);
589		assert_episode!(&db, 1, 1, 6, NotNullViolation; overview);
590		assert_episode!(&db, 1, 1, 7, NotNullViolation; date);
591	}
592}