"""Tests for man page parser.""" import pytest from unittest.mock import patch, MagicMock from man_card.man_parser import ManPageParser, CommandInfo, Option SAMPLE_MAN_PAGE = """ NAME ls - list directory contents SYNOPSIS ls [OPTION]... [FILE]... DESCRIPTION List information about the FILEs (the current directory by default). OPTIONS -a, --all do not ignore entries starting with . -l use a long listing format -h, --human-readable with -l and -s, print sizes like 1K 234M 2G etc. EXAMPLES ls -la ls -lh /var """ class TestCommandInfo: """Tests for CommandInfo dataclass.""" def test_default_values(self): """Test CommandInfo has correct default values.""" info = CommandInfo(name="test") assert info.name == "test" assert info.synopsis == "" assert info.description == "" assert info.options == [] assert info.examples == [] assert info.section == "1" def test_with_values(self): """Test CommandInfo with all values.""" info = CommandInfo( name="ls", synopsis="ls [OPTION]... [FILE]...", description="List directory contents", options=[Option(flag="-a", description="show all files")], examples=["ls -la"], section="1" ) assert info.name == "ls" assert info.synopsis == "ls [OPTION]... [FILE]..." assert len(info.options) == 1 class TestOption: """Tests for Option dataclass.""" def test_option_creation(self): """Test Option can be created with all fields.""" opt = Option(flag="-v", description="verbose output", argument="FILE") assert opt.flag == "-v" assert opt.description == "verbose output" assert opt.argument == "FILE" def test_option_optional_argument(self): """Test Option with optional argument.""" opt = Option(flag="-h", description="help") assert opt.flag == "-h" assert opt.argument is None class TestManPageParser: """Tests for ManPageParser class.""" def setup_method(self): """Set up test fixtures.""" self.parser = ManPageParser() @patch('subprocess.run') def test_parse_simple_man_page(self, mock_run): """Test parsing a simple man page.""" mock_run.return_value = MagicMock( returncode=0, stdout=SAMPLE_MAN_PAGE ) result = self.parser.parse("ls") assert result.name == "ls" assert "OPTION" in result.synopsis @patch('subprocess.run') def test_parse_extracts_options(self, mock_run): """Test that options are correctly extracted.""" mock_run.return_value = MagicMock( returncode=0, stdout=SAMPLE_MAN_PAGE ) result = self.parser.parse("ls") assert len(result.options) >= 2 @patch('subprocess.run') def test_parse_extracts_examples(self, mock_run): """Test that examples are correctly extracted.""" mock_run.return_value = MagicMock( returncode=0, stdout=SAMPLE_MAN_PAGE ) result = self.parser.parse("ls") assert len(result.examples) >= 1 @patch('subprocess.run') def test_parse_command_not_found(self, mock_run): """Test handling of missing command.""" mock_run.return_value = MagicMock(returncode=1, stderr="No manual entry for unknowncmd") with pytest.raises(ValueError) as exc_info: self.parser.parse("unknowncmd") assert "command not found" in str(exc_info.value) def test_parse_content_empty(self): """Test parsing empty content.""" result = self.parser._parse_content("", "test") assert result.name == "test" assert result.synopsis == "" def test_parse_content_with_font_codes(self): """Test parsing content with font formatting codes.""" content = """ NAME test - a test command SYNOPSIS test \\fB-v\\fR [FILE] OPTIONS \\fB-v\\fR enable verbose mode """ result = self.parser._parse_content(content, "test") assert result.name == "test" assert "-v" in result.synopsis or "v" in result.synopsis def test_section_pattern_recognition(self): """Test section pattern recognition.""" assert self.parser.SECTION_PATTERN.match("NAME") assert self.parser.SECTION_PATTERN.match("SYNOPSIS") assert self.parser.SECTION_PATTERN.match("OPTIONS") assert not self.parser.SECTION_PATTERN.match("This is not a section") def test_option_pattern_recognition(self): """Test option pattern recognition.""" match = self.parser.OPTION_PATTERN.match("-a, --all") assert match is not None assert "-a" in match.group(1)