diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e8099634..4af1b811 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,7 +40,7 @@ jobs: UPSTREAM_BASE_URL: "http://test" UPSTREAM_API_KEY: "test" run: | - uv run pytest --verbose --tb=short + uv run pytest -m "not requires_docker" --verbose --tb=short --junit-xml=pytest.xml --cov=routstr --cov-report=term --cov-report=xml - name: Upload test results if: always() @@ -49,5 +49,6 @@ jobs: name: test-results-${{ matrix.python-version }} path: | pytest.xml + coverage.xml .coverage retention-days: 30 diff --git a/migrations/versions/042f6b77d69d_introduce_reserved_balance.py b/migrations/versions/042f6b77d69d_introduce_reserved_balance.py index b9d8e3b0..8f24f0e2 100644 --- a/migrations/versions/042f6b77d69d_introduce_reserved_balance.py +++ b/migrations/versions/042f6b77d69d_introduce_reserved_balance.py @@ -9,10 +9,10 @@ import sqlalchemy as sa from alembic import op # revision identifiers, used by Alembic. -revision = "042f6b77d69d" -down_revision = "898f00ea481e" -branch_labels = None -depends_on = None +revision: str = "042f6b77d69d" +down_revision: str | None = "898f00ea481e" +branch_labels: None = None +depends_on: None = None def upgrade() -> None: diff --git a/migrations/versions/7bc4e8b02b9d_add_mint_field.py b/migrations/versions/7bc4e8b02b9d_add_mint_field.py index fdb05fac..d3dc6a21 100644 --- a/migrations/versions/7bc4e8b02b9d_add_mint_field.py +++ b/migrations/versions/7bc4e8b02b9d_add_mint_field.py @@ -10,10 +10,10 @@ from alembic import op from sqlmodel.sql import sqltypes # revision identifiers, used by Alembic. -revision = "7bc4e8b02b9d" -down_revision = "f6ce1348e266" -branch_labels = None -depends_on = None +revision: str = "7bc4e8b02b9d" +down_revision: str | None = "f6ce1348e266" +branch_labels: None = None +depends_on: None = None def upgrade() -> None: diff --git a/migrations/versions/898f00ea481e_add_mint_currency_refund_details.py b/migrations/versions/898f00ea481e_add_mint_currency_refund_details.py index b9ab2d40..9fcc97b4 100644 --- a/migrations/versions/898f00ea481e_add_mint_currency_refund_details.py +++ b/migrations/versions/898f00ea481e_add_mint_currency_refund_details.py @@ -10,10 +10,10 @@ from alembic import op from sqlmodel.sql import sqltypes # revision identifiers, used by Alembic. -revision = "898f00ea481e" -down_revision = "7bc4e8b02b9d" -branch_labels = None -depends_on = None +revision: str = "898f00ea481e" +down_revision: str | None = "7bc4e8b02b9d" +branch_labels: None = None +depends_on: None = None def upgrade() -> None: diff --git a/migrations/versions/f6ce1348e266_init.py b/migrations/versions/f6ce1348e266_init.py index 58cd8285..6a890eb0 100644 --- a/migrations/versions/f6ce1348e266_init.py +++ b/migrations/versions/f6ce1348e266_init.py @@ -10,10 +10,10 @@ from alembic import op from sqlmodel.sql import sqltypes # revision identifiers, used by Alembic. -revision = "f6ce1348e266" -down_revision = None -branch_labels = None -depends_on = None +revision: str = "f6ce1348e266" +down_revision: str | None = None +branch_labels: None = None +depends_on: None = None def upgrade() -> None: diff --git a/routstr/discovery.py b/routstr/discovery.py index 49ead23d..caa77e85 100644 --- a/routstr/discovery.py +++ b/routstr/discovery.py @@ -9,6 +9,10 @@ import httpx import websockets from fastapi import APIRouter +from .core.logging import get_logger + +logger = get_logger(__name__) + providers_router = APIRouter(prefix="/v1/providers") @@ -44,7 +48,7 @@ async def query_nostr_relay_for_providers( try: async with websockets.connect(relay_url, timeout=timeout) as websocket: - print("Connected to relay, searching for kind 31338 events") + logger.debug("Connected to relay, searching for kind 31338 events") await websocket.send(req_message) while True: @@ -54,27 +58,27 @@ async def query_nostr_relay_for_providers( if data[0] == "EVENT" and data[1] == sub_id: event = data[2] - print(f"Found provider announcement: {event['id']}") + logger.debug(f"Found provider announcement: {event['id']}") events.append(event) elif data[0] == "EOSE" and data[1] == sub_id: - print("Received EOSE message") + logger.debug("Received EOSE message") break elif data[0] == "NOTICE": - print(f"Relay notice: {data[1]}") + logger.warning(f"Relay notice: {data[1]}") except asyncio.TimeoutError: - print("Timeout waiting for message") + logger.debug("Timeout waiting for message") break except json.JSONDecodeError: - print("Failed to decode message as JSON") + logger.warning("Failed to decode message as JSON") continue await websocket.send(json.dumps(["CLOSE", sub_id])) except Exception as e: - print(f"Query failed: {e}") + logger.error(f"Query failed: {e}") - print(f"Query complete. Found {len(events)} provider announcements") + logger.info(f"Query complete. Found {len(events)} provider announcements") return events @@ -103,7 +107,7 @@ def parse_provider_announcement(event: dict[str, Any]) -> dict[str, Any] | None: # Validate required fields if not endpoint_url or not provider_name or not d_tag: - print( + logger.warning( f"Invalid provider announcement - missing required tags: {event['id']}" ) return None @@ -140,7 +144,7 @@ def parse_provider_announcement(event: dict[str, Any]) -> dict[str, Any] | None: } except Exception as e: - print(f"Error parsing provider announcement {event.get('id', 'unknown')}: {e}") + logger.error(f"Error parsing provider announcement {event.get('id', 'unknown')}: {e}") return None @@ -221,7 +225,7 @@ async def get_providers( # Query multiple relays for provider announcements for relay_url in discovery_relays: - print(f"\nQuerying relay for providers: {relay_url}") + logger.info(f"Querying relay for providers: {relay_url}") try: events = await query_nostr_relay_for_providers( relay_url=relay_url, @@ -235,13 +239,13 @@ async def get_providers( event_ids.add(event["id"]) all_events.append(event) - print(f"Got {len(events)} provider announcements from {relay_url}") + logger.info(f"Got {len(events)} provider announcements from {relay_url}") except Exception as e: - print(f"Failed to query {relay_url}: {e}") + logger.error(f"Failed to query {relay_url}: {e}") continue - print(f"Found {len(all_events)} total unique provider announcements") + logger.info(f"Found {len(all_events)} total unique provider announcements") # Parse provider announcements according to RIP-02 providers = [] @@ -250,7 +254,7 @@ async def get_providers( if parsed_provider: providers.append(parsed_provider) - print(f"Parsed {len(providers)} valid provider announcements") + logger.info(f"Parsed {len(providers)} valid provider announcements") # Check provider health if requested healthy_providers: list[dict[str, Any]] = [] diff --git a/routstr/payment/lnurl.py b/routstr/payment/lnurl.py index 587115ef..49bc8d65 100644 --- a/routstr/payment/lnurl.py +++ b/routstr/payment/lnurl.py @@ -278,18 +278,13 @@ async def raw_send_to_lnurl( estimated_fees_msat = estimated_fees_sat * 1000 final_amount = amount_msat - estimated_fees_msat - print(f"Final amount: {final_amount} {unit}") - print(f"Estimated fees: {estimated_fees_msat} msat") - print(f"Amount before fees: {amount_msat} {unit}") bolt11_invoice, _ = await get_lnurl_invoice( lnurl_data["callback_url"], final_amount ) - print(f"Bolt11 invoice: {bolt11_invoice}") melt_quote_resp = await wallet.melt_quote( invoice=bolt11_invoice, amount_msat=final_amount ) - print(melt_quote_resp) _ = await wallet.melt( proofs=proofs, invoice=bolt11_invoice, diff --git a/routstr/payment/models.py b/routstr/payment/models.py index 48dce0f8..5849a489 100644 --- a/routstr/payment/models.py +++ b/routstr/payment/models.py @@ -87,7 +87,7 @@ def fetch_openrouter_models(source_filter: str | None = None) -> list[dict]: return models_data except Exception as e: - print(f"Error fetching models from OpenRouter API: {e}") + logger.error(f"Error fetching models from OpenRouter API: {e}") return [] @@ -110,7 +110,7 @@ def load_models() -> list[Model]: data = json.load(f) return [Model(**model) for model in data.get("models", [])] except Exception as e: - print(f"Error loading models from {models_path}: {e}") + logger.error(f"Error loading models from {models_path}: {e}") # Fall through to auto-generation # Auto-generate models from OpenRouter API @@ -168,7 +168,7 @@ async def update_sats_pricing() -> None: except asyncio.CancelledError: break except Exception as e: - print("Error updating sats pricing: ", e) + logger.error(f"Error updating sats pricing: {e}") try: await asyncio.sleep(10) except asyncio.CancelledError: diff --git a/routstr/wallet.py b/routstr/wallet.py index da500de0..a17f442b 100644 --- a/routstr/wallet.py +++ b/routstr/wallet.py @@ -76,7 +76,6 @@ async def swap_to_primary_mint( ) # Ensure amount is an integer if not isinstance(token_obj.amount, int): - print(f"Token amount is not an integer: {token_obj.amount}") token_amount = int(token_obj.amount) else: token_amount = token_obj.amount