Academic paper knowledge organizer
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121"""Unit tests for paper_library.cli.
These tests cover the validate and init commands. They mock config so noreal vault or API key is needed."""
import pytestfrom pathlib import Pathfrom unittest.mock import patch, MagicMockfrom click.testing import CliRunner
from paper_library.cli import cli
class TestValidateCommand: """validate is read-only: it must never create directories."""
def test_validate_passes_when_api_key_and_vault_exist(self, tmp_path): """Both checks green → exit 0.""" runner = CliRunner() mock_config = MagicMock() mock_config.anthropic_api_key = "sk-test-key" mock_config.vault_path = tmp_path # tmp_path exists
with patch("paper_library.cli.config", mock_config): result = runner.invoke(cli, ["validate"])
assert result.exit_code == 0 assert "All required checks passed" in result.output # validate must not create any new paths assert not (tmp_path / "Papers").exists()
def test_validate_fails_when_api_key_missing(self, tmp_path): """Missing API key → exit 1, error message shown.""" runner = CliRunner() mock_config = MagicMock() mock_config.anthropic_api_key = None mock_config.vault_path = tmp_path
with patch("paper_library.cli.config", mock_config): result = runner.invoke(cli, ["validate"])
assert result.exit_code == 1 assert "ANTHROPIC_API_KEY" in result.output
def test_validate_fails_when_vault_missing(self, tmp_path): """Non-existent vault → exit 1, no directory created, init hint shown.""" missing_vault = tmp_path / "nonexistent_vault" runner = CliRunner() mock_config = MagicMock() mock_config.anthropic_api_key = "sk-test-key" mock_config.vault_path = missing_vault
with patch("paper_library.cli.config", mock_config): result = runner.invoke(cli, ["validate"])
assert result.exit_code == 1 assert not missing_vault.exists(), "validate must NOT create the vault" assert "alcanzai init" in result.output
def test_validate_reports_both_failures(self, tmp_path): """Two failures → mentions both, exits 1.""" missing_vault = tmp_path / "nonexistent" runner = CliRunner() mock_config = MagicMock() mock_config.anthropic_api_key = None mock_config.vault_path = missing_vault
with patch("paper_library.cli.config", mock_config): result = runner.invoke(cli, ["validate"])
assert result.exit_code == 1 assert "ANTHROPIC_API_KEY" in result.output assert "alcanzai init" in result.output
class TestInitCommand: """init creates the vault structure; safe to re-run."""
def test_init_creates_vault_subdirectories(self, tmp_path): """init must create Papers, Articles, PDFs, Sources, _meta.""" vault = tmp_path / "vault" runner = CliRunner() mock_config = MagicMock() mock_config.vault_path = vault
with patch("paper_library.cli.config", mock_config): result = runner.invoke(cli, ["init"])
assert result.exit_code == 0 for subdir in ("Papers", "Articles", "PDFs", "Sources", "_meta"): assert (vault / subdir).exists(), f"Missing subdirectory: {subdir}"
def test_init_is_idempotent(self, tmp_path): """Running init twice on an existing vault does not error.""" vault = tmp_path / "vault" vault.mkdir() runner = CliRunner() mock_config = MagicMock() mock_config.vault_path = vault
with patch("paper_library.cli.config", mock_config): result1 = runner.invoke(cli, ["init"]) result2 = runner.invoke(cli, ["init"])
assert result1.exit_code == 0 assert result2.exit_code == 0
def test_init_reports_success(self, tmp_path): """init output includes the vault path and a success message.""" vault = tmp_path / "vault" runner = CliRunner() mock_config = MagicMock() mock_config.vault_path = vault
with patch("paper_library.cli.config", mock_config): result = runner.invoke(cli, ["init"])
assert "Vault initialized" in result.output