This commit is contained in:
@@ -217,6 +217,90 @@ def test_ollama_options_include_num_ctx():
|
||||
assert agent._ollama_options() == {"num_ctx": 64000}
|
||||
|
||||
|
||||
def test_codex_prompt_mentions_tools_and_images(tmp_path):
|
||||
memory = MemoryStore(str(tmp_path / "memory.sqlite3"))
|
||||
agent = OllamaAgent("codex", "gpt-5.3-codex", EmptyTools(), memory=memory, provider="codex")
|
||||
|
||||
prompt = agent._codex_cli_prompt(
|
||||
"check listing",
|
||||
[
|
||||
{"role": "system", "content": SYSTEM_PROMPT},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Look at this",
|
||||
"images": ["ZmFrZQ=="],
|
||||
"image_content_types": ["image/png"],
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_123",
|
||||
"type": "function",
|
||||
"function": {"name": "search_marketplace_listings", "arguments": "{\"commodity\":\"gold\"}"},
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"tool_name": "search_marketplace_listings",
|
||||
"tool_call_id": "call_123",
|
||||
"content": "{\"ok\":true}",
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
assert "Available tools" in prompt
|
||||
assert "attached images: 1" in prompt
|
||||
assert "search_marketplace_listings" in prompt
|
||||
assert "tool search_marketplace_listings" in prompt
|
||||
|
||||
|
||||
def test_codex_structured_response_extracts_text_and_tool_calls():
|
||||
agent = OllamaAgent("codex", "gpt-5.3-codex", EmptyTools(), provider="codex")
|
||||
|
||||
result = agent._codex_structured_response(
|
||||
{
|
||||
"kind": "tool_call",
|
||||
"message": "",
|
||||
"tool_name": "search_marketplace_listings",
|
||||
"arguments_json": "{\"commodity\":\"gold\"}",
|
||||
}
|
||||
)
|
||||
|
||||
assert result["message"]["content"] == ""
|
||||
assert result["message"]["tool_calls"] == [
|
||||
{
|
||||
"id": result["message"]["tool_calls"][0]["id"],
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "search_marketplace_listings",
|
||||
"arguments": "{\"commodity\":\"gold\"}",
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_parse_codex_exec_output_reads_final_json():
|
||||
agent = OllamaAgent("codex", "gpt-5.3-codex", EmptyTools(), provider="codex")
|
||||
|
||||
result = agent._parse_codex_exec_output(
|
||||
{
|
||||
"returncode": 0,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"events": [
|
||||
{"type": "thread.started", "thread_id": "abc"},
|
||||
{"type": "item.completed", "item": {"type": "agent_message", "text": "{\"kind\":\"final\",\"message\":\"hello\",\"tool_name\":\"\",\"arguments_json\":\"{}\"}"}},
|
||||
{"type": "turn.completed"},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
assert result == {"kind": "final", "message": "hello", "tool_name": "", "arguments_json": "{}"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_wake_response_executes_tool_calls(tmp_path):
|
||||
memory = MemoryStore(str(tmp_path / "memory.sqlite3"))
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
from traderai.config import Settings
|
||||
|
||||
|
||||
def test_model_provider_accepts_codex():
|
||||
settings = Settings(model_provider="codex")
|
||||
|
||||
assert settings.model_provider == "codex"
|
||||
|
||||
|
||||
def test_model_provider_invalid_value_falls_back_to_ollama():
|
||||
settings = Settings(model_provider="something-else")
|
||||
|
||||
assert settings.model_provider == "ollama"
|
||||
|
||||
|
||||
def test_reasoning_effort_normalizes_invalid_values():
|
||||
settings = Settings(model_reasoning_effort="whatever")
|
||||
|
||||
assert settings.model_reasoning_effort == "medium"
|
||||
|
||||
|
||||
def test_reasoning_effort_accepts_supported_values():
|
||||
settings = Settings(model_reasoning_effort="high")
|
||||
|
||||
assert settings.model_reasoning_effort == "high"
|
||||
@@ -104,6 +104,26 @@ def test_continual_plan_store_creates_buying_checklist(tmp_path):
|
||||
assert plan["items"][0]["desired_quantity"] == 2
|
||||
|
||||
|
||||
def test_continual_plan_store_deletes_plan_and_related_records(tmp_path):
|
||||
_, store, _, _, _ = plan_stack(tmp_path)
|
||||
|
||||
plan = store.create_plan(
|
||||
"Delete me",
|
||||
objective="Remove everything",
|
||||
items=[{"item_name": "Wikelo Idris panel", "desired_quantity": 1}],
|
||||
)
|
||||
item_id = int(plan["items"][0]["id"])
|
||||
candidate = store.upsert_candidate(plan["id"], item_id, {"id": "listing-1", "title": "Panel", "price": 10}, 0.9)
|
||||
store.add_negotiation(plan["id"], item_id, int(candidate["id"]), {"listing_id": "listing-1", "listing_slug": "panel", "id_negotiation": "neg-1", "hash": "hash-1"})
|
||||
|
||||
assert store.delete_plan(plan["id"]) is True
|
||||
assert store.get_plan(plan["id"]) is None
|
||||
assert store.list_items(plan["id"]) == []
|
||||
assert store.list_candidates(plan["id"]) == []
|
||||
assert store.list_negotiations(plan["id"]) == []
|
||||
assert store.list_events(plan["id"]) == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_buying_runner_tracks_candidates_and_drafts_only(tmp_path):
|
||||
memory, store, tools, runner, _ = plan_stack(tmp_path)
|
||||
@@ -215,3 +235,19 @@ async def test_scheduler_schedules_overdue_plan_catchup_on_start(tmp_path):
|
||||
|
||||
assert catchup is not None
|
||||
assert any(event["kind"] == "catchup_scheduled" for event in snapshot["events"])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tools_delete_continual_plan_removes_it(tmp_path):
|
||||
_, store, tools, _, _ = plan_stack(tmp_path)
|
||||
plan = store.create_plan(
|
||||
"Delete through tools",
|
||||
objective="Remove via registry",
|
||||
items=[{"item_name": "Wikelo Idris panel"}],
|
||||
)
|
||||
|
||||
result = await tools.delete_continual_plan(plan["id"])
|
||||
|
||||
assert result["deleted"] is True
|
||||
assert result["plan_id"] == plan["id"]
|
||||
assert store.get_plan(plan["id"]) is None
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import traderai.server as server
|
||||
|
||||
|
||||
def test_config_update_rebuilds_runtime_without_restart(monkeypatch, tmp_path):
|
||||
state = {"settings": make_settings(tmp_path, model_provider="ollama", ollama_model="qwen3.5:9b")}
|
||||
|
||||
class FakeScheduler:
|
||||
def __init__(self, memory):
|
||||
self.memory = memory
|
||||
|
||||
def bind_agent(self, agent):
|
||||
self.agent = agent
|
||||
|
||||
def bind_plan_runner(self, plan_runner):
|
||||
self.plan_runner = plan_runner
|
||||
|
||||
def bind_uex_notifications(self, uex, poll_seconds=60):
|
||||
self.uex = uex
|
||||
self.poll_seconds = poll_seconds
|
||||
|
||||
def start(self):
|
||||
return None
|
||||
|
||||
def shutdown(self):
|
||||
return None
|
||||
|
||||
def list_jobs(self):
|
||||
return []
|
||||
|
||||
class FakeUEXClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
async def get_user(self, username=None, authenticated=False):
|
||||
return {}
|
||||
|
||||
class FakeToolRegistry:
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.pending_actions = {}
|
||||
self.plan_runner = None
|
||||
|
||||
async def approve(self, action_id):
|
||||
return {"approved": action_id}
|
||||
|
||||
async def decline(self, action_id):
|
||||
return {"declined": action_id}
|
||||
|
||||
class FakePlanRunner:
|
||||
def __init__(self, store, tools, memory, agent=None):
|
||||
self.store = store
|
||||
self.tools = tools
|
||||
self.memory = memory
|
||||
self.agent = agent
|
||||
|
||||
def bind_agent(self, agent):
|
||||
self.agent = agent
|
||||
|
||||
class FakeClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
async def fake_health(self):
|
||||
return {
|
||||
"online": True,
|
||||
"provider": self.provider,
|
||||
"model": self.model,
|
||||
"model_available": True,
|
||||
"message": f"{self.provider} ready",
|
||||
}
|
||||
|
||||
async def fake_chat(self, content, thread_id=None, images=None):
|
||||
return {"message": f"{self.provider}:{self.model}", "pending_actions": [], "thread_id": thread_id}
|
||||
|
||||
def fake_get_settings():
|
||||
return state["settings"]
|
||||
|
||||
def fake_save_settings(values):
|
||||
state["settings"] = make_settings(
|
||||
tmp_path,
|
||||
model_provider=values.get("model_provider", state["settings"].model_provider),
|
||||
ollama_model=values.get("ollama_model", state["settings"].ollama_model),
|
||||
codex_model=values.get("codex_model", state["settings"].codex_model),
|
||||
)
|
||||
return {"values": values, "fields": {}, "secrets_configured": {}, "app_data_dir": str(tmp_path)}
|
||||
|
||||
monkeypatch.setattr(server, "WakeScheduler", FakeScheduler)
|
||||
monkeypatch.setattr(server, "UEXClient", FakeUEXClient)
|
||||
monkeypatch.setattr(server, "ToolRegistry", FakeToolRegistry)
|
||||
monkeypatch.setattr(server, "ContinualPlanRunner", FakePlanRunner)
|
||||
monkeypatch.setattr(server, "SCMDBClient", FakeClient)
|
||||
monkeypatch.setattr(server, "CornerstoneClient", FakeClient)
|
||||
monkeypatch.setattr(server, "StarCitizenWikiClient", FakeClient)
|
||||
monkeypatch.setattr(server, "get_settings", fake_get_settings)
|
||||
monkeypatch.setattr(server, "save_settings", fake_save_settings)
|
||||
monkeypatch.setattr(
|
||||
server,
|
||||
"settings_payload",
|
||||
lambda settings=None: {"app_data_dir": str(tmp_path), "values": {}, "fields": {}, "secrets_configured": {}},
|
||||
)
|
||||
monkeypatch.setattr(server.OllamaAgent, "health", fake_health)
|
||||
monkeypatch.setattr(server.OllamaAgent, "chat", fake_chat)
|
||||
|
||||
app = server.create_app()
|
||||
with TestClient(app) as client:
|
||||
before = client.get("/api/health").json()
|
||||
assert before["model_provider"] == "ollama"
|
||||
assert before["inference"]["provider"] == "ollama"
|
||||
|
||||
updated = client.post(
|
||||
"/api/config",
|
||||
json={"values": {"model_provider": "codex", "codex_model": "gpt-5.4"}},
|
||||
).json()
|
||||
assert updated["restart_required"] is False
|
||||
|
||||
after = client.get("/api/health").json()
|
||||
assert after["model_provider"] == "codex"
|
||||
assert after["inference"]["provider"] == "codex"
|
||||
|
||||
chat = client.post("/api/chat", json={"message": "hi", "thread_id": "thread-1", "images": []}).json()
|
||||
assert chat["message"] == "codex:gpt-5.4"
|
||||
|
||||
|
||||
def make_settings(tmp_path, model_provider="ollama", ollama_model="qwen3.5:9b", codex_model="gpt-5.4"):
|
||||
return SimpleNamespace(
|
||||
traderai_memory_path=str(tmp_path / "memory.sqlite3"),
|
||||
model_provider=model_provider,
|
||||
ollama_base_url="http://localhost:11434",
|
||||
ollama_model=ollama_model,
|
||||
ollama_num_ctx=64512,
|
||||
openai_base_url="https://api.openai.com/v1",
|
||||
openai_api_key=None,
|
||||
openai_model="gpt-5.4-mini",
|
||||
model_reasoning_effort="medium",
|
||||
codex_command="codex",
|
||||
codex_model=codex_model,
|
||||
uex_base_url="https://api.uexcorp.space/2.0",
|
||||
uex_secret_key=None,
|
||||
uex_bearer_token=None,
|
||||
traderai_user_name=None,
|
||||
uex_notification_poll_seconds=60,
|
||||
require_write_approval=True,
|
||||
scmdb_base_url="https://scmdb.net",
|
||||
cornerstone_base_url="https://finder.cstone.space",
|
||||
scwiki_base_url="https://starcitizen.tools",
|
||||
scwiki_api_base_url="https://api.star-citizen.wiki",
|
||||
)
|
||||
@@ -10,8 +10,10 @@ from traderai.uex_client import UEXClient
|
||||
class FakeUEX:
|
||||
def __init__(self):
|
||||
self.posts = []
|
||||
self.get_calls = []
|
||||
|
||||
async def get(self, path, params=None, authenticated=False):
|
||||
self.get_calls.append({"path": path, "params": params, "authenticated": authenticated})
|
||||
if path == "commodities_prices_history":
|
||||
return {
|
||||
"status": "ok",
|
||||
@@ -80,6 +82,34 @@ class FakeUEX:
|
||||
},
|
||||
],
|
||||
}
|
||||
if path == "marketplace_trends":
|
||||
return {
|
||||
"status": "ok",
|
||||
"data": [
|
||||
{
|
||||
"id_item": 2791,
|
||||
"item_name": "\"Quantanium\" Water Bottle",
|
||||
"item_slug": "quantanium-water-bottle",
|
||||
"currency": "UEC",
|
||||
"price_avg_sell": "937500",
|
||||
"price_avg_month_sell": "1072222",
|
||||
"price_min_sell": "750000",
|
||||
"price_max_sell": "1200000",
|
||||
"listings_count_sell": 4,
|
||||
"price_avg_buy": "500000",
|
||||
"price_avg_month_buy": "525000",
|
||||
"price_min_buy": "450000",
|
||||
"price_max_buy": "550000",
|
||||
"listings_count_buy": 2,
|
||||
"total_listings_count": 6,
|
||||
"negotiations_count": 18,
|
||||
"negotiations_open": 7,
|
||||
"negotiations_success": 9,
|
||||
"link_prices": "https://uexcorp.space/marketplace/home/?id_item=2791&mode=list",
|
||||
"link_prices_history": "https://uexcorp.space/marketplace/averages/?id_item=2791&quality_tier=q0&unit=unit",
|
||||
}
|
||||
],
|
||||
}
|
||||
assert path == "marketplace_listings"
|
||||
return {
|
||||
"data": [
|
||||
@@ -259,6 +289,85 @@ class FakeCornerstone:
|
||||
}
|
||||
|
||||
|
||||
class FakeSCWiki:
|
||||
base_url = "https://starcitizen.tools"
|
||||
api_base_url = "https://api.star-citizen.wiki"
|
||||
|
||||
async def search_pages(self, query, limit=5):
|
||||
assert query == "Carrack"
|
||||
return [
|
||||
{
|
||||
"pageid": 415,
|
||||
"title": "Carrack",
|
||||
"description": "Deep-space multi-crew explorer manufactured by Anvil Aerospace",
|
||||
"extract": "The Anvil Carrack is a multi-crew explorer.",
|
||||
"thumbnail": "https://media.starcitizen.tools/carrack.webp",
|
||||
"url": "https://starcitizen.tools/Carrack",
|
||||
}
|
||||
][:limit]
|
||||
|
||||
async def get_page_summary(self, title=None, pageid=None, chars=700):
|
||||
assert title == "Carrack" or pageid == 415
|
||||
return {
|
||||
"pageid": 415,
|
||||
"title": "Carrack",
|
||||
"description": "Deep-space multi-crew explorer manufactured by Anvil Aerospace",
|
||||
"extract": "The Anvil Carrack is a multi-crew explorer.",
|
||||
"thumbnail": "https://media.starcitizen.tools/carrack.webp",
|
||||
"url": "https://starcitizen.tools/Carrack",
|
||||
}
|
||||
|
||||
async def search_verse(self, query):
|
||||
assert query == "Carrack"
|
||||
return [
|
||||
{
|
||||
"type": "vehicles",
|
||||
"label": "Vehicles",
|
||||
"results": [
|
||||
{
|
||||
"name": "Anvil Carrack",
|
||||
"class_name": "ANVL_Carrack",
|
||||
"extra_label": "Exploration",
|
||||
"web_url": "https://api.star-citizen.wiki/vehicles/anvl-carrack",
|
||||
"api_url": "https://api.star-citizen.wiki/api/vehicles/anvl-carrack",
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
async def get_vehicle(self, slug):
|
||||
assert slug == "anvl-carrack"
|
||||
return {
|
||||
"name": "Carrack",
|
||||
"game_name": "Anvil Carrack",
|
||||
"slug": "anvl-carrack",
|
||||
"manufacturer": {"name": "Anvil Aerospace"},
|
||||
"career": "Exploration",
|
||||
"role": "Expedition",
|
||||
"size_class": 5,
|
||||
"cargo_capacity": 456,
|
||||
"crew": {"min": 6, "max": 6},
|
||||
"msrp": 600,
|
||||
"pledge_url": "https://robertsspaceindustries.com/pledge/ships/carrack/Carrack",
|
||||
"uex_prices": {
|
||||
"purchase": [
|
||||
{
|
||||
"price_buy": 34398000,
|
||||
"terminal_name": "Astro Armada - Area 18",
|
||||
"starmap_location": {"name": "Area18", "parent_name": "ArcCorp", "star_system_name": "Stanton"},
|
||||
"game_version": "4.8.1-LIVE.11952564",
|
||||
"date_updated": "2026-05-20T18:39:37-04:00",
|
||||
"uex_link": "https://uexcorp.space/vehicles/home/list/in_game_sell/?id_terminal=148",
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": {"en_EN": "The Anvil Carrack features reinforced fuel tanks for long-duration flight."},
|
||||
"web_url": "https://api.star-citizen.wiki/vehicles/anvl-carrack",
|
||||
"updated_at": "2026-06-08T00:34:00Z",
|
||||
"version": "4.8.1-LIVE.11952564",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_marketplace_listings_filters_locally():
|
||||
registry = ToolRegistry(FakeUEX())
|
||||
@@ -333,6 +442,65 @@ async def test_uex_get_projects_and_limits_results():
|
||||
assert result["items"] == [{"id": 10, "commodity_name": "Gold", "price_buy": 4120}]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uex_get_marketplace_listings_accepts_item_and_operation_filters():
|
||||
fake = FakeUEX()
|
||||
registry = ToolRegistry(fake)
|
||||
|
||||
result = await registry.execute(
|
||||
"get_uex_marketplace_listings",
|
||||
{
|
||||
"id_item": 2791,
|
||||
"operation": "sell",
|
||||
"fields": ["id", "slug", "operation"],
|
||||
},
|
||||
)
|
||||
|
||||
assert result["params"] == {"id_item": 2791, "operation": "sell"}
|
||||
assert fake.get_calls[-1]["path"] == "marketplace_listings"
|
||||
assert fake.get_calls[-1]["params"] == {"id_item": 2791, "operation": "sell"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_marketplace_trends_returns_compact_wts_wtb_and_negotiation_metrics():
|
||||
fake = FakeUEX()
|
||||
registry = ToolRegistry(fake)
|
||||
|
||||
result = await registry.get_marketplace_trends(item_name="Quantanium", currency="UEC", quality_tier=0)
|
||||
|
||||
assert result["status"] == "ok"
|
||||
assert result["count"] == 1
|
||||
assert result["filters"] == {"item_name": "Quantanium", "currency": "UEC", "quality_tier": 0}
|
||||
assert fake.get_calls[-1]["path"] == "marketplace_trends"
|
||||
assert fake.get_calls[-1]["params"] == {"id_item": None, "item_name": "Quantanium", "item_slug": None, "id_category": None, "currency": "UEC", "quality_tier": 0}
|
||||
assert result["trends"][0] == {
|
||||
"id_item": 2791,
|
||||
"item_name": "\"Quantanium\" Water Bottle",
|
||||
"item_slug": "quantanium-water-bottle",
|
||||
"currency": "UEC",
|
||||
"sell": {
|
||||
"avg_price": "937500",
|
||||
"avg_price_month": "1072222",
|
||||
"min_price": "750000",
|
||||
"max_price": "1200000",
|
||||
"listings_count": 4,
|
||||
},
|
||||
"buy": {
|
||||
"avg_price": "500000",
|
||||
"avg_price_month": "525000",
|
||||
"min_price": "450000",
|
||||
"max_price": "550000",
|
||||
"listings_count": 2,
|
||||
},
|
||||
"total_listings_count": 6,
|
||||
"negotiations_count": 18,
|
||||
"negotiations_open": 7,
|
||||
"negotiations_success": 9,
|
||||
"link_prices": "https://uexcorp.space/marketplace/home/?id_item=2791&mode=list",
|
||||
"link_prices_history": "https://uexcorp.space/marketplace/averages/?id_item=2791&quality_tier=q0&unit=unit",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uex_api_catalog_exposes_resources_without_live_call():
|
||||
registry = ToolRegistry(FakeUEX())
|
||||
@@ -368,6 +536,7 @@ def test_schemas_expose_specific_uex_tools_instead_of_generic_api_tool():
|
||||
|
||||
assert "get_uex_commodities_prices" in names
|
||||
assert "get_uex_vehicles" in names
|
||||
assert "get_marketplace_trends" in names
|
||||
assert "draft_uex_marketplace_advertise" in names
|
||||
assert "delete_uex_marketplace_listings" in names
|
||||
assert "uex_get" not in names
|
||||
@@ -395,6 +564,17 @@ def test_schemas_expose_cornerstone_item_tools():
|
||||
assert "draft_marketplace_listing_with_cornerstone_image" in names
|
||||
|
||||
|
||||
def test_schemas_expose_scwiki_tools():
|
||||
registry = ToolRegistry(FakeUEX(), scwiki=FakeSCWiki())
|
||||
|
||||
names = {schema["function"]["name"] for schema in registry.schemas}
|
||||
|
||||
assert "search_scwiki_pages" in names
|
||||
assert "get_scwiki_page" in names
|
||||
assert "search_scwiki_vehicles" in names
|
||||
assert "get_scwiki_vehicle" in names
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_scmdb_missions_returns_reward_summary():
|
||||
registry = ToolRegistry(FakeUEX(), scmdb=FakeSCMDB())
|
||||
@@ -469,6 +649,43 @@ async def test_get_cornerstone_item_media_returns_absolute_image_urls():
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_scwiki_pages_returns_general_knowledge_matches():
|
||||
registry = ToolRegistry(FakeUEX(), scwiki=FakeSCWiki())
|
||||
|
||||
result = await registry.search_scwiki_pages(query="Carrack")
|
||||
|
||||
assert result["source"] == "https://starcitizen.tools"
|
||||
assert result["matched"] == 1
|
||||
assert result["pages"][0]["title"] == "Carrack"
|
||||
assert result["pages"][0]["url"] == "https://starcitizen.tools/Carrack"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_scwiki_vehicle_returns_ship_prices_and_store_context():
|
||||
registry = ToolRegistry(FakeUEX(), scwiki=FakeSCWiki())
|
||||
|
||||
result = await registry.get_scwiki_vehicle(query="Carrack")
|
||||
|
||||
assert result["source"] == "https://api.star-citizen.wiki"
|
||||
vehicle = result["vehicle"]
|
||||
assert vehicle["name"] == "Carrack"
|
||||
assert vehicle["manufacturer"] == "Anvil Aerospace"
|
||||
assert vehicle["msrp"] == 600
|
||||
assert vehicle["purchase_locations"] == [
|
||||
{
|
||||
"price_buy": 34398000,
|
||||
"terminal_name": "Astro Armada - Area 18",
|
||||
"location": "Area18",
|
||||
"parent_location": "ArcCorp",
|
||||
"star_system": "Stanton",
|
||||
"game_version": "4.8.1-LIVE.11952564",
|
||||
"date_updated": "2026-05-20T18:39:37-04:00",
|
||||
"uex_link": "https://uexcorp.space/vehicles/home/list/in_game_sell/?id_terminal=148",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_draft_marketplace_listing_with_cornerstone_image_adds_image_data_and_redacts_display():
|
||||
registry = ToolRegistry(FakeUEX(), cornerstone=FakeCornerstone())
|
||||
|
||||
Reference in New Issue
Block a user