import pytest from typing import Mapping import httpx from fastapi.testclient import TestClient from app import app client = TestClient(app) # These tests run in a clean database. # Note, however, that they do not _each_ run in a clean database - it persists between executions. # Note the use of `cleanups` (defined in `conftest.py`) to allow for cleanup operations that should leave the database # in a clean state after each test - but also, note the comment above the commented-out # `test_adding_games_with_ties` def test_add_and_retrieve_player(test_client: TestClient, cleanups): response = _json_get(test_client, "/player/1") assert response.status_code == 404 create_player_response = _json_post(client, "/player", {"name": "jason"}) assert create_player_response.status_code == 201 response_1 = _json_get(client, "/player/1") assert response_1.json()["name"] == "jason" def cleanup(): delete_response = _json_delete(client, "/player/1") assert delete_response.status_code == 204 cleanups.add_success(cleanup) def test_add_and_retrieve_deck(test_client: TestClient, cleanups): not_found_response = _json_get(test_client, "/deck/1") assert not_found_response.status_code == 404 # Try (and fail) to create a deck owned by a non-existent player invalid_owner_response = _json_post( test_client, "/deck", {"name": "Baby's First Deck", "owner_id": 1} ) assert invalid_owner_response.status_code == 400 assert invalid_owner_response.json()["detail"] == "Owner id 1 not found" create_jim_response = _json_post(test_client, "/player", {"name": "jim"}) assert create_jim_response.status_code == 201 jim_id = create_jim_response.json()["id"] create_deck_response = _json_post( test_client, "/deck", {"name": "Baby's First Deck", "owner_id": str(jim_id)} ) assert create_deck_response.status_code == 201 # _Should_ always be 1, since we expect to start with an empty database, but why risk it? deck_id = create_deck_response.json()["id"] get_deck_response = _json_get(test_client, f"/deck/{deck_id}") assert get_deck_response.status_code == 200 assert get_deck_response.json()["name"] == "Baby's First Deck" # Very basic HTML testing html_response = test_client.get(f"/deck/{deck_id}") assert """owned by jim""" in html_response.text def success_cleanup(): delete_response = _json_delete(test_client, f"/deck/{deck_id}") assert delete_response.status_code == 204 cleanups.add_success(success_cleanup) # Keeping this around because it would be useful to reintroduce it if I factor out data-sourcing # (I did briefly try doing so, but because the logic of full-seeding involves recreating the iterable, # and the two different data sources have different object-types, it wasn't obvious how to reinitialize. # Probably would be possible, but given that I'm likely gonna deprecate the csv-upload model anyway, probably not worth it.) @pytest.mark.skip( reason="Moved from an injected-data model, to reading from Google Sheets" ) def test_incremental_add_of_games(test_client: TestClient, cleanups): latest_game_response = _json_get(test_client, "/game/latest_game") assert latest_game_response.status_code == 404 # https://github.com/tiangolo/fastapi/issues/1536#issuecomment-640781718 with open("seed-data/all-in-one.csv", "rb") as f: test_client.post( "/api/seed/all_in_one", files={"file": ("fake_all_in_one_filename.csv", f, "text/csv")}, ) latest_game_response = _json_get(test_client, "/game/latest_game") assert latest_game_response.status_code == 200 print(latest_game_response.json()) assert latest_game_response.json()["date"] == "2024-07-05T00:00:00" # then seed again, and check that it successfully gets the expected latest with open( "seed-data/all-in-one-updated-for-incremental-add-testing.csv", "rb" ) as f: test_client.post( "/api/seed/all_in_one", files={"file": ("fake_all_in_one_filename.csv", f, "text/csv")}, ) latest_deck_response = _json_get(test_client, "/game/latest_game") assert latest_deck_response.status_code == 200 assert latest_deck_response.json()["date"] == "2024-07-25T00:00:00" def success_cleanup(): games = _json_get(test_client, "/game/list") for game in games.json(): _json_delete(test_client, f"/game/{game['id']}") decks = _json_get(test_client, "/deck/list") for deck in decks.json(): _json_delete(test_client, f"/deck/{deck['id']}") players = _json_get(test_client, "/player/list") for player in players.json(): _json_delete(test_client, f"/player/{player['id']}") cleanups.add_success(success_cleanup) # TODO - this test is valid and correct, but I can't find a way to run it. # The "cleanups" can only interact with the database via APIs (not directly), and the preceding test adds content to the # database that cannot (currently) be nuked (specifically - sequence numbers). # Either: # * Add a full "nuke the database" API (probably not a good thing to expose!) # * Find a way to initialize a full fresh database for each _test_ (see `isolated_database` in `tests/sql/test_crud.py` # and `tests/routers/test_stats.py` for inspiration ) # # def test_adding_games_with_ties(test_client: TestClient, cleanups): # latest_game_response = _json_get(test_client, "/game/latest_game") # assert latest_game_response.status_code == 404 # with open("seed-data/all-in-one-with-tied-games.csv", "rb") as f: # test_client.post( # "/api/seed/all_in_one", # files={"file": ("fake_all_in_one_filename.csv", f, "text/csv")}, # ) # tied_game_response = _json_get(test_client, "/game/141") # assert tied_game_response.status_code == 200 # winning_deck_id = tied_game_response.json()["winning_deck_id"] # other_winning_deck_ids = tied_game_response.json()["other_winning_deck_ids"].split(',') # assert _json_get(test_client, f"/deck/{winning_deck_id}").json()['name'] == "Narset, Enlightened Exile" # assert len(other_winning_deck_ids) == 1 # assert _json_get(test_client, f"/deck/{other_winning_deck_ids[0]}").json()['name'] == "Aminatou, Veil Piercer" def _json_get(c: TestClient, path: str) -> httpx.Response: return c.get(f"/api{path}", headers={"Content-Type": "application/json"}) def _json_post(c: TestClient, path: str, body: Mapping) -> httpx.Response: return c.post( f"/api{path}", headers={"Content-Type": "application/json"}, json=body ) def _json_delete(c: TestClient, path: str) -> httpx.Response: return c.delete(f"/api{path}", headers={"Content-Type": "application/json"})