197 lines
6.8 KiB
Python
197 lines
6.8 KiB
Python
import pytest
|
|
import respx
|
|
from httpx import Response
|
|
|
|
from traderai.tools import ToolRegistry
|
|
from traderai.uex_client import UEXClient
|
|
|
|
|
|
class FakeUEX:
|
|
async def get(self, path, params=None, authenticated=False):
|
|
if path == "commodities_prices":
|
|
return {
|
|
"status": "ok",
|
|
"data": [
|
|
{
|
|
"id": 10,
|
|
"commodity_name": "Gold",
|
|
"terminal_name": "Port Tressler",
|
|
"price_buy": 4120,
|
|
"price_sell": 5020,
|
|
"scu_buy": 1200,
|
|
"verbose_note": "x" * 300,
|
|
},
|
|
{
|
|
"id": 11,
|
|
"commodity_name": "Beryl",
|
|
"terminal_name": "Area18",
|
|
"price_buy": 2500,
|
|
"price_sell": 3100,
|
|
},
|
|
],
|
|
}
|
|
assert path == "marketplace_listings"
|
|
return {
|
|
"data": [
|
|
{
|
|
"id": 1,
|
|
"slug": "gold-haul",
|
|
"title": "Gold haul escort",
|
|
"description": "Escort service",
|
|
"operation": "sell",
|
|
"type": "service",
|
|
"price": 5000,
|
|
"currency": "UEC",
|
|
"unit": "run",
|
|
"location": "Port Tressler",
|
|
"user_username": "pilot_a",
|
|
"date_expiration": 123,
|
|
},
|
|
{
|
|
"id": 2,
|
|
"slug": "armor-set",
|
|
"title": "Armor set",
|
|
"description": "Clean set",
|
|
"operation": "sell",
|
|
"type": "item",
|
|
"price": 15000,
|
|
"currency": "UEC",
|
|
"unit": "set",
|
|
"location": "Area18",
|
|
"user_username": "pilot_b",
|
|
"date_expiration": 456,
|
|
},
|
|
]
|
|
}
|
|
|
|
async def delete(self, path, params=None, authenticated=True):
|
|
return {"status": "ok", "deleted": {"path": path, "params": params, "authenticated": authenticated}}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_search_marketplace_listings_filters_locally():
|
|
registry = ToolRegistry(FakeUEX())
|
|
result = await registry.search_marketplace_listings(query="gold", type="service", max_price=6000)
|
|
assert result["count"] == 1
|
|
assert result["listings"][0]["slug"] == "gold-haul"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_draft_message_creates_pending_action():
|
|
registry = ToolRegistry(FakeUEX())
|
|
result = await registry.draft_negotiation_message(hash="abc", message="Would you take 4500 UEC?")
|
|
pending = result["pending_action"]
|
|
assert pending["endpoint"] == "marketplace_negotiations_messages"
|
|
assert pending["payload"]["message"] == "Would you take 4500 UEC?"
|
|
assert pending["id"] in registry.pending_actions
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_decline_pending_action_removes_without_sending():
|
|
registry = ToolRegistry(FakeUEX())
|
|
result = await registry.draft_negotiation_message(hash="abc", message="Would you take 4500 UEC?")
|
|
action_id = result["pending_action"]["id"]
|
|
|
|
declined = await registry.decline(action_id)
|
|
|
|
assert declined["declined"] is True
|
|
assert declined["pending_action"]["id"] == action_id
|
|
assert action_id not in registry.pending_actions
|
|
|
|
|
|
def test_uex_client_uses_bearer_and_secret_headers():
|
|
client = UEXClient("https://api.uexcorp.space/2.0", secret_key="secret", bearer_token="bearer")
|
|
|
|
headers = client._headers(authenticated=True)
|
|
|
|
assert headers["secret-key"] == "secret"
|
|
assert headers["Authorization"] == "Bearer bearer"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_uex_get_projects_and_limits_results():
|
|
registry = ToolRegistry(FakeUEX())
|
|
|
|
result = await registry.execute(
|
|
"get_uex_commodities_prices",
|
|
{
|
|
"commodity_name": "Gold",
|
|
"ignored": "drop",
|
|
"fields": ["id", "commodity_name", "price_buy"],
|
|
"limit": 1,
|
|
},
|
|
)
|
|
|
|
assert result["resource"] == "commodities_prices"
|
|
assert result["params"] == {"commodity_name": "Gold"}
|
|
assert result["returned"] == 1
|
|
assert result["truncated"] is True
|
|
assert result["items"] == [{"id": 10, "commodity_name": "Gold", "price_buy": 4120}]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_uex_api_catalog_exposes_resources_without_live_call():
|
|
registry = ToolRegistry(FakeUEX())
|
|
|
|
result = await registry.uex_api_catalog(group="vehicles")
|
|
|
|
resources = [item["resource"] for item in result["get"]["vehicles"]]
|
|
assert "vehicles" in resources
|
|
assert "vehicles_prices" in resources
|
|
assert "wallet_add" in result["post"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_draft_delete_approves_with_delete_method():
|
|
registry = ToolRegistry(FakeUEX())
|
|
result = await registry.execute("delete_uex_marketplace_listings", {"id": 123, "label": "Remove listing"})
|
|
action_id = result["pending_action"]["id"]
|
|
|
|
approved = await registry.approve(action_id)
|
|
|
|
assert result["pending_action"]["method"] == "DELETE"
|
|
assert approved["deleted"] == {
|
|
"path": "marketplace_listings",
|
|
"params": {"id": 123},
|
|
"authenticated": True,
|
|
}
|
|
|
|
|
|
def test_schemas_expose_specific_uex_tools_instead_of_generic_api_tool():
|
|
registry = ToolRegistry(FakeUEX())
|
|
|
|
names = {schema["function"]["name"] for schema in registry.schemas}
|
|
|
|
assert "get_uex_commodities_prices" in names
|
|
assert "get_uex_vehicles" in names
|
|
assert "draft_uex_marketplace_advertise" in names
|
|
assert "delete_uex_marketplace_listings" in names
|
|
assert "uex_get" not in names
|
|
assert "uex_draft_post" not in names
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_uex_client_get_user_normalizes_user_payload():
|
|
respx.get("https://api.uexcorp.space/2.0/user/").mock(
|
|
return_value=Response(200, json={"status": "ok", "data": [{"username": "pilot_hudson"}]})
|
|
)
|
|
client = UEXClient("https://api.uexcorp.space/2.0", bearer_token="bearer")
|
|
|
|
result = await client.get_user(authenticated=True)
|
|
|
|
assert result == {"status": "ok", "user": {"username": "pilot_hudson"}}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_uex_client_get_user_notifications_normalizes_payload():
|
|
respx.get("https://api.uexcorp.space/2.0/user_notifications/").mock(
|
|
return_value=Response(200, json={"status": "ok", "data": {"id": 7, "message": "Reply waiting", "date_read": 0}})
|
|
)
|
|
client = UEXClient("https://api.uexcorp.space/2.0", bearer_token="bearer")
|
|
|
|
result = await client.get_user_notifications()
|
|
|
|
assert result == {"status": "ok", "notifications": [{"id": 7, "message": "Reply waiting", "date_read": 0}]}
|