diff --git a/backend/api/main.py b/backend/api/main.py index 21b9623..d12e26d 100644 --- a/backend/api/main.py +++ b/backend/api/main.py @@ -20,7 +20,7 @@ from backend.repository.tribe_sqlite_repository import TribeSQLiteRepository # session = sessionmaker(bind=engine)() # tribe_repo = TribeSQLAlchemyRepository(session) -conn = sqlite3.connect(":memory:") +conn = sqlite3.connect("sqlite.db") create_db(conn) tribe_repo = TribeSQLiteRepository(conn) diff --git a/backend/config.py b/backend/config.py new file mode 100644 index 0000000..b238df0 --- /dev/null +++ b/backend/config.py @@ -0,0 +1,16 @@ +import os +import sqlite3 + +from backend.adapters.sqlite import create_db + + +def sqlite_conn(sqlite_file: str = ":memory"): + conn = sqlite3.connect(sqlite_file) + create_db(conn) + return conn + + +def get_api_url(): + host = os.environ.get("API_HOST", "localhost") + port = 8000 if host == "localhost" else 80 + return f"http://{host}:{port}" diff --git a/poetry.lock b/poetry.lock index 43f918c..79ae925 100644 --- a/poetry.lock +++ b/poetry.lock @@ -39,6 +39,18 @@ docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +[[package]] +name = "certifi" +version = "2022.12.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] + [[package]] name = "cfgv" version = "3.3.1" @@ -51,6 +63,21 @@ files = [ {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, ] +[[package]] +name = "charset-normalizer" +version = "2.1.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "dev" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, +] + +[package.extras] +unicode-backport = ["unicodedata2"] + [[package]] name = "click" version = "8.1.3" @@ -504,6 +531,28 @@ files = [ {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] +[[package]] +name = "requests" +version = "2.28.1" +description = "Python HTTP for Humans." +category = "dev" +optional = false +python-versions = ">=3.7, <4" +files = [ + {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, + {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<3" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + [[package]] name = "setuptools" version = "65.6.3" @@ -674,6 +723,23 @@ files = [ {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] +[[package]] +name = "urllib3" +version = "1.26.13" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"}, + {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + [[package]] name = "uvicorn" version = "0.20.0" @@ -717,4 +783,4 @@ testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7 [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "9f84fef203ddda9214c28ec94c4e928b850209b4a88af267f447aaf2c9fd00b4" +content-hash = "ab4854a75d6c6394039fd873f164e7e37118b70c79c2052de9acacae98511e8a" diff --git a/pyproject.toml b/pyproject.toml index a64287c..f16a17a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ uvicorn = "^0.20.0" pre-commit = "^2.20.0" pytest = "^7.2.0" faker = "^15.3.4" +requests = "^2.28.1" [build-system] requires = ["poetry-core"] diff --git a/tests/conftest.py b/tests/conftest.py index 51ce08b..d60ca59 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,10 +1,17 @@ import sqlite3 +import time +from pathlib import Path import pytest +import requests from sqlalchemy import create_engine from sqlalchemy.orm import clear_mappers, sessionmaker +from backend import config from backend.adapters.orm import metadata, start_mappers +from backend.adapters.sqlite import create_db +from tests.integration.test_repository_student_sqlite import populate_students +from tests.integration.test_repository_tribe_sqlite import populate_tribes @pytest.fixture @@ -23,6 +30,64 @@ def session(in_memory_db): @pytest.fixture def sqlite_conn(): - conn = sqlite3.connect(":memory:") + conn = sqlite3.connect("sqlite.db") + create_db(conn) yield conn conn.close() + + +@pytest.fixture +def clean_db(sqlite_conn): + sqlite_conn.execute("""DROP TABLE tribes""") + sqlite_conn.execute("""DROP TABLE students""") + sqlite_conn.commit() + create_db(sqlite_conn) + + +@pytest.fixture +def populate_db(sqlite_conn): + _tribes = [] + _students = [] + + def _populate_db(): + tribes = populate_tribes(sqlite_conn) + _tribes += tribes + students = populate_students(sqlite_conn, tribes) + _students += students + return tribes, students + + yield _populate_db + + for student in _students: + sqlite_conn.execute( + """ + DELETE FROM students WHERE id=:id + """, + {"id": student.id}, + ) + for tribe in _tribes: + sqlite_conn.execute( + """ + DELETE FROM tribes WHERE name=:name + """, + {"name": tribe.name}, + ) + sqlite_conn.commit() + + +def wait_for_webapp_to_come_up(): + deadline = time.time() + 10 + url = config.get_api_url() + while time.time() < deadline: + try: + return requests.get(url) + except ConnectionError: + time.sleep(0.5) + pytest.fail("API never came up") + + +@pytest.fixture +def restart_api(): + (Path(__file__).parent.parent / "backend" / "api" / "main.py").touch() + time.sleep(0.5) + wait_for_webapp_to_come_up() diff --git a/tests/e2e/test_api.py b/tests/e2e/test_api.py new file mode 100644 index 0000000..2702021 --- /dev/null +++ b/tests/e2e/test_api.py @@ -0,0 +1,59 @@ +import pytest +import requests + +from backend import config +from tests.model.fakes import build_tribes + + +@pytest.mark.usefixtures("restart_api") +@pytest.mark.usefixtures("clean_db") +def test_api_post_tribe(): + data = {"name": "tribe", "level": "2nd"} + + url = config.get_api_url() + r = requests.post(f"{url}/tribes", json=data) + + assert r.status_code == 201 + assert r.json() == { + "assessments": [], + "level": "2nd", + "name": "tribe", + "students": [], + } + + +@pytest.mark.usefixtures("restart_api") +@pytest.mark.usefixtures("clean_db") +def test_api_post_list_tribe(): + tribe = build_tribes(1)[0] + + url = config.get_api_url() + r = requests.post(f"{url}/tribes", json=tribe.to_dict()) + + assert r.status_code == 201 + + r = requests.get(f"{url}/tribes") + assert r.json() == [ + { + "assessments": [], + "level": tribe.level, + "name": tribe.name, + "students": [], + } + ] + + +@pytest.mark.usefixtures("restart_api") +@pytest.mark.usefixtures("clean_db") +def test_api_post_student(): + url = config.get_api_url() + tribe = build_tribes(1)[0] + requests.post(f"{url}/tribes", json=tribe.to_dict()) + + data = {"name": "zart", "tribe_name": tribe.name} + r = requests.post(f"{url}/students", json=data) + + assert r.status_code == 201 + assert r.json()["name"] == "zart" + assert r.json()["tribe_name"] == tribe.name + assert r.json()["id"] diff --git a/tests/integration/test_repository_student_sqlite.py b/tests/integration/test_repository_student_sqlite.py index a363beb..2e70854 100644 --- a/tests/integration/test_repository_student_sqlite.py +++ b/tests/integration/test_repository_student_sqlite.py @@ -2,7 +2,6 @@ import sqlite3 import pytest -from backend.adapters.sqlite import create_db from backend.model.student import Student from backend.model.tribe import Tribe from backend.repository.student_sqlite_repository import StudentSQLiteRepository @@ -25,7 +24,6 @@ def populate_students(conn, tribes: list[Tribe]) -> list[Student]: def test_get_student(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) prebuild_students = populate_students(sqlite_conn, prebuild_tribes) @@ -38,7 +36,6 @@ def test_get_student(sqlite_conn): def test_get_student_not_exists(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) prebuild_students = populate_students(sqlite_conn, prebuild_tribes) @@ -48,7 +45,6 @@ def test_get_student_not_exists(sqlite_conn): def test_list_students(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) prebuild_students = populate_students(sqlite_conn, prebuild_tribes) @@ -59,7 +55,6 @@ def test_list_students(sqlite_conn): def test_add_student(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) student_repo = StudentSQLiteRepository(sqlite_conn) @@ -82,7 +77,6 @@ def test_add_student(sqlite_conn): def test_add_student_fail_exists(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) student_repo = StudentSQLiteRepository(sqlite_conn) @@ -97,7 +91,6 @@ def test_add_student_fail_exists(sqlite_conn): def test_update_student(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) prebuild_students = populate_students(sqlite_conn, prebuild_tribes) @@ -118,7 +111,6 @@ def test_update_student(sqlite_conn): def test_delete_student(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) prebuild_students = populate_students(sqlite_conn, prebuild_tribes) diff --git a/tests/integration/test_repository_tribe_sqlite.py b/tests/integration/test_repository_tribe_sqlite.py index 6c9a7a3..e4492d2 100644 --- a/tests/integration/test_repository_tribe_sqlite.py +++ b/tests/integration/test_repository_tribe_sqlite.py @@ -2,7 +2,6 @@ import sqlite3 import pytest -from backend.adapters.sqlite import create_db from backend.model.tribe import Tribe from backend.repository.tribe_sqlite_repository import TribeSQLiteRepository from tests.model.fakes import build_tribes @@ -23,7 +22,6 @@ def populate_tribes(conn) -> list[Tribe]: def test_get_tribe(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) name = prebuild_tribes[0].name @@ -35,7 +33,6 @@ def test_get_tribe(sqlite_conn): def test_get_tribe_not_exists(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) tribe_repo = TribeSQLiteRepository(sqlite_conn) @@ -44,7 +41,6 @@ def test_get_tribe_not_exists(sqlite_conn): def test_list_tribes(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) tribe_repo = TribeSQLiteRepository(sqlite_conn) @@ -54,7 +50,6 @@ def test_list_tribes(sqlite_conn): def test_add_tribe(sqlite_conn): - create_db(sqlite_conn) tribe_repo = TribeSQLiteRepository(sqlite_conn) @@ -76,7 +71,6 @@ def test_add_tribe(sqlite_conn): def test_add_tribe_fail_exists(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) tribe_repo = TribeSQLiteRepository(sqlite_conn) @@ -87,7 +81,6 @@ def test_add_tribe_fail_exists(sqlite_conn): def test_update_tribe(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) tribe_repo = TribeSQLiteRepository(sqlite_conn) @@ -102,7 +95,6 @@ def test_update_tribe(sqlite_conn): def test_delete_tribe(sqlite_conn): - create_db(sqlite_conn) prebuild_tribes = populate_tribes(sqlite_conn) tribe_repo = TribeSQLiteRepository(sqlite_conn)