"""Tests for paperlib CLI functionality.""" import shutil import subprocess from pathlib import Path from unittest.mock import patch import pytest class TestCLI: """Test CLI functionality.""" @pytest.fixture def temp_library(self): """Create a temporary library for testing.""" temp_dir = Path("./.tmp") / f"test_cli_{hash(self)}" temp_dir.mkdir(parents=True, exist_ok=True) yield temp_dir # Cleanup if temp_dir.exists(): shutil.rmtree(temp_dir) @pytest.fixture def sample_pdf(self): """Create a sample PDF file for testing.""" pdf_file = Path("./.tmp") / f"cli_test_{hash(self)}.pdf" with pdf_file.open("wb") as f: # Minimal PDF content f.write(b"%PDF-1.4\n") f.write(b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n") f.write(b"%%EOF\n") yield pdf_file # Cleanup if pdf_file.exists(): pdf_file.unlink() def run_paperlib_cmd(self, *args): """Helper to run paperlib commands.""" cmd = ["uv", "run", "paperlib"] + list(args) result = subprocess.run(cmd, capture_output=True, text=True, cwd=Path.cwd()) return result def test_cli_help(self): """Test CLI help output.""" result = self.run_paperlib_cmd("--help") assert result.returncode == 0 assert "paperlib" in result.stdout assert "Local-first paper library engine" in result.stdout assert "init" in result.stdout assert "import" in result.stdout assert "convert" in result.stdout def test_cli_version(self): """Test CLI version output.""" result = self.run_paperlib_cmd("--version") assert result.returncode == 0 assert "paperlib" in result.stdout assert "0.1.0" in result.stdout def test_init_command(self, temp_library): """Test library initialization command.""" result = self.run_paperlib_cmd("init", str(temp_library)) assert result.returncode == 0 assert "Initialized paper library" in result.stdout # Check directory structure was created assert (temp_library / "config").exists() assert (temp_library / "papers").exists() assert (temp_library / "inbox").exists() assert (temp_library / "db").exists() assert (temp_library / "cache").exists() def test_status_command(self, temp_library): """Test status command.""" # Initialize library first self.run_paperlib_cmd("init", str(temp_library)) result = self.run_paperlib_cmd("status", "--library", str(temp_library)) assert result.returncode == 0 # Check for absolute path since that's what we get assert str(temp_library.resolve()) in result.stdout assert "config:" in result.stdout assert "database:" in result.stdout assert "papers:" in result.stdout def test_import_local_pdf_command(self, temp_library, sample_pdf): """Test importing local PDF via CLI.""" # Initialize library self.run_paperlib_cmd("init", str(temp_library)) # Import PDF result = self.run_paperlib_cmd( "import", "--pdf", str(sample_pdf), "--title", "Test CLI Paper", "--tags", "test", "cli", "--library", str(temp_library), ) assert result.returncode == 0 assert "Successfully imported local PDF" in result.stdout assert "Test CLI Paper" in result.stdout def test_list_command_empty(self, temp_library): """Test list command with empty library.""" self.run_paperlib_cmd("init", str(temp_library)) result = self.run_paperlib_cmd("list", "--library", str(temp_library)) assert result.returncode == 0 assert "No papers found" in result.stdout def test_list_command_with_papers(self, temp_library, sample_pdf): """Test list command with papers.""" # Initialize and import self.run_paperlib_cmd("init", str(temp_library)) self.run_paperlib_cmd( "import", "--pdf", str(sample_pdf), "--title", "Test Paper for List", "--library", str(temp_library), ) result = self.run_paperlib_cmd("list", "--library", str(temp_library)) assert result.returncode == 0 assert "Found 1 papers" in result.stdout assert "Test Paper for List" in result.stdout def test_show_command(self, temp_library, sample_pdf): """Test show command.""" # Initialize and import self.run_paperlib_cmd("init", str(temp_library)) import_result = self.run_paperlib_cmd( "import", "--pdf", str(sample_pdf), "--title", "Test Paper for Show", "--library", str(temp_library), ) # Extract paper ID from import output paper_id = None for line in import_result.stdout.split("\n"): if "Successfully imported local PDF:" in line: paper_id = line.split(":")[-1].strip() break assert paper_id is not None # Show paper details result = self.run_paperlib_cmd("show", paper_id, "--library", str(temp_library)) assert result.returncode == 0 assert f"Paper ID: {paper_id}" in result.stdout assert "Test Paper for Show" in result.stdout assert "Source: local" in result.stdout def test_show_nonexistent_paper(self, temp_library): """Test show command with nonexistent paper.""" self.run_paperlib_cmd("init", str(temp_library)) result = self.run_paperlib_cmd( "show", "nonexistent", "--library", str(temp_library) ) assert result.returncode == 1 assert "Paper not found" in result.stdout def test_reindex_command(self, temp_library, sample_pdf): """Test reindex command.""" # Initialize and import self.run_paperlib_cmd("init", str(temp_library)) self.run_paperlib_cmd( "import", "--pdf", str(sample_pdf), "--library", str(temp_library) ) # Reindex result = self.run_paperlib_cmd("reindex", "--library", str(temp_library)) assert result.returncode == 0 assert "Rebuilding search index" in result.stdout assert "papers indexed" in result.stdout assert "Total papers: 1" in result.stdout def test_convert_command_no_papers(self, temp_library): """Test convert command with no papers.""" self.run_paperlib_cmd("init", str(temp_library)) result = self.run_paperlib_cmd("convert", "--library", str(temp_library)) assert result.returncode == 0 assert "Complete: 0 successful, 0 failed" in result.stdout def test_convert_command_with_papers_no_mineru(self, temp_library, sample_pdf): """Test convert command with papers when MinerU is not available.""" # Initialize and import self.run_paperlib_cmd("init", str(temp_library)) self.run_paperlib_cmd( "import", "--pdf", str(sample_pdf), "--library", str(temp_library) ) # Convert (will fail because MinerU command may not be properly set up) result = self.run_paperlib_cmd("convert", "--library", str(temp_library)) # Should complete but may have failures due to MinerU setup assert "Complete:" in result.stdout def test_invalid_command(self): """Test invalid command.""" result = self.run_paperlib_cmd("invalid-command") assert result.returncode != 0 def test_missing_required_arguments(self): """Test commands with missing required arguments.""" # Import without PDF or arXiv result = self.run_paperlib_cmd("import") assert result.returncode != 0 # Show without paper ID result = self.run_paperlib_cmd("show") assert result.returncode != 0