From b06ac8df4762d303b62261008bbf29a1dd107cf3 Mon Sep 17 00:00:00 2001 From: Nuelo Date: Sun, 24 Nov 2024 19:15:12 +0100 Subject: [PATCH 1/3] test: create test cases for vault endpoints --- web_app/tests/test_vault.py | 158 ++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 web_app/tests/test_vault.py diff --git a/web_app/tests/test_vault.py b/web_app/tests/test_vault.py new file mode 100644 index 00000000..ca246e37 --- /dev/null +++ b/web_app/tests/test_vault.py @@ -0,0 +1,158 @@ +""" +test_vault.py +This module contains unit tests for the vault functionality within the web_app. +It verifies deposit operations, balance retrieval, and balance updates. +""" + +import pytest +from fastapi import HTTPException +from fastapi.testclient import TestClient +from httpx import AsyncClient +from unittest.mock import patch, MagicMock +import uuid + +from web_app.api.main import app +from web_app.db.crud import DepositDBConnector, UserDBConnector + +client = TestClient(app) + +@pytest.mark.anyio +async def test_deposit_to_vault_success(mock_user_db_connector): + """ + Test successful vault deposit with valid data. + """ + test_data = { + "wallet_id": "test_wallet", + "symbol": "ETH", + "amount": "1.0" + } + + mock_user = MagicMock() + mock_vault = MagicMock() + mock_vault.id = str(uuid.uuid4()) + mock_vault.amount = "1.0" + + with ( + patch.object(UserDBConnector, "__new__", return_value=mock_user_db_connector), + patch("web_app.db.crud.DepositDBConnector.create_vault", return_value=mock_vault), + ): + mock_user_db_connector.get_user_by_wallet_id.return_value = mock_user + + async with AsyncClient(app=app, base_url="http://test") as ac: + response = await ac.post("/api/vault/deposit", json=test_data) + + assert response.status_code == 200 + assert response.json() == { + "deposit_id": mock_vault.id, + "wallet_id": test_data["wallet_id"], + "amount": test_data["amount"], + "symbol": test_data["symbol"] + } + + +@pytest.mark.anyio +async def test_deposit_to_vault_user_not_found(mock_user_db_connector): + """ + Test vault deposit with non-existent user. + """ + test_data = { + "wallet_id": "invalid_wallet", + "symbol": "ETH", + "amount": "1.0" + } + + with patch.object(UserDBConnector, "__new__", return_value=mock_user_db_connector): + mock_user_db_connector.get_user_by_wallet_id.return_value = None + + async with AsyncClient(app=app, base_url="http://test") as ac: + response = await ac.post("/api/vault/deposit", json=test_data) + + assert response.status_code == 404 + assert response.json() == {"detail": "User not found"} + + +@pytest.mark.anyio +async def test_get_vault_balance_success(): + """ + Test successful retrieval of vault balance. + """ + wallet_id = "test_wallet" + symbol = "ETH" + expected_balance = "1.5" + + with patch("web_app.db.crud.DepositDBConnector.get_vault_balance", return_value=expected_balance): + async with AsyncClient(app=app, base_url="http://test") as ac: + response = await ac.get(f"/api/vault/api/balance?wallet_id={wallet_id}&symbol={symbol}") + + assert response.status_code == 200 + assert response.json() == { + "wallet_id": wallet_id, + "symbol": symbol, + "amount": expected_balance + } + + +@pytest.mark.anyio +async def test_get_vault_balance_not_found(): + """ + Test retrieval of non-existent vault balance. + """ + wallet_id = "invalid_wallet" + symbol = "ETH" + + with patch("web_app.db.crud.DepositDBConnector.get_vault_balance", return_value=None): + async with AsyncClient(app=app, base_url="http://test") as ac: + response = await ac.get(f"/api/vault/api/balance?wallet_id={wallet_id}&symbol={symbol}") + + assert response.status_code == 404 + assert response.json() == {"detail": "Vault not found or user does not exist"} + + +@pytest.mark.anyio +async def test_add_vault_balance_success(): + """ + Test successful addition to vault balance. + """ + test_data = { + "wallet_id": "test_wallet", + "symbol": "ETH", + "amount": "0.5" + } + + mock_vault = MagicMock() + mock_vault.amount = "2.0" + + with patch("web_app.db.crud.DepositDBConnector.add_vault_balance", return_value=mock_vault): + async with AsyncClient(app=app, base_url="http://test") as ac: + response = await ac.post("/api/vault/api/add_balance", json=test_data) + + assert response.status_code == 200 + assert response.json() == { + "wallet_id": test_data["wallet_id"], + "symbol": test_data["symbol"], + "amount": mock_vault.amount + } + + +@pytest.mark.anyio +async def test_add_vault_balance_invalid_amount(): + """ + Test adding invalid amount to vault balance. + """ + test_data = { + "wallet_id": "test_wallet", + "symbol": "ETH", + "amount": "-1.0" + } + + with patch( + "web_app.db.crud.DepositDBConnector.add_vault_balance", + side_effect=ValueError("Amount must be positive") + ): + async with AsyncClient(app=app, base_url="http://test") as ac: + response = await ac.post("/api/vault/api/add_balance", json=test_data) + + assert response.status_code == 400 + assert response.json() == { + "detail": "Failed to update vault balance: Amount must be positive" + } From fff3014cd787171acc5736932b29fe9175505606 Mon Sep 17 00:00:00 2001 From: Nuelo Date: Sun, 24 Nov 2024 20:30:13 +0100 Subject: [PATCH 2/3] refact: implement requested changes --- web_app/tests/test_vault.py | 206 +++++++++++++++--------------------- 1 file changed, 87 insertions(+), 119 deletions(-) diff --git a/web_app/tests/test_vault.py b/web_app/tests/test_vault.py index ca246e37..94966a97 100644 --- a/web_app/tests/test_vault.py +++ b/web_app/tests/test_vault.py @@ -16,143 +16,111 @@ client = TestClient(app) + +@pytest.fixture +async def async_client(): + """Fixture that provides an async client for testing.""" + async with AsyncClient(app=app, base_url="http://test") as ac: + yield ac + + @pytest.mark.anyio -async def test_deposit_to_vault_success(mock_user_db_connector): - """ - Test successful vault deposit with valid data. - """ - test_data = { - "wallet_id": "test_wallet", - "symbol": "ETH", - "amount": "1.0" - } - +@pytest.mark.parametrize("test_data, expected_status, expected_response", [ + ( + {"wallet_id": "test_wallet", "symbol": "ETH", "amount": "1.0"}, + 200, + lambda vault_id: { + "deposit_id": vault_id, + "wallet_id": "test_wallet", + "amount": "1.0", + "symbol": "ETH" + } + ), + ( + {"wallet_id": "invalid_wallet", "symbol": "ETH", "amount": "1.0"}, + 404, + {"detail": "User not found"} + ), +]) +async def test_deposit_to_vault(test_data, expected_status, expected_response, mock_user_db_connector, async_client): + """Test vault deposit with different scenarios.""" mock_user = MagicMock() mock_vault = MagicMock() mock_vault.id = str(uuid.uuid4()) - mock_vault.amount = "1.0" - - with ( - patch.object(UserDBConnector, "__new__", return_value=mock_user_db_connector), - patch("web_app.db.crud.DepositDBConnector.create_vault", return_value=mock_vault), - ): - mock_user_db_connector.get_user_by_wallet_id.return_value = mock_user - - async with AsyncClient(app=app, base_url="http://test") as ac: - response = await ac.post("/api/vault/deposit", json=test_data) - - assert response.status_code == 200 - assert response.json() == { - "deposit_id": mock_vault.id, - "wallet_id": test_data["wallet_id"], - "amount": test_data["amount"], - "symbol": test_data["symbol"] - } - - -@pytest.mark.anyio -async def test_deposit_to_vault_user_not_found(mock_user_db_connector): - """ - Test vault deposit with non-existent user. - """ - test_data = { - "wallet_id": "invalid_wallet", - "symbol": "ETH", - "amount": "1.0" - } + mock_vault.amount = test_data["amount"] with patch.object(UserDBConnector, "__new__", return_value=mock_user_db_connector): - mock_user_db_connector.get_user_by_wallet_id.return_value = None - - async with AsyncClient(app=app, base_url="http://test") as ac: - response = await ac.post("/api/vault/deposit", json=test_data) + mock_user_db_connector.get_user_by_wallet_id.return_value = \ + mock_user if test_data["wallet_id"] == "test_wallet" else None - assert response.status_code == 404 - assert response.json() == {"detail": "User not found"} - - -@pytest.mark.anyio -async def test_get_vault_balance_success(): - """ - Test successful retrieval of vault balance. - """ - wallet_id = "test_wallet" - symbol = "ETH" - expected_balance = "1.5" + if test_data["wallet_id"] == "test_wallet": + with patch("web_app.db.crud.DepositDBConnector.create_vault", return_value=mock_vault): + response = await async_client.post("/api/vault/deposit", json=test_data) + else: + response = await async_client.post("/api/vault/deposit", json=test_data) - with patch("web_app.db.crud.DepositDBConnector.get_vault_balance", return_value=expected_balance): - async with AsyncClient(app=app, base_url="http://test") as ac: - response = await ac.get(f"/api/vault/api/balance?wallet_id={wallet_id}&symbol={symbol}") - - assert response.status_code == 200 - assert response.json() == { - "wallet_id": wallet_id, - "symbol": symbol, - "amount": expected_balance - } + assert response.status_code == expected_status + expected = expected_response(mock_vault.id) if callable(expected_response) else expected_response + assert response.json() == expected @pytest.mark.anyio -async def test_get_vault_balance_not_found(): - """ - Test retrieval of non-existent vault balance. - """ - wallet_id = "invalid_wallet" - symbol = "ETH" - - with patch("web_app.db.crud.DepositDBConnector.get_vault_balance", return_value=None): - async with AsyncClient(app=app, base_url="http://test") as ac: - response = await ac.get(f"/api/vault/api/balance?wallet_id={wallet_id}&symbol={symbol}") +@pytest.mark.parametrize("wallet_id, symbol, balance, expected_status, expected_response", [ + ( + "test_wallet", + "ETH", + "1.5", + 200, + lambda w, s, b: {"wallet_id": w, "symbol": s, "amount": b} + ), + ( + "invalid_wallet", + "ETH", + None, + 404, + {"detail": "Vault not found or user does not exist"} + ), +]) +async def test_get_vault_balance(wallet_id, symbol, balance, expected_status, expected_response, async_client): + """Test vault balance retrieval with different scenarios.""" + with patch("web_app.db.crud.DepositDBConnector.get_vault_balance", return_value=balance): + response = await async_client.get(f"/api/vault/api/balance?wallet_id={wallet_id}&symbol={symbol}") - assert response.status_code == 404 - assert response.json() == {"detail": "Vault not found or user does not exist"} + assert response.status_code == expected_status + expected = expected_response(wallet_id, symbol, balance) if callable(expected_response) else expected_response + assert response.json() == expected @pytest.mark.anyio -async def test_add_vault_balance_success(): - """ - Test successful addition to vault balance. - """ - test_data = { - "wallet_id": "test_wallet", - "symbol": "ETH", - "amount": "0.5" - } - +@pytest.mark.parametrize("test_data, expected_status, expected_response", [ + ( + {"wallet_id": "test_wallet", "symbol": "ETH", "amount": "0.5"}, + 200, + lambda amount: {"wallet_id": "test_wallet", "symbol": "ETH", "amount": amount} + ), + ( + {"wallet_id": "test_wallet", "symbol": "ETH", "amount": "-1.0"}, + 400, + {"detail": "Failed to update vault balance: Amount must be positive"} + ), +]) +async def test_add_vault_balance(test_data, expected_status, expected_response, async_client): + """Test adding to vault balance with different scenarios.""" mock_vault = MagicMock() mock_vault.amount = "2.0" - with patch("web_app.db.crud.DepositDBConnector.add_vault_balance", return_value=mock_vault): - async with AsyncClient(app=app, base_url="http://test") as ac: - response = await ac.post("/api/vault/api/add_balance", json=test_data) - - assert response.status_code == 200 - assert response.json() == { - "wallet_id": test_data["wallet_id"], - "symbol": test_data["symbol"], - "amount": mock_vault.amount + if test_data["amount"].startswith("-"): + patch_kwargs = { + "side_effect": ValueError("Amount must be positive") + } + else: + patch_kwargs = { + "return_value": mock_vault } - - -@pytest.mark.anyio -async def test_add_vault_balance_invalid_amount(): - """ - Test adding invalid amount to vault balance. - """ - test_data = { - "wallet_id": "test_wallet", - "symbol": "ETH", - "amount": "-1.0" - } - with patch( - "web_app.db.crud.DepositDBConnector.add_vault_balance", - side_effect=ValueError("Amount must be positive") - ): - async with AsyncClient(app=app, base_url="http://test") as ac: - response = await ac.post("/api/vault/api/add_balance", json=test_data) + with patch("web_app.db.crud.DepositDBConnector.add_vault_balance", **patch_kwargs): + response = await async_client.post("/api/vault/api/add_balance", json=test_data) - assert response.status_code == 400 - assert response.json() == { - "detail": "Failed to update vault balance: Amount must be positive" - } + assert response.status_code == expected_status + expected = expected_response(mock_vault.amount) if callable(expected_response) else expected_response + assert response.json() == expected From 8d1a16bf917e2dd85116a5058f48e17a54b82a34 Mon Sep 17 00:00:00 2001 From: Nuelo Date: Sun, 24 Nov 2024 20:43:25 +0100 Subject: [PATCH 3/3] fix: pylint issues fixed --- web_app/tests/test_vault.py | 103 ++++++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 28 deletions(-) diff --git a/web_app/tests/test_vault.py b/web_app/tests/test_vault.py index 94966a97..7423426a 100644 --- a/web_app/tests/test_vault.py +++ b/web_app/tests/test_vault.py @@ -42,7 +42,13 @@ async def async_client(): {"detail": "User not found"} ), ]) -async def test_deposit_to_vault(test_data, expected_status, expected_response, mock_user_db_connector, async_client): +async def test_deposit_to_vault( + test_data, + expected_status, + expected_response, + mock_user_db_connector, + async_client +): """Test vault deposit with different scenarios.""" mock_user = MagicMock() mock_vault = MagicMock() @@ -50,44 +56,70 @@ async def test_deposit_to_vault(test_data, expected_status, expected_response, m mock_vault.amount = test_data["amount"] with patch.object(UserDBConnector, "__new__", return_value=mock_user_db_connector): - mock_user_db_connector.get_user_by_wallet_id.return_value = \ + mock_user_db_connector.get_user_by_wallet_id.return_value = ( mock_user if test_data["wallet_id"] == "test_wallet" else None + ) if test_data["wallet_id"] == "test_wallet": - with patch("web_app.db.crud.DepositDBConnector.create_vault", return_value=mock_vault): + with patch( + "web_app.db.crud.DepositDBConnector.create_vault", + return_value=mock_vault + ): response = await async_client.post("/api/vault/deposit", json=test_data) else: response = await async_client.post("/api/vault/deposit", json=test_data) assert response.status_code == expected_status - expected = expected_response(mock_vault.id) if callable(expected_response) else expected_response + expected = ( + expected_response(mock_vault.id) + if callable(expected_response) + else expected_response + ) assert response.json() == expected @pytest.mark.anyio -@pytest.mark.parametrize("wallet_id, symbol, balance, expected_status, expected_response", [ - ( - "test_wallet", - "ETH", - "1.5", - 200, - lambda w, s, b: {"wallet_id": w, "symbol": s, "amount": b} - ), - ( - "invalid_wallet", - "ETH", - None, - 404, - {"detail": "Vault not found or user does not exist"} - ), -]) -async def test_get_vault_balance(wallet_id, symbol, balance, expected_status, expected_response, async_client): +@pytest.mark.parametrize( + "wallet_id, symbol, balance, expected_status, expected_response", + [ + ( + "test_wallet", + "ETH", + "1.5", + 200, + lambda w, s, b: {"wallet_id": w, "symbol": s, "amount": b} + ), + ( + "invalid_wallet", + "ETH", + None, + 404, + {"detail": "Vault not found or user does not exist"} + ), + ] +) +async def test_get_vault_balance( + wallet_id, + symbol, + balance, + expected_status, + expected_response, + async_client +): """Test vault balance retrieval with different scenarios.""" - with patch("web_app.db.crud.DepositDBConnector.get_vault_balance", return_value=balance): - response = await async_client.get(f"/api/vault/api/balance?wallet_id={wallet_id}&symbol={symbol}") + with patch( + "web_app.db.crud.DepositDBConnector.get_vault_balance", + return_value=balance + ): + url = f"/api/vault/api/balance?wallet_id={wallet_id}&symbol={symbol}" + response = await async_client.get(url) assert response.status_code == expected_status - expected = expected_response(wallet_id, symbol, balance) if callable(expected_response) else expected_response + expected = ( + expected_response(wallet_id, symbol, balance) + if callable(expected_response) + else expected_response + ) assert response.json() == expected @@ -104,7 +136,12 @@ async def test_get_vault_balance(wallet_id, symbol, balance, expected_status, ex {"detail": "Failed to update vault balance: Amount must be positive"} ), ]) -async def test_add_vault_balance(test_data, expected_status, expected_response, async_client): +async def test_add_vault_balance( + test_data, + expected_status, + expected_response, + async_client +): """Test adding to vault balance with different scenarios.""" mock_vault = MagicMock() mock_vault.amount = "2.0" @@ -118,9 +155,19 @@ async def test_add_vault_balance(test_data, expected_status, expected_response, "return_value": mock_vault } - with patch("web_app.db.crud.DepositDBConnector.add_vault_balance", **patch_kwargs): - response = await async_client.post("/api/vault/api/add_balance", json=test_data) + with patch( + "web_app.db.crud.DepositDBConnector.add_vault_balance", + **patch_kwargs + ): + response = await async_client.post( + "/api/vault/api/add_balance", + json=test_data + ) assert response.status_code == expected_status - expected = expected_response(mock_vault.amount) if callable(expected_response) else expected_response + expected = ( + expected_response(mock_vault.amount) + if callable(expected_response) + else expected_response + ) assert response.json() == expected