diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..366a2ce --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[run] +omit = + # omit test files + test_*.py \ No newline at end of file diff --git a/Makefile b/Makefile index 35d5eb0..81954ab 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: test test: - pytest -v --cov=. --cov-config=.coveragerc --cov-fail-under=50 --cov-report term-missing + pytest -v --cov=. --cov-config=.coveragerc --cov-fail-under=80 --cov-report term-missing .PHONY: clean clean: diff --git a/evergreen.py b/evergreen.py index 9b57b4f..88d7d58 100644 --- a/evergreen.py +++ b/evergreen.py @@ -9,7 +9,7 @@ from dependabot_file import build_dependabot_file -def main(): +def main(): # pragma: no cover """Run the main program""" # Get the environment variables @@ -174,4 +174,4 @@ def commit_changes(title, body, repo, dependabot_file): if __name__ == "__main__": - main() + main() # pragma: no cover diff --git a/test_dependabot_file.py b/test_dependabot_file.py index 5d6ab1e..770b7dc 100644 --- a/test_dependabot_file.py +++ b/test_dependabot_file.py @@ -3,6 +3,7 @@ import unittest from unittest.mock import MagicMock +import github3 from dependabot_file import build_dependabot_file @@ -11,13 +12,24 @@ class TestDependabotFile(unittest.TestCase): Test the dependabot_file.py functions. """ + def test_not_found_error(self): + """Test that the dependabot.yml file is built correctly with no package manager""" + repo = MagicMock() + response = MagicMock() + response.status_code = 404 + repo.file_contents.side_effect = github3.exceptions.NotFoundError(resp=response) + + result = build_dependabot_file(repo) + self.assertEqual(result, None) + def test_build_dependabot_file_with_bundler(self): """Test that the dependabot.yml file is built correctly with bundler""" repo = MagicMock() + filename_list = ["Gemfile", "Gemfile.lock"] - # return true for bundler only - repo.file_contents.side_effect = lambda filename: filename == "Gemfile.lock" - expected_result = """--- + for filename in filename_list: + repo.file_contents.side_effect = lambda f, filename=filename: f == filename + expected_result = """--- version: 2 updates: - package-ecosystem: 'bundler' @@ -25,17 +37,17 @@ def test_build_dependabot_file_with_bundler(self): schedule: interval: 'weekly' """ - result = build_dependabot_file(repo) - self.assertEqual(result, expected_result) + result = build_dependabot_file(repo) + self.assertEqual(result, expected_result) def test_build_dependabot_file_with_npm(self): """Test that the dependabot.yml file is built correctly with npm""" repo = MagicMock() - repo.file_contents.side_effect = ( - lambda filename: filename == "package-lock.json" - ) + filename_list = ["package.json", "package-lock.json", "yarn.lock"] - expected_result = """--- + for filename in filename_list: + repo.file_contents.side_effect = lambda f, filename=filename: f == filename + expected_result = """--- version: 2 updates: - package-ecosystem: 'npm' @@ -43,15 +55,23 @@ def test_build_dependabot_file_with_npm(self): schedule: interval: 'weekly' """ - result = build_dependabot_file(repo) - self.assertEqual(result, expected_result) + result = build_dependabot_file(repo) + self.assertEqual(result, expected_result) def test_build_dependabot_file_with_pip(self): """Test that the dependabot.yml file is built correctly with pip""" repo = MagicMock() - repo.file_contents.side_effect = lambda filename: filename == "requirements.txt" - - expected_result = """--- + filename_list = [ + "requirements.txt", + "Pipfile", + "Pipfile.lock", + "pyproject.toml", + "poetry.lock", + ] + + for filename in filename_list: + repo.file_contents.side_effect = lambda f, filename=filename: f == filename + expected_result = """--- version: 2 updates: - package-ecosystem: 'pip' @@ -59,15 +79,20 @@ def test_build_dependabot_file_with_pip(self): schedule: interval: 'weekly' """ - result = build_dependabot_file(repo) - self.assertEqual(result, expected_result) + result = build_dependabot_file(repo) + self.assertEqual(result, expected_result) def test_build_dependabot_file_with_cargo(self): """Test that the dependabot.yml file is built correctly with Cargo""" repo = MagicMock() - repo.file_contents.side_effect = lambda filename: filename == "Cargo.toml" - - expected_result = """--- + filename_list = [ + "Cargo.toml", + "Cargo.lock", + ] + + for filename in filename_list: + repo.file_contents.side_effect = lambda f, filename=filename: f == filename + expected_result = """--- version: 2 updates: - package-ecosystem: 'cargo' @@ -75,8 +100,8 @@ def test_build_dependabot_file_with_cargo(self): schedule: interval: 'weekly' """ - result = build_dependabot_file(repo) - self.assertEqual(result, expected_result) + result = build_dependabot_file(repo) + self.assertEqual(result, expected_result) def test_build_dependabot_file_with_gomod(self): """Test that the dependabot.yml file is built correctly with Go module""" @@ -97,9 +122,14 @@ def test_build_dependabot_file_with_gomod(self): def test_build_dependabot_file_with_composer(self): """Test that the dependabot.yml file is built correctly with Composer""" repo = MagicMock() - repo.file_contents.side_effect = lambda filename: filename == "composer.json" - - expected_result = """--- + filename_list = [ + "composer.json", + "composer.lock", + ] + + for filename in filename_list: + repo.file_contents.side_effect = lambda f, filename=filename: f == filename + expected_result = """--- version: 2 updates: - package-ecosystem: 'composer' @@ -107,15 +137,20 @@ def test_build_dependabot_file_with_composer(self): schedule: interval: 'weekly' """ - result = build_dependabot_file(repo) - self.assertEqual(result, expected_result) + result = build_dependabot_file(repo) + self.assertEqual(result, expected_result) def test_build_dependabot_file_with_hex(self): """Test that the dependabot.yml file is built correctly with Hex""" repo = MagicMock() - repo.file_contents.side_effect = lambda filename: filename == "mix.exs" - - expected_result = """--- + filename_list = [ + "mix.exs", + "mix.lock", + ] + + for filename in filename_list: + repo.file_contents.side_effect = lambda f, filename=filename: f == filename + expected_result = """--- version: 2 updates: - package-ecosystem: 'hex' @@ -123,8 +158,8 @@ def test_build_dependabot_file_with_hex(self): schedule: interval: 'weekly' """ - result = build_dependabot_file(repo) - self.assertEqual(result, expected_result) + result = build_dependabot_file(repo) + self.assertEqual(result, expected_result) def test_build_dependabot_file_with_nuget(self): """Test that the dependabot.yml file is built correctly with NuGet""" diff --git a/test_evergreen.py b/test_evergreen.py index e0a33b8..95bb4b3 100644 --- a/test_evergreen.py +++ b/test_evergreen.py @@ -1,9 +1,13 @@ """ Test the evergreen.py module. """ import unittest -from unittest.mock import patch +import uuid +from unittest.mock import MagicMock, patch from evergreen import ( + check_pending_pulls_for_duplicates, + commit_changes, enable_dependabot_security_updates, + get_repos_iterator, is_dependabot_security_updates_enabled, ) @@ -176,5 +180,126 @@ def test_enable_dependabot_security_updates_failed(self): ) +class TestCommitChanges(unittest.TestCase): + """Test the commit_changes function in evergreen.py""" + + @patch("uuid.uuid4") + def test_commit_changes(self, mock_uuid): + """Test the commit_changes function.""" + mock_uuid.return_value = uuid.UUID( + "12345678123456781234567812345678" + ) # Mock UUID generation + mock_repo = MagicMock() # Mock repo object + mock_repo.default_branch = "main" + mock_repo.ref.return_value.object.sha = "abc123" # Mock SHA for latest commit + mock_repo.create_ref.return_value = True + mock_repo.create_file.return_value = True + mock_repo.create_pull.return_value = "MockPullRequest" + + title = "Test Title" + body = "Test Body" + dependabot_file = 'dependencies:\n - package_manager: "python"\n directory: "/"\n update_schedule: "live"' + branch_name = "dependabot-12345678-1234-5678-1234-567812345678" + result = commit_changes(title, body, mock_repo, dependabot_file) + + # Assert that the methods were called with the correct arguments + mock_repo.create_ref.assert_called_once_with( + f"refs/heads/{branch_name}", "abc123" + ) + mock_repo.create_file.assert_called_once_with( + path=".github/dependabot.yaml", + message="Create dependabot.yaml", + content=dependabot_file.encode(), + branch=branch_name, + ) + mock_repo.create_pull.assert_called_once_with( + title=title, + body=body, + head=branch_name, + base="main", + ) + + # Assert that the function returned the expected result + self.assertEqual(result, "MockPullRequest") + + +class TestCheckPendingPullsForDuplicates(unittest.TestCase): + """Test the check_pending_pulls_for_duplicates function.""" + + def test_check_pending_pulls_for_duplicates_no_duplicates(self): + """Test the check_pending_pulls_for_duplicates function where there are no duplicates to be found.""" + mock_repo = MagicMock() # Mock repo object + mock_pull_request = MagicMock() + mock_pull_request.head.ref = "not-dependabot-branch" + mock_repo.pull_requests.return_value = [mock_pull_request] + + result = check_pending_pulls_for_duplicates(mock_repo) + + # Assert that the function returned the expected result + self.assertEqual(result, False) + + def test_check_pending_pulls_for_duplicates_with_duplicates(self): + """Test the check_pending_pulls_for_duplicates function where there are duplicates to be found.""" + mock_repo = MagicMock() # Mock repo object + mock_pull_request = MagicMock() + mock_pull_request.head.ref = "dependabot-branch" + mock_repo.pull_requests.return_value = [mock_pull_request] + + result = check_pending_pulls_for_duplicates(mock_repo) + + # Assert that the function returned the expected result + self.assertEqual(result, True) + + +class TestGetReposIterator(unittest.TestCase): + """Test the get_repos_iterator function in evergreen.py""" + + @patch("github3.login") + def test_get_repos_iterator_with_organization(self, mock_github): + """Test the get_repos_iterator function with an organization""" + organization = "my_organization" + repository_list = [] + github_connection = mock_github.return_value + + mock_organization = MagicMock() + mock_repositories = MagicMock() + mock_organization.repositories.return_value = mock_repositories + github_connection.organization.return_value = mock_organization + + result = get_repos_iterator(organization, repository_list, github_connection) + + # Assert that the organization method was called with the correct argument + github_connection.organization.assert_called_once_with(organization) + + # Assert that the repositories method was called on the organization object + mock_organization.repositories.assert_called_once() + + # Assert that the function returned the expected result + self.assertEqual(result, mock_repositories) + + @patch("github3.login") + def test_get_repos_iterator_with_repository_list(self, mock_github): + """Test the get_repos_iterator function with a repository list""" + organization = None + repository_list = ["org/repo1", "org/repo2"] + github_connection = mock_github.return_value + + mock_repository = MagicMock() + mock_repository_list = [mock_repository, mock_repository] + github_connection.repository.side_effect = mock_repository_list + + result = get_repos_iterator(organization, repository_list, github_connection) + + # Assert that the repository method was called with the correct arguments for each repository in the list + expected_calls = [ + unittest.mock.call("org", "repo1"), + unittest.mock.call("org", "repo2"), + ] + github_connection.repository.assert_has_calls(expected_calls) + + # Assert that the function returned the expected result + self.assertEqual(result, mock_repository_list) + + if __name__ == "__main__": unittest.main()