From 044176b111d7395b7a8b025972a382b203a9c3db Mon Sep 17 00:00:00 2001 From: AlexMachin1997 Date: Wed, 30 Oct 2024 23:20:32 +0000 Subject: [PATCH] chore Added some unit tests too cover the functionality so far, added more documentation and fixed a bugs noticed whilst creating the unit tests --- .eslintrc | 3 + ActingGroup.json | 1519 +++++++ README.md | 97 +- src/config/filePaths.ts | 3 +- src/config/graphql/generated/schema.ts | 2 - .../entertainment.service.spec.ts | 536 +++ .../entertainment/entertainment.service.ts | 4 +- src/core/socials/socials.service.spec.ts | 123 + src/graphql/generated/schema.ts | 705 +-- .../models/Discover/FiltersFormData.graphql | 1 - .../models/Discover/FiltersInput.graphql | 1 - src/graphql/models/Person/Credit.graphql | 5 +- src/graphql/models/Person/Person.graphql | 2 +- .../models/Person/PersonCredits.graphql | 1 + src/modules/discover/README.md | 33 + src/modules/discover/filtering/README.md | 72 + .../discover-filtering.service.spec.ts | 235 + .../filtering/discover-filtering.service.ts | 24 +- src/modules/discover/form-data/README.md | 56 + .../discover-form-data.service.spec.ts | 225 + .../form-data/discover-form-data.service.ts | 11 +- src/modules/discover/options/README.md | 39 + .../options/filtering-options.service.spec.ts | 75 + src/modules/discover/types/Discover.ts | 7 +- src/modules/movies/movie.service.spec.ts | 208 + .../person/mocks/CombinedCreditsQuery.ts | 4050 +++++++++++++++++ .../person/mocks/GetCreditGroupResponse.ts | 1718 +++++++ src/modules/person/mocks/PersonLookupQuery.ts | 27 + src/modules/person/person.service.spec.ts | 199 + src/modules/person/person.service.ts | 28 +- src/modules/person/types/Credits.ts | 8 +- src/modules/shows/show.service.spec.ts | 282 ++ src/modules/utils/utils.service.spec.ts | 84 + 33 files changed, 9946 insertions(+), 437 deletions(-) create mode 100644 ActingGroup.json create mode 100644 src/core/entertainment/entertainment.service.spec.ts create mode 100644 src/core/socials/socials.service.spec.ts create mode 100644 src/modules/discover/README.md create mode 100644 src/modules/discover/filtering/README.md create mode 100644 src/modules/discover/filtering/discover-filtering.service.spec.ts create mode 100644 src/modules/discover/form-data/README.md create mode 100644 src/modules/discover/form-data/discover-form-data.service.spec.ts create mode 100644 src/modules/discover/options/README.md create mode 100644 src/modules/discover/options/filtering-options.service.spec.ts create mode 100644 src/modules/movies/movie.service.spec.ts create mode 100644 src/modules/person/mocks/CombinedCreditsQuery.ts create mode 100644 src/modules/person/mocks/GetCreditGroupResponse.ts create mode 100644 src/modules/person/mocks/PersonLookupQuery.ts create mode 100644 src/modules/person/person.service.spec.ts create mode 100644 src/modules/shows/show.service.spec.ts create mode 100644 src/modules/utils/utils.service.spec.ts diff --git a/.eslintrc b/.eslintrc index b7181d5..b50ea73 100644 --- a/.eslintrc +++ b/.eslintrc @@ -19,6 +19,9 @@ ], "ignorePatterns": ["**/node_modules/**", "dist/**", "*.graphql"], "rules": { + "jest/prefer-expect-assertions": "error", + "jest/no-identical-title": "error", + "jest/valid-expect": "error", // These rules are for reference only. //#region eslint "class-methods-use-this": "off", diff --git a/ActingGroup.json b/ActingGroup.json new file mode 100644 index 0000000..d592925 --- /dev/null +++ b/ActingGroup.json @@ -0,0 +1,1519 @@ +[ + { + "year": "-", + "credits": [ + { + "mediaType": "tv", + "character": "Madison 'Madi' Cowart", + "title": "Just Cause", + "episodeCount": 1, + "year": "-", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "", + "title": "Tower of Terror", + "year": "-", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "The Bride", + "title": "Bride", + "year": "-", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Cousin Hilda", + "title": "The Phoenician Scheme", + "year": "-", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Augustina 'Gus' Mally", + "title": "The Gauntlet", + "year": "-", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "", + "title": "The Sea Change", + "year": "-", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Carol Blevins", + "title": "Featherwood", + "year": "-", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "", + "title": "Paris Paramount", + "year": "-", + "type": "cast" + } + ] + }, + { + "year": "2003", + "credits": [ + { + "mediaType": "movie", + "character": "Charlotte", + "title": "Lost in Translation", + "year": "2003", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Griet", + "title": "Girl with a Pearl Earring", + "year": "2003", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Jimmy Kimmel Live!", + "episodeCount": 3, + "year": "2003", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "The Sharon Osbourne Show", + "episodeCount": 1, + "year": "2003", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "The Ellen DeGeneres Show", + "episodeCount": 3, + "year": "2003", + "type": "cast" + } + ] + }, + { + "year": "2006", + "credits": [ + { + "mediaType": "movie", + "character": "Sondra Pransky", + "title": "Scoop", + "year": "2006", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Olivia Wenscombe", + "title": "The Prestige", + "year": "2006", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Kay Lake", + "title": "The Black Dahlia", + "year": "2006", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Close Up", + "episodeCount": 1, + "year": "2006", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Made in Hollywood: Teen Edition", + "episodeCount": 1, + "year": "2006", + "type": "cast" + } + ] + }, + { + "year": "1998", + "credits": [ + { + "mediaType": "movie", + "character": "Grace MacLean", + "title": "The Horse Whisperer", + "year": "1998", + "type": "cast" + } + ] + }, + { + "year": "2010", + "credits": [ + { + "mediaType": "movie", + "character": "Natalie Rushman / Natasha Romanoff", + "title": "Iron Man 2", + "year": "2010", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Daybreak", + "episodeCount": 1, + "year": "2010", + "type": "cast" + } + ] + }, + { + "year": "2001", + "credits": [ + { + "mediaType": "movie", + "character": "Rebecca", + "title": "Ghost World", + "year": "2001", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Birdy Abundas", + "title": "The Man Who Wasn't There", + "year": "2001", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Suzanne Sandor", + "title": "An American Rhapsody", + "year": "2001", + "type": "cast" + } + ] + }, + { + "year": "2009", + "credits": [ + { + "mediaType": "movie", + "character": "Anna Marks", + "title": "He's Just Not That Into You", + "year": "2009", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self (archive footage)", + "title": "Saturday Night Live: The Best of Amy Poehler", + "year": "2009", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self - Special Guest", + "title": "RuPaul's Drag Race", + "episodeCount": 1, + "year": "2009", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self (archive footage)", + "title": "RuPaul's Drag Race", + "episodeCount": 1, + "year": "2009", + "type": "cast" + } + ] + }, + { + "year": "2005", + "credits": [ + { + "mediaType": "movie", + "character": "Jordan Two Delta / Sarah Jordan", + "title": "The Island", + "year": "2005", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Nola Rice", + "title": "Match Point", + "year": "2005", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Made in Hollywood", + "episodeCount": 2, + "year": "2005", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Showbiz Tonight", + "episodeCount": 1, + "year": "2005", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Tooth Fairy (voice)", + "title": "Robot Chicken", + "episodeCount": 1, + "year": "2005", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Sailor Moon, Self, Della Bea Robinson (voice)", + "title": "Robot Chicken", + "episodeCount": 1, + "year": "2005", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Veronica Lodge, Dolores (voice)", + "title": "Robot Chicken", + "episodeCount": 1, + "year": "2005", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Amy, Darlene, Cheerleader Stick (voice)", + "title": "Robot Chicken", + "episodeCount": 1, + "year": "2005", + "type": "cast" + } + ] + }, + { + "year": "2004", + "credits": [ + { + "mediaType": "movie", + "character": "Alex", + "title": "In Good Company", + "year": "2004", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Mindy (voice)", + "title": "The SpongeBob SquarePants Movie", + "year": "2004", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Pursy Will", + "title": "A Love Song for Bobby Long", + "year": "2004", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Francesca Curtis", + "title": "The Perfect Score", + "year": "2004", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Meg Windermere", + "title": "A Good Woman", + "year": "2004", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self", + "title": "Lost on Location: Behind the Scenes of 'Lost in Translation'", + "year": "2004", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "omg! Insider", + "episodeCount": 2, + "year": "2004", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Entourage", + "episodeCount": 1, + "year": "2004", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Tinseltown TV", + "episodeCount": 1, + "year": "2004", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Le Grand Journal", + "episodeCount": 2, + "year": "2004", + "type": "cast" + } + ] + }, + { + "year": "1996", + "credits": [ + { + "mediaType": "movie", + "character": "Emily", + "title": "If Lucy Fell", + "year": "1996", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Amanda", + "title": "Manny & Lo", + "year": "1996", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self - Guest", + "title": "The Rosie O'Donnell Show", + "episodeCount": 1, + "year": "1996", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Critics Choice Awards", + "episodeCount": 1, + "year": "1996", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "The Daily Show", + "episodeCount": 1, + "year": "1996", + "type": "cast" + } + ] + }, + { + "year": "2008", + "credits": [ + { + "mediaType": "movie", + "character": "Cristina", + "title": "Vicky Cristina Barcelona", + "year": "2008", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Silken Floss", + "title": "The Spirit", + "year": "2008", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Mary Boleyn", + "title": "The Other Boleyn Girl", + "year": "2008", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self", + "title": "Translating History to Screen", + "year": "2008", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Stand Up to Cancer", + "episodeCount": 1, + "year": "2008", + "type": "cast" + } + ] + }, + { + "year": "2012", + "credits": [ + { + "mediaType": "movie", + "character": "Natasha Romanoff / Black Widow", + "title": "The Avengers", + "year": "2012", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "(archive footage)", + "title": "Final Cut: Ladies and Gentlemen", + "year": "2012", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Janet Leigh", + "title": "Hitchcock", + "year": "2012", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Narrator (voice)", + "title": "Escape from the World's Most Dangerous Place", + "year": "2012", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self", + "title": "The Avengers: A Visual Journey", + "year": "2012", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Finding Your Roots", + "episodeCount": 2, + "year": "2012", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Natasha Romanoff / Black Widow (archive footage)", + "title": "Honest Trailers", + "episodeCount": 1, + "year": "2012", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self - Interviewee", + "title": "Buenas noches y Buenafuente", + "episodeCount": 1, + "year": "2012", + "type": "cast" + } + ] + }, + { + "year": "2002", + "credits": [ + { + "mediaType": "movie", + "character": "Ashley Parker", + "title": "Eight Legged Freaks", + "year": "2002", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self", + "title": "Drive-Thru Records: Vol. 1", + "year": "2002", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "E! Live from the Red Carpet", + "episodeCount": 2, + "year": "2002", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Ant & Dec's Saturday Night Takeaway", + "episodeCount": 1, + "year": "2002", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Silenci?", + "episodeCount": 1, + "year": "2002", + "type": "cast" + } + ] + }, + { + "year": "1995", + "credits": [ + { + "mediaType": "movie", + "character": "Kate Armstrong", + "title": "Just Cause", + "year": "1995", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Jenna Halliwell", + "title": "The Client", + "episodeCount": 1, + "year": "1995", + "type": "cast" + } + ] + }, + { + "year": "1997", + "credits": [ + { + "mediaType": "movie", + "character": "Molly Pruitt", + "title": "Home Alone 3", + "year": "1997", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Little Girl", + "title": "Fall", + "year": "1997", + "type": "cast" + } + ] + }, + { + "year": "2007", + "credits": [ + { + "mediaType": "movie", + "character": "Annie Braddock", + "title": "The Nanny Diaries", + "year": "2007", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self", + "title": "The Director's Notebook: The Cinematic Sleight of Hand of Christopher Nolan", + "year": "2007", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Up Close with Carrie Keagan", + "episodeCount": 1, + "year": "2007", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Guys Choice Awards", + "episodeCount": 2, + "year": "2007", + "type": "cast" + } + ] + }, + { + "year": "2024", + "credits": [ + { + "mediaType": "movie", + "character": "Elita-1 (voice)", + "title": "Transformers One", + "year": "2024", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Kelly Jones", + "title": "Fly Me to the Moon", + "year": "2024", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Narrator (voice)", + "title": "Catching Fire: The Story of Anita Pallenberg", + "year": "2024", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Ash (voice)", + "title": "Sing: Thriller", + "year": "2024", + "type": "cast" + } + ] + }, + { + "year": "2011", + "credits": [ + { + "mediaType": "movie", + "character": "Self", + "title": "Woody Allen: A Documentary", + "year": "2011", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Kelly Foster", + "title": "We Bought a Zoo", + "year": "2011", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self (archive footage)", + "title": "Bert Stern: Original Madman", + "year": "2011", + "type": "cast" + } + ] + }, + { + "year": "2021", + "credits": [ + { + "mediaType": "movie", + "character": "Ash (voice)", + "title": "Come Home", + "year": "2021", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Natasha Romanoff / Black Widow", + "title": "Black Widow", + "year": "2021", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Ash (voice)", + "title": "Sing 2", + "year": "2021", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Narrator (voice)", + "title": "Moneymaker: Behind Black Widow", + "year": "2021", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Natasha Romanoff / Black Widow (archive footage) (uncredited)", + "title": "Marvel Studios Legends", + "episodeCount": 7, + "year": "2021", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Marvel Studios Assembled", + "episodeCount": 1, + "year": "2021", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Natasha Romanoff / Black Widow (archive footage) (uncredited)", + "title": "Hawkeye", + "episodeCount": 2, + "year": "2021", + "type": "cast" + } + ] + }, + { + "year": "1999", + "credits": [ + { + "mediaType": "movie", + "character": "Kathy Caldwell", + "title": "My Brother the Pig", + "year": "1999", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self - Guest", + "title": "The Late Late Show with Craig Kilborn", + "episodeCount": 1, + "year": "1999", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "The Early Show", + "episodeCount": 1, + "year": "1999", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Teen Choice Awards", + "episodeCount": 1, + "year": "1999", + "type": "cast" + } + ] + }, + { + "year": "2022", + "credits": [ + { + "mediaType": "movie", + "character": "Self (archive footage)", + "title": "Marvel Studios Assembled: The Making of Hawkeye", + "year": "2022", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Narrator (voice)", + "title": "Penglai", + "year": "2022", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "", + "title": "StoryBots: Answer Time", + "episodeCount": 1, + "year": "2022", + "type": "cast" + } + ] + }, + { + "year": "2014", + "credits": [ + { + "mediaType": "movie", + "character": "Natasha Romanoff / Black Widow", + "title": "Captain America: The Winter Soldier", + "year": "2014", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "", + "title": "Under the Skin", + "year": "2014", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Molly", + "title": "Chef", + "year": "2014", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Lucy Miller", + "title": "Lucy", + "year": "2014", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Narrator (voice)", + "title": "Deep Down", + "year": "2014", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self (archive footage)", + "title": "Her: Love in the Modern Age", + "year": "2014", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self", + "title": "Marvel: 75 Years, from Pulp to Pop!", + "year": "2014", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self", + "title": "Marvel Studios: Assembling a Universe", + "year": "2014", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Olivia (segment \"Two Player Game\") (voice)", + "title": "HitRECord on TV with Joseph Gordon-Levitt", + "episodeCount": 1, + "year": "2014", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "The Tonight Show Starring Jimmy Fallon", + "episodeCount": 3, + "year": "2014", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Variety Studio: Actors on Actors", + "episodeCount": 1, + "year": "2014", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Late Night with Seth Meyers", + "episodeCount": 1, + "year": "2014", + "type": "cast" + } + ] + }, + { + "year": "2015", + "credits": [ + { + "mediaType": "movie", + "character": "Natasha Romanoff / Black Widow", + "title": "Avengers: Age of Ultron", + "year": "2015", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Natasha Romanoff / Black Widow", + "title": "WHIH Newsfront", + "episodeCount": 4, + "year": "2015", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Pink Lady (voice)", + "title": "Assassin Banana", + "episodeCount": 2, + "year": "2015", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "The Late Show with Stephen Colbert", + "episodeCount": 1, + "year": "2015", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Close Up with The Hollywood Reporter", + "episodeCount": 1, + "year": "2015", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Hot Ones", + "episodeCount": 1, + "year": "2015", + "type": "cast" + } + ] + }, + { + "year": "2013", + "credits": [ + { + "mediaType": "movie", + "character": "Samantha (voice)", + "title": "Her", + "year": "2013", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Barbara Sugarman", + "title": "Don Jon", + "year": "2013", + "type": "cast" + } + ] + }, + { + "year": "2023", + "credits": [ + { + "mediaType": "movie", + "character": "Katherine", + "title": "North Star", + "year": "2023", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Midge Campbell", + "title": "Asteroid City", + "year": "2023", + "type": "cast" + } + ] + }, + { + "year": "2016", + "credits": [ + { + "mediaType": "movie", + "character": "Kaa (voice)", + "title": "The Jungle Book", + "year": "2016", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "DeeAnna Moran", + "title": "Hail, Caesar!", + "year": "2016", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Natasha Romanoff / Black Widow", + "title": "Captain America: Civil War", + "year": "2016", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Ash (voice)", + "title": "Sing", + "year": "2016", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Herself", + "title": "Floyd Norman: An Animated Life", + "year": "2016", + "type": "cast" + } + ] + }, + { + "year": "2017", + "credits": [ + { + "mediaType": "movie", + "character": "Major Mira Killian / Motoko Kusanagi", + "title": "Ghost in the Shell", + "year": "2017", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self", + "title": "Broadway: The Next Generation", + "year": "2017", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Jess", + "title": "Rough Night", + "year": "2017", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "self", + "title": "Ghost in the Shell: Hard-Wired Humanity - Making Ghost in the Shell", + "year": "2017", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self", + "title": "Art as Dialogue", + "year": "2017", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Narrator (voice)", + "title": "Jeff Koons", + "year": "2017", + "type": "cast" + } + ] + }, + { + "year": "2020", + "credits": [ + { + "mediaType": "movie", + "character": "Self", + "title": "VOMO: Vote or Miss Out", + "year": "2020", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self - Actress, \"Avengers: Endgame\"", + "title": "Chadwick Boseman: A Tribute for a King", + "year": "2020", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "The Drew Barrymore Show", + "episodeCount": 1, + "year": "2020", + "type": "cast" + } + ] + }, + { + "year": "1994", + "credits": [ + { + "mediaType": "movie", + "character": "Laura Nelson", + "title": "North", + "year": "1994", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Cartelera", + "episodeCount": 1, + "year": "1994", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Extra", + "episodeCount": 1, + "year": "1994", + "type": "cast" + } + ] + }, + { + "year": "2025", + "credits": [ + { + "mediaType": "movie", + "character": "Zora Bennett", + "title": "Jurassic World Rebirth", + "year": "2025", + "type": "cast" + } + ] + }, + { + "year": "2018", + "credits": [ + { + "mediaType": "movie", + "character": "Natasha Romanoff / Black Widow", + "title": "Avengers: Infinity War", + "year": "2018", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Nutmeg (voice)", + "title": "Isle of Dogs", + "year": "2018", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Motoko Kusanagi (archive footage)", + "title": "Walking to Nam Kok Hotel", + "year": "2018", + "type": "cast" + } + ] + }, + { + "year": "2019", + "credits": [ + { + "mediaType": "movie", + "character": "Nicole Barber", + "title": "Marriage Story", + "year": "2019", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Rosie", + "title": "Jojo Rabbit", + "year": "2019", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Natasha Romanoff / Black Widow (uncredited)", + "title": "Captain Marvel", + "year": "2019", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Self", + "title": "Marriage Story: From the Pages to the Performances", + "year": "2019", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Natasha Romanoff / Black Widow", + "title": "Avengers: Endgame", + "year": "2019", + "type": "cast" + }, + { + "mediaType": "movie", + "character": "Natasha Romanoff / Black Widow (archive footage)", + "title": "Celebrating Marvel's Stan Lee", + "year": "2019", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "The Kelly Clarkson Show", + "episodeCount": 1, + "year": "2019", + "type": "cast" + } + ] + }, + { + "year": "1986", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "American Masters", + "episodeCount": 1, + "year": "1986", + "type": "cast" + } + ] + }, + { + "year": "1984", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "Cinema 3", + "episodeCount": 1, + "year": "1984", + "type": "cast" + } + ] + }, + { + "year": "2000", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "The Queen Latifah Show", + "episodeCount": 1, + "year": "2000", + "type": "cast" + } + ] + }, + { + "year": "1992", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "Gomorron", + "episodeCount": 2, + "year": "1992", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self - Presenter", + "title": "MTV Movie & TV Awards", + "episodeCount": 3, + "year": "1992", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "The Tonight Show with Jay Leno", + "episodeCount": 8, + "year": "1992", + "type": "cast" + } + ] + }, + { + "year": "1956", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "Tony Awards", + "episodeCount": 3, + "year": "1956", + "type": "cast" + } + ] + }, + { + "year": "1993", + "credits": [ + { + "mediaType": "tv", + "character": "Self - Guest", + "title": "Late Night with Conan O'Brien", + "episodeCount": 1, + "year": "1993", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Late Show with David Letterman", + "episodeCount": 4, + "year": "1993", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self", + "title": "Good Day L.A.", + "episodeCount": 1, + "year": "1993", + "type": "cast" + } + ] + }, + { + "year": "1988", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "LIVE with Kelly and Mark", + "episodeCount": 6, + "year": "1988", + "type": "cast" + } + ] + }, + { + "year": "1991", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "Días de cine", + "episodeCount": 1, + "year": "1991", + "type": "cast" + } + ] + }, + { + "year": "1973", + "credits": [ + { + "mediaType": "tv", + "character": "Herself", + "title": "Fantástico", + "episodeCount": 0, + "year": "1973", + "type": "cast" + } + ] + }, + { + "year": "1987", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "Biography", + "episodeCount": 1, + "year": "1987", + "type": "cast" + } + ] + }, + { + "year": "1952", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "Today", + "episodeCount": 4, + "year": "1952", + "type": "cast" + } + ] + }, + { + "year": "1979", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "CBS News Sunday Morning", + "episodeCount": 1, + "year": "1979", + "type": "cast" + } + ] + }, + { + "year": "1977", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "Brit Awards", + "episodeCount": 2, + "year": "1977", + "type": "cast" + } + ] + }, + { + "year": "1953", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "The Oscars", + "episodeCount": 5, + "year": "1953", + "type": "cast" + } + ] + }, + { + "year": "1975", + "credits": [ + { + "mediaType": "tv", + "character": "Self - Host", + "title": "Saturday Night Live", + "episodeCount": 6, + "year": "1975", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "Self - Cameo (uncredited)", + "title": "Saturday Night Live", + "episodeCount": 7, + "year": "1975", + "type": "cast" + }, + { + "mediaType": "tv", + "character": "", + "title": "Saturday Night Live", + "episodeCount": 1, + "year": "1975", + "type": "cast" + } + ] + }, + { + "year": "1966", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "Goldene Kamera Verleihung", + "episodeCount": 1, + "year": "1966", + "type": "cast" + } + ] + }, + { + "year": "1981", + "credits": [ + { + "mediaType": "tv", + "character": "Self", + "title": "Entertainment Tonight", + "episodeCount": 2, + "year": "1981", + "type": "cast" + } + ] + } +] \ No newline at end of file diff --git a/README.md b/README.md index 8372941..50b3ccf 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,70 @@ +# The Open Movie DB GraphQL NestJS Example +

Nest Logo

-[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 -[circleci-url]: https://circleci.com/gh/nestjs/nest - -

A progressive Node.js framework for building efficient and scalable server-side applications.

-

-NPM Version -Package License -NPM Downloads -CircleCI -Coverage -Discord -Backers on Open Collective -Sponsors on Open Collective - - Support us - +

+ NPM Version + Package License + CI Status + Coverage

- -## Description +A GraphQL API built with NestJS that wraps [The Movie Database (TMDB) API](https://developer.themoviedb.org/docs) to provide movie and TV show data through a GraphQL interface. -[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. +## Features -## Installation +- GraphQL API built with NestJS and Apollo Server +- Integration with TMDB API for movie and TV show data +- Type-safe GraphQL schema with TypeScript +- Environment configuration with validation +- Modular architecture following NestJS best practices +- Comprehensive test coverage with Jest +- Code quality tools (ESLint, Prettier) +- CI/CD pipeline with GitHub Actions -```bash -$ yarn install -``` +## Prerequisites -## Running the app +- Node.js (v18+) +- Yarn package manager +- TMDB API key (Get one [here](https://developer.themoviedb.org/docs)) -```bash -# development -$ yarn run start +## Installation -# watch mode -$ yarn run start:dev +1. Install Node.js (v18 or higher) from [nodejs.org](https://nodejs.org/) -# production mode -$ yarn run start:prod -``` +2. Install Yarn package manager (v4): -## Test + ```bash + corepack enable + corepack prepare yarn@4.1.1 --activate + ``` -```bash -# unit tests -$ yarn run test +3. Generate an API token from [TMDB API](https://developer.themoviedb.org/docs/getting-started) -# e2e tests -$ yarn run test:e2e +4. Clone the repository: -# test coverage -$ yarn run test:cov -``` + ```bash + git clone https://github.com/AlexMachin1997/TheOpenMovieDB-GraphQL-Example.git + cd TheOpenMovieDB-GraphQL-Example + ``` -## Support +5. Install dependencies: -Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). + ```bash + yarn install + ``` -## Stay in touch +6. Create a `.env` file in the root directory and add your TMDB API token: -- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) -- Website - [https://nestjs.com](https://nestjs.com/) -- Twitter - [@nestframework](https://twitter.com/nestframework) + ``` + TMDB_API_TOKEN=your_api_token_here + ``` -## License +7. Start the development server: + ```bash + yarn start:dev + ``` -Nest is [MIT licensed](LICENSE). +The GraphQL playground will be available at http://localhost:3000/graphql diff --git a/src/config/filePaths.ts b/src/config/filePaths.ts index f3f5d8f..649e5ad 100644 --- a/src/config/filePaths.ts +++ b/src/config/filePaths.ts @@ -54,8 +54,9 @@ export function getGraphQLPaths(pathType: 'absolute' | 'relative' = 'absolute') 'models/Discover/FiltersFormData.graphql', // Person specific models - 'models/Person/CreditGroup.graphql', 'models/Person/Credit.graphql', + 'models/Person/CreditGroup.graphql', + 'models/Person/PersonCredits.graphql', 'models/Person/Person.graphql', // Individual resource schemas diff --git a/src/config/graphql/generated/schema.ts b/src/config/graphql/generated/schema.ts index 2264ce5..c78828e 100644 --- a/src/config/graphql/generated/schema.ts +++ b/src/config/graphql/generated/schema.ts @@ -36,7 +36,6 @@ export interface DiscoverFormDataInput { vote_count?: Nullable; search_first_air_date?: Nullable; restrict_services?: Nullable; - ott_region?: Nullable; with_ott_providers?: Nullable[]>; } @@ -102,7 +101,6 @@ export interface DiscoverFormData { vote_count?: Nullable; search_first_air_date?: Nullable; restrict_services?: Nullable; - ott_region?: Nullable; with_ott_providers?: Nullable[]>; } diff --git a/src/core/entertainment/entertainment.service.spec.ts b/src/core/entertainment/entertainment.service.spec.ts new file mode 100644 index 0000000..26fff09 --- /dev/null +++ b/src/core/entertainment/entertainment.service.spec.ts @@ -0,0 +1,536 @@ +import { HttpService } from '@nestjs/axios'; +import { ConfigService } from '@nestjs/config'; +import { Test, TestingModule } from '@nestjs/testing'; +import { of } from 'rxjs'; + +import { EntertainmentService } from './entertainment.service'; +import { + IReviewQuery, + IVideosQueryResponse, + IKeywordsQueryResponse, + ICreditGroupQueryResponse, + ICast +} from './types'; +import { ENTERTAINMENT_TYPES } from '../../graphql/generated/schema'; +import { UtilsService } from '../../modules/utils/utils.service'; +import { SocialsService } from '../socials/socials.service'; + +describe('EntertainmentService', () => { + let service: EntertainmentService; + + const mockHttpService = { + get: jest.fn() + }; + + const mockConfigService = { + get: jest.fn() + }; + + const mockUtilsService = { + getFullImageUrlPath: jest.fn(), + getNumberAsPercentage: jest.fn(), + getGender: jest.fn() + }; + + const mockSocialsService = { + getSocials: jest.fn() + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + EntertainmentService, + { + provide: HttpService, + useValue: mockHttpService + }, + { + provide: ConfigService, + useValue: mockConfigService + }, + { + provide: UtilsService, + useValue: mockUtilsService + }, + { + provide: SocialsService, + useValue: mockSocialsService + } + ] + }).compile(); + + service = module.get(EntertainmentService); + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect.hasAssertions(); + expect(service).toBeDefined(); + }); + + describe('getReview', () => { + it('should properly format review data from external API', async () => { + expect.hasAssertions(); + const mockReviewData: IReviewQuery = { + id: 123, + page: 1, + results: [ + { + author: 'John Doe', + author_details: { + name: 'John Doe', + username: 'johndoe', + avatar_path: '/avatar.jpg', + rating: 8 + }, + content: 'Great movie!', + created_at: '2023-01-01', + id: 'review1', + updated_at: '2023-01-01', + url: 'http://review.url' + } + ] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockReviewData })); + mockUtilsService.getFullImageUrlPath.mockReturnValue('http://image.url/avatar.jpg'); + mockUtilsService.getNumberAsPercentage.mockReturnValue(80); + + const result = await service.getReview({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + expect(mockUtilsService.getFullImageUrlPath).toHaveBeenCalledWith('/avatar.jpg'); + expect(mockUtilsService.getNumberAsPercentage).toHaveBeenCalledWith(8, 10); + expect(result?.author?.avatarUrl).toBe('http://image.url/avatar.jpg'); + expect(result?.author?.rating).toBe(80); + expect(result?.createdOn).toBe('1 Jan 2023'); + }); + + it('should return null when no review with rating is found', async () => { + expect.hasAssertions(); + const mockReviewData: IReviewQuery = { + id: 123, + page: 1, + results: [ + { + author: '', + author_details: { + name: '', + username: '', + avatar_path: null, + rating: null + }, + content: '', + created_at: '', + id: 'review1', + updated_at: '', + url: '' + } + ] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockReviewData })); + + const result = await service.getReview({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + expect(result).toBeNull(); + }); + }); + + describe('getTopBilledCast', () => { + it('should properly format cast data from external API', async () => { + expect.hasAssertions(); + const mockCastData: ICreditGroupQueryResponse = { + id: 123, + cast: [ + { + id: 1, + character: 'Main Character', + profile_path: '/profile1.jpg', + gender: 1, + order: 2, + name: 'Actor 1', + original_name: 'Actor 1', + popularity: 10, + cast_id: 1, + credit_id: 'credit1', + adult: false, + known_for_department: 'Acting' + }, + { + id: 2, + character: 'Supporting Role', + profile_path: '/profile2.jpg', + gender: 2, + order: 1, + name: 'Actor 2', + original_name: 'Actor 2', + popularity: 8, + cast_id: 2, + credit_id: 'credit2', + adult: false, + known_for_department: 'Acting' + } + ], + crew: [] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockCastData })); + mockUtilsService.getFullImageUrlPath + .mockReturnValueOnce('http://image.url/profile2.jpg') + .mockReturnValueOnce('http://image.url/profile1.jpg'); + mockUtilsService.getGender.mockReturnValueOnce('Female').mockReturnValueOnce('Male'); + + const result = await service.getTopBilledCast({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + // Check sorting + expect(result[0].id).toBe(2); + expect(result[1].id).toBe(1); + + // Check transformations + expect(mockUtilsService.getFullImageUrlPath).toHaveBeenCalledWith('/profile2.jpg'); + expect(mockUtilsService.getGender).toHaveBeenCalledWith(2); + expect(result[0].profileImageUrl).toBe('http://image.url/profile2.jpg'); + expect(result[0].gender).toBe('Female'); + }); + + it('should limit to top 9 cast members', async () => { + expect.hasAssertions(); + const mockCastMember: ICast = { + id: 1, + character: 'Character', + profile_path: '/profile.jpg', + gender: 1, + order: 1, + name: 'Actor', + original_name: 'Actor', + popularity: 5, + cast_id: 1, + credit_id: 'credit1', + adult: false, + known_for_department: 'Acting' + }; + + const mockCastData: ICreditGroupQueryResponse = { + id: 123, + cast: Array.from({ length: 12 }, () => ({ ...mockCastMember })), + crew: [] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockCastData })); + mockUtilsService.getFullImageUrlPath.mockReturnValue('http://image.url/profile.jpg'); + mockUtilsService.getGender.mockReturnValue('Male'); + + const result = await service.getTopBilledCast({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + expect(result).toHaveLength(9); + }); + }); + + describe('getFeaturedCrewMembers', () => { + it('should properly format and deduplicate crew data', async () => { + expect.hasAssertions(); + const mockCrewData: ICreditGroupQueryResponse = { + id: 123, + cast: [], + crew: [ + { + id: 1, + name: 'Jane Doe', + job: 'Director', + profile_path: '/director.jpg', + gender: 1, + credit_id: 'credit1', + department: 'Directing', + adult: false, + known_for_department: 'Directing', + original_name: 'Jane Doe', + popularity: 10 + }, + { + id: 1, + name: 'Jane Doe', + job: 'Writer', + profile_path: '/director.jpg', + gender: 1, + credit_id: 'credit2', + department: 'Writing', + adult: false, + known_for_department: 'Writing', + original_name: 'Jane Doe', + popularity: 10 + }, + { + id: 2, + name: 'John Smith', + job: 'Story', + profile_path: '/writer.jpg', + gender: 2, + credit_id: 'credit3', + department: 'Writing', + adult: false, + known_for_department: 'Writing', + original_name: 'John Smith', + popularity: 8 + } + ] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockCrewData })); + + const result = await service.getFeaturedCrewMembers({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + expect(result).toHaveLength(2); + expect(result[0].name).toBe('Jane Doe'); + expect(result[0].roles).toBe('Director, Writer'); + expect(result[1].name).toBe('John Smith'); + expect(result[1].roles).toBe('Story'); + }); + + it('should filter crew by specific job roles', async () => { + expect.hasAssertions(); + const mockCrewData: ICreditGroupQueryResponse = { + id: 123, + cast: [], + crew: [ + { + id: 1, + name: 'Jane Doe', + job: 'Director', + profile_path: '/director.jpg', + credit_id: 'credit1', + department: 'Directing', + adult: false, + gender: 1, + known_for_department: 'Directing', + original_name: 'Jane Doe', + popularity: 10 + }, + { + id: 2, + name: 'John Smith', + job: 'Cameraman', + profile_path: '/camera.jpg', + credit_id: 'credit2', + department: 'Camera', + adult: false, + gender: 2, + known_for_department: 'Camera', + original_name: 'John Smith', + popularity: 8 + } + ] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockCrewData })); + + const result = await service.getFeaturedCrewMembers({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + expect(result).toHaveLength(1); + expect(result[0].name).toBe('Jane Doe'); + }); + }); + + describe('getKeywords', () => { + it('should properly format movie keywords', async () => { + expect.hasAssertions(); + const mockKeywordData: IKeywordsQueryResponse = { + id: 123, + keywords: [ + { id: '1', name: 'action' }, + { id: '2', name: 'adventure' } + ] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockKeywordData })); + + const result = await service.getKeywords({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + expect(result).toEqual([ + { id: '1', name: 'action' }, + { id: '2', name: 'adventure' } + ]); + }); + + it('should properly format tv show keywords', async () => { + expect.hasAssertions(); + const mockKeywordData: IKeywordsQueryResponse = { + id: 456, + keywords: [ + { id: '3', name: 'drama' }, + { id: '4', name: 'thriller' } + ] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockKeywordData })); + + const result = await service.getKeywords({ + entertainmentType: ENTERTAINMENT_TYPES.TV, + entertainmentId: 456 + }); + + expect(result).toEqual([ + { id: '3', name: 'drama' }, + { id: '4', name: 'thriller' } + ]); + }); + }); + + describe('getSocials', () => { + it('should properly format social media links', async () => { + expect.hasAssertions(); + mockSocialsService.getSocials.mockReturnValue({ + facebook: 'https://facebook.com/movie123', + instagram: 'https://instagram.com/movie_insta', + twitter: 'https://twitter.com/movie_twitter' + }); + + const result = await service.getSocials({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + expect(result.facebook).toBe('https://facebook.com/movie123'); + expect(result.instagram).toBe('https://instagram.com/movie_insta'); + expect(result.twitter).toBe('https://twitter.com/movie_twitter'); + }); + }); + + describe('getYouTubeVideo', () => { + it('should properly format YouTube video URLs and prioritize trailers', async () => { + expect.hasAssertions(); + const mockVideoData: IVideosQueryResponse = { + id: 123, + results: [ + { + site: 'YouTube', + type: 'Clip', + key: 'clip123', + id: '1', + iso_639_1: 'en', + iso_3166_1: 'US', + name: 'Clip', + official: true, + published_at: '2023-01-01', + size: 1080 + }, + { + site: 'YouTube', + type: 'Trailer', + key: 'trailer456', + id: '2', + iso_639_1: 'en', + iso_3166_1: 'US', + name: 'Trailer', + official: true, + published_at: '2023-01-01', + size: 1080 + }, + { + site: 'Vimeo', + type: 'Trailer', + key: 'vimeo789', + id: '3', + iso_639_1: 'en', + iso_3166_1: 'US', + name: 'Vimeo Trailer', + official: true, + published_at: '2023-01-01', + size: 1080 + } + ] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockVideoData })); + + const result = await service.getYouTubeVideo({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + expect(result?.url).toBe('https://www.youtube.com/watch?v=trailer456'); + expect(result?.type).toBe('Trailer'); + }); + + it('should fall back to clips when no trailer is available', async () => { + expect.hasAssertions(); + const mockVideoData: IVideosQueryResponse = { + id: 123, + results: [ + { + site: 'YouTube', + type: 'Clip', + key: 'clip123', + id: '1', + iso_639_1: 'en', + iso_3166_1: 'US', + name: 'Clip', + official: true, + published_at: '2023-01-01', + size: 1080 + } + ] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockVideoData })); + + const result = await service.getYouTubeVideo({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + expect(result?.url).toBe('https://www.youtube.com/watch?v=clip123'); + expect(result?.type).toBe('Clip'); + }); + + it('should return null for non-YouTube videos', async () => { + expect.hasAssertions(); + const mockVideoData: IVideosQueryResponse = { + id: 123, + results: [ + { + site: 'Vimeo', + type: 'Trailer', + key: 'vimeo123', + id: '1', + iso_639_1: 'en', + iso_3166_1: 'US', + name: 'Vimeo Trailer', + official: true, + published_at: '2023-01-01', + size: 1080 + } + ] + }; + + mockHttpService.get.mockReturnValue(of({ data: mockVideoData })); + + const result = await service.getYouTubeVideo({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + entertainmentId: 123 + }); + + expect(result).toBeNull(); + }); + }); +}); diff --git a/src/core/entertainment/entertainment.service.ts b/src/core/entertainment/entertainment.service.ts index 750a87e..e93b94e 100644 --- a/src/core/entertainment/entertainment.service.ts +++ b/src/core/entertainment/entertainment.service.ts @@ -1,4 +1,5 @@ -/* eslint-disable import/extensions */ +/* eslint-disable @typescript-eslint/naming-convention */ + import { HttpService } from '@nestjs/axios'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; @@ -22,7 +23,6 @@ import { import { UtilsService } from '../../modules/utils/utils.service'; import { SocialsService } from '../socials/socials.service'; -// eslint-disable-next-line @typescript-eslint/naming-convention interface IEntertainmentCommonArguments { entertainmentType: ENTERTAINMENT_TYPES; entertainmentId: number; diff --git a/src/core/socials/socials.service.spec.ts b/src/core/socials/socials.service.spec.ts new file mode 100644 index 0000000..eca019f --- /dev/null +++ b/src/core/socials/socials.service.spec.ts @@ -0,0 +1,123 @@ +import { HttpService } from '@nestjs/axios'; +import { ConfigService } from '@nestjs/config'; +import { Test, TestingModule } from '@nestjs/testing'; +import { of } from 'rxjs'; + +import { SocialsService } from './socials.service'; +import { ENTERTAINMENT_TYPES } from '../../graphql/generated/schema'; + +describe('SocialsService', () => { + let service: SocialsService; + + const mockHttpService = { + get: jest.fn() + }; + + const mockConfigService = { + get: jest.fn() + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + SocialsService, + { + provide: HttpService, + useValue: mockHttpService + }, + { + provide: ConfigService, + useValue: mockConfigService + } + ] + }).compile(); + + service = module.get(SocialsService); + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect.hasAssertions(); + expect(service).toBeDefined(); + }); + + describe('getSocials', () => { + it('should properly format social media links when all IDs are present', async () => { + expect.hasAssertions(); + + const mockSocialData = { + facebook_id: 'facebookUser', + instagram_id: 'InstagramUser', + twitter_id: 'twitterUser', + tiktok_id: 'tiktokUser', + youtube_id: 'youtubeChannelId' + }; + + mockHttpService.get.mockReturnValue(of({ data: mockSocialData })); + mockConfigService.get.mockReturnValue('mock-api-key'); + + const result = await service.getSocials({ + resourceType: ENTERTAINMENT_TYPES.MOVIE, + resourceId: 123 + }); + + expect(result.facebook).toBe('https://www.facebook.com/facebookUser'); + expect(result.instagram).toBe('https://www.instagram.com/instagramuser'); + expect(result.twitter).toBe('https://www.twitter.com/twitterUser'); + expect(result.tiktok).toBe('https://www.tiktok.com/@tiktokUser/'); + expect(result.youtube).toBe('https://www.youtube.com/channel/youtubeChannelId'); + }); + + it('should return null for missing social media IDs', async () => { + expect.hasAssertions(); + + const mockSocialData = { + facebook_id: null, + instagram_id: null, + twitter_id: null, + tiktok_id: null, + youtube_id: null + }; + + mockHttpService.get.mockReturnValue(of({ data: mockSocialData })); + mockConfigService.get.mockReturnValue('mock-api-key'); + + const result = await service.getSocials({ + resourceType: ENTERTAINMENT_TYPES.MOVIE, + resourceId: 123 + }); + + expect(result.facebook).toBeNull(); + expect(result.instagram).toBeNull(); + expect(result.twitter).toBeNull(); + expect(result.tiktok).toBeNull(); + expect(result.youtube).toBeNull(); + }); + + it('should make HTTP request with correct parameters', async () => { + expect.hasAssertions(); + + const mockSocialData = { + facebook_id: 'facebookUser' + }; + + mockHttpService.get.mockReturnValue(of({ data: mockSocialData })); + mockConfigService.get.mockReturnValue('mock-api-key'); + + await service.getSocials({ + resourceType: ENTERTAINMENT_TYPES.MOVIE, + resourceId: 123 + }); + + expect(mockHttpService.get).toHaveBeenCalledWith( + 'https://api.themoviedb.org/3/movie/123/external_ids?language=en-U', + { + headers: { + Accept: 'application/json', + Authorization: 'Bearer mock-api-key' + } + } + ); + }); + }); +}); diff --git a/src/graphql/generated/schema.ts b/src/graphql/generated/schema.ts index d4ac849..4d40ac0 100644 --- a/src/graphql/generated/schema.ts +++ b/src/graphql/generated/schema.ts @@ -1,344 +1,361 @@ - -/* - * ------------------------------------------------------- - * THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY) - * ------------------------------------------------------- - */ - -/* tslint:disable */ -/* eslint-disable */ - -export enum ENTERTAINMENT_TYPES { - MOVIE = "MOVIE", - TV = "TV" -} - -export enum RESOURCE_TYPE { - TOP_RATED = "TOP_RATED", - POPULAR = "POPULAR", - NOW_PLAYING = "NOW_PLAYING", - UPCOMING = "UPCOMING", - AIRING_TODAY = "AIRING_TODAY", - ON_THE_AIR = "ON_THE_AIR" -} - -export interface DiscoverFormDataInput { - sort_by?: Nullable; - show_me?: Nullable; - with_watch_monetization_types?: Nullable[]>; - with_genres?: Nullable[]>; - certifications?: Nullable[]>; - with_release_types?: Nullable[]>; - release_date?: Nullable; - air_date?: Nullable; - with_original_language?: Nullable; - region?: Nullable; - vote_average?: Nullable; - with_runtime?: Nullable; - vote_count?: Nullable; - search_first_air_date?: Nullable; - restrict_services?: Nullable; - ott_region?: Nullable; - with_ott_providers?: Nullable[]>; -} - -export interface DateRangeFilterInput { - gte?: Nullable; - lte?: Nullable; -} - -export interface NumberRangeFilterInput { - gte?: Nullable; - lte?: Nullable; -} - -export interface VoteCountFilterInput { - gte?: Nullable; - lte?: Nullable; -} - -export interface PaginatedResult { - meta: PaginationMetaData; -} - -export interface PaginationMetaData { - __typename?: 'PaginationMetaData'; - page: number; - pageCount: number; - total: number; -} - -export interface DiscoverResult { - __typename?: 'DiscoverResult'; - adult?: Nullable; - backdropUrl?: Nullable; - posterUrl?: Nullable; - name?: Nullable; - homepage?: Nullable; - id: string; - originCountry?: Nullable[]>; - originalLanguage?: Nullable; - overview?: Nullable; - releaseDate?: Nullable; - popularity?: Nullable; - posterPath?: Nullable; - status?: Nullable; - tagline?: Nullable; - voteAverage?: Nullable; - voteCount?: Nullable; -} - -export interface PaginatedDiscoverResult extends PaginatedResult { - __typename?: 'PaginatedDiscoverResult'; - meta: PaginationMetaData; - results: DiscoverResult[]; -} - -export interface DiscoverFormData { - __typename?: 'DiscoverFormData'; - sort_by?: Nullable; - show_me?: Nullable; - with_watch_monetization_types?: Nullable[]>; - with_genres?: Nullable[]>; - certifications?: Nullable[]>; - with_release_types?: Nullable[]>; - release_date?: Nullable; - air_date?: Nullable; - with_original_language?: Nullable; - region?: Nullable; - vote_average?: Nullable; - with_runtime?: Nullable; - vote_count?: Nullable; - search_first_air_date?: Nullable; - restrict_services?: Nullable; - ott_region?: Nullable; - with_ott_providers?: Nullable[]>; -} - -export interface DateRangeFilter { - __typename?: 'DateRangeFilter'; - gte?: Nullable; - lte?: Nullable; -} - -export interface NumberRangeFilter { - __typename?: 'NumberRangeFilter'; - gte?: Nullable; - lte?: Nullable; -} - -export interface VoteCountFilter { - __typename?: 'VoteCountFilter'; - gte?: Nullable; - lte?: Nullable; -} - -export interface BelongsToCollection { - __typename?: 'BelongsToCollection'; - id?: Nullable; - name?: Nullable; - backgroundUrl?: Nullable; - posterUrl?: Nullable; -} - -export interface Cast { - __typename?: 'Cast'; - id?: Nullable; - character?: Nullable; - profileImageUrl?: Nullable; - gender?: Nullable; - episodeCount?: Nullable; -} - -export interface Company { - __typename?: 'Company'; - id?: Nullable; - logo?: Nullable; - name?: Nullable; -} - -export interface Crew { - __typename?: 'Crew'; - name?: Nullable; - roles?: Nullable; -} - -export interface Genre { - __typename?: 'Genre'; - id?: Nullable; - name?: Nullable; -} - -export interface Keyword { - __typename?: 'Keyword'; - id?: Nullable; - name?: Nullable; -} - -export interface Recommendation { - __typename?: 'Recommendation'; - name?: Nullable; - releaseDate?: Nullable; - backgroundUrl?: Nullable; - rating?: Nullable; -} - -export interface Author { - __typename?: 'Author'; - name?: Nullable; - username?: Nullable; - avatarUrl?: Nullable; - rating?: Nullable; -} - -export interface Review { - __typename?: 'Review'; - author?: Nullable; - isFeatured?: Nullable; - content?: Nullable; - createdOn?: Nullable; -} - -export interface Social { - __typename?: 'Social'; - id?: Nullable; - freebase?: Nullable; - imdb?: Nullable; - tvrage?: Nullable; - wikidata?: Nullable; - facebook?: Nullable; - instagram?: Nullable; - tiktok?: Nullable; - twitter?: Nullable; - youtube?: Nullable; -} - -export interface Video { - __typename?: 'Video'; - id?: Nullable; - name?: Nullable; - url?: Nullable; - type?: Nullable; - site?: Nullable; -} - -export interface Movie { - __typename?: 'Movie'; - id?: Nullable; - name?: Nullable; - overview?: Nullable; - backgroundUrl?: Nullable; - posterUrl?: Nullable; - genres?: Nullable[]>; - homepage?: Nullable; - originalLanguage?: Nullable; - productionCompanies?: Nullable[]>; - releaseDate?: Nullable; - voteAverage?: Nullable; - status?: Nullable; - review?: Nullable; - recommendations?: Nullable[]>; - keywords?: Nullable[]>; - social?: Nullable; - topBilledCast?: Nullable[]>; - featuredCrew?: Nullable[]>; - youtubeVideo?: Nullable