From e6ba876aa33e68d4bdb85417496de02c8fc73754 Mon Sep 17 00:00:00 2001 From: luisfponce Date: Tue, 3 Mar 2026 14:47:45 -0600 Subject: [PATCH 1/2] refactor tests into functional and nonfunctional suites --- .github/workflows/cd.yml | 2 +- webapi/tests/functional/__init__.py | 0 .../{ => functional}/test_auth_routes.py | 6 +- .../{test_main.py => functional/test_cd.py} | 42 +++++----- .../{ => functional}/test_prompts_routes.py | 0 .../{ => functional}/test_users_routes.py | 0 webapi/tests/nonfunctional/__init__.py | 0 webapi/tests/nonfunctional/test_ci.py | 74 ++++++++++++++++++ webapi/tests/test_ci.py | 78 +++---------------- webapi/tests/test_release.py | 14 ++++ 10 files changed, 125 insertions(+), 91 deletions(-) create mode 100644 webapi/tests/functional/__init__.py rename webapi/tests/{ => functional}/test_auth_routes.py (97%) rename webapi/tests/{test_main.py => functional/test_cd.py} (57%) rename webapi/tests/{ => functional}/test_prompts_routes.py (100%) rename webapi/tests/{ => functional}/test_users_routes.py (100%) create mode 100644 webapi/tests/nonfunctional/__init__.py create mode 100644 webapi/tests/nonfunctional/test_ci.py create mode 100644 webapi/tests/test_release.py diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 36e296c..77561ea 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -36,7 +36,7 @@ jobs: run: | cd webapi if [ -f bank_db.db ]; then rm -f bank_db.db; fi - pytest tests/test_main.py -v + pytest tests/test_release.py -v container-smoke-test: name: Container Build & Healthcheck Smoke Test diff --git a/webapi/tests/functional/__init__.py b/webapi/tests/functional/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/webapi/tests/test_auth_routes.py b/webapi/tests/functional/test_auth_routes.py similarity index 97% rename from webapi/tests/test_auth_routes.py rename to webapi/tests/functional/test_auth_routes.py index b1df994..7f8f6bb 100644 --- a/webapi/tests/test_auth_routes.py +++ b/webapi/tests/functional/test_auth_routes.py @@ -87,14 +87,14 @@ def test_profile_success(client, auth_header, created_user): response = client.get("/api/v1/auth/profile", headers=auth_header) assert response.status_code == 200 - assert response.json()["profile darta"]["sub"] == created_user.username + assert response.json()["profile data"]["sub"] == created_user.username def test_profile_invalid_token_returns_401(client): response = client.get("/api/v1/auth/profile", headers={"Authorization": "Token abc"}) - assert response.status_code == 401 - assert response.json()["detail"] == "Unauthorized token" + assert response.status_code == 403 + assert response.json()["detail"] == "Invalid authentication credentials" def test_generate_password_user_not_found_returns_404(client): diff --git a/webapi/tests/test_main.py b/webapi/tests/functional/test_cd.py similarity index 57% rename from webapi/tests/test_main.py rename to webapi/tests/functional/test_cd.py index cbcde32..714ca32 100644 --- a/webapi/tests/test_main.py +++ b/webapi/tests/functional/test_cd.py @@ -1,32 +1,38 @@ from fastapi.testclient import TestClient import sys from pathlib import Path -sys.path.append(str(Path(__file__).resolve().parents[1])) +sys.path.append(str(Path(__file__).resolve().parents[2])) from main import myapp client = TestClient(myapp) -def test_register_user(): - response = client.post("/api/v1/auth/signup", json={ - "username": "pytest", - "name": "pytest", - "last_name": "testing", - "phone": 3334353637, - "email": "pytestmyapp@testing.com", - "hashed_password": "pytest" - }) - assert response.status_code == 200 - assert response.json() == { - "message": "User created successfully" - } def test_login_and_access_private(): + # Ensure user exists; tolerate pre-existing data in local DB runs + signup_response = client.post( + "/api/v1/auth/signup", + json={ + "username": "pytest", + "name": "Py", + "last_name": "Tester", + "phone": 5511111111, + "email": "pytest@example.com", + "hashed_password": "pytest", + }, + ) + assert signup_response.status_code in (200, 400) + if signup_response.status_code == 400: + assert signup_response.json().get("detail") == "username already taken" + # First, get a token - response = client.post("/api/v1/auth/login", json={ # <-- use json instead of data - "username": "pytest", - "password": "pytest" - }) + response = client.post( + "/api/v1/auth/login", + json={ + "username": "pytest", + "password": "pytest", + }, + ) assert response.status_code == 200 token = response.json()["access_token"] diff --git a/webapi/tests/test_prompts_routes.py b/webapi/tests/functional/test_prompts_routes.py similarity index 100% rename from webapi/tests/test_prompts_routes.py rename to webapi/tests/functional/test_prompts_routes.py diff --git a/webapi/tests/test_users_routes.py b/webapi/tests/functional/test_users_routes.py similarity index 100% rename from webapi/tests/test_users_routes.py rename to webapi/tests/functional/test_users_routes.py diff --git a/webapi/tests/nonfunctional/__init__.py b/webapi/tests/nonfunctional/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/webapi/tests/nonfunctional/test_ci.py b/webapi/tests/nonfunctional/test_ci.py new file mode 100644 index 0000000..a074128 --- /dev/null +++ b/webapi/tests/nonfunctional/test_ci.py @@ -0,0 +1,74 @@ +from fastapi.testclient import TestClient +import sys +from pathlib import Path +sys.path.append(str(Path(__file__).resolve().parents[2])) +from main import myapp +client = TestClient(myapp) + +TEST_USER = "testuser" +TEST_PSW = "testPSW" + +def test_root(): + response = client.get("/") + assert response.status_code == 200 + assert response.json() == { + "Portfolio App": "This is a simple app using FastAPI and mariadb." + } + + +def test_register_user(): + response = client.post("/api/v1/auth/signup", json={ + "username": TEST_USER, + "name": "user to test", + "last_name": "my webapp", + "phone": 3132333435, + "email": "pytestmyapp@testing.com", + "hashed_password": TEST_PSW + }) + assert response.status_code == 200 + assert response.json() == { + "message": "User created successfully" + } + +def get_token(): + response = client.post("/api/v1/auth/login", json={ + "username": TEST_USER, + "password": TEST_PSW + }) + assert response.status_code == 200 + token = response.json()["access_token"] + return token + + +def test_register_prompt(): + token = get_token() + headers = {"Authorization": f"Bearer {token}", "send_email": "false"} + response = client.post("/api/v1/prompts", json={ + "user_id": 1, + "model_name": "gpt-4.1", + "prompt_text": "Generate a test response", + "category": "qa", + "rate": "high", + }, headers=headers) + assert response.status_code == 200 + assert response.json() == { + "id": 1, + "user_id": 1, + "model_name": "gpt-4.1", + "prompt_text": "Generate a test response", + "category": "qa", + "rate": "high" + } + +def test_read_users(): + token = get_token() + headers = {"Authorization": f"Bearer {token}"} + response = client.get("/api/v1/users", headers=headers) + assert response.status_code == 200 + + +def test_read_prompts(): + token = get_token() + headers = {"Authorization": f"Bearer {token}"} + response = client.get("/api/v1/prompts", headers=headers) + assert response.status_code == 200 diff --git a/webapi/tests/test_ci.py b/webapi/tests/test_ci.py index ceda2b1..703938f 100644 --- a/webapi/tests/test_ci.py +++ b/webapi/tests/test_ci.py @@ -1,74 +1,14 @@ -from fastapi.testclient import TestClient +import subprocess import sys from pathlib import Path -sys.path.append(str(Path(__file__).resolve().parents[1])) -from main import myapp -client = TestClient(myapp) -TEST_USER = "testuser" -TEST_PSW = "testPSW" -def test_root(): - response = client.get("/") - assert response.status_code == 200 - assert response.json() == { - "Portfolio App": "This is a simple app using FastAPI and mariadb." - } +def test_run_nonfunctional_suite(): + project_root = Path(__file__).resolve().parents[1] + result = subprocess.run( + [sys.executable, "-m", "pytest", "tests/nonfunctional", "-v"], + cwd=project_root, + check=False, + ) + assert result.returncode == 0 - -def test_register_user(): - response = client.post("/api/v1/auth/signup", json={ - "username": TEST_USER, - "name": "user to test", - "last_name": "my webapp", - "phone": 3132333435, - "email": "pytestmyapp@testing.com", - "hashed_password": TEST_PSW - }) - assert response.status_code == 200 - assert response.json() == { - "message": "User created successfully" - } - -def get_token(): - response = client.post("/api/v1/auth/login", json={ - "username": TEST_USER, - "password": TEST_PSW - }) - assert response.status_code == 200 - token = response.json()["access_token"] - return token - - -def test_register_prompt(): - token = get_token() - headers = {"Authorization": f"Bearer {token}", "send_email": "false"} - response = client.post("/api/v1/prompts", json={ - "user_id": 1, - "model_name": "gpt-4.1", - "prompt_text": "Generate a test response", - "category": "qa", - "rate": "high", - }, headers=headers) - assert response.status_code == 200 - assert response.json() == { - "id": 1, - "user_id": 1, - "model_name": "gpt-4.1", - "prompt_text": "Generate a test response", - "category": "qa", - "rate": "high" - } - -def test_read_users(): - token = get_token() - headers = {"Authorization": f"Bearer {token}"} - response = client.get("/api/v1/users", headers=headers) - assert response.status_code == 200 - - -def test_read_prompts(): - token = get_token() - headers = {"Authorization": f"Bearer {token}"} - response = client.get("/api/v1/prompts", headers=headers) - assert response.status_code == 200 diff --git a/webapi/tests/test_release.py b/webapi/tests/test_release.py new file mode 100644 index 0000000..8b4cf51 --- /dev/null +++ b/webapi/tests/test_release.py @@ -0,0 +1,14 @@ +import subprocess +import sys +from pathlib import Path + + +def test_run_functional_suite(): + project_root = Path(__file__).resolve().parents[1] + result = subprocess.run( + [sys.executable, "-m", "pytest", "tests/functional", "-v"], + cwd=project_root, + check=False, + ) + assert result.returncode == 0 + From 1f8708513db1f802f15bbbf3f9a0b3f1cf45bbe5 Mon Sep 17 00:00:00 2001 From: luisfponce Date: Tue, 3 Mar 2026 14:54:11 -0600 Subject: [PATCH 2/2] chore(git): add ignore rules for test and coverage artifacts --- .gitignore | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a00fae1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Python bytecode and cache +__pycache__/ +*.pyc +*.pyo + +# SQLite / local database files +*.db + +# Pytest remnants +.pytest_cache/ +.cache/ +.pytest_cache/** + +# Test and coverage artifacts +.coverage +coverage.xml +htmlcov/