""" Unit tests for argument parser. """ import pytest from terminal_client.arg_parser import ( CommandArgumentParser, ArgumentParseError, parse_new_game_args, parse_defensive_args, parse_offensive_args, parse_quick_play_args, parse_use_game_args ) class TestCommandArgumentParser: """Tests for CommandArgumentParser.""" def test_parse_simple_string_arg(self): """Test parsing simple string argument.""" schema = {'name': {'type': str, 'default': 'default'}} result = CommandArgumentParser.parse_args('--name test', schema) assert result['name'] == 'test' def test_parse_integer_arg(self): """Test parsing integer argument.""" schema = {'count': {'type': int, 'default': 1}} result = CommandArgumentParser.parse_args('--count 42', schema) assert result['count'] == 42 def test_parse_flag_arg(self): """Test parsing boolean flag.""" schema = { 'verbose': {'type': bool, 'flag': True, 'default': False} } result = CommandArgumentParser.parse_args('--verbose', schema) assert result['verbose'] is True def test_parse_int_list(self): """Test parsing comma-separated integer list.""" schema = {'bases': {'type': 'int_list', 'default': []}} result = CommandArgumentParser.parse_args('--bases 1,2,3', schema) assert result['bases'] == [1, 2, 3] def test_parse_quoted_string(self): """Test parsing quoted string with spaces.""" schema = {'message': {'type': str, 'default': ''}} result = CommandArgumentParser.parse_args('--message "hello world"', schema) assert result['message'] == 'hello world' def test_parse_positional_arg(self): """Test parsing positional argument.""" schema = { 'count': {'type': int, 'positional': True, 'default': 1} } result = CommandArgumentParser.parse_args('10', schema) assert result['count'] == 10 def test_parse_mixed_args(self): """Test parsing mix of options and positional.""" schema = { 'count': {'type': int, 'positional': True, 'default': 1}, 'league': {'type': str, 'default': 'sba'} } result = CommandArgumentParser.parse_args('5 --league pd', schema) assert result['count'] == 5 assert result['league'] == 'pd' def test_parse_unknown_option_raises(self): """Test that unknown option raises error.""" schema = {'name': {'type': str, 'default': 'default'}} with pytest.raises(ArgumentParseError, match="Unknown option"): CommandArgumentParser.parse_args('--invalid test', schema) def test_parse_missing_value_raises(self): """Test that missing value for option raises error.""" schema = {'name': {'type': str, 'default': 'default'}} with pytest.raises(ArgumentParseError, match="requires a value"): CommandArgumentParser.parse_args('--name', schema) def test_parse_invalid_type_raises(self): """Test that invalid type conversion raises error.""" schema = {'count': {'type': int, 'default': 1}} with pytest.raises(ArgumentParseError, match="expected int"): CommandArgumentParser.parse_args('--count abc', schema) def test_parse_empty_string(self): """Test parsing empty string returns defaults.""" schema = { 'name': {'type': str, 'default': 'default'}, 'count': {'type': int, 'default': 1} } result = CommandArgumentParser.parse_args('', schema) assert result['name'] == 'default' assert result['count'] == 1 def test_parse_hyphen_to_underscore(self): """Test that hyphens in options convert to underscores.""" schema = {'home_team': {'type': int, 'default': 1}} result = CommandArgumentParser.parse_args('--home-team 5', schema) assert result['home_team'] == 5 def test_parse_float_arg(self): """Test parsing float argument.""" schema = {'ratio': {'type': float, 'default': 1.0}} result = CommandArgumentParser.parse_args('--ratio 3.14', schema) assert result['ratio'] == 3.14 def test_parse_string_list(self): """Test parsing comma-separated string list.""" schema = {'tags': {'type': list, 'default': []}} result = CommandArgumentParser.parse_args('--tags one,two,three', schema) assert result['tags'] == ['one', 'two', 'three'] def test_parse_multiple_flags(self): """Test parsing multiple boolean flags.""" schema = { 'verbose': {'type': bool, 'flag': True, 'default': False}, 'debug': {'type': bool, 'flag': True, 'default': False} } result = CommandArgumentParser.parse_args('--verbose --debug', schema) assert result['verbose'] is True assert result['debug'] is True def test_parse_unexpected_positional_raises(self): """Test that unexpected positional argument raises error.""" schema = {'name': {'type': str, 'default': 'default'}} with pytest.raises(ArgumentParseError, match="Unexpected positional"): CommandArgumentParser.parse_args('extra_arg', schema) def test_parse_invalid_int_list_raises(self): """Test that invalid integer in list raises error.""" schema = {'bases': {'type': 'int_list', 'default': []}} with pytest.raises(ArgumentParseError, match="expected int_list"): CommandArgumentParser.parse_args('--bases 1,abc,3', schema) def test_parse_invalid_syntax_raises(self): """Test that invalid shell syntax raises error.""" schema = {'name': {'type': str, 'default': 'default'}} with pytest.raises(ArgumentParseError, match="Invalid argument syntax"): CommandArgumentParser.parse_args('--name "unclosed quote', schema) def test_parse_game_id_with_option(self): """Test parsing game ID from option.""" arg_string = '--game-id a1b2c3d4-e5f6-7890-abcd-ef1234567890' result = CommandArgumentParser.parse_game_id(arg_string) assert result == 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' def test_parse_game_id_positional(self): """Test parsing game ID as positional argument.""" arg_string = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' result = CommandArgumentParser.parse_game_id(arg_string) assert result == 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' def test_parse_game_id_none(self): """Test parsing game ID returns None when not found.""" arg_string = '--other-option value' result = CommandArgumentParser.parse_game_id(arg_string) assert result is None def test_parse_game_id_invalid_syntax(self): """Test parsing game ID with invalid syntax returns None.""" arg_string = '"unclosed quote' result = CommandArgumentParser.parse_game_id(arg_string) assert result is None class TestPrebuiltParsers: """Tests for pre-built parser functions.""" def test_parse_new_game_args_defaults(self): """Test new_game parser with defaults.""" result = parse_new_game_args('') assert result['league'] == 'sba' assert result['home_team'] == 1 assert result['away_team'] == 2 def test_parse_new_game_args_custom(self): """Test new_game parser with custom values.""" result = parse_new_game_args('--league pd --home-team 5 --away-team 3') assert result['league'] == 'pd' assert result['home_team'] == 5 assert result['away_team'] == 3 def test_parse_defensive_args_defaults(self): """Test defensive parser with defaults.""" result = parse_defensive_args('') assert result['alignment'] == 'normal' assert result['infield'] == 'normal' assert result['outfield'] == 'normal' assert result['hold'] == [] def test_parse_defensive_args_with_hold(self): """Test defensive parser with hold runners.""" result = parse_defensive_args('--alignment shifted_left --hold 1,3') assert result['alignment'] == 'shifted_left' assert result['hold'] == [1, 3] def test_parse_defensive_args_all_options(self): """Test defensive parser with all options.""" result = parse_defensive_args('--alignment extreme_shift --infield back --outfield in --hold 1,2,3') assert result['alignment'] == 'extreme_shift' assert result['infield'] == 'back' assert result['outfield'] == 'in' assert result['hold'] == [1, 2, 3] def test_parse_offensive_args_defaults(self): """Test offensive parser with defaults.""" result = parse_offensive_args('') assert result['approach'] == 'normal' assert result['steal'] == [] assert result['hit_run'] is False assert result['bunt'] is False def test_parse_offensive_args_flags(self): """Test offensive parser with flags.""" result = parse_offensive_args('--approach power --hit-run --bunt') assert result['approach'] == 'power' assert result['hit_run'] is True assert result['bunt'] is True def test_parse_offensive_args_steal(self): """Test offensive parser with steal attempts.""" result = parse_offensive_args('--steal 2,3') assert result['steal'] == [2, 3] def test_parse_offensive_args_all_options(self): """Test offensive parser with all options.""" result = parse_offensive_args('--approach patient --steal 2 --hit-run') assert result['approach'] == 'patient' assert result['steal'] == [2] assert result['hit_run'] is True assert result['bunt'] is False def test_parse_quick_play_args_default(self): """Test quick_play parser with default.""" result = parse_quick_play_args('') assert result['count'] == 1 def test_parse_quick_play_args_positional(self): """Test quick_play parser with positional count.""" result = parse_quick_play_args('10') assert result['count'] == 10 def test_parse_quick_play_args_large_count(self): """Test quick_play parser with large count.""" result = parse_quick_play_args('100') assert result['count'] == 100 def test_parse_use_game_args_valid(self): """Test use_game parser with valid UUID.""" result = parse_use_game_args('a1b2c3d4-e5f6-7890-abcd-ef1234567890') assert result['game_id'] == 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' def test_parse_use_game_args_missing_returns_empty(self): """Test use_game parser returns empty dict when game_id missing.""" result = parse_use_game_args('') # game_id is positional without default, so it won't be in result assert 'game_id' not in result or result.get('game_id') is None