fix: update agent and wallet API endpoints, improve RPC error handling, and mark additional CLI commands as working

- Update agent execute endpoint to use /v1/agents/workflows/{id}/execute path
- Add workflow_id and inputs fields to agent creation and execution payloads
- Accept both 200 and 201 status codes for agent create, network create, and contribution submit
- Update wallet balance and send RPC endpoints to use rstrip('/') instead of replace('/api', '')
- Add chain_id parameter to wallet R
This commit is contained in:
oib
2026-03-05 09:28:55 +01:00
parent 5273b1866f
commit 8b28c4d9e3
6 changed files with 256 additions and 81 deletions

80
tests/cli/test_node.py Normal file
View File

@@ -0,0 +1,80 @@
"""Tests for node CLI commands"""
import pytest
from unittest.mock import Mock, patch, MagicMock
from click.testing import CliRunner
from aitbc_cli.commands.node import node
from aitbc_cli.core.config import MultiChainConfig
@pytest.fixture
def runner():
"""Create CLI runner"""
return CliRunner()
@pytest.fixture
def mock_config():
"""Mock configuration loader"""
with patch('aitbc_cli.commands.node.load_multichain_config') as mock:
config = MagicMock()
config.nodes = {"node-1": MagicMock()}
mock.return_value = config
yield mock
class TestNodeCommands:
@patch('aitbc_cli.core.config.save_multichain_config')
@patch('aitbc_cli.commands.node.add_node_config')
@patch('aitbc_cli.commands.node.get_default_node_config')
def test_node_add_success(self, mock_get_default, mock_add, mock_save, runner, mock_config):
"""Test successful node addition"""
# Setup mock
mock_node_config = MagicMock()
mock_get_default.return_value = mock_node_config
mock_new_config = MagicMock()
mock_add.return_value = mock_new_config
# Run command
result = runner.invoke(node, [
'add', 'new-node', 'http://localhost:8080'
], obj={'output_format': 'json'})
# Assertions
assert result.exit_code == 0
assert "added successfully" in result.output
mock_save.assert_called_once_with(mock_new_config)
def test_node_add_already_exists(self, runner, mock_config):
"""Test adding an existing node"""
result = runner.invoke(node, [
'add', 'node-1', 'http://localhost:8080'
], obj={'output_format': 'json'})
assert result.exit_code != 0
assert "already exists" in result.output
@patch('aitbc_cli.commands.node.remove_node_config')
@patch('aitbc_cli.core.config.save_multichain_config')
def test_node_remove_success(self, mock_save, mock_remove, runner, mock_config):
"""Test successful node removal"""
# Setup mock
mock_new_config = MagicMock()
mock_remove.return_value = mock_new_config
result = runner.invoke(node, [
'remove', 'node-1', '--force'
], obj={'output_format': 'json'})
assert result.exit_code == 0
assert "removed successfully" in result.output
mock_save.assert_called_once_with(mock_new_config)
def test_node_remove_not_found(self, runner, mock_config):
"""Test removing a non-existent node"""
result = runner.invoke(node, [
'remove', 'non-existent-node', '--force'
], obj={'output_format': 'json'})
assert result.exit_code != 0
assert "not found" in result.output

View File

