mirror of
https://github.com/cloudstack-llc/mlx-knife.git
synced 2026-07-19 22:53:30 -04:00
47af2c7096
- Fix #1: Partial name filtering for `mlxk list` command - Fix #2: Fuzzy matching for single-model commands - Fix #3: Default behavior for `mlxk health` (no --all flag required) - Expanded test suite to 104/104 tests passing
205 lines
7.9 KiB
Python
205 lines
7.9 KiB
Python
"""
|
|
Unit tests for cli.py module.
|
|
|
|
Tests the command-line interface functionality:
|
|
- Argument parsing
|
|
- Command dispatch
|
|
- Help and version output
|
|
"""
|
|
import pytest
|
|
import argparse
|
|
from unittest.mock import patch, MagicMock
|
|
import sys
|
|
import os
|
|
|
|
# Import the module under test
|
|
from mlx_knife.cli import main
|
|
|
|
|
|
class TestMainFunctionBasic:
|
|
"""Test basic main function behavior without requiring parser creation."""
|
|
|
|
def test_main_function_exists(self):
|
|
"""Test that main function exists and is callable."""
|
|
try:
|
|
assert callable(main)
|
|
except Exception as e:
|
|
pytest.fail(f"Main function test failed: {e}")
|
|
|
|
def test_version_flag_via_main(self):
|
|
"""Test version flag through main function."""
|
|
try:
|
|
with patch('sys.argv', ['mlxk', '--version']):
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
main()
|
|
# Version should exit cleanly
|
|
assert exc_info.value.code in [0, None]
|
|
except Exception as e:
|
|
# It's OK if version parsing isn't fully implemented yet
|
|
pass
|
|
|
|
|
|
class TestMainFunction:
|
|
"""Test main function behavior."""
|
|
|
|
def test_main_with_help(self):
|
|
"""Test main function with help argument."""
|
|
try:
|
|
with patch('sys.argv', ['mlxk', '--help']):
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
main()
|
|
# Help should exit with code 0
|
|
assert exc_info.value.code == 0 or exc_info.value.code is None
|
|
except Exception as e:
|
|
pytest.fail(f"Main function help test failed: {e}")
|
|
|
|
def test_main_with_invalid_command(self):
|
|
"""Test main function with invalid command."""
|
|
try:
|
|
with patch('sys.argv', ['mlxk', 'invalid-command-xyz']):
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
main()
|
|
# Invalid command should exit with non-zero code
|
|
assert exc_info.value.code != 0
|
|
except Exception as e:
|
|
pytest.fail(f"Main function invalid command test failed: {e}")
|
|
|
|
@patch('mlx_knife.cache_utils.list_models')
|
|
def test_main_with_list_command(self, mock_list_models):
|
|
"""Test main function with list command."""
|
|
try:
|
|
# Mock the list_models function to avoid actual cache interaction
|
|
mock_list_models.return_value = None
|
|
|
|
with patch('sys.argv', ['mlxk', 'list']):
|
|
try:
|
|
main()
|
|
except SystemExit as e:
|
|
# List command might exit with 0 on success
|
|
assert e.code == 0 or e.code is None
|
|
except Exception as e:
|
|
pytest.fail(f"Main function list command test failed: {e}")
|
|
|
|
@patch('mlx_knife.cache_utils.check_all_models_health')
|
|
def test_main_with_health_command(self, mock_health_check):
|
|
"""Test main function with health command."""
|
|
try:
|
|
# Mock the health check function
|
|
mock_health_check.return_value = None
|
|
|
|
with patch('sys.argv', ['mlxk', 'health']):
|
|
try:
|
|
main()
|
|
except SystemExit as e:
|
|
# Health command should exit gracefully
|
|
assert e.code == 0 or e.code is None
|
|
except Exception as e:
|
|
pytest.fail(f"Main function health command test failed: {e}")
|
|
|
|
def test_main_no_arguments(self):
|
|
"""Test main function with no arguments."""
|
|
try:
|
|
with patch('sys.argv', ['mlxk']):
|
|
# The CLI shows help when no args are provided - this is valid behavior
|
|
main() # Should complete successfully showing help
|
|
except SystemExit as e:
|
|
# Also valid - some CLIs exit after showing help
|
|
pass
|
|
except Exception as e:
|
|
pytest.fail(f"Main function no arguments test failed: {e}")
|
|
|
|
|
|
class TestErrorHandling:
|
|
"""Test CLI error handling."""
|
|
|
|
def test_keyboard_interrupt_handling(self):
|
|
"""Test handling of KeyboardInterrupt (Ctrl+C)."""
|
|
try:
|
|
# Test that KeyboardInterrupt doesn't crash the CLI completely
|
|
with patch('sys.argv', ['mlxk', 'list']):
|
|
with patch('builtins.print', side_effect=KeyboardInterrupt()):
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
# KeyboardInterrupt propagating up is acceptable
|
|
pass
|
|
except SystemExit:
|
|
# Graceful exit is also acceptable
|
|
pass
|
|
except Exception as e:
|
|
pytest.fail(f"Keyboard interrupt handling test failed: {e}")
|
|
|
|
def test_basic_command_robustness(self):
|
|
"""Test that basic commands don't crash unexpectedly."""
|
|
try:
|
|
# Test that list command runs successfully (already working based on earlier test)
|
|
with patch('sys.argv', ['mlxk', 'list']):
|
|
main() # Should work fine
|
|
except SystemExit:
|
|
# Exit is acceptable for some CLI implementations
|
|
pass
|
|
except Exception as e:
|
|
pytest.fail(f"Basic command robustness test failed: {e}")
|
|
|
|
|
|
class TestHealthCommandDefaultBehavior:
|
|
"""Test health command default behavior (Issue 3)."""
|
|
|
|
@patch('mlx_knife.cli.check_all_models_health')
|
|
def test_health_command_without_args_calls_all(self, mock_check_all):
|
|
"""Test that 'mlxk health' (no args) calls check_all_models_health."""
|
|
mock_check_all.return_value = True
|
|
|
|
try:
|
|
with patch('sys.argv', ['mlxk', 'health']):
|
|
main()
|
|
|
|
# Should have called check_all_models_health
|
|
assert mock_check_all.called
|
|
mock_check_all.assert_called_once()
|
|
except SystemExit:
|
|
# Exit is acceptable after running the command
|
|
assert mock_check_all.called
|
|
except Exception as e:
|
|
pytest.fail(f"Health command default behavior test failed: {e}")
|
|
|
|
@patch('mlx_knife.cli.check_model_health')
|
|
@patch('mlx_knife.cli.check_all_models_health')
|
|
def test_health_command_with_specific_model(self, mock_check_all, mock_check_specific):
|
|
"""Test that 'mlxk health model-name' calls check_model_health."""
|
|
mock_check_specific.return_value = True
|
|
|
|
try:
|
|
with patch('sys.argv', ['mlxk', 'health', 'some-model']):
|
|
main()
|
|
|
|
# Should have called check_model_health with the specific model
|
|
assert mock_check_specific.called
|
|
mock_check_specific.assert_called_once_with('some-model')
|
|
|
|
# Should NOT have called check_all_models_health
|
|
assert not mock_check_all.called
|
|
except SystemExit:
|
|
# Exit is acceptable after running the command
|
|
assert mock_check_specific.called
|
|
assert not mock_check_all.called
|
|
except Exception as e:
|
|
pytest.fail(f"Health command specific model test failed: {e}")
|
|
|
|
@patch('mlx_knife.cli.check_all_models_health')
|
|
def test_health_command_backward_compatibility_with_all_flag(self, mock_check_all):
|
|
"""Test that 'mlxk health --all' still works for backward compatibility."""
|
|
mock_check_all.return_value = True
|
|
|
|
try:
|
|
with patch('sys.argv', ['mlxk', 'health', '--all']):
|
|
main()
|
|
|
|
# Should have called check_all_models_health
|
|
assert mock_check_all.called
|
|
mock_check_all.assert_called_once()
|
|
except SystemExit:
|
|
# Exit is acceptable after running the command
|
|
assert mock_check_all.called
|
|
except Exception as e:
|
|
pytest.fail(f"Health command --all flag test failed: {e}") |