refactor: consolidate blockchain explorer into single app and update backup ignore patterns
- Remove standalone explorer-web app (README, HTML, package files) - Add /web endpoint to blockchain-explorer for web interface access - Update .gitignore to exclude application backup archives (*.tar.gz, *.zip) - Add backup documentation files to .gitignore (BACKUP_INDEX.md, README.md) - Consolidate explorer functionality into main blockchain-explorer application
This commit is contained in:
9
apps/wallet/tests/conftest.py
Normal file
9
apps/wallet/tests/conftest.py
Normal file
@@ -0,0 +1,9 @@
|
||||
"""Wallet daemon test configuration"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add src to path for imports
|
||||
src_path = Path(__file__).parent.parent / "src"
|
||||
if str(src_path) not in sys.path:
|
||||
sys.path.insert(0, str(src_path))
|
||||
38
apps/wallet/tests/test_ledger.py
Normal file
38
apps/wallet/tests/test_ledger.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from app.ledger_mock import SQLiteLedgerAdapter
|
||||
|
||||
|
||||
def test_upsert_and_get_wallet(tmp_path: Path) -> None:
|
||||
db_path = tmp_path / "ledger.db"
|
||||
adapter = SQLiteLedgerAdapter(db_path)
|
||||
|
||||
adapter.upsert_wallet("wallet-1", "pubkey", {"label": "primary"})
|
||||
|
||||
record = adapter.get_wallet("wallet-1")
|
||||
assert record is not None
|
||||
assert record.wallet_id == "wallet-1"
|
||||
assert record.public_key == "pubkey"
|
||||
assert record.metadata["label"] == "primary"
|
||||
|
||||
# Update metadata and ensure persistence
|
||||
adapter.upsert_wallet("wallet-1", "pubkey", {"label": "updated"})
|
||||
updated = adapter.get_wallet("wallet-1")
|
||||
assert updated is not None
|
||||
assert updated.metadata["label"] == "updated"
|
||||
|
||||
|
||||
def test_event_ordering(tmp_path: Path) -> None:
|
||||
db_path = tmp_path / "ledger.db"
|
||||
adapter = SQLiteLedgerAdapter(db_path)
|
||||
|
||||
adapter.upsert_wallet("wallet-1", "pubkey", {})
|
||||
adapter.record_event("wallet-1", "created", {"step": 1})
|
||||
adapter.record_event("wallet-1", "unlock", {"step": 2})
|
||||
adapter.record_event("wallet-1", "sign", {"step": 3})
|
||||
|
||||
events = list(adapter.list_events("wallet-1"))
|
||||
assert [event.event_type for event in events] == ["created", "unlock", "sign"]
|
||||
assert [event.payload["step"] for event in events] == [1, 2, 3]
|
||||
404
apps/wallet/tests/test_multichain.py
Normal file
404
apps/wallet/tests/test_multichain.py
Normal file
@@ -0,0 +1,404 @@
|
||||
"""
|
||||
Multi-Chain Wallet Daemon Tests
|
||||
|
||||
Tests for multi-chain functionality including chain management,
|
||||
chain-specific wallet operations, and cross-chain migrations.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import tempfile
|
||||
import json
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
from datetime import datetime
|
||||
|
||||
from app.chain.manager import ChainManager, ChainConfig, ChainStatus
|
||||
from app.chain.multichain_ledger import MultiChainLedgerAdapter, ChainWalletMetadata
|
||||
from app.chain.chain_aware_wallet_service import ChainAwareWalletService
|
||||
|
||||
|
||||
class TestChainManager:
|
||||
"""Test the chain manager functionality"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = Path(tempfile.mkdtemp())
|
||||
self.config_path = self.temp_dir / "test_chains.json"
|
||||
self.chain_manager = ChainManager(self.config_path)
|
||||
|
||||
def teardown_method(self):
|
||||
"""Clean up test environment"""
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_create_default_chain(self):
|
||||
"""Test default chain creation"""
|
||||
assert len(self.chain_manager.chains) == 1
|
||||
assert "ait-devnet" in self.chain_manager.chains
|
||||
assert self.chain_manager.default_chain_id == "ait-devnet"
|
||||
|
||||
def test_add_chain(self):
|
||||
"""Test adding a new chain"""
|
||||
chain_config = ChainConfig(
|
||||
chain_id="test-chain",
|
||||
name="Test Chain",
|
||||
coordinator_url="http://localhost:8001",
|
||||
coordinator_api_key="test-key"
|
||||
)
|
||||
|
||||
success = self.chain_manager.add_chain(chain_config)
|
||||
assert success is True
|
||||
assert "test-chain" in self.chain_manager.chains
|
||||
assert len(self.chain_manager.chains) == 2
|
||||
|
||||
def test_add_duplicate_chain(self):
|
||||
"""Test adding a duplicate chain"""
|
||||
chain_config = ChainConfig(
|
||||
chain_id="ait-devnet", # Already exists
|
||||
name="Duplicate Chain",
|
||||
coordinator_url="http://localhost:8001",
|
||||
coordinator_api_key="test-key"
|
||||
)
|
||||
|
||||
success = self.chain_manager.add_chain(chain_config)
|
||||
assert success is False
|
||||
assert len(self.chain_manager.chains) == 1
|
||||
|
||||
def test_remove_chain(self):
|
||||
"""Test removing a chain"""
|
||||
# First add a test chain
|
||||
chain_config = ChainConfig(
|
||||
chain_id="test-chain",
|
||||
name="Test Chain",
|
||||
coordinator_url="http://localhost:8001",
|
||||
coordinator_api_key="test-key"
|
||||
)
|
||||
self.chain_manager.add_chain(chain_config)
|
||||
|
||||
# Remove it
|
||||
success = self.chain_manager.remove_chain("test-chain")
|
||||
assert success is True
|
||||
assert "test-chain" not in self.chain_manager.chains
|
||||
assert len(self.chain_manager.chains) == 1
|
||||
|
||||
def test_remove_default_chain(self):
|
||||
"""Test removing the default chain (should fail)"""
|
||||
success = self.chain_manager.remove_chain("ait-devnet")
|
||||
assert success is False
|
||||
assert "ait-devnet" in self.chain_manager.chains
|
||||
|
||||
def test_set_default_chain(self):
|
||||
"""Test setting default chain"""
|
||||
# Add a test chain first
|
||||
chain_config = ChainConfig(
|
||||
chain_id="test-chain",
|
||||
name="Test Chain",
|
||||
coordinator_url="http://localhost:8001",
|
||||
coordinator_api_key="test-key"
|
||||
)
|
||||
self.chain_manager.add_chain(chain_config)
|
||||
|
||||
# Set as default
|
||||
success = self.chain_manager.set_default_chain("test-chain")
|
||||
assert success is True
|
||||
assert self.chain_manager.default_chain_id == "test-chain"
|
||||
|
||||
def test_validate_chain_id(self):
|
||||
"""Test chain ID validation"""
|
||||
# Valid active chain
|
||||
assert self.chain_manager.validate_chain_id("ait-devnet") is True
|
||||
|
||||
# Invalid chain
|
||||
assert self.chain_manager.validate_chain_id("nonexistent") is False
|
||||
|
||||
# Add inactive chain
|
||||
chain_config = ChainConfig(
|
||||
chain_id="inactive-chain",
|
||||
name="Inactive Chain",
|
||||
coordinator_url="http://localhost:8001",
|
||||
coordinator_api_key="test-key",
|
||||
status=ChainStatus.INACTIVE
|
||||
)
|
||||
self.chain_manager.add_chain(chain_config)
|
||||
|
||||
# Inactive chain should be invalid
|
||||
assert self.chain_manager.validate_chain_id("inactive-chain") is False
|
||||
|
||||
def test_get_chain_stats(self):
|
||||
"""Test getting chain statistics"""
|
||||
stats = self.chain_manager.get_chain_stats()
|
||||
|
||||
assert stats["total_chains"] == 1
|
||||
assert stats["active_chains"] == 1
|
||||
assert stats["default_chain"] == "ait-devnet"
|
||||
assert len(stats["chain_list"]) == 1
|
||||
|
||||
|
||||
class TestMultiChainLedger:
|
||||
"""Test the multi-chain ledger adapter"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = Path(tempfile.mkdtemp())
|
||||
self.chain_manager = ChainManager(self.temp_dir / "chains.json")
|
||||
self.ledger = MultiChainLedgerAdapter(self.chain_manager, self.temp_dir)
|
||||
|
||||
def teardown_method(self):
|
||||
"""Clean up test environment"""
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_create_wallet(self):
|
||||
"""Test creating a wallet in a specific chain"""
|
||||
success = self.ledger.create_wallet(
|
||||
chain_id="ait-devnet",
|
||||
wallet_id="test-wallet",
|
||||
public_key="test-public-key",
|
||||
address="test-address"
|
||||
)
|
||||
|
||||
assert success is True
|
||||
|
||||
# Verify wallet exists
|
||||
wallet = self.ledger.get_wallet("ait-devnet", "test-wallet")
|
||||
assert wallet is not None
|
||||
assert wallet.wallet_id == "test-wallet"
|
||||
assert wallet.chain_id == "ait-devnet"
|
||||
assert wallet.public_key == "test-public-key"
|
||||
|
||||
def test_create_wallet_invalid_chain(self):
|
||||
"""Test creating wallet in invalid chain"""
|
||||
success = self.ledger.create_wallet(
|
||||
chain_id="invalid-chain",
|
||||
wallet_id="test-wallet",
|
||||
public_key="test-public-key"
|
||||
)
|
||||
|
||||
assert success is False
|
||||
|
||||
def test_list_wallets(self):
|
||||
"""Test listing wallets"""
|
||||
# Create multiple wallets
|
||||
self.ledger.create_wallet("ait-devnet", "wallet1", "pub1")
|
||||
self.ledger.create_wallet("ait-devnet", "wallet2", "pub2")
|
||||
|
||||
wallets = self.ledger.list_wallets("ait-devnet")
|
||||
assert len(wallets) == 2
|
||||
wallet_ids = [wallet.wallet_id for wallet in wallets]
|
||||
assert "wallet1" in wallet_ids
|
||||
assert "wallet2" in wallet_ids
|
||||
|
||||
def test_record_event(self):
|
||||
"""Test recording events"""
|
||||
success = self.ledger.record_event(
|
||||
chain_id="ait-devnet",
|
||||
wallet_id="test-wallet",
|
||||
event_type="test-event",
|
||||
data={"test": "data"}
|
||||
)
|
||||
|
||||
assert success is True
|
||||
|
||||
# Get events
|
||||
events = self.ledger.get_wallet_events("ait-devnet", "test-wallet")
|
||||
assert len(events) == 1
|
||||
assert events[0].event_type == "test-event"
|
||||
assert events[0].data["test"] == "data"
|
||||
|
||||
def test_get_chain_stats(self):
|
||||
"""Test getting chain statistics"""
|
||||
# Create a wallet first
|
||||
self.ledger.create_wallet("ait-devnet", "test-wallet", "test-pub")
|
||||
|
||||
stats = self.ledger.get_chain_stats("ait-devnet")
|
||||
assert stats["chain_id"] == "ait-devnet"
|
||||
assert stats["wallet_count"] == 1
|
||||
assert "database_path" in stats
|
||||
|
||||
|
||||
class TestChainAwareWalletService:
|
||||
"""Test the chain-aware wallet service"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = Path(tempfile.mkdtemp())
|
||||
self.chain_manager = ChainManager(self.temp_dir / "chains.json")
|
||||
self.ledger = MultiChainLedgerAdapter(self.chain_manager, self.temp_dir)
|
||||
|
||||
# Mock keystore service
|
||||
with patch('app.chain.chain_aware_wallet_service.PersistentKeystoreService') as mock_keystore:
|
||||
self.mock_keystore = mock_keystore.return_value
|
||||
self.mock_keystore.create_wallet.return_value = Mock(
|
||||
public_key="test-pub-key",
|
||||
metadata={}
|
||||
)
|
||||
self.mock_keystore.sign_message.return_value = b"test-signature"
|
||||
self.mock_keystore.unlock_wallet.return_value = True
|
||||
self.mock_keystore.lock_wallet.return_value = True
|
||||
|
||||
self.wallet_service = ChainAwareWalletService(self.chain_manager, self.ledger)
|
||||
|
||||
def teardown_method(self):
|
||||
"""Clean up test environment"""
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_create_wallet(self):
|
||||
"""Test creating a wallet in a specific chain"""
|
||||
wallet = self.wallet_service.create_wallet(
|
||||
chain_id="ait-devnet",
|
||||
wallet_id="test-wallet",
|
||||
password="test-password"
|
||||
)
|
||||
|
||||
assert wallet is not None
|
||||
assert wallet.wallet_id == "test-wallet"
|
||||
assert wallet.chain_id == "ait-devnet"
|
||||
assert wallet.public_key == "test-pub-key"
|
||||
|
||||
def test_create_wallet_invalid_chain(self):
|
||||
"""Test creating wallet in invalid chain"""
|
||||
wallet = self.wallet_service.create_wallet(
|
||||
chain_id="invalid-chain",
|
||||
wallet_id="test-wallet",
|
||||
password="test-password"
|
||||
)
|
||||
|
||||
assert wallet is None
|
||||
|
||||
def test_sign_message(self):
|
||||
"""Test signing a message"""
|
||||
# First create a wallet
|
||||
self.wallet_service.create_wallet("ait-devnet", "test-wallet", "test-password")
|
||||
|
||||
signature = self.wallet_service.sign_message(
|
||||
chain_id="ait-devnet",
|
||||
wallet_id="test-wallet",
|
||||
password="test-password",
|
||||
message=b"test message"
|
||||
)
|
||||
|
||||
assert signature == "test-signature" # Mocked signature
|
||||
|
||||
def test_unlock_wallet(self):
|
||||
"""Test unlocking a wallet"""
|
||||
# First create a wallet
|
||||
self.wallet_service.create_wallet("ait-devnet", "test-wallet", "test-password")
|
||||
|
||||
success = self.wallet_service.unlock_wallet(
|
||||
chain_id="ait-devnet",
|
||||
wallet_id="test-wallet",
|
||||
password="test-password"
|
||||
)
|
||||
|
||||
assert success is True
|
||||
|
||||
def test_list_wallets(self):
|
||||
"""Test listing wallets"""
|
||||
# Create wallets in different chains
|
||||
self.wallet_service.create_wallet("ait-devnet", "wallet1", "password1")
|
||||
|
||||
# Add another chain
|
||||
chain_config = ChainConfig(
|
||||
chain_id="test-chain",
|
||||
name="Test Chain",
|
||||
coordinator_url="http://localhost:8001",
|
||||
coordinator_api_key="test-key"
|
||||
)
|
||||
self.chain_manager.add_chain(chain_config)
|
||||
|
||||
# Create wallet in new chain
|
||||
self.wallet_service.create_wallet("test-chain", "wallet2", "password2")
|
||||
|
||||
# List all wallets
|
||||
all_wallets = self.wallet_service.list_wallets()
|
||||
assert len(all_wallets) == 2
|
||||
|
||||
# List specific chain wallets
|
||||
devnet_wallets = self.wallet_service.list_wallets("ait-devnet")
|
||||
assert len(devnet_wallets) == 1
|
||||
assert devnet_wallets[0].wallet_id == "wallet1"
|
||||
|
||||
def test_get_chain_wallet_stats(self):
|
||||
"""Test getting chain wallet statistics"""
|
||||
# Create a wallet
|
||||
self.wallet_service.create_wallet("ait-devnet", "test-wallet", "test-password")
|
||||
|
||||
stats = self.wallet_service.get_chain_wallet_stats("ait-devnet")
|
||||
assert stats["chain_id"] == "ait-devnet"
|
||||
assert "ledger_stats" in stats
|
||||
assert "keystore_stats" in stats
|
||||
|
||||
|
||||
class TestMultiChainIntegration:
|
||||
"""Integration tests for multi-chain functionality"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = Path(tempfile.mkdtemp())
|
||||
self.chain_manager = ChainManager(self.temp_dir / "chains.json")
|
||||
self.ledger = MultiChainLedgerAdapter(self.chain_manager, self.temp_dir)
|
||||
|
||||
# Add a second chain
|
||||
chain_config = ChainConfig(
|
||||
chain_id="test-chain",
|
||||
name="Test Chain",
|
||||
coordinator_url="http://localhost:8001",
|
||||
coordinator_api_key="test-key"
|
||||
)
|
||||
self.chain_manager.add_chain(chain_config)
|
||||
|
||||
def teardown_method(self):
|
||||
"""Clean up test environment"""
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_cross_chain_wallet_isolation(self):
|
||||
"""Test that wallets are properly isolated between chains"""
|
||||
# Create wallet with same ID in different chains
|
||||
self.ledger.create_wallet("ait-devnet", "same-wallet", "pub1", "addr1")
|
||||
self.ledger.create_wallet("test-chain", "same-wallet", "pub2", "addr2")
|
||||
|
||||
# Verify they are different
|
||||
wallet1 = self.ledger.get_wallet("ait-devnet", "same-wallet")
|
||||
wallet2 = self.ledger.get_wallet("test-chain", "same-wallet")
|
||||
|
||||
assert wallet1.chain_id == "ait-devnet"
|
||||
assert wallet2.chain_id == "test-chain"
|
||||
assert wallet1.public_key != wallet2.public_key
|
||||
assert wallet1.address != wallet2.address
|
||||
|
||||
def test_chain_specific_events(self):
|
||||
"""Test that events are chain-specific"""
|
||||
# Create wallets in different chains
|
||||
self.ledger.create_wallet("ait-devnet", "wallet1", "pub1")
|
||||
self.ledger.create_wallet("test-chain", "wallet2", "pub2")
|
||||
|
||||
# Record events
|
||||
self.ledger.record_event("ait-devnet", "wallet1", "event1", {"chain": "devnet"})
|
||||
self.ledger.record_event("test-chain", "wallet2", "event2", {"chain": "test"})
|
||||
|
||||
# Verify events are chain-specific
|
||||
events1 = self.ledger.get_wallet_events("ait-devnet", "wallet1")
|
||||
events2 = self.ledger.get_wallet_events("test-chain", "wallet2")
|
||||
|
||||
assert len(events1) == 1
|
||||
assert len(events2) == 1
|
||||
assert events1[0].data["chain"] == "devnet"
|
||||
assert events2[0].data["chain"] == "test"
|
||||
|
||||
def test_all_chain_stats(self):
|
||||
"""Test getting statistics for all chains"""
|
||||
# Create wallets in different chains
|
||||
self.ledger.create_wallet("ait-devnet", "wallet1", "pub1")
|
||||
self.ledger.create_wallet("test-chain", "wallet2", "pub2")
|
||||
|
||||
stats = self.ledger.get_all_chain_stats()
|
||||
assert stats["total_chains"] == 2
|
||||
assert stats["total_wallets"] == 2
|
||||
assert "ait-devnet" in stats["chain_stats"]
|
||||
assert "test-chain" in stats["chain_stats"]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__])
|
||||
81
apps/wallet/tests/test_receipts.py
Normal file
81
apps/wallet/tests/test_receipts.py
Normal file
@@ -0,0 +1,81 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from nacl.signing import SigningKey
|
||||
|
||||
from app.receipts import ReceiptValidationResult, ReceiptVerifierService
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def sample_receipt() -> dict:
|
||||
return {
|
||||
"version": "1.0",
|
||||
"receipt_id": "rcpt-1",
|
||||
"job_id": "job-123",
|
||||
"provider": "miner-abc",
|
||||
"client": "client-xyz",
|
||||
"units": 1.0,
|
||||
"unit_type": "gpu_seconds",
|
||||
"price": 3.5,
|
||||
"started_at": 1700000000,
|
||||
"completed_at": 1700000005,
|
||||
"metadata": {},
|
||||
}
|
||||
|
||||
|
||||
class _DummyClient:
|
||||
def __init__(self, latest=None, history=None):
|
||||
self.latest = latest
|
||||
self.history = history or []
|
||||
|
||||
def fetch_latest(self, job_id: str):
|
||||
return self.latest
|
||||
|
||||
def fetch_history(self, job_id: str):
|
||||
return list(self.history)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def signer():
|
||||
return SigningKey.generate()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def signed_receipt(sample_receipt: dict, signer: SigningKey) -> dict:
|
||||
from aitbc_crypto.signing import ReceiptSigner
|
||||
|
||||
receipt = dict(sample_receipt)
|
||||
receipt["signature"] = ReceiptSigner(signer.encode()).sign(sample_receipt)
|
||||
return receipt
|
||||
|
||||
|
||||
def test_verify_latest_success(monkeypatch, signed_receipt: dict):
|
||||
service = ReceiptVerifierService("http://coordinator", "api-key")
|
||||
client = _DummyClient(latest=signed_receipt)
|
||||
monkeypatch.setattr(service, "client", client)
|
||||
|
||||
result = service.verify_latest("job-123")
|
||||
assert isinstance(result, ReceiptValidationResult)
|
||||
assert result.job_id == "job-123"
|
||||
assert result.receipt_id == "rcpt-1"
|
||||
assert result.miner_valid is True
|
||||
assert result.all_valid is True
|
||||
|
||||
|
||||
def test_verify_latest_none(monkeypatch):
|
||||
service = ReceiptVerifierService("http://coordinator", "api-key")
|
||||
client = _DummyClient(latest=None)
|
||||
monkeypatch.setattr(service, "client", client)
|
||||
|
||||
assert service.verify_latest("job-123") is None
|
||||
|
||||
|
||||
def test_verify_history(monkeypatch, signed_receipt: dict):
|
||||
service = ReceiptVerifierService("http://coordinator", "api-key")
|
||||
client = _DummyClient(history=[signed_receipt])
|
||||
monkeypatch.setattr(service, "client", client)
|
||||
|
||||
results = service.verify_history("job-123")
|
||||
assert len(results) == 1
|
||||
assert results[0].miner_valid is True
|
||||
assert results[0].job_id == "job-123"
|
||||
98
apps/wallet/tests/test_wallet_api.py
Normal file
98
apps/wallet/tests/test_wallet_api.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.deps import get_keystore, get_ledger, get_settings
|
||||
from app.main import create_app
|
||||
from app.keystore.service import KeystoreService
|
||||
from app.ledger_mock import SQLiteLedgerAdapter
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def client_fixture(tmp_path, monkeypatch):
|
||||
# Override ledger path to temporary directory
|
||||
from app.settings import Settings
|
||||
|
||||
test_settings = Settings(LEDGER_DB_PATH=str(tmp_path / "ledger.db"))
|
||||
|
||||
monkeypatch.setattr("app.settings.settings", test_settings)
|
||||
|
||||
from app import deps
|
||||
|
||||
deps.get_settings.cache_clear()
|
||||
deps.get_keystore.cache_clear()
|
||||
deps.get_ledger.cache_clear()
|
||||
|
||||
app = create_app()
|
||||
|
||||
keystore = KeystoreService()
|
||||
ledger = SQLiteLedgerAdapter(Path(test_settings.ledger_db_path))
|
||||
|
||||
app.dependency_overrides[get_settings] = lambda: test_settings
|
||||
app.dependency_overrides[get_keystore] = lambda: keystore
|
||||
app.dependency_overrides[get_ledger] = lambda: ledger
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
def _create_wallet(client: TestClient, wallet_id: str, password: str = "Password!234") -> None:
|
||||
payload = {
|
||||
"wallet_id": wallet_id,
|
||||
"password": password,
|
||||
}
|
||||
response = client.post("/v1/wallets", json=payload)
|
||||
assert response.status_code == 201, response.text
|
||||
|
||||
|
||||
def test_wallet_workflow(client: TestClient):
|
||||
wallet_id = "wallet-1"
|
||||
password = "StrongPass!234"
|
||||
|
||||
# Create wallet
|
||||
response = client.post(
|
||||
"/v1/wallets",
|
||||
json={
|
||||
"wallet_id": wallet_id,
|
||||
"password": password,
|
||||
"metadata": {"label": "test"},
|
||||
},
|
||||
)
|
||||
assert response.status_code == 201, response.text
|
||||
data = response.json()["wallet"]
|
||||
assert data["wallet_id"] == wallet_id
|
||||
assert "public_key" in data
|
||||
|
||||
# List wallets
|
||||
response = client.get("/v1/wallets")
|
||||
assert response.status_code == 200
|
||||
items = response.json()["items"]
|
||||
assert any(item["wallet_id"] == wallet_id for item in items)
|
||||
|
||||
# Unlock wallet
|
||||
response = client.post(f"/v1/wallets/{wallet_id}/unlock", json={"password": password})
|
||||
assert response.status_code == 200
|
||||
assert response.json()["unlocked"] is True
|
||||
|
||||
# Sign payload
|
||||
message = base64.b64encode(b"hello").decode()
|
||||
response = client.post(
|
||||
f"/v1/wallets/{wallet_id}/sign",
|
||||
json={"password": password, "message_base64": message},
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
signature = response.json()["signature_base64"]
|
||||
assert isinstance(signature, str) and len(signature) > 0
|
||||
|
||||
|
||||
def test_wallet_password_rules(client: TestClient):
|
||||
response = client.post(
|
||||
"/v1/wallets",
|
||||
json={"wallet_id": "weak", "password": "short"},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
body = response.json()
|
||||
assert body["detail"]["reason"] == "password_too_weak"
|
||||
assert "min_length" in body["detail"]
|
||||
Reference in New Issue
Block a user