"""Tests for history parsers.""" import pytest import tempfile import os from shell_alias_gen.parsers import ( HistoryParserFactory, BashHistoryParser, ZshHistoryParser, FishHistoryParser, ParsedCommand, ) class TestParsedCommand: """Tests for ParsedCommand class.""" def test_normalized(self): """Test command normalization.""" cmd = ParsedCommand(raw_command=" git status ") assert cmd.normalized == "git status" def test_words(self): """Test word splitting.""" cmd = ParsedCommand(raw_command="git checkout -b feature") assert cmd.words == ["git", "checkout", "-b", "feature"] class TestBashHistoryParser: """Tests for BashHistoryParser class.""" def test_parse_content(self): """Test parsing bash history content.""" parser = BashHistoryParser() content = "ls -la\ncd /tmp\ngit status" commands = parser.parse_content(content) assert len(commands) == 3 assert commands[0].raw_command == "ls -la" assert commands[1].raw_command == "cd /tmp" def test_parse_file(self): """Test parsing bash history file.""" parser = BashHistoryParser() with tempfile.NamedTemporaryFile(mode='w', suffix='.bash_history', delete=False) as f: f.write("echo hello\nworld\n") f.flush() filepath = f.name try: commands = parser.parse_file(filepath) assert len(commands) == 2 finally: os.unlink(filepath) def test_parse_file_not_found(self): """Test parsing non-existent file.""" parser = BashHistoryParser() commands = parser.parse_file("/nonexistent/path") assert commands == [] def test_parse_content_skips_comments(self): """Test that comments are skipped.""" parser = BashHistoryParser() content = "# this is a comment\nls -la\n# another comment" commands = parser.parse_content(content) assert len(commands) == 1 assert commands[0].raw_command == "ls -la" class TestZshHistoryParser: """Tests for ZshHistoryParser class.""" def test_parse_content(self): """Test parsing zsh history content.""" parser = ZshHistoryParser() content = ": 1704067200:0;ls -la\n: 1704067201:0;cd /tmp" commands = parser.parse_content(content) assert len(commands) == 2 assert commands[0].raw_command == "ls -la" assert commands[1].raw_command == "cd /tmp" def test_parse_content_with_special_chars(self): """Test parsing zsh content with special characters.""" parser = ZshHistoryParser() content = ": 1704067200:0;git commit -m 'fix: bug fix'" commands = parser.parse_content(content) assert len(commands) == 1 assert commands[0].raw_command == "git commit -m 'fix: bug fix'" def test_parse_file(self): """Test parsing zsh history file.""" parser = ZshHistoryParser() with tempfile.NamedTemporaryFile(mode='w', suffix='.zsh_history', delete=False) as f: f.write(": 1704067200:0;echo test\n") f.flush() filepath = f.name try: commands = parser.parse_file(filepath) assert len(commands) == 1 finally: os.unlink(filepath) class TestFishHistoryParser: """Tests for FishHistoryParser class.""" def test_parse_content(self): """Test parsing fish history content.""" parser = FishHistoryParser() import json content = json.dumps([ {"text": "ls -la", "timestamp": 1704067200}, {"text": "cd /tmp", "timestamp": 1704067201}, ]) commands = parser.parse_content(content) assert len(commands) == 2 assert commands[0].raw_command == "ls -la" def test_parse_content_invalid_json(self): """Test parsing invalid json returns empty.""" parser = FishHistoryParser() commands = parser.parse_content("not valid json") assert commands == [] def test_parse_content_empty_list(self): """Test parsing empty list.""" parser = FishHistoryParser() commands = parser.parse_content("[]") assert commands == [] def test_parse_file(self): """Test parsing fish history file.""" parser = FishHistoryParser() with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f: import json json.dump([{"text": "test command"}], f) filepath = f.name try: commands = parser.parse_file(filepath) assert len(commands) == 1 finally: os.unlink(filepath) class TestHistoryParserFactory: """Tests for HistoryParserFactory class.""" def test_get_bash_parser(self): """Test getting bash parser.""" parser = HistoryParserFactory.get_parser("bash") assert isinstance(parser, BashHistoryParser) def test_get_zsh_parser(self): """Test getting zsh parser.""" parser = HistoryParserFactory.get_parser("zsh") assert isinstance(parser, ZshHistoryParser) def test_get_fish_parser(self): """Test getting fish parser.""" parser = HistoryParserFactory.get_parser("fish") assert isinstance(parser, FishHistoryParser) def test_get_unknown_parser(self): """Test getting unknown parser returns None.""" parser = HistoryParserFactory.get_parser("unknown") assert parser is None def test_get_supported_shells(self): """Test getting supported shells list.""" shells = HistoryParserFactory.get_supported_shells() assert "bash" in shells assert "zsh" in shells assert "fish" in shells def test_detect_shell_from_file(self): """Test shell detection from file path.""" assert HistoryParserFactory.detect_shell_from_file("/path/.bash_history") == "bash" assert HistoryParserFactory.detect_shell_from_file("/path/.zsh_history") == "zsh" assert HistoryParserFactory.detect_shell_from_file("/path/commands.json") == "fish" def test_case_insensitive_shell(self): """Test shell type is case insensitive.""" parser1 = HistoryParserFactory.get_parser("BASH") parser2 = HistoryParserFactory.get_parser("Bash") assert isinstance(parser1, BashHistoryParser) assert isinstance(parser2, BashHistoryParser) class TestBaseParserValidation: """Tests for base parser validation methods.""" def test_valid_alias_names(self): """Test valid alias name validation.""" parser = BashHistoryParser() assert parser.validate_alias_name("gco") is True assert parser.validate_alias_name("_git") is True assert parser.validate_alias_name("git_checkout") is True def test_invalid_alias_names(self): """Test invalid alias name validation.""" parser = BashHistoryParser() assert parser.validate_alias_name("") is False assert parser.validate_alias_name("123abc") is False assert parser.validate_alias_name("git-commit") is False def test_sanitize_alias_name(self): """Test alias name sanitization.""" parser = BashHistoryParser() assert parser.sanitize_alias_name("git checkout") == "gitcheckout" assert parser.sanitize_alias_name("123test").startswith("alias") assert parser.sanitize_alias_name("docker-compose") == "dockercompose"