from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool from sqlalchemy import MetaData from database import Base from dev import app, get_db from models import Movie import pytest import crud import contextlib import random import inspect import unittest SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:" engine = create_engine( SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}, poolclass=StaticPool, ) TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base.metadata.create_all(bind=engine) def override_get_db(): try: db = TestingSessionLocal() yield db finally: db.close() app.dependency_overrides[get_db] = override_get_db client = TestClient(app) def rand_name(): import sys caller = sys._getframe(1).f_code.co_name name = f"{caller}_{random.randint(1, 1000)}" return name class BaseCrud(unittest.TestCase): def setUp(self): name = f"rand_{random.randint(1, 1000)}" self.create_payload = { "title": name, } def test_get_delete_movie_404_if_not_found(self): response = client.get("/movies/-1") assert response.status_code == 404 response_delete = client.delete("/movies/-1") assert response_delete.status_code == 404 def test_create_movie_api(self): response = client.post("/movies/", json=self.create_payload) assert response.status_code == 200 movie_id = response.json()["id"] assert f"Created {self.create_payload['title']}" in response.json()["message"] response = client.get(f"/movies/{movie_id}") assert response.json()["title"] == self.create_payload["title"] def test_delete_movie(self): response = client.post("/movies/", json=self.create_payload) movie_id = response.json()["id"] created_movie_path = f"/movies/{movie_id}" response_delete = client.delete(created_movie_path) response_missing = client.get(created_movie_path) assert response_delete.status_code == 204 assert response_missing.status_code == 404 def test_update_movie_api(self): self.create_payload["genres"] = ["anime"] self.create_payload["description"] = legacy_description = "will be deleted" response = client.post("/movies/", json=self.create_payload) movie_id = response.json()["id"] created_movie_path = f"/movies/{movie_id}" new_name = self.create_payload["title"].replace("rand", "update") new_genres = ["Drama", "war"] response_get = client.get(f"/movies/{movie_id}") assert response_get.json()["title"] != new_name assert response_get.json()["genres"] != new_genres response_update = client.put( created_movie_path, json={"title": new_name, "genres": new_genres} ) assert response_update.status_code == 200 response_get = client.get(f"/movies/{movie_id}") assert response_get.json()["title"] == new_name assert response_get.json()["genres"] == new_genres assert response_get.json()["description"] == "" def test_list_movies(self): response = client.get("/movies/") assert response.status_code == 200 # assert response.json() == [] N = 10 names = [] for _ in range(N): name = rand_name() names.append(name) self.create_payload["title"] = name response = client.post("/movies/", json=self.create_payload) assert response.status_code == 200 movies = client.get("/movies/") movies_by_title = {m["title"]: m for m in movies.json()} found = list(movies_by_title[title] for title in names) assert all(movies_by_title[title] for title in names) def test_list_movies_payload_format(self): response = client.get("/movies/") assert response.status_code == 200 # assert response.json() == [] N = 10 names = [] for _ in range(N): name = rand_name() names.append(name) self.create_payload["title"] = name response = client.post("/movies/", json=self.create_payload) assert response.status_code == 200 movies = client.get("/movies/").json() assert insinstance(movies["count"], int) assert insinstance(movies["movies"], list) movies_by_title = {m["title"]: m for m in movies.json()} found = list(movies_by_title[title] for title in names) assert all(movies_by_title[title] for title in names) class ApiTestCase(unittest.TestCase): def test_payload_content_in_and_out_loopback(self): be_the_fun_in_de_funes = { "id": 1, "title": "La Grande Vadrouille", "description": "During World War II, two French civilians and a downed English Bomber Crew set " "out from Paris to cross the demarcation line between Nazi-occupied Northern France and the " "South. From there they will be able to escape to England. First, they must avoid German troops -" "and the consequences of their own blunders.", "genres": ["Comedy", "War"], "release_date": "1966-12-07", "vote_average": 7.7, "vote_count": 1123, } domain_keys = sorted( {k for k in be_the_fun_in_de_funes if k not in ["id"]} ) # Make it deterministic non_primtive = ["genres", "release_date"] payload = {k: be_the_fun_in_de_funes[k] for k in domain_keys} # FIXME response = client.post("/movies/", json=payload) assert response.status_code == 200 movie_id = response.json()["id"] loopback_fetch = client.get(f"/movies/{movie_id}") assert loopback_fetch.status_code == 200 loopback_payload = loopback_fetch.json() # check for keys for attribute_name in domain_keys: with self.subTest(attribute_name=attribute_name): assert attribute_name in loopback_payload if attribute_name not in non_primtive: assert ( loopback_payload[attribute_name] == be_the_fun_in_de_funes[attribute_name] ) def test_payload_content_bad_format_status_code(self): be_the_fun_in_de_funes = { "id": 1, "title": "La Grande Vadrouille", "description": "During World War II, two French civilians and a downed English Bomber Crew set " "out from Paris to cross the demarcation line between Nazi-occupied Northern France and the " "South. From there they will be able to escape to England. First, they must avoid German troops -" "and the consequences of their own blunders.", "genres": ["Comedy", "War"], "release_date": "1966-12-07", "vote_average": 7.7, "vote_count": 1123, } domain_keys = sorted( {k for k in be_the_fun_in_de_funes if k not in ["id"]} ) # Make it deterministic payload = {k: be_the_fun_in_de_funes[k] for k in domain_keys} missing_key = "title" payload.pop(missing_key) response = client.post("/movies/", json=payload) assert 400 <= response.status_code < 500 assert missing_key in response.text def test_payload_content_bad_format_detail(self): be_the_fun_in_de_funes = { "id": 1, "title": "La Grande Vadrouille", "description": "During World War II, two French civilians and a downed English Bomber Crew set " "out from Paris to cross the demarcation line between Nazi-occupied Northern France and the " "South. From there they will be able to escape to England. First, they must avoid German troops -" "and the consequences of their own blunders.", "genres": ["Comedy", "War"], "release_date": "1966-12-07", "vote_average": 7.7, "vote_count": 1123, } domain_keys = sorted( {k for k in be_the_fun_in_de_funes if k not in ["id"]} ) # Make it deterministic payload = {k: be_the_fun_in_de_funes[k] for k in domain_keys} missing_key = "title" payload.pop(missing_key) response = client.post("/movies/", json=payload) assert 400 <= response.status_code < 500 assert response.status_code == 400 def test_payload_content_in_and_out_loopback(self): be_the_fun_in_de_funes = { "id": 1, "title": "La Grande Vadrouille", "description": "During World War II, two French civilians and a downed English Bomber Crew set " "out from Paris to cross the demarcation line between Nazi-occupied Northern France and the " "South. From there they will be able to escape to England. First, they must avoid German troops -" "and the consequences of their own blunders.", "genres": ["Comedy", "War"], "release_date": "1966-12-07", "vote_average": 7.7, "vote_count": 1123, } domain_keys = sorted( {k for k in be_the_fun_in_de_funes if k not in ["id"]} ) # Make it deterministic non_primtive = ["genres", "release_date"] payload = {k: be_the_fun_in_de_funes[k] for k in domain_keys} # FIXME response = client.post("/movies/", json=payload) assert response.status_code == 200 movie_id = response.json()["id"] loopback_fetch = client.get(f"/movies/{movie_id}") assert loopback_fetch.status_code == 200 loopback_payload = loopback_fetch.json() # check for keys for attribute_name in domain_keys: with self.subTest(attribute_name=attribute_name): assert attribute_name in loopback_payload if attribute_name not in non_primtive: assert ( loopback_payload[attribute_name] == be_the_fun_in_de_funes[attribute_name] ) def test_payload_content_in_and_out_loopback_non_primitive(self): be_the_fun_in_de_funes = { "id": 1, "title": "La Grande Vadrouille", "description": "During World War II, two French civilians and a downed English Bomber Crew set " "out from Paris to cross the demarcation line between Nazi-occupied Northern France and the " "South. From there they will be able to escape to England. First, they must avoid German troops -" "and the consequences of their own blunders.", "genres": ["Comedy", "War"], "release_date": "1966-12-07", "vote_average": 7.7, "vote_count": 1123, } domain_keys = sorted( {k for k in be_the_fun_in_de_funes if k not in ["id"]} ) # Make it deterministic payload = {k: be_the_fun_in_de_funes[k] for k in domain_keys} response = client.post("/movies/", json=payload) assert response.status_code == 200 movie_id = response.json()["id"] loopback_fetch = client.get(f"/movies/{movie_id}") assert loopback_fetch.status_code == 200 loopback_payload = loopback_fetch.json() # check for keys for attribute_name in domain_keys: assert ( loopback_payload[attribute_name] == be_the_fun_in_de_funes[attribute_name] )