mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-08-09 19:04:47 +00:00
Merge pull request #117 from Routstr/chdir-router-routstr
change dir 'router' -> 'routstr'
This commit is contained in:
+2
-1
@@ -12,6 +12,7 @@ RUN apk add --no-cache \
|
||||
RUN apk add git
|
||||
|
||||
COPY uv.lock pyproject.toml ./
|
||||
COPY routstr ./routstr
|
||||
|
||||
RUN uv add git+https://github.com/saschanaz/secp256k1-py.git#branch=upgrade060
|
||||
# RUN uv sync
|
||||
@@ -25,4 +26,4 @@ ENV PYTHONUNBUFFERED=1
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["/.venv/bin/fastapi", "run", "router", "--host", "0.0.0.0"]
|
||||
CMD ["/.venv/bin/fastapi", "run", "routstr", "--host", "0.0.0.0"]
|
||||
|
||||
@@ -92,7 +92,7 @@ docker-down:
|
||||
lint:
|
||||
@echo "🔍 Running linting checks..."
|
||||
$(RUFF) check .
|
||||
$(MYPY) router/ --ignore-missing-imports
|
||||
$(MYPY) routstr/ --ignore-missing-imports
|
||||
|
||||
format:
|
||||
@echo "✨ Formatting code..."
|
||||
@@ -101,7 +101,7 @@ format:
|
||||
|
||||
type-check:
|
||||
@echo "🔎 Running type checks..."
|
||||
$(MYPY) router/ --ignore-missing-imports
|
||||
$(MYPY) routstr/ --ignore-missing-imports
|
||||
|
||||
# Development setup
|
||||
dev-setup:
|
||||
@@ -209,7 +209,7 @@ db-clean:
|
||||
# Advanced testing options
|
||||
test-coverage:
|
||||
@echo "📊 Running tests with coverage..."
|
||||
$(PYTEST) --cov=router --cov-report=html --cov-report=term
|
||||
$(PYTEST) --cov=routstr --cov-report=html --cov-report=term
|
||||
@echo "Coverage report generated in htmlcov/"
|
||||
|
||||
test-watch:
|
||||
@@ -228,7 +228,7 @@ ci-test:
|
||||
ci-lint:
|
||||
@echo "🤖 Running CI linting..."
|
||||
$(RUFF) check . --exit-non-zero-on-fix
|
||||
$(MYPY) router/ --ignore-missing-imports --no-error-summary
|
||||
$(MYPY) routstr/ --ignore-missing-imports --no-error-summary
|
||||
|
||||
# Debug helpers
|
||||
test-debug:
|
||||
|
||||
@@ -69,7 +69,7 @@ cp .env.example .env
|
||||
### Running Locally
|
||||
|
||||
```bash
|
||||
fastapi run router --host 0.0.0.0 --port 8000
|
||||
fastapi run routstr --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
The service forwards requests to `UPSTREAM_BASE_URL`. Supply the upstream API key via the `UPSTREAM_API_KEY` environment variable if required.
|
||||
|
||||
+2
-7
@@ -1,9 +1,9 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
router:
|
||||
routstr:
|
||||
build: .
|
||||
command: ["/.venv/bin/fastapi", "dev", "router", "--host", "0.0.0.0", "--port", "8000"]
|
||||
command: ["/.venv/bin/fastapi", "dev", "routstr", "--host", "0.0.0.0", "--port", "8000"]
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
@@ -27,9 +27,6 @@ services:
|
||||
- "REFUND_PROCESSING_INTERVAL=3600"
|
||||
- "MINIMUM_PAYOUT=1000"
|
||||
- "PAYOUT_INTERVAL=86400"
|
||||
volumes:
|
||||
- ./:/app
|
||||
- ./logs:/app/logs
|
||||
depends_on:
|
||||
- mock-mint
|
||||
- mock-openai
|
||||
@@ -40,8 +37,6 @@ services:
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8088:8080" # host:container
|
||||
volumes:
|
||||
- ./relay-data:/usr/src/app/db
|
||||
environment:
|
||||
- LISTEN_ADDR=0.0.0.0
|
||||
- LISTEN_PORT=8080
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
router:
|
||||
routstr:
|
||||
build: .
|
||||
volumes:
|
||||
- .:/app
|
||||
@@ -21,9 +21,9 @@ services:
|
||||
- tor-data:/var/lib/tor
|
||||
environment:
|
||||
# Format: HS_<NAME>=<TARGET_HOST>:<TARGET_PORT>:<VIRTUAL_PORT>
|
||||
- HS_ROUTER=router:8000:80
|
||||
- HS_ROUTER=routstr:8000:80
|
||||
depends_on:
|
||||
- router
|
||||
- routstr
|
||||
|
||||
volumes:
|
||||
tor-data:
|
||||
|
||||
+2
-2
@@ -9,10 +9,10 @@ from sqlalchemy.engine import Connection
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
# Add the parent directory to the Python path so we can import router modules
|
||||
# Add the parent directory to the Python path so we can import routstr modules
|
||||
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
|
||||
|
||||
from router.core.db import DATABASE_URL
|
||||
from routstr.core.db import DATABASE_URL
|
||||
|
||||
config = context.config
|
||||
if config.config_file_name is None:
|
||||
|
||||
+8
-4
@@ -58,6 +58,13 @@ markers = [
|
||||
"performance: marks tests that measure performance metrics",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["routstr"]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "I"]
|
||||
ignore = ["E501"]
|
||||
@@ -72,8 +79,5 @@ disallow_incomplete_defs = true
|
||||
disallow_untyped_decorators = true
|
||||
|
||||
[tool.uv.sources]
|
||||
secp256k1 = { git = "https://github.com/saschanaz/secp256k1-py", branch = "upgrade060" }
|
||||
routstr = { workspace = true }
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = ["."]
|
||||
secp256k1 = { git = "https://github.com/saschanaz/secp256k1-py", branch = "upgrade060" }
|
||||
|
||||
@@ -89,7 +89,7 @@ def get_package_version() -> str:
|
||||
return version
|
||||
current_path = current_path.parent
|
||||
|
||||
# Fallback: try the simple path resolution (3 levels up for router/logging/logging_config.py)
|
||||
# Fallback: try the simple path resolution (3 levels up for routstr/logging/logging_config.py)
|
||||
pyproject_path = Path(__file__).parent.parent.parent / "pyproject.toml"
|
||||
if pyproject_path.exists():
|
||||
with open(pyproject_path, "rb") as f:
|
||||
@@ -229,27 +229,22 @@ def setup_logging() -> None:
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"router": {
|
||||
"routstr": {
|
||||
"level": log_level,
|
||||
"handlers": handlers,
|
||||
"propagate": True,
|
||||
},
|
||||
"routstr.payment": {
|
||||
"level": log_level,
|
||||
"handlers": handlers,
|
||||
"propagate": False,
|
||||
},
|
||||
"router.payment": {
|
||||
"routstr.proxy": {
|
||||
"level": log_level,
|
||||
"handlers": handlers,
|
||||
"propagate": False,
|
||||
},
|
||||
"router.cashu": {
|
||||
"level": log_level,
|
||||
"handlers": handlers,
|
||||
"propagate": False,
|
||||
},
|
||||
"router.proxy": {
|
||||
"level": log_level,
|
||||
"handlers": handlers,
|
||||
"propagate": False,
|
||||
},
|
||||
"router.auth": {
|
||||
"routstr.auth": {
|
||||
"level": log_level,
|
||||
"handlers": handlers,
|
||||
"propagate": False,
|
||||
@@ -1,19 +0,0 @@
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
setup(
|
||||
name="routstr",
|
||||
version="0.1.0",
|
||||
packages=find_packages(),
|
||||
install_requires=[
|
||||
"fastapi[standard]>=0.115",
|
||||
"aiosqlite>=0.20",
|
||||
"sqlmodel>=0.0.24",
|
||||
"httpx[socks]>=0.25.2",
|
||||
"greenlet>=3.2.1",
|
||||
"python-json-logger>=2.0.0",
|
||||
"cashu",
|
||||
"secp256k1",
|
||||
"marshmallow>=3.13,<4.0",
|
||||
],
|
||||
python_requires=">=3.11",
|
||||
)
|
||||
@@ -11,7 +11,7 @@ from httpx import AsyncClient
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
|
||||
from sqlmodel import select
|
||||
|
||||
from router.core.logging import get_logger
|
||||
from routstr.core.logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -25,7 +25,7 @@ if use_local_services:
|
||||
"DATABASE_URL": "sqlite+aiosqlite:///:memory:",
|
||||
"UPSTREAM_BASE_URL": "http://localhost:3000", # Mock OpenAI service
|
||||
"UPSTREAM_API_KEY": "test-upstream-key",
|
||||
"CASHU_MINTS": "http://mint:3338", # Docker service name for router validation
|
||||
"CASHU_MINTS": "http://mint:3338", # Docker service name for routstr validation
|
||||
"MINT": "http://mint:3338",
|
||||
"MINT_URL": "http://mint:3338",
|
||||
"NOSTR_RELAY_URL": "ws://localhost:8088",
|
||||
@@ -63,8 +63,8 @@ else:
|
||||
# Set test environment variables before importing the app
|
||||
os.environ.update(test_env)
|
||||
|
||||
from router.core.db import ApiKey, get_session # noqa: E402
|
||||
from router.core.main import app, lifespan # noqa: E402
|
||||
from routstr.core.db import ApiKey, get_session # noqa: E402
|
||||
from routstr.core.main import app, lifespan # noqa: E402
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@@ -157,7 +157,7 @@ class TestmintWallet:
|
||||
mint_response = await wallet.mint(amount=amount, hash=quote)
|
||||
token = mint_response.token
|
||||
|
||||
# Replace connection URL with Docker service name for router validation
|
||||
# Replace connection URL with Docker service name for routstr validation
|
||||
if self.connection_url != self.mint_url:
|
||||
token = token.replace(self.connection_url, self.mint_url)
|
||||
|
||||
@@ -251,7 +251,7 @@ class TestmintWallet:
|
||||
async def send_token(
|
||||
self, amount: int, unit: str, mint_url: Optional[str] = None
|
||||
) -> str:
|
||||
"""Send token with compatible signature for mocking router.wallet.send_token"""
|
||||
"""Send token with compatible signature for mocking routstr.wallet.send_token"""
|
||||
return await self.send(amount)
|
||||
|
||||
async def send_to_lnurl(self, lnurl: str, amount: int) -> int:
|
||||
@@ -501,25 +501,25 @@ async def integration_app(
|
||||
|
||||
if use_real_mint:
|
||||
# Use real mint - no wallet patches needed
|
||||
with patch("router.core.db.engine", integration_engine):
|
||||
with patch("routstr.core.db.engine", integration_engine):
|
||||
yield test_app
|
||||
else:
|
||||
# Use testmint with wallet patches for all integration tests
|
||||
mint_url = os.environ.get("CASHU_MINTS", "http://localhost:3338")
|
||||
with (
|
||||
patch("router.core.db.engine", integration_engine),
|
||||
patch("router.wallet.TRUSTED_MINTS", [mint_url]),
|
||||
patch("router.wallet.PRIMARY_MINT_URL", mint_url),
|
||||
patch("router.auth.credit_balance", testmint_wallet.credit_balance),
|
||||
patch("router.wallet.credit_balance", testmint_wallet.credit_balance),
|
||||
patch("router.balance.credit_balance", testmint_wallet.credit_balance),
|
||||
patch("router.wallet.send_token", testmint_wallet.send_token),
|
||||
patch("router.balance.send_token", testmint_wallet.send_token),
|
||||
patch("router.wallet.recieve_token", testmint_wallet.redeem_token),
|
||||
patch("router.wallet.get_balance", testmint_wallet.get_balance),
|
||||
patch("routstr.core.db.engine", integration_engine),
|
||||
patch("routstr.wallet.TRUSTED_MINTS", [mint_url]),
|
||||
patch("routstr.wallet.PRIMARY_MINT_URL", mint_url),
|
||||
patch("routstr.auth.credit_balance", testmint_wallet.credit_balance),
|
||||
patch("routstr.wallet.credit_balance", testmint_wallet.credit_balance),
|
||||
patch("routstr.balance.credit_balance", testmint_wallet.credit_balance),
|
||||
patch("routstr.wallet.send_token", testmint_wallet.send_token),
|
||||
patch("routstr.balance.send_token", testmint_wallet.send_token),
|
||||
patch("routstr.wallet.recieve_token", testmint_wallet.redeem_token),
|
||||
patch("routstr.wallet.get_balance", testmint_wallet.get_balance),
|
||||
patch("websockets.connect") as mock_websockets,
|
||||
patch("router.payment.price.btc_usd_ask_price", return_value=50000.0),
|
||||
patch("router.payment.price.sats_usd_ask_price", return_value=0.0005),
|
||||
patch("routstr.payment.price.btc_usd_ask_price", return_value=50000.0),
|
||||
patch("routstr.payment.price.sats_usd_ask_price", return_value=0.0005),
|
||||
):
|
||||
# Configure the WebSocket mock for discovery service - fast failure for performance tests
|
||||
async def mock_websocket_connect(*args: Any, **kwargs: Any) -> None:
|
||||
@@ -689,8 +689,8 @@ async def background_tasks_controller() -> AsyncGenerator[Any, None]:
|
||||
original_periodic_payout: Optional[Callable] = None
|
||||
|
||||
try:
|
||||
from router.payment.models import update_sats_pricing
|
||||
from router.wallet import periodic_payout
|
||||
from routstr.payment.models import update_sats_pricing
|
||||
from routstr.wallet import periodic_payout
|
||||
|
||||
async def controlled_update_pricing() -> None:
|
||||
while not controller.cancelled:
|
||||
|
||||
@@ -139,7 +139,7 @@ async def main() -> None:
|
||||
print("WARNING: Proxy server may not be running properly")
|
||||
except Exception:
|
||||
print("ERROR: Proxy server is not running!")
|
||||
print("Please start the server with: uvicorn router.main:app")
|
||||
print("Please start the server with: uvicorn routstr.main:app")
|
||||
sys.exit(1)
|
||||
|
||||
# Run performance tests
|
||||
|
||||
@@ -9,9 +9,9 @@ from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from router.core.db import ApiKey
|
||||
from router.payment.models import MODELS, Model, Pricing, update_sats_pricing
|
||||
from router.wallet import periodic_payout
|
||||
from routstr.core.db import ApiKey
|
||||
from routstr.payment.models import MODELS, Model, Pricing, update_sats_pricing
|
||||
from routstr.wallet import periodic_payout
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -24,7 +24,7 @@ class TestPricingUpdateTask:
|
||||
mock_sats_usd = 0.00002 # 1 sat = $0.00002 (BTC at $50,000)
|
||||
|
||||
with patch(
|
||||
"router.payment.price.sats_usd_ask_price",
|
||||
"routstr.payment.price.sats_usd_ask_price",
|
||||
AsyncMock(return_value=mock_sats_usd),
|
||||
):
|
||||
# Create a test model
|
||||
@@ -114,7 +114,7 @@ class TestPricingUpdateTask:
|
||||
raise Exception("Price API error")
|
||||
return 0.00002
|
||||
|
||||
with patch("router.payment.price.sats_usd_ask_price", mock_price_func):
|
||||
with patch("routstr.payment.price.sats_usd_ask_price", mock_price_func):
|
||||
# Test the retry behavior directly
|
||||
# First call should fail
|
||||
try:
|
||||
@@ -122,7 +122,7 @@ class TestPricingUpdateTask:
|
||||
assert False, "Expected exception on first call"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# Second call should succeed
|
||||
result = await mock_price_func()
|
||||
assert result == 0.00002
|
||||
@@ -165,7 +165,7 @@ class TestPricingUpdateTask:
|
||||
|
||||
try:
|
||||
with patch(
|
||||
"router.payment.price.sats_usd_ask_price",
|
||||
"routstr.payment.price.sats_usd_ask_price",
|
||||
AsyncMock(return_value=0.00002),
|
||||
):
|
||||
# Initialize pricing once to ensure consistent state
|
||||
@@ -214,9 +214,9 @@ class TestRefundCheckTask:
|
||||
# Mock the wallet send_to_lnurl method and get_session
|
||||
with (
|
||||
patch(
|
||||
"router.wallet.send_to_lnurl", AsyncMock(return_value=5)
|
||||
"routstr.wallet.send_to_lnurl", AsyncMock(return_value=5)
|
||||
) as mock_send_to_lnurl,
|
||||
patch("router.core.db.get_session") as mock_get_session,
|
||||
patch("routstr.core.db.get_session") as mock_get_session,
|
||||
):
|
||||
# Make get_session return our integration session
|
||||
async def get_test_session() -> Any:
|
||||
@@ -279,9 +279,9 @@ class TestRefundCheckTask:
|
||||
|
||||
with (
|
||||
patch(
|
||||
"router.wallet.send_to_lnurl", mock_send_to_lnurl
|
||||
"routstr.wallet.send_to_lnurl", mock_send_to_lnurl
|
||||
) as mock_send_to_lnurl_patch,
|
||||
patch("router.core.db.get_session") as mock_get_session,
|
||||
patch("routstr.core.db.get_session") as mock_get_session,
|
||||
):
|
||||
# Make get_session return our integration session
|
||||
async def get_test_session() -> Any:
|
||||
@@ -366,9 +366,9 @@ class TestRefundCheckTask:
|
||||
|
||||
with (
|
||||
patch(
|
||||
"router.wallet.send_to_lnurl", AsyncMock(return_value=1)
|
||||
"routstr.wallet.send_to_lnurl", AsyncMock(return_value=1)
|
||||
) as mock_send_to_lnurl,
|
||||
patch("router.core.db.get_session") as mock_get_session,
|
||||
patch("routstr.core.db.get_session") as mock_get_session,
|
||||
):
|
||||
# Make get_session return our integration session
|
||||
async def get_test_session() -> Any:
|
||||
@@ -424,7 +424,7 @@ class TestRefundCheckTask:
|
||||
# async def test_refund_check_disabled(self) -> None:
|
||||
# """Test that refund check can be disabled by setting interval to 0"""
|
||||
# # Patch the constant directly to disable refunds
|
||||
# with patch.object(router.wallet, "REFUND_PROCESSING_INTERVAL", 0):
|
||||
# with patch.object(routstr.wallet, "REFUND_PROCESSING_INTERVAL", 0):
|
||||
# # Task should exit immediately
|
||||
# task = asyncio.create_task(check_for_refunds())
|
||||
# await task # Should complete without hanging
|
||||
@@ -466,9 +466,9 @@ class TestPeriodicPayoutTask:
|
||||
wallet_balance = 200000 # 200 sats total
|
||||
|
||||
with (
|
||||
patch("router.wallet.get_balance", AsyncMock(return_value=wallet_balance)),
|
||||
patch("routstr.wallet.get_balance", AsyncMock(return_value=wallet_balance)),
|
||||
patch(
|
||||
"router.wallet.send_to_lnurl", AsyncMock(return_value=None)
|
||||
"routstr.wallet.send_to_lnurl", AsyncMock(return_value=None)
|
||||
) as mock_send_to_lnurl,
|
||||
):
|
||||
# Mock environment variables
|
||||
@@ -481,7 +481,7 @@ class TestPeriodicPayoutTask:
|
||||
},
|
||||
):
|
||||
# Call periodic_payout directly (pay_out was renamed/refactored)
|
||||
from router.wallet import periodic_payout
|
||||
from routstr.wallet import periodic_payout
|
||||
|
||||
await periodic_payout()
|
||||
|
||||
@@ -506,7 +506,7 @@ class TestPeriodicPayoutTask:
|
||||
# integration_session.add(key)
|
||||
# await integration_session.commit()
|
||||
|
||||
# with patch("router.cashu.wallet") as mock_wallet:
|
||||
# with patch("routstr.cashu.wallet") as mock_wallet:
|
||||
# mock_wallet_instance = AsyncMock()
|
||||
# mock_wallet_instance.balance = AsyncMock(
|
||||
# return_value=100000
|
||||
@@ -522,7 +522,7 @@ class TestPeriodicPayoutTask:
|
||||
# "DEV_LN_ADDRESS": "dev@test.com",
|
||||
# },
|
||||
# ):
|
||||
# from router.cashu import pay_out
|
||||
# from routstr.cashu import pay_out
|
||||
|
||||
# await pay_out()
|
||||
|
||||
@@ -543,7 +543,7 @@ class TestPeriodicPayoutTask:
|
||||
# integration_session.add(key)
|
||||
# await integration_session.commit()
|
||||
|
||||
# with patch("router.cashu.wallet") as mock_wallet:
|
||||
# with patch("routstr.cashu.wallet") as mock_wallet:
|
||||
# mock_wallet_instance = AsyncMock()
|
||||
# mock_wallet_instance.balance = AsyncMock(
|
||||
# return_value=96000
|
||||
@@ -552,7 +552,7 @@ class TestPeriodicPayoutTask:
|
||||
# mock_wallet.return_value = mock_wallet_instance
|
||||
|
||||
# with patch.dict(os.environ, {"MINIMUM_PAYOUT": "10"}): # 10 sats minimum
|
||||
# from router.cashu import pay_out
|
||||
# from routstr.cashu import pay_out
|
||||
|
||||
# await pay_out()
|
||||
|
||||
@@ -571,9 +571,9 @@ class TestTaskInteractions:
|
||||
# """Test that all tasks can run concurrently without issues"""
|
||||
# # Mock all external dependencies
|
||||
# with (
|
||||
# patch("router.payment.price.sats_usd_ask_price", AsyncMock(return_value=0.00002)),
|
||||
# patch("router.cashu.wallet") as mock_wallet,
|
||||
# patch("router.cashu.pay_out", AsyncMock()),
|
||||
# patch("routstr.payment.price.sats_usd_ask_price", AsyncMock(return_value=0.00002)),
|
||||
# patch("routstr.cashu.wallet") as mock_wallet,
|
||||
# patch("routstr.cashu.pay_out", AsyncMock()),
|
||||
# ):
|
||||
# mock_wallet_instance = AsyncMock()
|
||||
# mock_wallet_instance.send_to_lnurl = AsyncMock(return_value=1)
|
||||
@@ -587,7 +587,7 @@ class TestTaskInteractions:
|
||||
# tasks.append(pricing_task)
|
||||
|
||||
# # Refund task (disabled to avoid interference)
|
||||
# with patch.object(router.wallet, "REFUND_PROCESSING_INTERVAL", 0):
|
||||
# with patch.object(routstr.wallet, "REFUND_PROCESSING_INTERVAL", 0):
|
||||
# refund_task = asyncio.create_task(check_for_refunds())
|
||||
# tasks.append(refund_task)
|
||||
|
||||
@@ -621,7 +621,7 @@ class TestTaskInteractions:
|
||||
processing.set()
|
||||
await asyncio.sleep(2) # Simulate long operation
|
||||
|
||||
with patch("router.payment.price.sats_usd_ask_price", slow_task):
|
||||
with patch("routstr.payment.price.sats_usd_ask_price", slow_task):
|
||||
# Start the pricing task
|
||||
task = asyncio.create_task(update_sats_pricing())
|
||||
|
||||
@@ -708,11 +708,15 @@ class TestTaskInteractions:
|
||||
# Patch the actual task functions
|
||||
with (
|
||||
patch(
|
||||
"router.payment.models.update_sats_pricing",
|
||||
"routstr.payment.models.update_sats_pricing",
|
||||
lambda: task_with_cleanup("pricing"),
|
||||
),
|
||||
patch("router.wallet.periodic_payout", lambda: task_with_cleanup("refund")),
|
||||
patch("router.wallet.periodic_payout", lambda: task_with_cleanup("payout")),
|
||||
patch(
|
||||
"routstr.wallet.periodic_payout", lambda: task_with_cleanup("refund")
|
||||
),
|
||||
patch(
|
||||
"routstr.wallet.periodic_payout", lambda: task_with_cleanup("payout")
|
||||
),
|
||||
):
|
||||
# Start all tasks
|
||||
tasks = [
|
||||
|
||||
@@ -11,7 +11,7 @@ from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlmodel import select
|
||||
|
||||
from router.core.db import ApiKey
|
||||
from routstr.core.db import ApiKey
|
||||
|
||||
|
||||
class TestTransactionAtomicity:
|
||||
@@ -114,7 +114,7 @@ class TestTransactionAtomicity:
|
||||
initial_balance = api_key.balance
|
||||
|
||||
# Mock wallet to fail after token validation
|
||||
with patch("router.wallet.send_token") as mock_wallet_func:
|
||||
with patch("routstr.wallet.send_token") as mock_wallet_func:
|
||||
mock_proof = MagicMock()
|
||||
mock_proof.amount = 1000
|
||||
mock_wallet = AsyncMock()
|
||||
@@ -256,7 +256,7 @@ class TestConcurrentOperations:
|
||||
await integration_session.commit()
|
||||
|
||||
# Mock wallet for topup
|
||||
with patch("router.wallet.send_token") as mock_wallet_func:
|
||||
with patch("routstr.wallet.send_token") as mock_wallet_func:
|
||||
mock_proof = MagicMock()
|
||||
mock_proof.amount = 2000
|
||||
mock_wallet = AsyncMock()
|
||||
@@ -521,7 +521,7 @@ class TestPerformance:
|
||||
operation_times["select"].append((end - start) * 1000) # Convert to ms
|
||||
|
||||
# Test UPDATE performance (via topup)
|
||||
with patch("router.wallet.send_token") as mock_wallet_func:
|
||||
with patch("routstr.wallet.send_token") as mock_wallet_func:
|
||||
mock_proof = MagicMock()
|
||||
mock_proof.amount = 100
|
||||
mock_wallet = AsyncMock()
|
||||
|
||||
@@ -10,7 +10,7 @@ from httpx import AsyncClient, ConnectError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlmodel import select
|
||||
|
||||
from router.core.db import ApiKey
|
||||
from routstr.core.db import ApiKey
|
||||
|
||||
|
||||
class TestNetworkFailureScenarios:
|
||||
@@ -26,11 +26,11 @@ class TestNetworkFailureScenarios:
|
||||
# Patch the wallet send function to simulate failure across all modules
|
||||
with (
|
||||
patch(
|
||||
"router.wallet.send_token",
|
||||
"routstr.wallet.send_token",
|
||||
AsyncMock(side_effect=ConnectError("Mint service unavailable")),
|
||||
),
|
||||
patch(
|
||||
"router.balance.send_token",
|
||||
"routstr.balance.send_token",
|
||||
AsyncMock(side_effect=ConnectError("Mint service unavailable")),
|
||||
),
|
||||
):
|
||||
@@ -46,8 +46,8 @@ class TestNetworkFailureScenarios:
|
||||
integration_session: AsyncSession,
|
||||
) -> None:
|
||||
"""Test proxy behavior when upstream LLM service is down"""
|
||||
# Mock at the router level to simulate upstream being down
|
||||
with patch("router.proxy.httpx.AsyncClient") as mock_client_class:
|
||||
# Mock at the routstr level to simulate upstream being down
|
||||
with patch("routstr.proxy.httpx.AsyncClient") as mock_client_class:
|
||||
# Create a mock client instance
|
||||
mock_client = AsyncMock()
|
||||
mock_client_class.return_value = mock_client
|
||||
|
||||
@@ -149,7 +149,7 @@ class TestPerformanceBaseline:
|
||||
"""Test database operation performance"""
|
||||
from sqlmodel import select
|
||||
|
||||
from router.core.db import ApiKey
|
||||
from routstr.core.db import ApiKey
|
||||
|
||||
# Create test data
|
||||
for i in range(100):
|
||||
|
||||
@@ -43,9 +43,9 @@ async def test_providers_endpoint_default_response(
|
||||
}
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch("router.discovery.fetch_provider_health") as mock_fetch:
|
||||
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
|
||||
# Configure mock to return appropriate responses
|
||||
mock_fetch.side_effect = lambda url: mock_fetch_responses.get(
|
||||
url, {"status_code": 500, "json": {"error": "Unknown provider"}}
|
||||
@@ -100,9 +100,9 @@ async def test_providers_endpoint_with_include_json(
|
||||
}
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch("router.discovery.fetch_provider_health") as mock_fetch:
|
||||
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
|
||||
mock_fetch.return_value = {
|
||||
"status_code": 200,
|
||||
"json": mock_provider_response,
|
||||
@@ -155,7 +155,7 @@ async def test_providers_data_structure_validation(
|
||||
["description", "A comprehensive AI provider"],
|
||||
["model", "gpt-3.5-turbo"],
|
||||
["model", "gpt-4"],
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
@@ -165,15 +165,15 @@ async def test_providers_data_structure_validation(
|
||||
"json": {
|
||||
"data": [
|
||||
{"id": "gpt-3.5-turbo", "object": "model"},
|
||||
{"id": "gpt-4", "object": "model"}
|
||||
{"id": "gpt-4", "object": "model"},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch("router.discovery.fetch_provider_health") as mock_fetch:
|
||||
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
|
||||
mock_fetch.return_value = mock_health_response
|
||||
|
||||
response = await integration_client.get("/v1/providers/?include_json=true")
|
||||
@@ -188,7 +188,7 @@ async def test_providers_data_structure_validation(
|
||||
# Should have provider and health keys based on actual implementation
|
||||
assert "provider" in provider_data
|
||||
assert "health" in provider_data
|
||||
|
||||
|
||||
provider_info = provider_data["provider"]
|
||||
# Expected fields from RIP-02 parser
|
||||
expected_fields = ["id", "name", "endpoint_url", "supported_models"]
|
||||
@@ -215,7 +215,7 @@ async def test_providers_endpoint_no_providers_found(
|
||||
mock_events: list[dict[str, Any]] = []
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
response = await integration_client.get("/v1/providers/")
|
||||
|
||||
@@ -245,33 +245,41 @@ async def test_providers_endpoint_offline_providers(
|
||||
["d", "healthy-provider"],
|
||||
["endpoint", "http://healthy-provider.onion"],
|
||||
["name", "Healthy Provider"],
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "event2",
|
||||
"pubkey": "offline_provider_pubkey",
|
||||
"pubkey": "offline_provider_pubkey",
|
||||
"created_at": 1234567891,
|
||||
"content": "Offline provider announcement",
|
||||
"tags": [
|
||||
["d", "offline-provider"],
|
||||
["endpoint", "http://offline-provider.onion"],
|
||||
["name", "Offline Provider"],
|
||||
]
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
# Mock one healthy and one offline provider
|
||||
def mock_fetch_provider_health(url: str) -> dict[str, Any]:
|
||||
if "healthy" in url:
|
||||
return {"status_code": 200, "endpoint": "root", "json": {"status": "online"}}
|
||||
return {
|
||||
"status_code": 200,
|
||||
"endpoint": "root",
|
||||
"json": {"status": "online"},
|
||||
}
|
||||
else:
|
||||
return {"status_code": 500, "endpoint": "error", "json": {"error": "Service unavailable"}}
|
||||
return {
|
||||
"status_code": 500,
|
||||
"endpoint": "error",
|
||||
"json": {"error": "Service unavailable"},
|
||||
}
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch(
|
||||
"router.discovery.fetch_provider_health",
|
||||
"routstr.discovery.fetch_provider_health",
|
||||
side_effect=mock_fetch_provider_health,
|
||||
):
|
||||
response = await integration_client.get("/v1/providers/?include_json=true")
|
||||
@@ -286,10 +294,10 @@ async def test_providers_endpoint_offline_providers(
|
||||
for provider_data in data["providers"]:
|
||||
assert "provider" in provider_data
|
||||
assert "health" in provider_data
|
||||
|
||||
|
||||
provider_info = provider_data["provider"]
|
||||
health_info = provider_data["health"]
|
||||
|
||||
|
||||
if "offline" in provider_info["endpoint_url"]:
|
||||
# Offline provider should have error information in health
|
||||
assert health_info["status_code"] == 500
|
||||
@@ -297,7 +305,10 @@ async def test_providers_endpoint_offline_providers(
|
||||
else:
|
||||
# Healthy provider should have successful health check
|
||||
assert health_info["status_code"] == 200
|
||||
assert "status" in health_info["json"] or "error" not in health_info["json"]
|
||||
assert (
|
||||
"status" in health_info["json"]
|
||||
or "error" not in health_info["json"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@@ -318,7 +329,7 @@ async def test_providers_endpoint_duplicate_urls(
|
||||
["d", "provider-1"],
|
||||
["endpoint", "http://provider.onion"],
|
||||
["name", "Provider"],
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "event2",
|
||||
@@ -329,15 +340,19 @@ async def test_providers_endpoint_duplicate_urls(
|
||||
["d", "other-provider"],
|
||||
["endpoint", "http://other-provider.onion"],
|
||||
["name", "Other Provider"],
|
||||
]
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch("router.discovery.fetch_provider_health") as mock_fetch:
|
||||
mock_fetch.return_value = {"status_code": 200, "endpoint": "root", "json": {"status": "online"}}
|
||||
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
|
||||
mock_fetch.return_value = {
|
||||
"status_code": 200,
|
||||
"endpoint": "root",
|
||||
"json": {"status": "online"},
|
||||
}
|
||||
|
||||
response = await integration_client.get("/v1/providers/")
|
||||
|
||||
@@ -352,7 +367,7 @@ async def test_providers_endpoint_duplicate_urls(
|
||||
endpoint_urls = []
|
||||
for provider_data in providers:
|
||||
endpoint_urls.append(provider_data["endpoint_url"])
|
||||
|
||||
|
||||
unique_endpoints = set(endpoint_urls)
|
||||
assert len(unique_endpoints) == len(endpoint_urls)
|
||||
|
||||
@@ -369,7 +384,7 @@ async def test_providers_endpoint_nostr_relay_failures(
|
||||
raise Exception("Connection to relay failed")
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", side_effect=failing_query
|
||||
"routstr.discovery.query_nostr_relay_for_providers", side_effect=failing_query
|
||||
):
|
||||
response = await integration_client.get("/v1/providers/")
|
||||
|
||||
@@ -407,9 +422,9 @@ async def test_providers_endpoint_malformed_urls(
|
||||
]
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch("router.discovery.fetch_provider_health") as mock_fetch:
|
||||
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
|
||||
mock_fetch.return_value = {"status_code": 200, "json": {"status": "online"}}
|
||||
|
||||
response = await integration_client.get("/v1/providers/")
|
||||
@@ -440,9 +455,9 @@ async def test_providers_endpoint_response_format(
|
||||
]
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch("router.discovery.fetch_provider_health") as mock_fetch:
|
||||
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
|
||||
mock_fetch.return_value = {"status_code": 200, "json": {"status": "online"}}
|
||||
|
||||
# Test default format
|
||||
@@ -490,9 +505,9 @@ async def test_providers_endpoint_performance(integration_client: AsyncClient) -
|
||||
validator = PerformanceValidator()
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch("router.discovery.fetch_provider_health") as mock_fetch:
|
||||
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
|
||||
mock_fetch.return_value = {"status_code": 200, "json": {"status": "online"}}
|
||||
|
||||
# Test multiple requests
|
||||
@@ -530,9 +545,9 @@ async def test_providers_endpoint_concurrent_requests(
|
||||
]
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch("router.discovery.fetch_provider_health") as mock_fetch:
|
||||
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
|
||||
mock_fetch.return_value = {"status_code": 200, "json": {"status": "online"}}
|
||||
|
||||
# Create concurrent requests
|
||||
@@ -566,9 +581,9 @@ async def test_providers_endpoint_parameter_validation(
|
||||
]
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch("router.discovery.fetch_provider_health") as mock_fetch:
|
||||
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
|
||||
mock_fetch.return_value = {"status_code": 200, "json": {"status": "online"}}
|
||||
|
||||
# Test various parameter values
|
||||
@@ -617,9 +632,9 @@ async def test_no_database_changes_during_provider_operations(
|
||||
]
|
||||
|
||||
with patch(
|
||||
"router.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
|
||||
):
|
||||
with patch("router.discovery.fetch_provider_health") as mock_fetch:
|
||||
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
|
||||
mock_fetch.return_value = {"status_code": 200, "json": {"status": "online"}}
|
||||
|
||||
# Make multiple requests with different parameters
|
||||
|
||||
@@ -14,7 +14,7 @@ import pytest
|
||||
from httpx import AsyncClient
|
||||
from sqlmodel import select
|
||||
|
||||
from router.core.db import ApiKey
|
||||
from routstr.core.db import ApiKey
|
||||
|
||||
from .utils import (
|
||||
ConcurrencyTester,
|
||||
|
||||
@@ -11,7 +11,7 @@ import pytest
|
||||
from httpx import AsyncClient
|
||||
from sqlmodel import select
|
||||
|
||||
from router.core.db import ApiKey
|
||||
from routstr.core.db import ApiKey
|
||||
|
||||
from .utils import (
|
||||
CashuTokenGenerator,
|
||||
|
||||
@@ -11,7 +11,7 @@ import pytest
|
||||
from httpx import AsyncClient
|
||||
from sqlmodel import select, update
|
||||
|
||||
from router.core.db import ApiKey
|
||||
from routstr.core.db import ApiKey
|
||||
|
||||
from .utils import ConcurrencyTester, ResponseValidator
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import pytest
|
||||
from httpx import AsyncClient
|
||||
from sqlmodel import select
|
||||
|
||||
from router.core.db import ApiKey
|
||||
from router.wallet import CurrencyUnit
|
||||
from routstr.core.db import ApiKey
|
||||
from routstr.wallet import CurrencyUnit
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@@ -207,12 +207,12 @@ async def test_refund_with_lightning_address(
|
||||
await db_snapshot.capture()
|
||||
|
||||
# Mock send_to_lnurl function directly
|
||||
with patch("router.balance.send_to_lnurl") as mock_send_to_lnurl:
|
||||
with patch("routstr.balance.send_to_lnurl") as mock_send_to_lnurl:
|
||||
mock_send_to_lnurl.return_value = {
|
||||
"amount_sent": balance,
|
||||
"unit": "msat",
|
||||
"lnurl": refund_address,
|
||||
"status": "completed"
|
||||
"status": "completed",
|
||||
}
|
||||
|
||||
# Request refund
|
||||
@@ -411,7 +411,7 @@ async def test_mint_unavailability_handling(
|
||||
|
||||
# Make the send_token method raise an exception
|
||||
with patch(
|
||||
"router.balance.send_token",
|
||||
"routstr.balance.send_token",
|
||||
side_effect=Exception("Mint unavailable: Connection refused"),
|
||||
):
|
||||
# The exception should propagate as a 503 error (Service Unavailable)
|
||||
@@ -534,7 +534,7 @@ async def test_refund_with_expired_key(
|
||||
integration_client.headers["Authorization"] = f"Bearer {api_key}"
|
||||
|
||||
# Mock the refund to LN address
|
||||
with patch("router.wallet.send_token") as mock_wallet_func:
|
||||
with patch("routstr.wallet.send_token") as mock_wallet_func:
|
||||
mock_wallet = AsyncMock()
|
||||
mock_wallet.send_to_lnurl = AsyncMock(return_value=500) # type: ignore[method-assign]
|
||||
mock_wallet_func.return_value = mock_wallet
|
||||
|
||||
@@ -11,7 +11,7 @@ import pytest
|
||||
from httpx import AsyncClient
|
||||
from sqlmodel import select
|
||||
|
||||
from router.core.db import ApiKey
|
||||
from routstr.core.db import ApiKey
|
||||
|
||||
from .utils import (
|
||||
CashuTokenGenerator,
|
||||
@@ -425,7 +425,7 @@ async def test_network_failure_during_token_verification( # type: ignore[no-unt
|
||||
token = await testmint_wallet.mint_tokens(300)
|
||||
|
||||
# Mock credit_balance to simulate network failure during token verification
|
||||
with patch("router.balance.credit_balance") as mock_credit_balance:
|
||||
with patch("routstr.balance.credit_balance") as mock_credit_balance:
|
||||
mock_credit_balance.side_effect = Exception("Network error: Connection timeout")
|
||||
|
||||
response = await authenticated_client.post(
|
||||
@@ -470,7 +470,11 @@ async def test_topup_with_zero_amount_token( # type: ignore[no-untyped-def]
|
||||
|
||||
# Create a token with 0 amount (edge case)
|
||||
# The testmint wallet should handle this
|
||||
with patch.object(testmint_wallet, "redeem_token", return_value=(0, "sat", testmint_wallet.mint_url)):
|
||||
with patch.object(
|
||||
testmint_wallet,
|
||||
"redeem_token",
|
||||
return_value=(0, "sat", testmint_wallet.mint_url),
|
||||
):
|
||||
token = await testmint_wallet.mint_tokens(0)
|
||||
|
||||
response = await authenticated_client.post(
|
||||
|
||||
@@ -9,7 +9,7 @@ import httpx
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlmodel import select
|
||||
|
||||
from router.core.db import ApiKey
|
||||
from routstr.core.db import ApiKey
|
||||
|
||||
|
||||
class CashuTokenGenerator:
|
||||
|
||||
@@ -43,8 +43,8 @@ def check_imports() -> bool:
|
||||
|
||||
print("Conftest fixtures imported successfully")
|
||||
|
||||
# Check router modules - imports are for verification only
|
||||
from router.core.db import ApiKey
|
||||
# Check routstr modules - imports are for verification only
|
||||
from routstr.core.db import ApiKey
|
||||
|
||||
del ApiKey
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ pytest
|
||||
To run tests with coverage:
|
||||
|
||||
```bash
|
||||
pytest --cov=router --cov-report=html
|
||||
pytest --cov=routstr --cov-report=html
|
||||
```
|
||||
|
||||
To run specific test files:
|
||||
|
||||
@@ -5,7 +5,7 @@ from unittest.mock import Mock, patch
|
||||
os.environ["UPSTREAM_BASE_URL"] = "http://test"
|
||||
os.environ["UPSTREAM_API_KEY"] = "test"
|
||||
|
||||
from router.payment.helpers import get_max_cost_for_model # noqa: E402
|
||||
from routstr.payment.helpers import get_max_cost_for_model # noqa: E402
|
||||
|
||||
|
||||
def test_get_max_cost_for_model_known() -> None:
|
||||
@@ -14,21 +14,21 @@ def test_get_max_cost_for_model_known() -> None:
|
||||
mock_model.sats_pricing = Mock()
|
||||
mock_model.sats_pricing.max_cost = 500
|
||||
|
||||
with patch("router.payment.helpers.MODELS", [mock_model]):
|
||||
with patch("router.payment.helpers.MODEL_BASED_PRICING", True):
|
||||
with patch("routstr.payment.helpers.MODELS", [mock_model]):
|
||||
with patch("routstr.payment.helpers.MODEL_BASED_PRICING", True):
|
||||
cost = get_max_cost_for_model("gpt-4")
|
||||
assert cost == 500000 # 500 sats * 1000 = msats
|
||||
|
||||
|
||||
def test_get_max_cost_for_model_unknown() -> None:
|
||||
with patch("router.payment.helpers.MODELS", []):
|
||||
with patch("router.payment.helpers.COST_PER_REQUEST", 100):
|
||||
with patch("routstr.payment.helpers.MODELS", []):
|
||||
with patch("routstr.payment.helpers.COST_PER_REQUEST", 100):
|
||||
cost = get_max_cost_for_model("unknown-model")
|
||||
assert cost == 100
|
||||
|
||||
|
||||
def test_get_max_cost_for_model_disabled() -> None:
|
||||
with patch("router.payment.helpers.MODEL_BASED_PRICING", False):
|
||||
with patch("router.payment.helpers.COST_PER_REQUEST", 200):
|
||||
with patch("routstr.payment.helpers.MODEL_BASED_PRICING", False):
|
||||
with patch("routstr.payment.helpers.COST_PER_REQUEST", 200):
|
||||
cost = get_max_cost_for_model("any-model")
|
||||
assert cost == 200
|
||||
|
||||
+13
-13
@@ -4,7 +4,7 @@ from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from router.wallet import credit_balance, get_balance, recieve_token, send_token
|
||||
from routstr.wallet import credit_balance, get_balance, recieve_token, send_token
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -13,7 +13,7 @@ async def test_get_balance() -> None:
|
||||
mock_wallet.available_balance = Mock(amount=50000)
|
||||
mock_wallet.load_proofs = AsyncMock()
|
||||
|
||||
with patch("router.wallet.Wallet.with_db", return_value=mock_wallet):
|
||||
with patch("routstr.wallet.Wallet.with_db", return_value=mock_wallet):
|
||||
balance = await get_balance("sat")
|
||||
assert balance == 50000
|
||||
|
||||
@@ -38,8 +38,8 @@ async def test_recieve_token_valid() -> None:
|
||||
mock_wallet = Mock()
|
||||
mock_wallet.redeem = AsyncMock()
|
||||
|
||||
with patch("router.wallet.TRUSTED_MINTS", ["http://mint:3338"]):
|
||||
with patch("router.wallet.deserialize_token_from_string") as mock_deserialize:
|
||||
with patch("routstr.wallet.TRUSTED_MINTS", ["http://mint:3338"]):
|
||||
with patch("routstr.wallet.deserialize_token_from_string") as mock_deserialize:
|
||||
mock_token = Mock()
|
||||
mock_token.keysets = ["keyset1"]
|
||||
mock_token.mint = "http://mint:3338"
|
||||
@@ -48,7 +48,7 @@ async def test_recieve_token_valid() -> None:
|
||||
mock_token.proofs = [{"amount": 1000}]
|
||||
mock_deserialize.return_value = mock_token
|
||||
|
||||
with patch("router.wallet.Wallet.with_db", return_value=mock_wallet):
|
||||
with patch("routstr.wallet.Wallet.with_db", return_value=mock_wallet):
|
||||
mock_wallet.load_mint = AsyncMock()
|
||||
|
||||
amount, unit, mint = await recieve_token(token_str)
|
||||
@@ -61,8 +61,8 @@ async def test_recieve_token_valid() -> None:
|
||||
async def test_send_token() -> None:
|
||||
mock_wallet = Mock()
|
||||
|
||||
with patch("router.wallet.Wallet.with_db", return_value=mock_wallet):
|
||||
with patch("router.wallet.send", return_value=(1000, "test_token")):
|
||||
with patch("routstr.wallet.Wallet.with_db", return_value=mock_wallet):
|
||||
with patch("routstr.wallet.send", return_value=(1000, "test_token")):
|
||||
token = await send_token(1000, "sat", "http://mint:3338")
|
||||
assert token == "test_token"
|
||||
|
||||
@@ -81,9 +81,9 @@ async def test_credit_balance() -> None:
|
||||
mock_key.balance = 5000000
|
||||
mock_session = AsyncMock()
|
||||
|
||||
with patch("router.wallet.PRIMARY_MINT_URL", "http://mint:3338"):
|
||||
with patch("routstr.wallet.PRIMARY_MINT_URL", "http://mint:3338"):
|
||||
with patch(
|
||||
"router.wallet.recieve_token",
|
||||
"routstr.wallet.recieve_token",
|
||||
return_value=(1000, "sat", "http://mint:3338"),
|
||||
):
|
||||
amount = await credit_balance(token_str, mock_key, mock_session)
|
||||
@@ -99,7 +99,7 @@ async def test_credit_balance_invalid_mint() -> None:
|
||||
mock_session = AsyncMock()
|
||||
|
||||
with patch(
|
||||
"router.wallet.recieve_token", return_value=(1000, "sat", "http://other:3338")
|
||||
"routstr.wallet.recieve_token", return_value=(1000, "sat", "http://other:3338")
|
||||
):
|
||||
with pytest.raises(ValueError, match="Mint URL is not supported"):
|
||||
await credit_balance("test_token", mock_key, mock_session)
|
||||
@@ -109,7 +109,7 @@ async def test_credit_balance_invalid_mint() -> None:
|
||||
async def test_recieve_token_untrusted_mint() -> None:
|
||||
mock_wallet = Mock()
|
||||
|
||||
with patch("router.wallet.deserialize_token_from_string") as mock_deserialize:
|
||||
with patch("routstr.wallet.deserialize_token_from_string") as mock_deserialize:
|
||||
mock_token = Mock()
|
||||
mock_token.keysets = ["keyset1"]
|
||||
mock_token.mint = "http://untrusted:3338"
|
||||
@@ -117,10 +117,10 @@ async def test_recieve_token_untrusted_mint() -> None:
|
||||
mock_token.amount = 1000
|
||||
mock_deserialize.return_value = mock_token
|
||||
|
||||
with patch("router.wallet.Wallet.with_db", return_value=mock_wallet):
|
||||
with patch("routstr.wallet.Wallet.with_db", return_value=mock_wallet):
|
||||
mock_wallet.load_mint = AsyncMock()
|
||||
with patch(
|
||||
"router.wallet.swap_to_primary_mint",
|
||||
"routstr.wallet.swap_to_primary_mint",
|
||||
return_value=(900, "sat", "http://mint:3338"),
|
||||
):
|
||||
amount, unit, mint = await recieve_token("test_token")
|
||||
|
||||
@@ -1768,7 +1768,7 @@ wheels = [
|
||||
[[package]]
|
||||
name = "routstr"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiosqlite" },
|
||||
{ name = "alembic" },
|
||||
@@ -1822,7 +1822,7 @@ dev = [
|
||||
{ name = "pytest-asyncio", specifier = ">=0.24.0" },
|
||||
{ name = "pytest-benchmark", specifier = ">=4.0.0" },
|
||||
{ name = "pytest-cov", specifier = ">=6.1.1" },
|
||||
{ name = "routstr", virtual = "." },
|
||||
{ name = "routstr", editable = "." },
|
||||
{ name = "ruff", specifier = ">=0.11.6" },
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user