From 2e06c90b64367cbe80ca13c0f4d86475f9d96920 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sun, 1 Feb 2026 08:51:40 +0000 Subject: [PATCH] Initial upload: Shell History Alias Generator with full test suite --- tests/test_parsers.py | 218 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 tests/test_parsers.py diff --git a/tests/test_parsers.py b/tests/test_parsers.py new file mode 100644 index 0000000..1a7f6a4 --- /dev/null +++ b/tests/test_parsers.py @@ -0,0 +1,218 @@ +"""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"