-
Notifications
You must be signed in to change notification settings - Fork 174
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
1,599 additions
and
6,440 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
name: Margin Tests | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
paths: | ||
- 'margin_app/**' | ||
pull_request: | ||
branches: | ||
- main | ||
paths: | ||
- 'margin_app/**' | ||
jobs: | ||
shared: | ||
uses: ./.github/workflows/shared_workflow.yml | ||
with: | ||
python-version: "3.12" | ||
|
||
margin-tests: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Create .env file | ||
run: cp margin_app/.env.dev margin_app/.env | ||
|
||
- name: Build and Start Containers | ||
run: | | ||
docker compose -f margin_app/docker-compose.yml --env-file margin_app/.env up -d --build | ||
echo "Waiting for containers to be ready..." | ||
sleep 30 | ||
- name: Run Integration Tests with Coverage | ||
run: | | ||
docker compose -f margin_app/docker-compose.yml exec backend python -m pytest app/tests -v | ||
- name: Clean Up | ||
if: always() | ||
run: | | ||
docker compose -f margin_app/docker-compose.yml logs > docker-logs.txt || true | ||
docker compose -f margin_app/docker-compose.yml down -v | ||
- name: Upload Docker Logs on Failure | ||
if: failure() | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: docker-logs | ||
path: docker-logs.txt |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
APP_ENV=development | ||
SECRET_KEY=SECRET_KEY | ||
|
||
# Database creds | ||
POSTGRES_USER=user | ||
POSTGRES_PASSWORD=password | ||
POSTGRES_DB=db_name | ||
DB_HOST=db |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
""" | ||
This module contains tests for the deposit CRUD functionality. | ||
""" | ||
|
||
import uuid | ||
from decimal import Decimal | ||
from unittest.mock import AsyncMock, MagicMock | ||
import pytest | ||
from sqlalchemy.exc import SQLAlchemyError | ||
|
||
from app.crud.deposit import DepositCRUD | ||
from app.models.deposit import Deposit | ||
|
||
|
||
@pytest.fixture | ||
def deposit_crud(): | ||
"""Fixture to create an instance of DepositCRUD.""" | ||
return DepositCRUD() | ||
|
||
|
||
@pytest.fixture | ||
def sample_deposit(): | ||
"""Create a sample deposit for testing.""" | ||
return Deposit( | ||
id=uuid.uuid4(), | ||
user_id=uuid.uuid4(), | ||
token="NGN", | ||
amount=Decimal("1.5"), | ||
transaction_id="tx234" | ||
) | ||
|
||
|
||
### Positive Test Cases ### | ||
|
||
@pytest.mark.asyncio | ||
async def test_create_deposit_success(deposit_crud): | ||
"""Test successfully creating a new deposit.""" | ||
user_id = uuid.uuid4() | ||
token = "NGN" | ||
amount = Decimal("1.5") | ||
transaction_id = "tx234" | ||
|
||
deposit_crud.write_to_db = AsyncMock(return_value=Deposit( | ||
id=uuid.uuid4(), user_id=user_id, token=token, amount=amount, transaction_id=transaction_id | ||
)) | ||
|
||
deposit = await deposit_crud.create_deposit(user_id, token, amount, transaction_id) | ||
|
||
# Confirm tests | ||
assert deposit.user_id == user_id | ||
assert deposit.token == token | ||
assert deposit.amount == amount | ||
assert deposit.transaction_id == transaction_id | ||
assert hasattr(deposit, "id") | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_update_deposit_success(deposit_crud, sample_deposit): | ||
"""Test successfully updating an existing deposit.""" | ||
|
||
mock_session_obj = MagicMock() | ||
mock_session_obj.get = AsyncMock(return_value=sample_deposit) | ||
mock_session_obj.commit = AsyncMock() | ||
mock_session_obj.refresh = AsyncMock() | ||
|
||
mock_session_obj.__aenter__.return_value = mock_session_obj | ||
mock_session_obj.__aexit__.return_value = None | ||
|
||
deposit_crud.session = MagicMock(return_value=mock_session_obj) | ||
|
||
update_data = {"token": "SOL", "amount": Decimal("2.0"), "transaction_id": "tx365"} | ||
updated_deposit = await deposit_crud.update_deposit(sample_deposit.id, update_data) | ||
|
||
# Confirm tests | ||
mock_session_obj.get.assert_called_once() | ||
assert updated_deposit.token == "SOL" | ||
assert updated_deposit.amount == Decimal("2.0") | ||
assert updated_deposit.transaction_id == "tx365" | ||
|
||
|
||
### Negative Test Cases ### | ||
|
||
@pytest.mark.asyncio | ||
async def test_create_deposit_failure(deposit_crud): | ||
"""Test failure when creating a deposit.""" | ||
deposit_crud.write_to_db = AsyncMock(side_effect=SQLAlchemyError("DB error")) | ||
|
||
with pytest.raises(Exception, match="Could not create deposit"): | ||
await deposit_crud.create_deposit(uuid.uuid4(), "ETH", Decimal("3.5"), "tx222") | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_update_deposit_not_found(deposit_crud): | ||
"""Test updating a deposit that does not exist.""" | ||
|
||
mock_session_obj = MagicMock() | ||
mock_session_obj.get = AsyncMock(return_value=None) | ||
mock_session_obj.__aenter__.return_value = mock_session_obj | ||
mock_session_obj.__aexit__.return_value = None | ||
|
||
deposit_crud.session = MagicMock(return_value=mock_session_obj) | ||
|
||
result = await deposit_crud.update_deposit(uuid.uuid4(), {"token": "BTC"}) | ||
assert result is None | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_update_deposit_failure(deposit_crud, sample_deposit): | ||
"""Test failure when updating a deposit due to commit error.""" | ||
|
||
mock_session_obj = MagicMock() | ||
mock_session_obj.get = AsyncMock(return_value=sample_deposit) | ||
mock_session_obj.commit = AsyncMock(side_effect=SQLAlchemyError("Commit error")) | ||
mock_session_obj.__aenter__.return_value = mock_session_obj | ||
mock_session_obj.__aexit__.return_value = None | ||
|
||
deposit_crud.session = MagicMock(return_value=mock_session_obj) | ||
|
||
with pytest.raises(SQLAlchemyError, match="Commit error"): | ||
await deposit_crud.update_deposit(sample_deposit.id, {"token": "BTC"}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
"""Tests for LiquidationCRUD in spotnet/margin_app/app/crud/liquidation.py""" | ||
# pylint: disable=redefined-outer-name | ||
|
||
import pytest | ||
from app.crud.liquidation import LiquidationCRUD | ||
from app.models.liquidation import Liquidation | ||
|
||
@pytest.fixture | ||
def session_fixture(): | ||
"""Fixture providing a mock database session.""" | ||
class MockSession: | ||
"""Mock session to simulate database operations.""" | ||
def __init__(self): | ||
self.data = [] | ||
|
||
def add(self, obj): | ||
"""Simulate adding an object to the database.""" | ||
self.data.append(obj) | ||
|
||
def commit(self): | ||
"""Simulate committing a transaction (no operation).""" | ||
|
||
def query(self, _): | ||
"""Return a mock query object.""" | ||
class Query: | ||
"""Mock Query class to filter and retrieve data.""" | ||
def __init__(self, data): | ||
self.data = data | ||
|
||
def filter_by(self, **kwargs): | ||
"""Filter data based on provided keyword arguments.""" | ||
filtered = [ | ||
item for item in self.data | ||
if all(getattr(item, key, None) == value for key, value in kwargs.items()) | ||
] | ||
return Query(filtered) | ||
|
||
def first(self): | ||
"""Return the first element in the filtered data, or None if empty.""" | ||
return self.data[0] if self.data else None | ||
|
||
return Query(self.data) | ||
|
||
return MockSession() | ||
|
||
def test_create_liquidation_success(session_fixture): | ||
"""Test creating a liquidation record with valid data.""" | ||
liquidation_data = {"id": 1, "amount": 1000, "status": "pending"} | ||
liquidation = LiquidationCRUD().create(session_fixture, liquidation_data) # pylint: disable=no-member | ||
assert liquidation is not None | ||
assert liquidation.id == 1 | ||
|
||
def test_create_liquidation_failure(session_fixture): | ||
"""Test that creating a liquidation record with invalid data raises an error.""" | ||
liquidation_data = {"amount": -500} # Invalid data: negative amount | ||
with pytest.raises(ValueError): | ||
LiquidationCRUD().create(session_fixture, liquidation_data) # pylint: disable=no-member | ||
|
||
def test_get_liquidation_success(session_fixture): | ||
"""Test retrieving an existing liquidation record.""" | ||
liquidation_instance = Liquidation(id=1, amount=1000, status="pending") | ||
session_fixture.add(liquidation_instance) | ||
liquidation = LiquidationCRUD().get(session_fixture, 1) # pylint: disable=no-member | ||
assert liquidation is not None | ||
assert liquidation.id == 1 | ||
|
||
def test_get_liquidation_not_found(session_fixture): | ||
"""Test retrieving a non-existent liquidation record returns None.""" | ||
liquidation = LiquidationCRUD().get(session_fixture, 999) # pylint: disable=no-member | ||
assert liquidation is None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"""Test to check if worklow works properly""" | ||
|
||
import pytest | ||
|
||
@pytest.fixture | ||
def sample_data(): | ||
"""Test fixture""" | ||
return {"key": "value"} | ||
|
||
def test_sample(sample_data): | ||
"""Test function""" | ||
assert sample_data["key"] == "value" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.