mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-05 12:28:22 +00:00
makefile + test improvements
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
# Makefile for Routstr Proxy
|
||||
|
||||
# Detect if we're in a virtual environment
|
||||
VENV_EXISTS := $(shell test -d .venv && echo 1)
|
||||
ifeq ($(VENV_EXISTS), 1)
|
||||
PYTHON := .venv/bin/python
|
||||
PYTEST := .venv/bin/pytest
|
||||
RUFF := .venv/bin/ruff
|
||||
MYPY := .venv/bin/mypy
|
||||
else
|
||||
PYTHON := python
|
||||
PYTEST := pytest
|
||||
RUFF := ruff
|
||||
MYPY := mypy
|
||||
endif
|
||||
|
||||
.PHONY: help setup test test-unit test-integration test-integration-docker test-all test-fast test-performance clean docker-up docker-down lint format type-check dev-setup check-deps
|
||||
|
||||
# Default target
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " make test - Run all tests (unit + integration with mocks)"
|
||||
@echo " make test-unit - Run unit tests only"
|
||||
@echo " make test-integration - Run integration tests with mocks (fast)"
|
||||
@echo " make test-integration-docker - Run integration tests with Docker services"
|
||||
@echo " make test-all - Run all tests including Docker integration"
|
||||
@echo " make test-fast - Run fast tests only (skip slow tests)"
|
||||
@echo " make test-performance - Run performance tests"
|
||||
@echo " make docker-up - Start Docker test services"
|
||||
@echo " make docker-down - Stop Docker test services"
|
||||
@echo " make clean - Clean up test artifacts and caches"
|
||||
@echo " make lint - Run linting checks"
|
||||
@echo " make format - Format code with ruff"
|
||||
@echo " make type-check - Run mypy type checking"
|
||||
@echo " make dev-setup - Set up development environment"
|
||||
@echo " make check-deps - Check system dependencies"
|
||||
@echo " make setup - First-time project setup"
|
||||
|
||||
# First-time setup
|
||||
setup: check-deps dev-setup
|
||||
@echo ""
|
||||
@echo "🎉 Setup complete! Next steps:"
|
||||
@echo " 1. Run tests: make test"
|
||||
@echo " 2. Run integration: make test-integration-docker"
|
||||
@echo " 3. Start developing!"
|
||||
|
||||
# Test targets
|
||||
test: test-unit test-integration
|
||||
|
||||
test-unit:
|
||||
@echo "🧪 Running unit tests..."
|
||||
$(PYTEST) tests/unit/ -v
|
||||
|
||||
test-integration:
|
||||
@echo "🎭 Running integration tests with mocks..."
|
||||
$(PYTEST) tests/integration/ -v
|
||||
|
||||
test-integration-docker:
|
||||
@echo "🐳 Running integration tests with Docker services..."
|
||||
./tests/run_integration.py
|
||||
|
||||
test-all: test-unit test-integration-docker
|
||||
|
||||
test-fast:
|
||||
@echo "⚡ Running fast tests only..."
|
||||
$(PYTEST) -m "not slow and not requires_docker" -v
|
||||
|
||||
test-performance:
|
||||
@echo "📊 Running performance tests..."
|
||||
$(PYTEST) tests/integration/ -m "performance" -v -s
|
||||
|
||||
# Docker management
|
||||
docker-up:
|
||||
@echo "🚀 Starting Docker test services..."
|
||||
docker-compose -f compose.testing.yml up -d
|
||||
@echo "Waiting for services to be ready..."
|
||||
@sleep 5
|
||||
@echo "Services started. Run 'make test-integration-docker' to test."
|
||||
|
||||
docker-down:
|
||||
@echo "🛑 Stopping Docker test services..."
|
||||
docker-compose -f compose.testing.yml down -v
|
||||
|
||||
# Code quality
|
||||
lint:
|
||||
@echo "🔍 Running linting checks..."
|
||||
$(RUFF) check .
|
||||
$(MYPY) router/ --ignore-missing-imports
|
||||
|
||||
format:
|
||||
@echo "✨ Formatting code..."
|
||||
$(RUFF) format .
|
||||
$(RUFF) check --fix .
|
||||
|
||||
type-check:
|
||||
@echo "🔎 Running type checks..."
|
||||
$(MYPY) router/ --ignore-missing-imports
|
||||
|
||||
# Development setup
|
||||
dev-setup:
|
||||
@echo "🔧 Setting up development environment..."
|
||||
@# Check if uv is installed
|
||||
@if ! command -v uv >/dev/null 2>&1; then \
|
||||
echo "📦 uv not found. Installing uv..."; \
|
||||
if command -v curl >/dev/null 2>&1; then \
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh; \
|
||||
elif command -v pip >/dev/null 2>&1; then \
|
||||
pip install uv; \
|
||||
else \
|
||||
echo "❌ Neither curl nor pip found. Please install uv manually:"; \
|
||||
echo " Visit https://docs.astral.sh/uv/getting-started/installation/"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
echo "✅ uv installed successfully!"; \
|
||||
else \
|
||||
echo "✅ uv is already installed (version: $$(uv --version))"; \
|
||||
fi
|
||||
uv sync --dev
|
||||
uv pip install -e .
|
||||
@echo "✅ Development environment ready!"
|
||||
|
||||
# Check dependencies
|
||||
check-deps:
|
||||
@echo "🔍 Checking system dependencies..."
|
||||
@echo ""
|
||||
@echo "Core tools:"
|
||||
@printf " %-18s" "Python:"; if command -v python >/dev/null 2>&1; then python --version; else echo "❌ Not found"; fi
|
||||
@printf " %-18s" "uv:"; if command -v uv >/dev/null 2>&1; then uv --version; else echo "❌ Not found - run 'make dev-setup' to install"; fi
|
||||
@printf " %-18s" "Docker:"; if command -v docker >/dev/null 2>&1; then docker --version; else echo "⚠️ Not found (optional, needed for integration tests)"; fi
|
||||
@printf " %-18s" "Docker Compose:"; if command -v docker-compose >/dev/null 2>&1; then docker-compose --version; else echo "⚠️ Not found (optional, needed for integration tests)"; fi
|
||||
@echo ""
|
||||
@echo "Development tools:"
|
||||
@printf " %-18s" "pytest:"; if $(PYTEST) --version >/dev/null 2>&1; then $(PYTEST) --version | head -1; else echo "❌ Not found - run 'make dev-setup'"; fi
|
||||
@printf " %-18s" "ruff:"; if $(RUFF) --version >/dev/null 2>&1; then $(RUFF) --version; else echo "❌ Not found - run 'make dev-setup'"; fi
|
||||
@printf " %-18s" "mypy:"; if $(MYPY) --version >/dev/null 2>&1; then $(MYPY) --version; else echo "❌ Not found - run 'make dev-setup'"; fi
|
||||
@echo ""
|
||||
@echo "Virtual environment:"
|
||||
@if [ -d ".venv" ]; then \
|
||||
echo " ✅ .venv exists"; \
|
||||
echo " Python: $$(.venv/bin/python --version)"; \
|
||||
else \
|
||||
echo " ❌ .venv not found - run 'make dev-setup'"; \
|
||||
fi
|
||||
@echo ""
|
||||
@echo "To set up missing dependencies, run: make dev-setup"
|
||||
|
||||
# Cleanup
|
||||
clean:
|
||||
@echo "🧹 Cleaning up..."
|
||||
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
||||
find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true
|
||||
find . -type d -name ".mypy_cache" -exec rm -rf {} + 2>/dev/null || true
|
||||
find . -type f -name "*.pyc" -delete
|
||||
find . -type f -name ".coverage" -delete
|
||||
rm -rf htmlcov/
|
||||
rm -rf dist/
|
||||
rm -rf build/
|
||||
rm -rf *.egg-info
|
||||
@echo "✨ Cleanup complete!"
|
||||
|
||||
# Advanced testing options
|
||||
test-coverage:
|
||||
@echo "📊 Running tests with coverage..."
|
||||
$(PYTEST) --cov=router --cov-report=html --cov-report=term
|
||||
@echo "Coverage report generated in htmlcov/"
|
||||
|
||||
test-watch:
|
||||
@echo "👁️ Running tests in watch mode..."
|
||||
$(PYTEST)-watch
|
||||
|
||||
test-parallel:
|
||||
@echo "🚀 Running tests in parallel..."
|
||||
$(PYTEST) -n auto -v
|
||||
|
||||
# CI/CD specific targets
|
||||
ci-test:
|
||||
@echo "🤖 Running CI test suite..."
|
||||
$(PYTEST) -m "not requires_docker" --tb=short -v
|
||||
|
||||
ci-lint:
|
||||
@echo "🤖 Running CI linting..."
|
||||
$(RUFF) check . --exit-non-zero-on-fix
|
||||
$(MYPY) router/ --ignore-missing-imports --no-error-summary
|
||||
|
||||
# Debug helpers
|
||||
test-debug:
|
||||
@echo "🐛 Running tests with debugging enabled..."
|
||||
$(PYTEST) -vvs --tb=long --pdb-trace
|
||||
|
||||
test-failed:
|
||||
@echo "🔄 Re-running failed tests..."
|
||||
$(PYTEST) --lf -v
|
||||
|
||||
# Performance profiling
|
||||
profile:
|
||||
@echo "🔥 Running with profiling..."
|
||||
$(PYTHON) -m cProfile -o profile.stats -m pytest tests/integration/test_performance_load.py::TestPerformanceBaseline -v
|
||||
@echo "Profile saved to profile.stats. Use '$(PYTHON) -m pstats profile.stats' to analyze."
|
||||
+3
-3
@@ -53,6 +53,8 @@ markers = [
|
||||
"unit: marks tests as unit tests",
|
||||
"slow: marks tests as slow running (deselect with '-m \"not slow\"')",
|
||||
"requires_real_mint: marks tests that require a running Cashu mint instance",
|
||||
"requires_docker: marks tests that require Docker services running (deselect with '-m \"not requires_docker\"')",
|
||||
"performance: marks tests that measure performance metrics",
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
@@ -73,6 +75,4 @@ secp256k1 = { git = "https://github.com/saschanaz/secp256k1-py", branch = "upgra
|
||||
routstr = { workspace = true }
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = [
|
||||
".",
|
||||
]
|
||||
members = ["."]
|
||||
|
||||
+193
-17
@@ -2,10 +2,43 @@
|
||||
|
||||
End-to-end tests for API endpoints, Cashu wallet operations, and database interactions.
|
||||
|
||||
## Running Tests
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# All integration tests
|
||||
# First-time setup (installs uv if needed)
|
||||
make setup
|
||||
|
||||
# Check if all dependencies are installed
|
||||
make check-deps
|
||||
|
||||
# Run tests
|
||||
make test
|
||||
```
|
||||
|
||||
## Test Modes
|
||||
|
||||
The integration tests support two execution modes:
|
||||
|
||||
### 🎭 Mock Mode (Default - Fast)
|
||||
|
||||
- Uses in-memory mocks for external services
|
||||
- No Docker required
|
||||
- Runs quickly, ideal for CI/CD
|
||||
- Good for rapid development iteration
|
||||
|
||||
### 🐳 Docker Mode (Realistic)
|
||||
|
||||
- Uses real Docker services (Cashu mint, mock OpenAI, Nostr relay)
|
||||
- More accurate testing environment
|
||||
- Slower but catches more edge cases
|
||||
- Recommended before releases
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Quick Mode (Mocked Services)
|
||||
|
||||
```bash
|
||||
# All integration tests with mocks
|
||||
pytest tests/integration/ -v
|
||||
|
||||
# Specific test file
|
||||
@@ -13,40 +46,183 @@ pytest tests/integration/test_wallet_topup.py -v
|
||||
|
||||
# Skip slow tests
|
||||
pytest tests/integration/ -m "not slow" -v
|
||||
|
||||
# Run only unit-style integration tests
|
||||
pytest tests/integration/ -m "not requires_docker" -v
|
||||
```
|
||||
|
||||
### Full Integration Mode (Docker Services)
|
||||
|
||||
```bash
|
||||
# Using the automated script (recommended)
|
||||
./tests/run_integration.py
|
||||
|
||||
# Or manually:
|
||||
docker-compose -f compose.testing.yml up -d
|
||||
USE_LOCAL_SERVICES=1 pytest tests/integration/ -v
|
||||
docker-compose -f compose.testing.yml down -v
|
||||
```
|
||||
|
||||
### CI/CD Mode
|
||||
|
||||
```bash
|
||||
# Fast tests only for continuous integration
|
||||
pytest tests/integration/ -m "not slow and not requires_docker" -v
|
||||
|
||||
# Performance tests
|
||||
pytest tests/integration/ -m "performance" -v
|
||||
```
|
||||
|
||||
## Test Infrastructure
|
||||
|
||||
**TestmintWallet** - Mock Cashu wallet for generating test tokens
|
||||
**DatabaseSnapshot** - Captures database state changes
|
||||
**Test Utilities** - Validators for responses, performance, and concurrency
|
||||
### Core Fixtures
|
||||
|
||||
## Real Testmint Setup (Optional)
|
||||
- **`integration_client`** - Async HTTP client configured for testing
|
||||
- **`authenticated_client`** - Pre-authenticated client with API key
|
||||
- **`testmint_wallet`** - Mock/real Cashu wallet for token generation
|
||||
- **`db_snapshot`** - Database state tracking for verification
|
||||
- **`test_mode`** - Reports current execution mode (mock/docker)
|
||||
|
||||
By default, tests use a mock testmint. For testing against a real instance:
|
||||
### Utility Classes
|
||||
|
||||
```bash
|
||||
./tests/integration/setup_testmint.sh
|
||||
export USE_REAL_MINT=true
|
||||
export MINT_URL=http://localhost:3338
|
||||
pytest tests/integration/ -v
|
||||
```
|
||||
- **`ResponseValidator`** - Validates API response formats
|
||||
- **`PerformanceValidator`** - Tracks and validates performance metrics
|
||||
- **`ConcurrencyTester`** - Tests concurrent request handling
|
||||
- **`CashuTokenGenerator`** - Generates valid/invalid test tokens
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
Test environment configuration is handled directly in `conftest.py`. The configuration automatically switches between:
|
||||
|
||||
- **Mock mode**: Fast, uses mocked services (default)
|
||||
- **Docker mode**: Uses real Docker services when `USE_LOCAL_SERVICES=1`
|
||||
|
||||
This keeps all test configuration in one place and avoids file duplication.
|
||||
|
||||
## Writing Tests
|
||||
|
||||
### Basic Test Structure
|
||||
|
||||
```python
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.asyncio
|
||||
async def test_wallet_topup(integration_client, testmint_wallet, db_snapshot):
|
||||
async def test_wallet_topup(
|
||||
authenticated_client: AsyncClient,
|
||||
testmint_wallet: Any,
|
||||
db_snapshot: Any
|
||||
):
|
||||
# Capture initial state
|
||||
await db_snapshot.capture()
|
||||
|
||||
# Generate test token
|
||||
token = await testmint_wallet.mint_tokens(1000)
|
||||
|
||||
response = await integration_client.post(
|
||||
# Make API request
|
||||
response = await authenticated_client.post(
|
||||
"/v1/wallet/topup",
|
||||
params={"cashu_token": token}
|
||||
)
|
||||
|
||||
# Validate response
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify database changes
|
||||
diff = await db_snapshot.diff()
|
||||
assert len(diff["api_keys"]["added"]) == 1
|
||||
```
|
||||
assert len(diff["api_keys"]["modified"]) == 1
|
||||
```
|
||||
|
||||
### Testing Concurrent Operations
|
||||
|
||||
```python
|
||||
async def test_concurrent_topups(
|
||||
integration_client: AsyncClient,
|
||||
testmint_wallet: Any,
|
||||
create_api_key: Callable
|
||||
):
|
||||
# Create multiple API keys
|
||||
keys = []
|
||||
for i in range(5):
|
||||
key, _ = await create_api_key(integration_client, testmint_wallet)
|
||||
keys.append(key)
|
||||
|
||||
# Test concurrent requests
|
||||
tester = ConcurrencyTester()
|
||||
responses = await tester.run_concurrent_requests(
|
||||
integration_client,
|
||||
[{"method": "GET", "url": "/v1/wallet/",
|
||||
"headers": {"Authorization": f"Bearer {key}"}}
|
||||
for key in keys],
|
||||
max_concurrent=5
|
||||
)
|
||||
|
||||
# All should succeed
|
||||
assert all(r.status_code == 200 for r in responses)
|
||||
```
|
||||
|
||||
### Performance Testing
|
||||
|
||||
```python
|
||||
@pytest.mark.performance
|
||||
async def test_endpoint_performance(
|
||||
authenticated_client: AsyncClient,
|
||||
performance_validator: PerformanceValidator
|
||||
):
|
||||
# Run multiple requests
|
||||
for i in range(100):
|
||||
start = performance_validator.start_timing("wallet_info")
|
||||
response = await authenticated_client.get("/v1/wallet/")
|
||||
performance_validator.end_timing("wallet_info", start)
|
||||
|
||||
# Validate 95th percentile < 100ms
|
||||
result = performance_validator.validate_response_time(
|
||||
"wallet_info", max_duration=0.1, percentile=0.95
|
||||
)
|
||||
assert result["valid"], f"P95: {result['percentile_time']:.3f}s"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Tests Failing with Connection Errors
|
||||
|
||||
- Ensure Docker services are running: `docker ps`
|
||||
- Check service logs: `docker-compose -f compose.testing.yml logs`
|
||||
- Verify ports aren't in use: `lsof -i :3338,3000,8000,8088`
|
||||
|
||||
### Mock vs Docker Mode Confusion
|
||||
|
||||
- Check current mode: Look for 🎭 or 🐳 emoji in test output
|
||||
- Force mock mode: Unset `USE_LOCAL_SERVICES`
|
||||
- Force Docker mode: `export USE_LOCAL_SERVICES=1`
|
||||
|
||||
### Slow Test Execution
|
||||
|
||||
- Use mock mode for development: `pytest tests/integration/`
|
||||
- Skip slow tests: `pytest -m "not slow"`
|
||||
- Run specific test files only
|
||||
- Use pytest-xdist for parallel execution: `pytest -n auto`
|
||||
|
||||
### Installing uv Manually
|
||||
|
||||
If `make dev-setup` fails to install uv automatically:
|
||||
|
||||
```bash
|
||||
# macOS/Linux
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# Or with pip
|
||||
pip install uv
|
||||
|
||||
# Or with Homebrew
|
||||
brew install uv
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Mock Mode for Development** - It's fast and catches most issues
|
||||
2. **Run Docker Mode Before PRs** - Ensures realistic testing
|
||||
3. **Add Appropriate Markers** - Help others run relevant test subsets
|
||||
- Use `@pytest.mark.slow` for tests that take significant time (e.g., memory/load tests)
|
||||
- Use `@pytest.mark.requires_docker` for tests needing Docker services
|
||||
4. **Verify Database State** - Use `db_snapshot` for state verification
|
||||
5. **Test Edge Cases** - Invalid inputs, network failures, race conditions
|
||||
6. **Monitor Performance** - Add performance tests for critical paths
|
||||
|
||||
@@ -15,18 +15,19 @@ from router.core.logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
# Configure environment based on whether we're using local services or not
|
||||
# Configure test environment based on whether we're using local services or not
|
||||
use_local_services = os.environ.get("USE_LOCAL_SERVICES", "0") == "1"
|
||||
|
||||
if use_local_services:
|
||||
# Use local Docker services for integration tests
|
||||
# Docker mode: Use Docker services for more realistic testing
|
||||
logger.info("🐳 Using Docker services for integration tests")
|
||||
test_env = {
|
||||
"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", # Mock Cashu mint (Docker service name)
|
||||
"MINT": "http://mint:3338", # Fallback mint URL (Docker service name)
|
||||
"MINT_URL": "http://mint:3338", # Another fallback (Docker service name)
|
||||
"CASHU_MINTS": "http://mint:3338", # Docker service name for router validation
|
||||
"MINT": "http://mint:3338",
|
||||
"MINT_URL": "http://mint:3338",
|
||||
"NOSTR_RELAY_URL": "ws://localhost:8088",
|
||||
"RECEIVE_LN_ADDRESS": "test@routstr.com",
|
||||
"REFUND_PROCESSING_INTERVAL": "3600",
|
||||
@@ -43,12 +44,13 @@ if use_local_services:
|
||||
"CORS_ORIGINS": "*",
|
||||
}
|
||||
else:
|
||||
# Use mock/in-memory services for unit-style integration tests
|
||||
# Mock mode: Use in-memory mocks for fast testing
|
||||
logger.info("🎭 Using mocked services for integration tests")
|
||||
test_env = {
|
||||
"DATABASE_URL": "sqlite+aiosqlite:///:memory:",
|
||||
"UPSTREAM_BASE_URL": "https://api.openai.com/v1",
|
||||
"UPSTREAM_API_KEY": "test-upstream-key",
|
||||
"CASHU_MINTS": "http://localhost:3338", # Use test mint URL
|
||||
"CASHU_MINTS": "http://localhost:3338",
|
||||
"RECEIVE_LN_ADDRESS": "test@routstr.com",
|
||||
"REFUND_PROCESSING_INTERVAL": "3600",
|
||||
"NSEC": "nsec1testkey1234567890abcdef",
|
||||
@@ -65,6 +67,17 @@ from router.core.db import ApiKey, get_session # noqa: E402
|
||||
from router.core.main import app, lifespan # noqa: E402
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def test_mode() -> str:
|
||||
"""Returns current test mode for clarity"""
|
||||
if os.environ.get("USE_LOCAL_SERVICES") == "1":
|
||||
print("\n🐳 Running with Docker services (realistic mode)")
|
||||
return "docker"
|
||||
else:
|
||||
print("\n🎭 Running with mocked services (fast mode)")
|
||||
return "mock"
|
||||
|
||||
|
||||
class TestmintWallet:
|
||||
"""Test wallet that simulates Cashu mint interactions for testing"""
|
||||
|
||||
@@ -206,7 +219,7 @@ class TestmintWallet:
|
||||
total_amount = 0
|
||||
mint_url = self.mint_url
|
||||
unit = token_data.get("unit", "sat")
|
||||
|
||||
|
||||
for mint_tokens in token_data["token"]:
|
||||
mint_url = mint_tokens.get("mint", self.mint_url)
|
||||
for proof in mint_tokens["proofs"]:
|
||||
@@ -221,7 +234,7 @@ class TestmintWallet:
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError(f"Failed to decode token: {str(e)}")
|
||||
|
||||
|
||||
async def redeem_token_simple(self, token: str) -> Tuple[int, str]:
|
||||
"""Redeem a Cashu token - simple version for credit_balance"""
|
||||
amount, unit, mint_url = await self.redeem_token(token)
|
||||
@@ -235,7 +248,9 @@ class TestmintWallet:
|
||||
# For testing, create a refund token
|
||||
return await self.mint_tokens(amount)
|
||||
|
||||
async def send_token(self, amount: int, unit: str, mint_url: Optional[str] = None) -> str:
|
||||
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"""
|
||||
return await self.send(amount)
|
||||
|
||||
@@ -266,8 +281,10 @@ class TestmintWallet:
|
||||
) -> int:
|
||||
"""Credit balance to API key - test implementation"""
|
||||
try:
|
||||
logger.info(f"TestmintWallet.credit_balance called with token: {cashu_token[:20]}...")
|
||||
|
||||
logger.info(
|
||||
f"TestmintWallet.credit_balance called with token: {cashu_token[:20]}..."
|
||||
)
|
||||
|
||||
# Redeem the token to get amount
|
||||
amount, _ = await self.redeem_token_simple(cashu_token)
|
||||
logger.info(f"TestmintWallet.credit_balance redeemed amount: {amount}")
|
||||
@@ -278,7 +295,7 @@ class TestmintWallet:
|
||||
|
||||
# Credit the balance using atomic database update to prevent race conditions
|
||||
from sqlmodel import col, update
|
||||
|
||||
|
||||
# Use atomic update to avoid lost update problem in concurrent scenarios
|
||||
stmt = (
|
||||
update(ApiKey)
|
||||
@@ -287,17 +304,22 @@ class TestmintWallet:
|
||||
)
|
||||
await session.execute(stmt)
|
||||
await session.commit()
|
||||
|
||||
|
||||
# Refresh the key object to get the updated balance
|
||||
await session.refresh(key)
|
||||
|
||||
logger.info(f"TestmintWallet.credit_balance successfully credited {amount_msat} msat")
|
||||
|
||||
logger.info(
|
||||
f"TestmintWallet.credit_balance successfully credited {amount_msat} msat"
|
||||
)
|
||||
|
||||
return amount_msat
|
||||
except Exception as e:
|
||||
logger.error(f"TestmintWallet.credit_balance failed: {e}")
|
||||
import traceback
|
||||
logger.error(f"TestmintWallet.credit_balance full traceback: {traceback.format_exc()}")
|
||||
|
||||
logger.error(
|
||||
f"TestmintWallet.credit_balance full traceback: {traceback.format_exc()}"
|
||||
)
|
||||
raise ValueError(f"Failed to redeem token: {str(e)}")
|
||||
|
||||
|
||||
@@ -483,10 +505,9 @@ async def integration_app(
|
||||
yield test_app
|
||||
else:
|
||||
# Use testmint with wallet patches for all integration tests
|
||||
mint_url = test_env.get("CASHU_MINTS", "http://localhost:3338")
|
||||
mint_url = os.environ.get("CASHU_MINTS", "http://localhost:3338")
|
||||
with (
|
||||
patch("router.core.db.engine", integration_engine),
|
||||
patch.dict(os.environ, test_env, clear=False),
|
||||
patch("router.wallet.TRUSTED_MINTS", [mint_url]),
|
||||
patch("router.wallet.PRIMARY_MINT_URL", mint_url),
|
||||
patch("router.auth.credit_balance", testmint_wallet.credit_balance),
|
||||
@@ -503,9 +524,9 @@ async def integration_app(
|
||||
# Configure the WebSocket mock for discovery service - fast failure for performance tests
|
||||
async def mock_websocket_connect(*args: Any, **kwargs: Any) -> None:
|
||||
raise ConnectionError("Mock connection failed")
|
||||
|
||||
|
||||
mock_websockets.side_effect = mock_websocket_connect
|
||||
|
||||
|
||||
yield test_app
|
||||
|
||||
|
||||
@@ -637,31 +658,6 @@ def mock_upstream_server() -> Any:
|
||||
return mock_server
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def integration_env_vars() -> Any:
|
||||
"""Fixture to manage integration test environment variables"""
|
||||
original_env = os.environ.copy()
|
||||
|
||||
# Set integration test specific environment variables
|
||||
test_env = {
|
||||
"TESTMINT_URL": "https://testmint.routstr.com",
|
||||
"INTEGRATION_TEST": "true",
|
||||
"LOG_LEVEL": "DEBUG",
|
||||
"DATABASE_POOL_SIZE": "10",
|
||||
"DATABASE_MAX_OVERFLOW": "20",
|
||||
"REQUEST_TIMEOUT": "30",
|
||||
"UPSTREAM_TIMEOUT": "25",
|
||||
}
|
||||
|
||||
os.environ.update(test_env)
|
||||
|
||||
yield test_env
|
||||
|
||||
# Restore original environment
|
||||
os.environ.clear()
|
||||
os.environ.update(original_env)
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def background_tasks_controller() -> AsyncGenerator[Any, None]:
|
||||
"""Control background tasks during tests"""
|
||||
|
||||
Reference in New Issue
Block a user