Skip to content

Commit

Permalink
Add migration and CRUD operations for new Order model
Browse files Browse the repository at this point in the history
  • Loading branch information
beeguy74 committed Mar 7, 2025
1 parent b5dfb1f commit 651033b
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 58 deletions.
48 changes: 48 additions & 0 deletions margin_app/app/alembic/versions/2025-03-07_new_order_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""new order model
Revision ID: a1b2c3d4e5f6
Revises: fe041c2f01ee
Create Date: 2025-03-07 09:00:00.000000
"""

import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects.postgresql import UUID

# revision identifiers, used by Alembic.
revision = "a1b2c3d4e5f6"
down_revision = "fe041c2f01ee"
branch_labels = None
depends_on = None


def upgrade():
"""Create order table."""
op.create_table(
"order",
sa.Column("id", UUID(as_uuid=True), primary_key=True),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column(
"updated_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column(
"user_id", UUID(as_uuid=True), sa.ForeignKey("user.id"), nullable=False
),
sa.Column("price", sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column("token", sa.VARCHAR(), nullable=False),
sa.Column("position", UUID(as_uuid=True), nullable=False),
)


def downgrade():
"""Drop order table."""
op.drop_table("order")
37 changes: 18 additions & 19 deletions margin_app/app/crud/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,56 @@
This module contains CRUD operations for orders.
"""

import uuid
import logging
from app.models.order import Order
import uuid

from app.crud.base import DBConnector
from app.models.order import Order

logger = logging.getLogger(__name__)


class OrderCRUD(DBConnector):
"""
CRUD operations for Order model.
Methods:
- add_new_order: Create and store a new order in the database
- execute_order: Process and execute an existing order
"""

async def add_new_order(self, user_id: uuid.UUID, price: float, token: str, position: uuid.UUID) -> Order:

async def add_new_order(
self, user_id: uuid.UUID, price: float, token: str, position: uuid.UUID
) -> Order:
"""
Creates a new order in the database.
Args:
user_id (uuid.UUID): ID of the user placing the order
price (float): Price of the order
token (str): Token symbol for the order
position (uuid.UUID): Position ID related to the order
Returns:
Order: The newly created order object
"""
order = Order(
user_id=user_id,
price=price,
token=token,
position=position
)

order = Order(user_id=user_id, price=price, token=token, position=position)

try:
order = await self.write_to_db(order)
logger.info(f"New order created with ID: {order.id}")
return order
except Exception as e:
logger.error(f"Error creating order: {str(e)}")
raise

async def execute_order(self, order_id: uuid.UUID) -> bool:
"""
Processes and executes an order by its ID.
Args:
order_id (uuid.UUID): ID of the order to execute
Returns:
bool: True if the order was successfully executed, False otherwise
"""
Expand All @@ -61,12 +60,12 @@ async def execute_order(self, order_id: uuid.UUID) -> bool:
if not order:
logger.warning(f"Order with ID {order_id} not found")
return False

# Order execution logic would go here
# This could include updating the order status, processing the transaction, etc.
logger.info(f"Order {order_id} executed successfully")
return True

except Exception as e:
logger.error(f"Failed to execute order {order_id}: {str(e)}")
return False
73 changes: 34 additions & 39 deletions margin_app/app/tests/test_order_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
"""

import uuid
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from unittest.mock import patch, MagicMock, AsyncMock
from sqlalchemy.exc import SQLAlchemyError

from app.crud.order import OrderCRUD
from app.models.order import Order

# Test data
TEST_USER_ID = uuid.uuid4()
TEST_POSITION_ID = uuid.uuid4()
TEST_ORDER_ID = uuid.uuid4()
Expand Down Expand Up @@ -39,19 +39,18 @@ def mock_order():
@pytest.mark.asyncio
async def test_add_new_order_success(order_crud, mock_order):
"""Test successfully adding a new order"""
# Arrange
with patch.object(order_crud, 'write_to_db', new_callable=AsyncMock) as mock_write_to_db:
with patch.object(
order_crud, "write_to_db", new_callable=AsyncMock
) as mock_write_to_db:
mock_write_to_db.return_value = mock_order

# Act

result = await order_crud.add_new_order(
user_id=TEST_USER_ID,
price=TEST_PRICE,
token=TEST_TOKEN,
position=TEST_POSITION_ID
position=TEST_POSITION_ID,
)

# Assert

assert mock_write_to_db.called
assert result == mock_order
assert result.user_id == TEST_USER_ID
Expand All @@ -63,77 +62,73 @@ async def test_add_new_order_success(order_crud, mock_order):
@pytest.mark.asyncio
async def test_add_new_order_exception(order_crud):
"""Test exception handling when adding a new order fails"""
# Arrange
with patch.object(order_crud, 'write_to_db', new_callable=AsyncMock) as mock_write_to_db:
with patch.object(
order_crud, "write_to_db", new_callable=AsyncMock
) as mock_write_to_db:
mock_write_to_db.side_effect = SQLAlchemyError("Database error")

# Act & Assert

with pytest.raises(Exception):
await order_crud.add_new_order(
user_id=TEST_USER_ID,
price=TEST_PRICE,
token=TEST_TOKEN,
position=TEST_POSITION_ID
position=TEST_POSITION_ID,
)


@pytest.mark.asyncio
async def test_execute_order_success(order_crud, mock_order):
"""Test successfully executing an order"""
# Arrange
with patch.object(order_crud, 'get_object', new_callable=AsyncMock) as mock_get_object:
with patch.object(
order_crud, "get_object", new_callable=AsyncMock
) as mock_get_object:
mock_get_object.return_value = mock_order

# Act

result = await order_crud.execute_order(TEST_ORDER_ID)

# Assert

mock_get_object.assert_called_once_with(Order, TEST_ORDER_ID)
assert result is True


@pytest.mark.asyncio
async def test_execute_order_not_found(order_crud):
"""Test executing an order that doesn't exist"""
# Arrange
with patch.object(order_crud, 'get_object', new_callable=AsyncMock) as mock_get_object:
with patch.object(
order_crud, "get_object", new_callable=AsyncMock
) as mock_get_object:
mock_get_object.return_value = None

# Act

result = await order_crud.execute_order(TEST_ORDER_ID)

# Assert

mock_get_object.assert_called_once_with(Order, TEST_ORDER_ID)
assert result is False


@pytest.mark.asyncio
async def test_execute_order_exception(order_crud):
"""Test exception handling when executing an order fails"""
# Arrange
with patch.object(order_crud, 'get_object', new_callable=AsyncMock) as mock_get_object:
with patch.object(
order_crud, "get_object", new_callable=AsyncMock
) as mock_get_object:
mock_get_object.side_effect = Exception("Unexpected error")

# Act

result = await order_crud.execute_order(TEST_ORDER_ID)

# Assert

mock_get_object.assert_called_once_with(Order, TEST_ORDER_ID)
assert result is False


@pytest.mark.asyncio
async def test_execute_order_integration(order_crud, mock_order):
"""
Integration test for execute_order that tests the full flow
Integration test for execute_order that tests the full flow
with minimal mocking
"""
# This test would typically use a test database
with patch.object(order_crud, 'get_object', new_callable=AsyncMock) as mock_get_object:
with patch.object(
order_crud, "get_object", new_callable=AsyncMock
) as mock_get_object:
mock_get_object.return_value = mock_order

# Act - execute the order

success = await order_crud.execute_order(TEST_ORDER_ID)

# Assert

assert success is True

0 comments on commit 651033b

Please sign in to comment.