nomy-data-models 0.2.7

Data model definitions for Nomy wallet analysis data processing
Documentation
"""Tests for the position module."""

import uuid
from datetime import datetime
from decimal import Decimal

import pytest
from sqlalchemy.orm import Session

from nomy_data_models.models.position import (
    MarketType,
    Position,
    PositionDirection,
    PositionStatus,
)


@pytest.fixture
def open_position() -> Position:
    """Fixture providing a Position instance with open status.

    Returns:
        Position: A fully populated Position instance with open status
    """
    position = Position()
    position.id = uuid.uuid4()
    position.position_id = uuid.uuid4()
    position.chain_id = 1
    position.market_type = MarketType.SPOT
    position.position_direction = PositionDirection.BUY
    position.wallet_address = "0x1234567890123456789012345678901234567890"
    position.token_symbol_pair = "ETH/USDT"
    position.token_address_pair = "0x1234567890123456789012345678901234567890/0x0987654321098765432109876543210987654321"
    position.base_token_symbol = "ETH"
    position.base_token_address = "0x1234567890123456789012345678901234567890"
    position.quote_token_symbol = "USDT"
    position.quote_token_address = "0x0987654321098765432109876543210987654321"
    position.status = PositionStatus.OPEN
    position.current_base_amount = Decimal("1.0")
    position.original_base_amount = Decimal("1.0")
    position.avg_entry_price = Decimal("1000.0")
    position.avg_exit_price = Decimal("0.0")
    position.cost_basis = Decimal("1000.0")
    position.leverage = Decimal("1.0")
    position.realized_pnl = Decimal("0.0")
    position.realized_pnl_usd = Decimal("0.0")
    position.opening_trades = {"trade_id": "123"}
    position.closing_trades = {}
    position.opened_at = datetime.now()
    position.exchange = "uniswap"
    position.trading_fee_symbol = "USDT"
    return position


@pytest.fixture
def closed_position() -> Position:
    """Fixture providing a Position instance with closed status.

    Returns:
        Position: A fully populated Position instance with closed status
    """
    position = Position()
    position.id = uuid.uuid4()
    position.position_id = uuid.uuid4()
    position.chain_id = 1
    position.market_type = MarketType.SPOT
    position.position_direction = PositionDirection.BUY
    position.wallet_address = "0x1234567890123456789012345678901234567890"
    position.token_symbol_pair = "ETH/USDT"
    position.token_address_pair = "0x1234567890123456789012345678901234567890/0x0987654321098765432109876543210987654321"
    position.base_token_symbol = "ETH"
    position.base_token_address = "0x1234567890123456789012345678901234567890"
    position.quote_token_symbol = "USDT"
    position.quote_token_address = "0x0987654321098765432109876543210987654321"
    position.status = PositionStatus.CLOSED
    position.current_base_amount = Decimal("0.0")
    position.original_base_amount = Decimal("1.0")
    position.avg_entry_price = Decimal("1000.0")
    position.avg_exit_price = Decimal("0.0")
    position.cost_basis = Decimal("1000.0")
    position.leverage = Decimal("1.0")
    position.realized_pnl = Decimal("100.0")
    position.realized_pnl_usd = Decimal("100.0")
    position.opening_trades = {"trade_id": "123"}
    position.closing_trades = {"trade_id": "456"}
    position.roi = Decimal("0.1")
    position.opened_at = datetime.now()
    position.exchange = "uniswap"
    return position


class TestPositionStatus:
    """Test cases for PositionStatus enum."""

    def test_position_status_values(self) -> None:
        """Test that the PositionStatus enum has the expected values."""
        assert PositionStatus.OPEN.value == "open", "OPEN value should be 'open'"
        assert (
            PositionStatus.CLOSED.value == "closed"
        ), "CLOSED value should be 'closed'"

    def test_position_status_comparison(self) -> None:
        """Test that the PositionStatus enum values can be compared."""
        assert PositionStatus.OPEN == PositionStatus.OPEN, "OPEN should equal itself"
        assert (
            PositionStatus.OPEN != PositionStatus.CLOSED
        ), "OPEN should not equal CLOSED"
        assert (
            PositionStatus.CLOSED == PositionStatus.CLOSED
        ), "CLOSED should equal itself"


class TestMarketType:
    """Test cases for MarketType enum."""

    def test_market_type_values(self) -> None:
        """Test that the MarketType enum has the expected values."""
        assert MarketType.SPOT.value == "spot", "SPOT value should be 'spot'"
        assert MarketType.PERPETUAL.value == "perp", "PERPETUAL value should be 'perp'"

    def test_market_type_comparison(self) -> None:
        """Test that the MarketType enum values can be compared."""
        assert MarketType.SPOT == MarketType.SPOT, "SPOT should equal itself"
        assert (
            MarketType.SPOT != MarketType.PERPETUAL
        ), "SPOT should not equal PERPETUAL"
        assert (
            MarketType.PERPETUAL == MarketType.PERPETUAL
        ), "PERPETUAL should equal itself"


