A backend for retrieving films, saving them with personal content and with the possibility of having users.
Use Node.js 24.9.0 or newer. Jest needs this version's synchronous VM-module support to load Nock 15 from the CommonJS test suite.
Clone the repo and:
$ npm installAlso you'll need sequelize-cli installed globally:
$ npm install -g sequelize-cliThe first thing is to run the migration with Sequelize:
$ sequelize-cli db:migrateThen create a file used for the secret keys:
$ touch secrets.jsand populate like this:
exports.API_KEY = "YOUR_TMDB_API_KEY";
exports.PRIVATE_KEY = "A RANDOM KEY TO SIGN THE JWT";Finally you can run the server by:
$ npm startRun ESLint across the backend with:
$ npm run lintPrettier is configured in .prettierrc.json, with exclusions in .prettierignore.
Both scripts cover the whole repo, including the test/ suite. Recorded TMDB
fixtures are excluded, since they are generated API dumps.
Format everything in place:
$ npm run formatCheck formatting without writing (useful in CI):
$ npm run format:checkRun the whole suite:
$ npm testOr one project at a time:
$ npm run test:unit
$ npm run test:integrationThe test scripts enable Node's --experimental-vm-modules flag for Nock 15's
ESM entry point. CommonJS consumers use require("nock").default. Both project
setups restore Nock's HTTP interception after each suite to avoid leaking it
into the next Jest environment.
PDF client-abort checks wait for server-side document destruction rather than
Supertest's end callback, which may not run after an abort.
Tests don't need a secrets.js file. The test setup injects API_KEY and
PRIVATE_KEY as environment variables, so you can clone and run the suite
without any local secrets.
$ npm run test:coverageThis prints a per-file table in the terminal with statement, branch, function
and line percentages, plus the exact uncovered line numbers for each file. It
also writes a browsable HTML report to coverage/lcov-report/index.html, where
you can click into a file and see the uncovered lines highlighted, and an
coverage/lcov.info for CI tools. The coverage directory is gitignored.
Coverage is measured over src/**/*.js only. You can scope a run to one
project:
$ npm run test:coverage:unit
$ npm run test:coverage:integrationNote that a single-project run reports low coverage for code the other project
exercises, so use npm run test:coverage for the real picture. Those two
scripts pass --coverageThreshold={} for exactly this reason.
src currently sits at 100% of statements, lines and functions, and 95.62% of
branches. jest.config.js enforces that as a floor, so npm run test:coverage
fails if a change drops below it.
The seven uncovered branches are deliberate, not gaps:
error?.message ?? "Internal server error"fallbacks inrecommendationanduserFavs. Sequelize always rejects with a realError, so the right-hand side is unreachable.if (res.headersSent)in the PDF route.doc.pipe(res)flushes PDFKit's header immediately, so the headers are always already sent by the time thatcatchruns.
$ npm run fixtures:recordThis hits the real TMDB API, so it needs a valid TMDB key in the environment.
Status: fixture recording can't complete right now. The recorder looks for a
pair of actor discover pages that naturally overlap, and no such pair was found
in the TMDB data we sampled. As a documented interim substitution, tests 20, 21
and 29-31 use synthetic data served through nock, shaped like real TMDB
responses. These should be swapped for real fixtures once recording works.
Some tests fail on purpose. They pin down bugs that are still in the codebase:
- IDOR in
userFavs: a user can read and modify another user's favourites. This is a live security issue and it's still unfixed. Treat it as the priority. signUpreturns an HTML error page instead of JSON when bcrypt throws.searchthrows aTypeErroron certain inputs.- the recommendation route answers 204 where it should answer 404.
userFavsuses a truthy check on the limit value, so a limit of 0 is ignored.- the PDF response is missing its
content-typeheader. searchdoesn't encode the search term into the URL properly.
B5 and B8 are missing from that list on purpose. Both were investigated and turned out not to be bugs, and the original numbering was kept so the notes and test names still line up.
Checks the presence of the auth headers and tries to decode it, if successful
create a property on the req object with the actual user ID, if not return a 401.`
Almost, now it checks the presence of the cookie.
The route accepts a POST request with the body containing the first name, last name (the only optional field), a unique email address and a password, that will be hashed when saving it to the DB. It either responds with 201 if successful or a 500 status code if errors were encountered.
The route accepts a POST request with the body containing the unique email address and the password. It finds the user by email and compares the hashed password with the received one, if successful it creates a JWT to be sent back to the client, if something doesn't match it send a 401 and if anything else goes wring a 404 status code.
A DELETE request to the route will delete the specified cookie/s.
Accepts a GET request with the actor's ID in the params, it then goes through all the existing pages and builds a list with all of the actor's films. In case of error return 501.
Accepts a GET request with the actor's ID in the params, it return an object with the actor's info, such as name.
Accepts a GET request with the film's ID in the params, retrieves it and before sending it back to the client it caches it in a Map for future use. In case of error return 404.
OR
"/:movieId/:movieResource"
Which can query a similar movie by sending an ID, or by sending a string that can't be parsed to an INT you could get other resources, comments in the file.
Accepts a GET request with the film's ID in the params, retrieves its credits and before sending it back to the client it caches it in a Map for future use. In case of error return 404.
Accepts a GET request with an optional query &page=INT, and retrieves the films from TMDB sorted by popularity.
Accepts a GET request with the film's ID in the params, retrieves its trailer/s and before sending it back to the client it caches it in a Map for future use. In case of error return 404.
A health check route. Responds to GET request to which, if alive, returns a 200 status code.
Accepts a GET request, and returns a random list of films. If it enters an infinite loop there is a fail safe mechanism to break out of it and return the list.
Accepts a GET request with two optional queries &pageNum=INT and &includeAdult=STRING and the search term. If successful returns the films found otherwise 501.
Return the user with it's favs.
Providing all the required info it create a new UserFavourite with the user requesting it as the owner.
Modifies the UserFavourite entry either with the boolean 'seen' and 'watchlist' or the string 'description', also it accepts a 'rating' property.
Removes the UserFavourite entry of the requesting user.