Update create_exchange endpoint to accept and validate bitcoin_transfer_method
This commit is contained in:
parent
d82829ab40
commit
28e8ba218f
2 changed files with 80 additions and 0 deletions
|
|
@ -14,6 +14,7 @@ from database import get_db
|
|||
from date_validation import validate_date_in_range
|
||||
from models import (
|
||||
Availability,
|
||||
BitcoinTransferMethod,
|
||||
Exchange,
|
||||
ExchangeStatus,
|
||||
Permission,
|
||||
|
|
@ -385,6 +386,18 @@ async def create_exchange(
|
|||
detail=f"Invalid direction: {request.direction}. Must be 'buy' or 'sell'.",
|
||||
) from None
|
||||
|
||||
# Validate bitcoin transfer method
|
||||
try:
|
||||
bitcoin_transfer_method = BitcoinTransferMethod(request.bitcoin_transfer_method)
|
||||
except ValueError:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=(
|
||||
f"Invalid bitcoin_transfer_method: {request.bitcoin_transfer_method}. "
|
||||
"Must be 'onchain' or 'lightning'."
|
||||
),
|
||||
) from None
|
||||
|
||||
# Validate EUR amount
|
||||
if request.eur_amount < EUR_TRADE_MIN * 100:
|
||||
raise HTTPException(
|
||||
|
|
@ -468,6 +481,7 @@ async def create_exchange(
|
|||
slot_start=request.slot_start,
|
||||
slot_end=slot_end_dt,
|
||||
direction=direction,
|
||||
bitcoin_transfer_method=bitcoin_transfer_method,
|
||||
eur_amount=request.eur_amount,
|
||||
sats_amount=sats_amount,
|
||||
market_price_eur=market_price,
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000, # €100 in cents
|
||||
},
|
||||
)
|
||||
|
|
@ -187,6 +188,7 @@ class TestCreateExchange:
|
|||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["direction"] == "buy"
|
||||
assert data["bitcoin_transfer_method"] == "onchain"
|
||||
assert data["eur_amount"] == 10000
|
||||
assert data["status"] == "booked"
|
||||
assert data["sats_amount"] > 0
|
||||
|
|
@ -207,6 +209,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{target_date}T10:00:00Z",
|
||||
"direction": "sell",
|
||||
"bitcoin_transfer_method": "lightning",
|
||||
"eur_amount": 20000, # €200 in cents
|
||||
},
|
||||
)
|
||||
|
|
@ -214,6 +217,7 @@ class TestCreateExchange:
|
|||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["direction"] == "sell"
|
||||
assert data["bitcoin_transfer_method"] == "lightning"
|
||||
assert data["eur_amount"] == 20000
|
||||
# For sell, agreed price is market * 0.95
|
||||
assert data["agreed_price_eur"] == pytest.approx(19000.0, rel=0.001)
|
||||
|
|
@ -233,6 +237,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -247,6 +252,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -268,6 +274,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "invalid",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -275,6 +282,48 @@ class TestCreateExchange:
|
|||
assert response.status_code == 400
|
||||
assert "Invalid direction" in response.json()["detail"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_missing_bitcoin_transfer_method_rejected(
|
||||
self, client_factory, regular_user, admin_user
|
||||
):
|
||||
"""Missing bitcoin_transfer_method is rejected."""
|
||||
target_date = await setup_availability_and_price(client_factory, admin_user)
|
||||
|
||||
with mock_price_fetcher(20000.0):
|
||||
async with client_factory.create(cookies=regular_user["cookies"]) as client:
|
||||
response = await client.post(
|
||||
"/api/exchange",
|
||||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_bitcoin_transfer_method_rejected(
|
||||
self, client_factory, regular_user, admin_user
|
||||
):
|
||||
"""Invalid bitcoin_transfer_method is rejected."""
|
||||
target_date = await setup_availability_and_price(client_factory, admin_user)
|
||||
|
||||
with mock_price_fetcher(20000.0):
|
||||
async with client_factory.create(cookies=regular_user["cookies"]) as client:
|
||||
response = await client.post(
|
||||
"/api/exchange",
|
||||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "invalid",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "Invalid bitcoin_transfer_method" in response.json()["detail"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_amount_below_minimum_rejected(
|
||||
self, client_factory, regular_user, admin_user
|
||||
|
|
@ -289,6 +338,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 5000, # €50, below min of €100
|
||||
},
|
||||
)
|
||||
|
|
@ -310,6 +360,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 400000, # €4000, above max of €3000
|
||||
},
|
||||
)
|
||||
|
|
@ -331,6 +382,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 11500, # €115, not multiple of €20
|
||||
},
|
||||
)
|
||||
|
|
@ -352,6 +404,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:07:00Z", # 07 is not on 15-min boundary
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -384,6 +437,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{tomorrow()}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -405,6 +459,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{target_date}T20:00:00Z", # Outside 09-17
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -421,6 +476,7 @@ class TestCreateExchange:
|
|||
json={
|
||||
"slot_start": f"{tomorrow()}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -460,6 +516,7 @@ class TestUserTrades:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -468,6 +525,7 @@ class TestUserTrades:
|
|||
json={
|
||||
"slot_start": f"{target_date}T10:00:00Z",
|
||||
"direction": "sell",
|
||||
"bitcoin_transfer_method": "lightning",
|
||||
"eur_amount": 20000,
|
||||
},
|
||||
)
|
||||
|
|
@ -496,6 +554,7 @@ class TestCancelTrade:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -524,6 +583,7 @@ class TestCancelTrade:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -567,6 +627,7 @@ class TestCancelTrade:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -601,6 +662,7 @@ class TestCancelTrade:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -671,6 +733,7 @@ class TestAdminUpcomingTrades:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -787,6 +850,7 @@ class TestAdminCompleteTrade:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -920,6 +984,7 @@ class TestAdminCancelTrade:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
@ -948,6 +1013,7 @@ class TestAdminCancelTrade:
|
|||
json={
|
||||
"slot_start": f"{target_date}T09:00:00Z",
|
||||
"direction": "buy",
|
||||
"bitcoin_transfer_method": "onchain",
|
||||
"eur_amount": 10000,
|
||||
},
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue