feat: add GPU-specific fields to marketplace offers and create dedicated GPU marketplace router
- Add GPU fields (model, memory, count, CUDA version, price, region) to MarketplaceOffer model - Create new marketplace_gpu router for GPU-specific operations - Update offer sync to populate GPU fields from miner capabilities - Move GPU attributes from generic attributes dict to dedicated fields - Update MarketplaceOfferView schema with GPU fields - Expand CLI README with comprehensive documentation and
This commit is contained in:
361
tests/cli/test_auth.py
Normal file
361
tests/cli/test_auth.py
Normal file
@@ -0,0 +1,361 @@
|
||||
"""Tests for auth CLI commands"""
|
||||
|
||||
import pytest
|
||||
import json
|
||||
import os
|
||||
from click.testing import CliRunner
|
||||
from unittest.mock import Mock, patch
|
||||
from aitbc_cli.commands.auth import auth
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def runner():
|
||||
"""Create CLI runner"""
|
||||
return CliRunner()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config():
|
||||
"""Mock configuration"""
|
||||
return {}
|
||||
|
||||
|
||||
class TestAuthCommands:
|
||||
"""Test auth command group"""
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_login_success(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test successful login"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'login',
|
||||
'test_api_key_12345',
|
||||
'--environment', 'dev'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['status'] == 'logged_in'
|
||||
assert data['environment'] == 'dev'
|
||||
|
||||
# Verify credential stored
|
||||
mock_auth_manager.store_credential.assert_called_once_with(
|
||||
'client', 'test_api_key_12345', 'dev'
|
||||
)
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_login_invalid_key(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test login with invalid API key"""
|
||||
# Run command with short key
|
||||
result = runner.invoke(auth, [
|
||||
'login',
|
||||
'short',
|
||||
'--environment', 'dev'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code != 0
|
||||
assert 'Invalid API key' in result.output
|
||||
|
||||
# Verify credential not stored
|
||||
mock_auth_manager_class.return_value.store_credential.assert_not_called()
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_logout_success(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test successful logout"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'logout',
|
||||
'--environment', 'prod'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['status'] == 'logged_out'
|
||||
assert data['environment'] == 'prod'
|
||||
|
||||
# Verify credential deleted
|
||||
mock_auth_manager.delete_credential.assert_called_once_with(
|
||||
'client', 'prod'
|
||||
)
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_token_show(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test token command with show flag"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager.get_credential.return_value = 'secret_key_123'
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'token',
|
||||
'--show',
|
||||
'--environment', 'staging'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['api_key'] == 'secret_key_123'
|
||||
assert data['environment'] == 'staging'
|
||||
|
||||
# Verify credential retrieved
|
||||
mock_auth_manager.get_credential.assert_called_once_with(
|
||||
'client', 'staging'
|
||||
)
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_token_hidden(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test token command without show flag"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager.get_credential.return_value = 'secret_key_123'
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'token',
|
||||
'--environment', 'staging'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['api_key'] == '***REDACTED***'
|
||||
assert data['length'] == len('secret_key_123')
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_token_not_found(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test token command when no credential stored"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager.get_credential.return_value = None
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'token',
|
||||
'--environment', 'nonexistent'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['message'] == 'No API key stored'
|
||||
assert data['environment'] == 'nonexistent'
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_status_authenticated(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test status when authenticated"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager.list_credentials.return_value = ['client@dev', 'miner@prod']
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'status'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['status'] == 'authenticated'
|
||||
assert len(data['stored_credentials']) == 2
|
||||
assert 'client@dev' in data['stored_credentials']
|
||||
assert 'miner@prod' in data['stored_credentials']
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_status_not_authenticated(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test status when not authenticated"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager.list_credentials.return_value = []
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'status'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['status'] == 'not_authenticated'
|
||||
assert data['message'] == 'No stored credentials found'
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_refresh_success(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test refresh command"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager.get_credential.return_value = 'valid_key'
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'refresh',
|
||||
'--environment', 'dev'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['status'] == 'refreshed'
|
||||
assert data['environment'] == 'dev'
|
||||
assert 'placeholder' in data['message']
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_refresh_no_key(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test refresh with no stored key"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager.get_credential.return_value = None
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'refresh',
|
||||
'--environment', 'nonexistent'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code != 0
|
||||
assert 'No API key found' in result.output
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_keys_list(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test keys list command"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager.list_credentials.return_value = [
|
||||
'client@dev', 'miner@dev', 'admin@prod'
|
||||
]
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'keys',
|
||||
'list'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert len(data['credentials']) == 3
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_keys_create(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test keys create command"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'keys',
|
||||
'create',
|
||||
'miner',
|
||||
'miner_key_abcdef',
|
||||
'--permissions', 'mine,poll',
|
||||
'--environment', 'prod'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['status'] == 'created'
|
||||
assert data['name'] == 'miner'
|
||||
assert data['environment'] == 'prod'
|
||||
assert data['permissions'] == 'mine,poll'
|
||||
|
||||
# Verify credential stored
|
||||
mock_auth_manager.store_credential.assert_called_once_with(
|
||||
'miner', 'miner_key_abcdef', 'prod'
|
||||
)
|
||||
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_keys_revoke(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test keys revoke command"""
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'keys',
|
||||
'revoke',
|
||||
'old_miner',
|
||||
'--environment', 'dev'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['status'] == 'revoked'
|
||||
assert data['name'] == 'old_miner'
|
||||
assert data['environment'] == 'dev'
|
||||
|
||||
# Verify credential deleted
|
||||
mock_auth_manager.delete_credential.assert_called_once_with(
|
||||
'old_miner', 'dev'
|
||||
)
|
||||
|
||||
@patch.dict(os.environ, {'CLIENT_API_KEY': 'env_test_key'})
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_import_env_success(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test successful import from environment"""
|
||||
import os
|
||||
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'import-env',
|
||||
'client'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code == 0
|
||||
data = json.loads(result.output)
|
||||
assert data['status'] == 'imported'
|
||||
assert data['name'] == 'client'
|
||||
assert data['source'] == 'CLIENT_API_KEY'
|
||||
|
||||
# Verify credential stored
|
||||
mock_auth_manager.store_credential.assert_called_once_with(
|
||||
'client', 'env_test_key'
|
||||
)
|
||||
|
||||
@patch.dict(os.environ, {})
|
||||
@patch('aitbc_cli.commands.auth.AuthManager')
|
||||
def test_import_env_not_set(self, mock_auth_manager_class, runner, mock_config):
|
||||
"""Test import when environment variable not set"""
|
||||
import os
|
||||
|
||||
# Setup mock
|
||||
mock_auth_manager = Mock()
|
||||
mock_auth_manager_class.return_value = mock_auth_manager
|
||||
|
||||
# Run command
|
||||
result = runner.invoke(auth, [
|
||||
'import-env',
|
||||
'client'
|
||||
], obj={'config': mock_config, 'output_format': 'json'})
|
||||
|
||||
# Assertions
|
||||
assert result.exit_code != 0
|
||||
assert 'CLIENT_API_KEY not set' in result.output
|
||||
Reference in New Issue
Block a user