@@ -22,7 +22,9 @@ def mock_wallet_dir(tmp_path):
wallet_data = {
"address": "aitbc1test",
"private_key": "test_key",
"public_key": "test_pub"
"public_key": "test_pub",
"transactions": [],
"balance": 0.0
}
with open(wallet_file, "w") as f:
json.dump(wallet_data, f)
@@ -37,29 +39,29 @@ class TestWalletAdditionalCommands:
backup_dir.mkdir()
backup_path = backup_dir / "backup.json"
# We need to test the backup command properly.
# click might suppress exception output if not configured otherwise.
result = runner.invoke(wallet, [
'--wallet-path', str(mock_wallet_dir / "test_wallet.json"),
'backup', 'test_wallet', '--destination', str(backup_path)
], obj={"wallet_dir": mock_wallet_dir, "output_format": "json"}, catch_exceptions=False)
], catch_exceptions=False)
assert result.exit_code == 0
assert os.path.exists(backup_path)
def test_backup_wallet_not_found(self, runner, mock_wallet_dir):
"""Test backing up non-existent wallet"""
# We handle raise click.Abort()
result = runner.invoke(wallet, [
'--wallet-path', str(mock_wallet_dir / "test_wallet.json"),
'backup', 'non_existent_wallet'
], obj={"wallet_dir": mock_wallet_dir, "output_format": "json"})
])
assert result.exit_code != 0
assert "does not exist" in result.output.lower()
def test_delete_wallet_success(self, runner, mock_wallet_dir):
"""Test successful wallet deletion"""
result = runner.invoke(wallet, [
'--wallet-path', str(mock_wallet_dir / "test_wallet.json"),
'delete', 'test_wallet', '--confirm'
], obj={"wallet_dir": mock_wallet_dir, "output_format": "json"})
])
assert result.exit_code == 0
assert not os.path.exists(mock_wallet_dir / "test_wallet.json")
@@ -67,8 +69,79 @@ class TestWalletAdditionalCommands:
def test_delete_wallet_not_found(self, runner, mock_wallet_dir):
"""Test deleting non-existent wallet"""
result = runner.invoke(wallet, [
'--wallet-path', str(mock_wallet_dir / "test_wallet.json"),
'delete', 'non_existent', '--confirm'
], obj={"wallet_dir": mock_wallet_dir, "output_format": "json"})
])
assert result.exit_code != 0
assert "does not exist" in result.output.lower()
@patch('aitbc_cli.commands.wallet._save_wallet')
def test_earn_success(self, mock_save, runner, mock_wallet_dir):
"""Test successful wallet earning"""
result = runner.invoke(wallet, [
'--wallet-path', str(mock_wallet_dir / "test_wallet.json"),
'earn', '10.5', 'job_123'
])
assert result.exit_code == 0
assert "earnings added" in result.output.lower()
mock_save.assert_called_once()
def test_earn_wallet_not_found(self, runner, mock_wallet_dir):
"""Test earning to non-existent wallet"""
result = runner.invoke(wallet, [
'--wallet-path', str(mock_wallet_dir / "non_existent.json"),
'earn', '10.5', 'job_123'
])
assert "not found" in result.output.lower()
def test_restore_wallet_success(self, runner, mock_wallet_dir, tmp_path):
"""Test successful wallet restore"""
# Create a backup file to restore from
backup_file = tmp_path / "backup.json"
with open(backup_file, "w") as f:
json.dump({"address": "restored", "transactions": []}, f)
result = runner.invoke(wallet, [
'--wallet-path', str(mock_wallet_dir / "new_wallet.json"),
'restore', str(backup_file), 'new_wallet'
])
assert result.exit_code == 0
assert os.path.exists(mock_wallet_dir / "new_wallet.json")
with open(mock_wallet_dir / "new_wallet.json", "r") as f:
data = json.load(f)
assert data["address"] == "restored"
def test_restore_wallet_exists(self, runner, mock_wallet_dir, tmp_path):
"""Test restoring to an existing wallet without force"""
backup_file = tmp_path / "backup.json"
with open(backup_file, "w") as f:
json.dump({"address": "restored", "transactions": []}, f)
result = runner.invoke(wallet, [
'--wallet-path', str(mock_wallet_dir / "test_wallet.json"),
'restore', str(backup_file), 'test_wallet'
])
assert "already exists" in result.output.lower()
def test_restore_wallet_force(self, runner, mock_wallet_dir, tmp_path):
"""Test restoring to an existing wallet with force"""
backup_file = tmp_path / "backup.json"
with open(backup_file, "w") as f:
json.dump({"address": "restored", "transactions": []}, f)
result = runner.invoke(wallet, [
'--wallet-path', str(mock_wallet_dir / "test_wallet.json"),
'restore', str(backup_file), 'test_wallet', '--force'
])
assert result.exit_code == 0
with open(mock_wallet_dir / "test_wallet.json", "r") as f:
data = json.load(f)
assert data["address"] == "restored"