Nekmo/pip-rating

View on GitHub
tests/sources/test_sourcecode_page.py

Summary

Maintainability
A
0 mins
Test Coverage
import unittest
from unittest.mock import patch, MagicMock, Mock, PropertyMock

from requests import RequestException

from pip_rating.sources.sourcecode_page import (
    get_github_readme,
    search_in_readme,
    SourcecodePage,
    replace_chars,
)


class TestGetGithubReadme(unittest.TestCase):
    """Test the get_github_readme function."""

    @patch("pip_rating.sources.sourcecode_page.click.echo")
    @patch("pip_rating.sources.sourcecode_page.github_token", new="token")
    @patch("pip_rating.sources.sourcecode_page.requests.get")
    def test_get_github_readme(
        self, mock_requests_get: MagicMock, mock_echo: MagicMock
    ):
        """Test the get_github_readme function."""
        with self.subTest("Test with readme"):
            mock_requests_get.return_value.__enter__.return_value.json.return_value = {
                "content": "Y29udGVudA=="
            }
            self.assertEqual("content", get_github_readme("owner", "repo"))
            mock_requests_get.return_value.__enter__.return_value.raise_for_status.assert_called_once_with()
            mock_requests_get.assert_called_once_with(
                "https://api.github.com/repos/owner/repo/readme",
                headers={"Authorization": "Bearer token"},
            )
        with self.subTest("Test rate limit exceded with GITHUB_TOKEN defined"):
            mock_requests_get.return_value.__enter__.return_value.raise_for_status.side_effect = RequestException(
                response=MagicMock(status_code=403, reason="rate limit exceeded")
            )
            self.assertEqual("", get_github_readme("owner", "repo"))
            mock_echo.assert_called_once_with(
                "GitHub rate limit exceeded. Check your GITHUB_TOKEN environment variable.",
                err=True,
            )
        mock_echo.reset_mock()
        with self.subTest(
            "Test rate limit exceded without GITHUB_TOKEN defined"
        ), patch("pip_rating.sources.sourcecode_page.github_token", new=None), patch(
            "pip_rating.sources.sourcecode_page.github_warning", new=False
        ):
            mock_requests_get.return_value.__enter__.return_value.raise_for_status.side_effect = RequestException(
                response=MagicMock(status_code=403, reason="rate limit exceeded")
            )
            self.assertEqual("", get_github_readme("owner", "repo"))
            mock_echo.assert_called_once_with(
                "GitHub rate limit exceeded. Set GITHUB_TOKEN environment variable to increase the limit.",
                err=True,
            )


class TestReplaceChars(unittest.TestCase):
    """Test the replace_chars function."""

    def test_replace_chars(self):
        """Test the replace_chars function."""
        with self.subTest("Test with dash char"):
            self.assertEqual("package-name123", replace_chars("package-name123"))
        with self.subTest("Test with other chars"):
            self.assertEqual("package-name-123", replace_chars("package_name.123"))


class TestSearchInReadme(unittest.TestCase):
    """Test the search_in_readme function."""

    def test_search_in_readme(self):
        """Test the search_in_readme function."""
        with self.subTest("Test with pip install pattern"):
            self.assertTrue(
                search_in_readme("pip  install -U  package_name123", "package-name123")
            )
        with self.subTest("Test with poetry add pattern"):
            self.assertTrue(
                search_in_readme("poetry  add  package-name123", "package-name123")
            )
        with self.subTest("Test with pipenv install pattern"):
            self.assertTrue(
                search_in_readme("pipenv  install  package-name123", "package-name123")
            )
        with self.subTest("Test with pipenv invalid pattern"):
            self.assertIsNone(search_in_readme("pip install -P", "package-name123"))
        with self.subTest("Test package name not in readme"):
            self.assertFalse(
                search_in_readme("pip install other-package", "package-name123")
            )


class TestSourcecodePage(unittest.TestCase):
    """Test the SourcecodePage class."""

    @patch("pip_rating.sources.sourcecode_page.SourceBase.__init__")
    def test_init(self, mock_init: MagicMock):
        """Test the __init__ method."""
        mock_init.return_value = None
        mock_package = Mock()
        sourcecode_page = SourcecodePage(mock_package)
        self.assertEqual(mock_package, sourcecode_page.package)
        mock_init.assert_called_once_with(mock_package.name)

    @patch("pip_rating.sources.sourcecode_page.datetime")
    @patch("pip_rating.sources.sourcecode_page.get_github_readme")
    def test_get_cache_data(
        self, mock_get_github_readme: MagicMock, mock_datetime: MagicMock
    ):
        """Test the get_cache_data method."""
        with self.subTest("Test package in readme"):
            mock_get_github_readme.return_value = "pip install repo"
            mock_package = Mock()
            mock_package.name = "repo"
            mock_package.pypi.package = {
                "info": {"project_urls": {"Source": "https://github.com/owner/repo"}}
            }
            cache_dict = SourcecodePage(mock_package).get_cache_data()
            self.assertEqual(
                {
                    "package_name": "repo",
                    "updated_at": mock_datetime.datetime.now.return_value.isoformat.return_value,
                    "source": "github",
                    "sourcecode": {
                        "package_in_readme": True,
                        "readme_content": mock_get_github_readme.return_value,
                    },
                },
                cache_dict,
            )
            mock_get_github_readme.assert_called_once_with("owner", "repo")
        with self.subTest("Test missing project urls in package"):
            mock_package = Mock()
            mock_package.name = "repo"
            mock_package.pypi.package = {"info": {"project_urls": None}}
            cache_dict = SourcecodePage(mock_package).get_cache_data()
            self.assertEqual(
                {
                    "package_name": "repo",
                    "updated_at": mock_datetime.datetime.now.return_value.isoformat.return_value,
                    "source": "github",
                    "sourcecode": {
                        "package_in_readme": None,
                        "readme_content": "",
                    },
                },
                cache_dict,
            )
            mock_get_github_readme.assert_called_once_with("owner", "repo")

    @patch(
        "pip_rating.sources.sourcecode_page.SourceBase.is_cache_expired",
        new_callable=PropertyMock,
    )
    def test_package(self, mock_is_cache_expired: MagicMock):
        """Test the package property."""
        mock_package = Mock()
        with self.subTest("Test cache not expired"), patch(
            "pip_rating.sources.sourcecode_page.SourcecodePage.get_from_cache"
        ) as mock_get_from_cache:
            mock_is_cache_expired.return_value = False
            mock_get_from_cache.return_value = {
                "sourcecode": {"package_in_readme": True}
            }
            sourcecode_page = SourcecodePage(mock_package)
            self.assertTrue(sourcecode_page.package_in_readme)
            mock_get_from_cache.assert_called_once_with()
        with self.subTest("Test cache expired"), patch(
            "pip_rating.sources.sourcecode_page.SourcecodePage.save_to_cache"
        ) as mock_save_to_cache:
            mock_is_cache_expired.return_value = True
            mock_save_to_cache.return_value = {
                "sourcecode": {"package_in_readme": True}
            }
            sourcecode_page = SourcecodePage(mock_package)
            self.assertTrue(sourcecode_page.package_in_readme)
            mock_save_to_cache.assert_called_once_with()