diff --git a/tests/conftest.py b/tests/conftest.py index a8b6c3d..6e1b963 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,6 +7,8 @@ os.environ["JWT_SECRET_KEY"] = os.urandom(24).hex() from app import app, db +from tests import utils + @pytest.fixture @@ -36,3 +38,14 @@ def _login(email, password): return client.post("/auth/login", json={"email": email, "password": password}) return _login + + +@pytest.fixture +def create_authenticated_headers(register_user, login_user): + def _get_headers(email="testuser@example.com", password="testpassword"): + register_user(email, password) + resp = login_user(email, password) + tokens = resp.get_json() + return utils.get_auth_header(tokens["access_token"]) + + return _get_headers diff --git a/tests/test_category.py b/tests/test_category.py index e227ae2..c4b8c0a 100644 --- a/tests/test_category.py +++ b/tests/test_category.py @@ -13,16 +13,6 @@ def setup(self, client): with client.application.app_context(): assert Category.query.count() == 0 - @pytest.fixture - def create_authenticated_headers(self, register_user, login_user): - def _get_headers(email="testuser@example.com", password="testpassword"): - register_user(email, password) - resp = login_user(email, password) - tokens = resp.get_json() - return utils.get_auth_header(tokens["access_token"]) - - return _get_headers - @pytest.fixture def create_category(self, create_authenticated_headers): def _create(name, headers=None): @@ -118,124 +108,63 @@ def test_delete_category(self, create_authenticated_headers, create_category): assert get_resp.status_code == 404 self._verify_category_in_db("ToDelete", should_exist=False) - def test_create_category_expired_token(self): - headers = utils.get_expired_token_headers(self.client.application.app_context()) + @pytest.mark.parametrize( + "get_headers, expected_code", + [ + (lambda self: utils.get_expired_token_headers(self.client.application.app_context()), "token_expired"), + (lambda self: utils.get_invalid_token_headers(), "invalid_token"), + (lambda self: None, "authorization_required") + ] + ) + def test_create_category_token_error(self, get_headers, expected_code): + headers = get_headers(self) response = self.client.post( - "/category/create", json={"name": "TestExpired"}, headers=headers - ) - - utils.verify_token_error_response(response, "token_expired") - self._verify_category_in_db("TestExpired", should_exist=False) - - def test_create_category_invalid_token(self): - headers = utils.get_invalid_token_headers() - response = self.client.post( - "/category/create", json={"name": "TestInvalid"}, headers=headers - ) - - utils.verify_token_error_response(response, "invalid_token") - self._verify_category_in_db("TestInvalid", should_exist=False) - - def test_create_category_missing_token(self): - response = self.client.post("/category/create", json={"name": "TestMissing"}) - utils.verify_token_error_response(response, "authorization_required") - self._verify_category_in_db("TestMissing", should_exist=False) - - def test_update_category_expired_token( - self, create_category, create_authenticated_headers - ): - headers = create_authenticated_headers() - response = create_category("UpdateExpired", headers) - data = response.get_json() - cat_id = data["id"] - - expired_headers = utils.get_expired_token_headers( - self.client.application.app_context() + "/category/create", json={"name": "CreateTokenError"}, headers=headers ) - update_resp = self.client.put( - f"/category/{cat_id}/update", - json={"name": "UpdatedName"}, - headers=expired_headers, - ) - - utils.verify_token_error_response(update_resp, "token_expired") - self._verify_category_in_db("UpdateExpired") - self._verify_category_in_db("UpdatedName", should_exist=False) - - def test_update_category_invalid_token( - self, create_category, create_authenticated_headers - ): + utils.verify_token_error_response(response, expected_code) + self._verify_category_in_db("CreateTokenError", should_exist=False) + + @pytest.mark.parametrize( + "get_headers, expected_code", + [ + (lambda self: utils.get_expired_token_headers(self.client.application.app_context()), "token_expired"), + (lambda self: utils.get_invalid_token_headers(), "invalid_token"), + (lambda self: None, "authorization_required") + ] + ) + def test_update_category_token_error(self, get_headers, create_category, create_authenticated_headers, expected_code): headers = create_authenticated_headers() - response = create_category("UpdateInvalid", headers) + response = create_category("UpdateTokenError", headers) data = response.get_json() cat_id = data["id"] - invalid_headers = utils.get_invalid_token_headers() + update_headers = get_headers(self) update_resp = self.client.put( f"/category/{cat_id}/update", json={"name": "UpdatedName"}, - headers=invalid_headers, + headers=update_headers, ) - utils.verify_token_error_response(update_resp, "invalid_token") - self._verify_category_in_db("UpdateInvalid") + utils.verify_token_error_response(update_resp, expected_code) + self._verify_category_in_db("UpdateTokenError") self._verify_category_in_db("UpdatedName", should_exist=False) - def test_update_category_missing_token( - self, create_category, create_authenticated_headers - ): - headers = create_authenticated_headers() - response = create_category("UpdateMissing", headers) - data = response.get_json() - cat_id = data["id"] - - update_resp = self.client.put( - f"/category/{cat_id}/update", json={"name": "UpdatedName"} - ) - - utils.verify_token_error_response(update_resp, "authorization_required") - self._verify_category_in_db("UpdateMissing") - self._verify_category_in_db("UpdatedName", should_exist=False) - - def test_delete_category_expired_token( - self, create_category, create_authenticated_headers - ): - headers = create_authenticated_headers() - response = create_category("DeleteExpired", headers) - data = response.get_json() - cat_id = data["id"] - - expired_headers = utils.get_expired_token_headers( - self.client.application.app_context() - ) - delete_resp = self.client.delete(f"/category/{cat_id}", headers=expired_headers) - - utils.verify_token_error_response(delete_resp, "token_expired") - self._verify_category_in_db("DeleteExpired") - - def test_delete_category_invalid_token( - self, create_category, create_authenticated_headers - ): - headers = create_authenticated_headers() - response = create_category("DeleteInvalid", headers) - data = response.get_json() - cat_id = data["id"] - - invalid_headers = utils.get_invalid_token_headers() - delete_resp = self.client.delete(f"/category/{cat_id}", headers=invalid_headers) - - utils.verify_token_error_response(delete_resp, "invalid_token") - self._verify_category_in_db("DeleteInvalid") - - def test_delete_category_missing_token( - self, create_category, create_authenticated_headers - ): + @pytest.mark.parametrize( + "get_headers, expected_code", + [ + (lambda self: utils.get_expired_token_headers(self.client.application.app_context()), "token_expired"), + (lambda self: utils.get_invalid_token_headers(), "invalid_token"), + (lambda self: None, "authorization_required") + ] + ) + def test_delete_category_token_error(self, get_headers, create_category, create_authenticated_headers, expected_code): headers = create_authenticated_headers() - response = create_category("DeleteMissing", headers) + response = create_category("DeleteTokenError", headers) data = response.get_json() cat_id = data["id"] - delete_resp = self.client.delete(f"/category/{cat_id}") + delete_headers = get_headers(self) + delete_resp = self.client.delete(f"/category/{cat_id}", headers=delete_headers) - utils.verify_token_error_response(delete_resp, "authorization_required") - self._verify_category_in_db("DeleteMissing") + utils.verify_token_error_response(delete_resp, expected_code) + self._verify_category_in_db("DeleteTokenError") diff --git a/tests/test_subcategory.py b/tests/test_subcategory.py new file mode 100644 index 0000000..0821f49 --- /dev/null +++ b/tests/test_subcategory.py @@ -0,0 +1,199 @@ +import pytest + +from app.models import Subcategory +from tests import utils + + +class TestSubcategory: + TEST_SUBCATEGORY_NAME = "Smartphones" + + @pytest.fixture(autouse=True) + def setup(self, client): + self.client = client + with client.application.app_context(): + assert Subcategory.query.count() == 0 + + @pytest.fixture + def create_subcategory(self, create_authenticated_headers): + def _create(name, categories=None, products=None, headers=None): + if headers is None: + headers = create_authenticated_headers() + payload = {"name": name} + if categories is not None: + payload["categories"] = categories + if products is not None: + payload["products"] = products + return self.client.post( + "/subcategory/create", json=payload, headers=headers + ) + return _create + + def _count_subcategories(self): + with self.client.application.app_context(): + return Subcategory.query.count() + + def _verify_subcategory_in_db(self, name, should_exist=True): + with self.client.application.app_context(): + subcategory = Subcategory.query.filter_by(name=name).first() + if should_exist: + assert subcategory is not None + assert subcategory.name == name + return subcategory + else: + assert subcategory is None + + def test_create_subcategory(self, create_subcategory): + response = create_subcategory(self.TEST_SUBCATEGORY_NAME) + + assert response.status_code == 201 + data = response.get_json() + assert data["name"] == self.TEST_SUBCATEGORY_NAME + assert "id" in data + self._verify_subcategory_in_db(self.TEST_SUBCATEGORY_NAME) + + def test_create_subcategory_duplicate_name(self, create_subcategory): + create_subcategory(self.TEST_SUBCATEGORY_NAME) + response = create_subcategory(self.TEST_SUBCATEGORY_NAME) + + assert response.status_code == 500 + assert self._count_subcategories() == 1 + self._verify_subcategory_in_db(self.TEST_SUBCATEGORY_NAME) + + # TODO: Add tests for creation with categories and products when those fixtures/utilities are available + + def test_get_subcategory_by_id(self, create_subcategory): + response = create_subcategory("Laptops") + data = response.get_json() + sc_id = data["id"] + get_resp = self.client.get(f"/subcategory/{sc_id}") + + assert get_resp.status_code == 200 + data = get_resp.get_json() + assert data["name"] == "Laptops" + assert data["id"] == sc_id + + def test_get_all_subcategories(self, create_subcategory): + create_subcategory("A") + create_subcategory("B") + resp = self.client.get("/subcategories") + + assert resp.status_code == 200 + data = resp.get_json() + assert "subcategories" in data + assert len(data["subcategories"]) == 2 + names = [sc["name"] for sc in data["subcategories"]] + assert "A" in names + assert "B" in names + + def test_update_subcategory(self, create_authenticated_headers, create_subcategory): + headers = create_authenticated_headers() + response = create_subcategory("OldSubcat", headers=headers) + data = response.get_json() + sc_id = data["id"] + update_resp = self.client.put( + f"/subcategory/{sc_id}/update", json={"name": "NewSubcat"}, headers=headers + ) + + assert update_resp.status_code == 201 + data = update_resp.get_json() + assert data["name"] == "NewSubcat" + assert data["id"] == sc_id + self._verify_subcategory_in_db("NewSubcat") + self._verify_subcategory_in_db("OldSubcat", should_exist=False) + + def test_delete_subcategory(self, create_authenticated_headers, create_subcategory): + headers = create_authenticated_headers() + response = create_subcategory("ToDelete", headers=headers) + data = response.get_json() + sc_id = data["id"] + delete_resp = self.client.delete(f"/subcategory/{sc_id}", headers=headers) + + assert delete_resp.status_code == 200 + get_resp = self.client.get(f"/subcategory/{sc_id}") + assert get_resp.status_code == 404 + self._verify_subcategory_in_db("ToDelete", should_exist=False) + + @pytest.mark.parametrize( + "get_headers, expected_code", + [ + (lambda self: utils.get_expired_token_headers(self.client.application.app_context()), "token_expired"), + (lambda self: utils.get_invalid_token_headers(), "invalid_token"), + (lambda self: None, "authorization_required") + ] + ) + def test_create_subcategory_token_error(self, get_headers, expected_code): + headers = get_headers(self) + response = self.client.post( + "/subcategory/create", json={"name": "CreateTokenError"}, headers=headers + ) + utils.verify_token_error_response(response, expected_code) + self._verify_subcategory_in_db("CreateTokenError", should_exist=False) + + @pytest.mark.parametrize( + "get_headers, expected_code", + [ + (lambda self: utils.get_expired_token_headers(self.client.application.app_context()), "token_expired"), + (lambda self: utils.get_invalid_token_headers(), "invalid_token"), + (lambda self: None, "authorization_required") + ] + ) + def test_update_subcategory_token_error(self, get_headers, create_subcategory, create_authenticated_headers, expected_code): + headers = create_authenticated_headers() + response = create_subcategory("UpdateTokenError", headers=headers) + data = response.get_json() + sc_id = data["id"] + + update_headers = get_headers(self) + update_resp = self.client.put( + f"/subcategory/{sc_id}/update", + json={"name": "UpdatedName"}, + headers=update_headers, + ) + + utils.verify_token_error_response(update_resp, expected_code) + self._verify_subcategory_in_db("UpdateTokenError") + self._verify_subcategory_in_db("UpdatedName", should_exist=False) + + @pytest.mark.parametrize( + "get_headers, expected_code", + [ + (lambda self: utils.get_expired_token_headers(self.client.application.app_context()), "token_expired"), + (lambda self: utils.get_invalid_token_headers(), "invalid_token"), + (lambda self: None, "authorization_required") + ] + ) + def test_delete_subcategory_token_error(self, get_headers, create_subcategory, create_authenticated_headers, expected_code): + headers = create_authenticated_headers() + response = create_subcategory("DeleteTokenError", headers=headers) + data = response.get_json() + sc_id = data["id"] + + delete_headers = get_headers(self) + delete_resp = self.client.delete(f"/subcategory/{sc_id}", headers=delete_headers) + + utils.verify_token_error_response(delete_resp, expected_code) + self._verify_subcategory_in_db("DeleteTokenError") + + def test_get_subcategory_categories_empty(self, create_subcategory): + response = create_subcategory("NoCatRel") + data = response.get_json() + sc_id = data["id"] + + resp = self.client.get(f"/subcategory/{sc_id}/categories") + + assert resp.status_code == 200 + data = resp.get_json() + assert "categories" in data + assert data["categories"] == [] + + def test_get_subcategory_products_empty(self, create_subcategory): + response = create_subcategory("NoProdRel") + data = response.get_json() + sc_id = data["id"] + + resp = self.client.get(f"/subcategory/{sc_id}/products") + + assert resp.status_code == 200 + data = resp.get_json() + assert "products" in data + assert data["products"] == []