"""Contains unittests for the gitlab_tools module.
This project is developed by using test driven design.
The gitlab_tools module is specified to work with GitLab API v3.
"""
import copy
import os
import random
import shutil
import unittest
import git
import iso8601
from singularity_autobuild.gitlab_tools import (
GitLabPushEventInfo,
call_gitlab_events_api
)
[docs]class TestGitLabPushEventInfo(unittest.TestCase):
""" Test the the class capable of extracting information from gitlab api data.
The GitLabPushEventInfo is specified to work with data from
the GitLab v3 API.
The API returns data in json format and can be read into a dict using
the json module.
A mock git repository is created via gitpython in the stUp method.
A mock GitLab response is created by ingesting data from the mock git
repository into template python dictionaries.
Filler and expected push dictionaries are then merged in a list.
This creates a List, similar to what the standard library json
would parse from a GitLab API response string.
:cvar str BRANCH: The branch ingested into the expected_push data.
Functionality using branch selection is not jet
implemented.
:cvar str NEWEST_PUSH_DATE: Datestring in a format used by the GitLab API.
Is supposed to be the newest push date, ingested
into the mock API response.
:cvar datetime PUSH_DATE_OBJECT: NEWEST_PUSH_DATE parsed to a date object.
:cvar str GIT_TEST_REPO_PATH: Path to the mock git repository to be created.
:cvar int FROM_COMMIT_INDEX: List index of the first commit, that is part of the
newest push from the mock API response.
:cvar int TO_COMMIT_INDEX: List index of the last commit, that is part of the
newest push from the mock API response.
:cvar list RESPONSE_FILLER: Mock API Response without the expected_push data.
:ivar dict expected_push: Represents a single push event from a GitLab
API response. It is the reference push event,
that is expected to be returned as latest
push event.
:ivar list all_push_events: List of all push data sections from the mock
API response.
:ivar list git_lab_response: Mock api response, as expected when the read via
the json.read() method.
:ivar dict changed_files: A dict with all files changed in the most recent push
as keys and the type of change as their value.
:ivar git.Repo repo: A gitpython git.Repo object that handles the
mock repository.
"""
BRANCH = 'master'
NEWEST_PUSH_DATE = "2000-04-11T11:35:15.188Z"
PUSH_DATE_OBJECT = iso8601.parse_date(NEWEST_PUSH_DATE)
GIT_TEST_REPO_PATH = os.path.dirname(os.path.realpath(__file__)) + '/test_repo'
FROM_COMMIT_INDEX = 2
TO_COMMIT_INDEX = 4
# Part of the Response, that should be ignored when selecting the latest push.
# i.e. filler data
RESPONSE_FILLER = [
{
"project_id":42,
"action_name":"pushed to",
"created_at":"1999-02-11T11:35:15.188Z",
"author":{},
"push_data":{
"commit_count":2,
"action":"pushed",
"ref_type":"branch",
"commit_from":"from_commit",
"commit_to":"to_commit",
"ref":"branch_name",
"commit_title":"did_something"
},
"author_username":"test"
},
{
"project_id":"some_id",
"author_username":"test"
}
]
[docs] def setUp(self):
""" Initiate a test git repository. """
self.expected_push = {
"project_id":42,
"action_name":"pushed to",
"created_at":self.NEWEST_PUSH_DATE,
"author":{},
"push_data":{
"commit_count":5,
"action":"pushed",
"ref_type":"branch",
"commit_from": "from_commit_dummy", # From commit is here
"commit_to": "to_commit_dummy", # To commit is here
"ref": self.BRANCH,
"commit_title":"did_something"
},
"author_username":"test"
}
self.all_push_events = list
self.git_lab_response = list
self.changed_files = {}
self.repo = None
## Cleanup beforehand
if os.path.isdir(self.GIT_TEST_REPO_PATH):
shutil.rmtree(self.GIT_TEST_REPO_PATH)
if os.path.isfile(self.GIT_TEST_REPO_PATH):
os.remove(self.GIT_TEST_REPO_PATH)
# Set up Repo
os.mkdir(self.GIT_TEST_REPO_PATH)
# odbt is set because default value did not work with create_head
_repo = git.Repo.init(path=self.GIT_TEST_REPO_PATH, odbt=git.GitDB)
# Create some files and do some commits
_file_to_commit = ""
for commit in range(0, 5):
_file_to_commit = "%s/%s" % (
self.GIT_TEST_REPO_PATH, str(commit)
)
# Create and fill file with some content.
with open(_file_to_commit, 'w') as _file:
_file.write(
str(
random.randint(0, 1000000000000000000)
)
)
_repo.index.add([_file_to_commit])
_repo.index.commit("Made %s commit." % commit)
# make a new branch
_branch_name = 'test_branch'
_master = _repo.head.reference
# Create Branch
_branch = _repo.create_head(_branch_name)
# Switch to
_repo.head.reference = _branch
# Commit to
open(self.GIT_TEST_REPO_PATH + '/' + _branch_name, 'wb').close()
_repo.index.add([_branch_name])
_repo.index.commit(_branch_name)
# Switch back
_repo.head.reference = _master
## Set up the mock GitLab API response to work with.
# Get the hashes of the commits that define content of the mock push.
_from_commit = _repo.head.log_entry(self.FROM_COMMIT_INDEX).newhexsha
_to_commit = _repo.head.log_entry(self.TO_COMMIT_INDEX).newhexsha
# Define the dictionary containing changed filenames
_from_commit_objects = _repo.commit(_from_commit).parents
for _from_commit_object in _from_commit_objects:
for _commit_file in _from_commit_object.diff(_repo.commit(_to_commit)):
_filepath = os.path.abspath(_commit_file.a_path)
self.changed_files[_filepath] = _commit_file.change_type
# Make a copy of the class constant
self.expected_push["push_data"]["commit_from"] = _from_commit
self.expected_push["push_data"]["commit_to"] = _to_commit
# Make a copy of the class constant
self.git_lab_response = copy.deepcopy(self.RESPONSE_FILLER)
self.all_push_events = [self.expected_push, self.git_lab_response[0]]
self.git_lab_response.append(self.expected_push)
self.repo = _repo
[docs] def test_is_modified_file(self):
""" Test the function to check if a file was modified since the last push. """
# Get full path of all repo files
_all_files_set = set()
for root, _, files in os.walk(self.GIT_TEST_REPO_PATH):
for file in files:
_all_files_set.add(
os.path.abspath(os.path.join(
root, file
))
)
# Get names of all changed files.
_changed_fileset = set(
[key for key in self.changed_files]
)
_unchanged_fileset = list(_all_files_set - _changed_fileset)
_object = GitLabPushEventInfo(
git_lab_response=self.git_lab_response,
local_repo=self.GIT_TEST_REPO_PATH
)
# Test if all changed files are categorized correctly
for _file in _changed_fileset:
self.assertTrue(_object.is_modified_file(
file_path=_file
))
# Test if all unchanged files are categorized correctly
for _file in _unchanged_fileset:
self.assertFalse(
_object.is_modified_file(file_path=_file)
)
[docs] def test_get_modified_files(self):
""" Test the function, that returns all modified files between two commits. """
_expected_filedict = self.changed_files
_test_filelist = GitLabPushEventInfo.get_changed_files(
self.repo,
self.expected_push["push_data"]["commit_from"],
self.expected_push["push_data"]["commit_to"]
)
self.assertEqual(_expected_filedict, _test_filelist)
[docs] def test_get_latest_push(self):
""" Test the function to get last push date.(not the most recent) """
_test_push_date = GitLabPushEventInfo.get_latest_push(
git_lab_response=self.all_push_events
)
_expected_value = self.expected_push
self.assertEqual(_test_push_date, _expected_value)
[docs] def test_instantiation(self):
""" Test the constructor of the class. """
_expected_from_commit_sha = self.expected_push["push_data"]["commit_from"]
_expected_to_commit_sha = self.expected_push["push_data"]["commit_to"]
_test_object = GitLabPushEventInfo(
git_lab_response=self.git_lab_response,
local_repo=self.GIT_TEST_REPO_PATH
)
_test_from_commit_sha = _test_object.from_commit.hexsha
_test_to_commit_sha = _test_object.to_commit.hexsha
self.assertEqual(_expected_from_commit_sha, _test_from_commit_sha)
self.assertEqual(_expected_to_commit_sha, _test_to_commit_sha)
[docs] def tearDown(self):
""" Clean up test git repo. """
if os.path.isdir(self.GIT_TEST_REPO_PATH):
shutil.rmtree(self.GIT_TEST_REPO_PATH)
if os.path.isfile(self.GIT_TEST_REPO_PATH):
os.remove(self.GIT_TEST_REPO_PATH)
[docs]class TestGitlabAPIRequest(unittest.TestCase):
"""Test the function to call a gitlab API.
The Functions tested here are specified to work with
a GitLab API v3.
"""
[docs] def test_gitlab_events_api_request(self):
"""Test the function to call a gitlab API.
The API should return a json list filled with objects.
"""
try:
_api_url = os.environ['GITLAB_API_STRING']
_api_key = os.environ['GITLAB_API_TOKEN']
except KeyError:
raise EnvironmentError("GitLab API environment variables are not set.")
_api_response = call_gitlab_events_api(api_url=_api_url, api_key=_api_key)
# First level is a list,
self.assertIsInstance(_api_response, list)
# filled with objects
self.assertIsInstance(_api_response[0], dict)