Adds EPUB as a first-class input source for skill generation. - EpubToSkillConverter (epub_scraper.py, ~1200 lines) following PDF scraper pattern - Dublin Core metadata, spine items, code blocks, tables, images extraction - DRM detection (Adobe ADEPT, Apple FairPlay, Readium LCP) with fail-fast - EPUB 3 NCX TOC bug workaround (ignore_ncx=True) - ebooklib as optional dep: pip install skill-seekers[epub] - Wired into create command with .epub auto-detection - 104 tests, all passing Review fixes: removed 3 empty test stubs, fixed SVG double-counting in _extract_images(), added logger.debug to bare except pass. Based on PR #310 by @christianbaumann. Co-authored-by: Christian Baumann <mail@chriss-baumann.de>
253 lines
8.5 KiB
Python
253 lines
8.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Tests for CLI Parser System
|
|
|
|
Tests the modular parser registration system.
|
|
"""
|
|
|
|
import argparse
|
|
import pytest
|
|
|
|
from skill_seekers.cli.parsers import (
|
|
PARSERS,
|
|
SubcommandParser,
|
|
get_parser_names,
|
|
register_parsers,
|
|
)
|
|
from skill_seekers.cli.parsers.scrape_parser import ScrapeParser
|
|
from skill_seekers.cli.parsers.github_parser import GitHubParser
|
|
from skill_seekers.cli.parsers.package_parser import PackageParser
|
|
|
|
|
|
class TestParserRegistry:
|
|
"""Test parser registry functionality."""
|
|
|
|
def test_all_parsers_registered(self):
|
|
"""Test that all parsers are registered."""
|
|
assert len(PARSERS) == 25, f"Expected 25 parsers, got {len(PARSERS)}"
|
|
|
|
def test_get_parser_names(self):
|
|
"""Test getting list of parser names."""
|
|
names = get_parser_names()
|
|
assert len(names) == 25
|
|
assert "scrape" in names
|
|
assert "github" in names
|
|
assert "package" in names
|
|
assert "upload" in names
|
|
assert "analyze" in names
|
|
assert "config" in names
|
|
assert "workflows" in names
|
|
assert "video" in names
|
|
|
|
def test_all_parsers_are_subcommand_parsers(self):
|
|
"""Test that all parsers inherit from SubcommandParser."""
|
|
for parser in PARSERS:
|
|
assert isinstance(parser, SubcommandParser)
|
|
|
|
def test_all_parsers_have_required_properties(self):
|
|
"""Test that all parsers have name, help, description."""
|
|
for parser in PARSERS:
|
|
assert hasattr(parser, "name")
|
|
assert hasattr(parser, "help")
|
|
assert hasattr(parser, "description")
|
|
assert isinstance(parser.name, str)
|
|
assert isinstance(parser.help, str)
|
|
assert isinstance(parser.description, str)
|
|
assert len(parser.name) > 0
|
|
assert len(parser.help) > 0
|
|
|
|
def test_all_parsers_have_add_arguments_method(self):
|
|
"""Test that all parsers implement add_arguments."""
|
|
for parser in PARSERS:
|
|
assert hasattr(parser, "add_arguments")
|
|
assert callable(parser.add_arguments)
|
|
|
|
def test_no_duplicate_parser_names(self):
|
|
"""Test that all parser names are unique."""
|
|
names = [p.name for p in PARSERS]
|
|
assert len(names) == len(set(names)), "Duplicate parser names found!"
|
|
|
|
|
|
class TestParserCreation:
|
|
"""Test parser creation functionality."""
|
|
|
|
def test_scrape_parser_creates_subparser(self):
|
|
"""Test that ScrapeParser creates valid subparser."""
|
|
main_parser = argparse.ArgumentParser()
|
|
subparsers = main_parser.add_subparsers()
|
|
|
|
scrape_parser = ScrapeParser()
|
|
subparser = scrape_parser.create_parser(subparsers)
|
|
|
|
assert subparser is not None
|
|
assert scrape_parser.name == "scrape"
|
|
assert scrape_parser.help == "Scrape documentation website"
|
|
|
|
def test_github_parser_creates_subparser(self):
|
|
"""Test that GitHubParser creates valid subparser."""
|
|
main_parser = argparse.ArgumentParser()
|
|
subparsers = main_parser.add_subparsers()
|
|
|
|
github_parser = GitHubParser()
|
|
subparser = github_parser.create_parser(subparsers)
|
|
|
|
assert subparser is not None
|
|
assert github_parser.name == "github"
|
|
|
|
def test_package_parser_creates_subparser(self):
|
|
"""Test that PackageParser creates valid subparser."""
|
|
main_parser = argparse.ArgumentParser()
|
|
subparsers = main_parser.add_subparsers()
|
|
|
|
package_parser = PackageParser()
|
|
subparser = package_parser.create_parser(subparsers)
|
|
|
|
assert subparser is not None
|
|
assert package_parser.name == "package"
|
|
|
|
def test_register_parsers_creates_all_subcommands(self):
|
|
"""Test that register_parsers creates all 19 subcommands."""
|
|
main_parser = argparse.ArgumentParser()
|
|
subparsers = main_parser.add_subparsers(dest="command")
|
|
|
|
# Register all parsers
|
|
register_parsers(subparsers)
|
|
|
|
# Test that all commands can be parsed
|
|
test_commands = [
|
|
"config --show",
|
|
"scrape --config test.json",
|
|
"github --repo owner/repo",
|
|
"package output/test/",
|
|
"upload test.zip",
|
|
"analyze --directory .",
|
|
"enhance output/test/",
|
|
"estimate test.json",
|
|
]
|
|
|
|
for cmd in test_commands:
|
|
args = main_parser.parse_args(cmd.split())
|
|
assert args.command is not None
|
|
|
|
|
|
class TestSpecificParsers:
|
|
"""Test specific parser implementations."""
|
|
|
|
def test_scrape_parser_arguments(self):
|
|
"""Test ScrapeParser has correct arguments."""
|
|
main_parser = argparse.ArgumentParser()
|
|
subparsers = main_parser.add_subparsers(dest="command")
|
|
|
|
scrape_parser = ScrapeParser()
|
|
scrape_parser.create_parser(subparsers)
|
|
|
|
# Test various argument combinations
|
|
args = main_parser.parse_args(["scrape", "--config", "test.json"])
|
|
assert args.command == "scrape"
|
|
assert args.config == "test.json"
|
|
|
|
args = main_parser.parse_args(["scrape", "--config", "test.json", "--max-pages", "100"])
|
|
assert args.max_pages == 100
|
|
|
|
args = main_parser.parse_args(["scrape", "--enhance-level", "2"])
|
|
assert args.enhance_level == 2
|
|
|
|
def test_github_parser_arguments(self):
|
|
"""Test GitHubParser has correct arguments."""
|
|
main_parser = argparse.ArgumentParser()
|
|
subparsers = main_parser.add_subparsers(dest="command")
|
|
|
|
github_parser = GitHubParser()
|
|
github_parser.create_parser(subparsers)
|
|
|
|
args = main_parser.parse_args(["github", "--repo", "owner/repo"])
|
|
assert args.command == "github"
|
|
assert args.repo == "owner/repo"
|
|
|
|
args = main_parser.parse_args(["github", "--repo", "owner/repo", "--non-interactive"])
|
|
assert args.non_interactive is True
|
|
|
|
def test_package_parser_arguments(self):
|
|
"""Test PackageParser has correct arguments."""
|
|
main_parser = argparse.ArgumentParser()
|
|
subparsers = main_parser.add_subparsers(dest="command")
|
|
|
|
package_parser = PackageParser()
|
|
package_parser.create_parser(subparsers)
|
|
|
|
args = main_parser.parse_args(["package", "output/test/"])
|
|
assert args.command == "package"
|
|
assert args.skill_directory == "output/test/"
|
|
|
|
args = main_parser.parse_args(["package", "output/test/", "--target", "gemini"])
|
|
assert args.target == "gemini"
|
|
|
|
args = main_parser.parse_args(["package", "output/test/", "--no-open"])
|
|
assert args.no_open is True
|
|
|
|
def test_analyze_parser_arguments(self):
|
|
"""Test AnalyzeParser has correct arguments."""
|
|
main_parser = argparse.ArgumentParser()
|
|
subparsers = main_parser.add_subparsers(dest="command")
|
|
|
|
from skill_seekers.cli.parsers.analyze_parser import AnalyzeParser
|
|
|
|
analyze_parser = AnalyzeParser()
|
|
analyze_parser.create_parser(subparsers)
|
|
|
|
args = main_parser.parse_args(["analyze", "--directory", "."])
|
|
assert args.command == "analyze"
|
|
assert args.directory == "."
|
|
|
|
args = main_parser.parse_args(["analyze", "--directory", ".", "--quick"])
|
|
assert args.quick is True
|
|
|
|
args = main_parser.parse_args(["analyze", "--directory", ".", "--comprehensive"])
|
|
assert args.comprehensive is True
|
|
|
|
args = main_parser.parse_args(["analyze", "--directory", ".", "--skip-patterns"])
|
|
assert args.skip_patterns is True
|
|
|
|
|
|
class TestBackwardCompatibility:
|
|
"""Test backward compatibility with old CLI."""
|
|
|
|
def test_all_original_commands_still_work(self):
|
|
"""Test that all original commands are still registered."""
|
|
names = get_parser_names()
|
|
|
|
# Original commands from old main.py
|
|
original_commands = [
|
|
"config",
|
|
"scrape",
|
|
"github",
|
|
"pdf",
|
|
"unified",
|
|
"enhance",
|
|
"enhance-status",
|
|
"package",
|
|
"upload",
|
|
"estimate",
|
|
"extract-test-examples",
|
|
"install-agent",
|
|
"analyze",
|
|
"install",
|
|
"resume",
|
|
"stream",
|
|
"update",
|
|
"multilang",
|
|
"quality",
|
|
]
|
|
|
|
for cmd in original_commands:
|
|
assert cmd in names, f"Command '{cmd}' not found in parser registry!"
|
|
|
|
def test_command_count_matches(self):
|
|
"""Test that we have exactly 25 commands (includes create, workflows, word, epub, video, and sync-config)."""
|
|
assert len(PARSERS) == 25
|
|
assert len(get_parser_names()) == 25
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|