class TestPositionDirection:
    """Test cases for PositionDirection enum."""

    def test_position_direction_values(self) -> None:
        """Test that the PositionDirection enum has the expected values."""
        assert PositionDirection.BUY.value == "buy", "BUY value should be 'buy'"
        assert PositionDirection.SELL.value == "sell", "SELL value should be 'sell'"

    def test_position_direction_comparison(self) -> None:
        """Test that the PositionDirection enum values can be compared."""
        assert PositionDirection.BUY == PositionDirection.BUY, "BUY should equal itself"
        assert (
            PositionDirection.BUY != PositionDirection.SELL
        ), "BUY should not equal SELL"
        assert (
            PositionDirection.SELL == PositionDirection.SELL
        ), "SELL should equal itself"


class TestPosition:
    """Test cases for Position model."""

    def test_position_creation(self, open_position: Position) -> None:
        """Test creating a Position instance with open status.

        Args:
            open_position: Fixture providing a Position instance with open status
        """
        # Verify the instance was created correctly
        assert open_position.id is not None, "ID should be set"
        assert open_position.position_id is not None, "Position ID should be set"
        assert open_position.chain_id == 1, "Chain ID should be 1"
        assert (
            open_position.market_type == MarketType.SPOT
        ), "Position type should be SPOT"
        assert (
            open_position.position_direction == PositionDirection.BUY
        ), "Position direction should be BUY"
        assert (
            open_position.wallet_address == "0x1234567890123456789012345678901234567890"
        ), "Wallet address should match"
        assert (
            open_position.token_symbol_pair == "ETH/USDT"
        ), "Token symbol pair should be 'ETH/USDT'"
        assert (
            open_position.token_address_pair
            == "0x1234567890123456789012345678901234567890/0x0987654321098765432109876543210987654321"
        ), "Token address pair should match"
        assert (
            open_position.base_token_symbol == "ETH"
        ), "Base token symbol should be 'ETH'"
        assert (
            open_position.base_token_address
            == "0x1234567890123456789012345678901234567890"
        ), "Base token address should match"
        assert (
            open_position.quote_token_symbol == "USDT"
        ), "Quote token symbol should be 'USDT'"
        assert (
            open_position.quote_token_address
            == "0x0987654321098765432109876543210987654321"
        ), "Quote token address should match"
        assert open_position.status == PositionStatus.OPEN, "Status should be OPEN"
        assert open_position.current_base_amount == Decimal(
            "1.0"
        ), "Current base amount should be 1.0"
        assert open_position.original_base_amount == Decimal(
            "1.0"
        ), "Original base amount should be 1.0"
        assert open_position.avg_entry_price == Decimal(
            "1000.0"
        ), "Average entry price should be 1000.0"
        assert open_position.cost_basis == Decimal(
            "1000.0"
        ), "Cost basis should be 1000.0"
        assert open_position.leverage == Decimal("1.0"), "Leverage should be 1.0"
        assert open_position.realized_pnl == Decimal(
            "0.0"
        ), "Realized PnL should be 0.0"
        assert open_position.realized_pnl_usd == Decimal(
            "0.0"
        ), "Realized PnL USD should be 0.0"
        assert open_position.opening_trades == {
            "trade_id": "123"
        }, "Opening trades should match"
        assert open_position.closing_trades == {}, "Closing trades should be empty"
        assert open_position.opened_at is not None, "Opened at timestamp should be set"
        assert open_position.exchange == "uniswap", "Exchange should be 'uniswap'"

    def test_position_with_closed_status(self, closed_position: Position) -> None:
        """Test creating a Position instance with closed status.

        Args:
            closed_position: Fixture providing a Position instance with closed status
        """
        # Verify the instance was created correctly
        assert closed_position.id is not None, "ID should be set"
        assert closed_position.position_id is not None, "Position ID should be set"
        assert closed_position.chain_id == 1, "Chain ID should be 1"
        assert (
            closed_position.market_type == MarketType.SPOT
        ), "Position type should be SPOT"
        assert (
            closed_position.position_direction == PositionDirection.BUY
        ), "Position direction should be BUY"
        assert (
            closed_position.wallet_address
            == "0x1234567890123456789012345678901234567890"
        ), "Wallet address should match"
        assert (
            closed_position.token_symbol_pair == "ETH/USDT"
        ), "Token symbol pair should be 'ETH/USDT'"
        assert (
            closed_position.token_address_pair
            == "0x1234567890123456789012345678901234567890/0x0987654321098765432109876543210987654321"
        ), "Token address pair should match"
        assert (
            closed_position.base_token_symbol == "ETH"
        ), "Base token symbol should be 'ETH'"
        assert (
            closed_position.base_token_address
            == "0x1234567890123456789012345678901234567890"
        ), "Base token address should match"
        assert (
            closed_position.quote_token_symbol == "USDT"
        ), "Quote token symbol should be 'USDT'"
        assert (
            closed_position.quote_token_address
            == "0x0987654321098765432109876543210987654321"
        ), "Quote token address should match"
        assert (
            closed_position.status == PositionStatus.CLOSED
        ), "Status should be CLOSED"
        assert closed_position.current_base_amount == Decimal(
            "0.0"
        ), "Current base amount should be 0.0"
        assert closed_position.original_base_amount == Decimal(
            "1.0"
        ), "Original base amount should be 1.0"
        assert closed_position.avg_entry_price == Decimal(
            "1000.0"
        ), "Average entry price should be 1000.0"
        assert closed_position.cost_basis == Decimal(
            "1000.0"
        ), "Cost basis should be 1000.0"
        assert closed_position.leverage == Decimal("1.0"), "Leverage should be 1.0"
        assert closed_position.realized_pnl == Decimal(
            "100.0"
        ), "Realized PnL should be 100.0"
        assert closed_position.realized_pnl_usd == Decimal(
            "100.0"
        ), "Realized PnL USD should be 100.0"
        assert closed_position.opening_trades == {
            "trade_id": "123"
        }, "Opening trades should match"
        assert closed_position.closing_trades == {
            "trade_id": "456"
        }, "Closing trades should match"
        assert closed_position.roi == Decimal("0.1"), "ROI should be 0.1"
        assert (
            closed_position.opened_at is not None
        ), "Opened at timestamp should be set"
        assert closed_position.exchange == "uniswap", "Exchange should be 'uniswap'"

    def test_position_db_integration(self, session: Session) -> None:
        """Test database integration with the Position model.

        Args:
            session: SQLAlchemy session fixture
        """
        # Create a new Position instance
        position = Position()
        position.position_id = uuid.uuid4()
        position.chain_id = 1
        position.market_type = MarketType.SPOT
        position.position_direction = PositionDirection.BUY
        position.wallet_address = "0x1234567890123456789012345678901234567890"
        position.token_symbol_pair = "ETH/USDT"
        position.token_address_pair = "0x1234567890123456789012345678901234567890/0x0987654321098765432109876543210987654321"
        position.base_token_symbol = "ETH"
        position.base_token_address = "0x1234567890123456789012345678901234567890"
        position.quote_token_symbol = "USDT"
        position.quote_token_address = "0x0987654321098765432109876543210987654321"
        position.status = PositionStatus.OPEN
        position.current_base_amount = Decimal("1.0")
        position.original_base_amount = Decimal("1.0")
        position.avg_entry_price = Decimal("1000.0")
        position.avg_exit_price = Decimal("0.0")
        position.cost_basis = Decimal("1000.0")
        position.leverage = Decimal("1.0")
        position.realized_pnl = Decimal("0.0")
        position.realized_pnl_usd = Decimal("0.0")
        position.opening_trades = {"trade_id": "123"}
        position.closing_trades = {}
        position.opened_at = datetime.now()
        position.exchange = "uniswap"
        position.trading_fee_symbol = "USDT"

        # Add to session and commit
        session.add(position)
        session.commit()

        # Verify the position was saved to the database
        retrieved = session.query(Position).filter_by(id=position.id).first()
        assert retrieved is not None, "Position should be retrievable from database"
        assert retrieved.id == position.id, "Retrieved position ID should match"
        assert (
            retrieved.position_id == position.position_id
        ), "Retrieved position_id should match"
        assert (
            retrieved.wallet_address == "0x1234567890123456789012345678901234567890"
        ), "Retrieved wallet address should match"
        assert (
            retrieved.token_symbol_pair == "ETH/USDT"
        ), "Retrieved token symbol pair should match"
        assert (
            retrieved.status == PositionStatus.OPEN
        ), "Retrieved status should be OPEN"
        assert retrieved.current_base_amount == Decimal(
            "1.0"
        ), "Retrieved current base amount should match"
        assert retrieved.original_base_amount == Decimal(
            "1.0"
        ), "Retrieved original base amount should match"
        assert retrieved.avg_entry_price == Decimal(
            "1000.0"
        ), "Retrieved average entry price should match"
        assert retrieved.leverage == Decimal("1.0"), "Retrieved leverage should match"


if __name__ == "__main__":
    pytest.main(["-v", __file__])