Files
aitbc/tests/cli/test_auth.py
oib 5120861e17 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
2026-02-12 19:08:17 +01:00

362 lines
12 KiB
Python

"""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