Merge pull request #2394 from coreos-inc/new-trigger-ux-validation
Testing for build triggers
This commit is contained in:
commit
1c52427cd2
14 changed files with 1123 additions and 48 deletions
|
@ -6,6 +6,79 @@ from endpoints.building import PreparedBuild
|
||||||
from data import model
|
from data import model
|
||||||
from buildtrigger.triggerutil import get_trigger_config, InvalidServiceException
|
from buildtrigger.triggerutil import get_trigger_config, InvalidServiceException
|
||||||
|
|
||||||
|
NAMESPACES_SCHEMA = {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'personal': {
|
||||||
|
'type': 'boolean',
|
||||||
|
'description': 'True if the namespace is the user\'s personal namespace',
|
||||||
|
},
|
||||||
|
'score': {
|
||||||
|
'type': 'number',
|
||||||
|
'description': 'Score of the relevance of the namespace',
|
||||||
|
},
|
||||||
|
'avatar_url': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'URL of the avatar for this namespace',
|
||||||
|
},
|
||||||
|
'url': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'URL of the website to view the namespace',
|
||||||
|
},
|
||||||
|
'id': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'Trigger-internal ID of the namespace',
|
||||||
|
},
|
||||||
|
'title': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'Human-readable title of the namespace',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['personal', 'score', 'avatar_url', 'url', 'id', 'title'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
BUILD_SOURCES_SCHEMA = {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'name': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'The name of the repository, without its namespace',
|
||||||
|
},
|
||||||
|
'full_name': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'The name of the repository, with its namespace',
|
||||||
|
},
|
||||||
|
'description': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'The description of the repository. May be an empty string',
|
||||||
|
},
|
||||||
|
'last_updated': {
|
||||||
|
'type': 'number',
|
||||||
|
'description': 'The date/time when the repository was last updated, since epoch in UTC',
|
||||||
|
},
|
||||||
|
'url': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'The URL at which to view the repository in the browser',
|
||||||
|
},
|
||||||
|
'has_admin_permissions': {
|
||||||
|
'type': 'boolean',
|
||||||
|
'description': 'True if the current user has admin permissions on the repository',
|
||||||
|
},
|
||||||
|
'private': {
|
||||||
|
'type': 'boolean',
|
||||||
|
'description': 'True if the repository is private',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['name', 'full_name', 'description', 'last_updated', 'url',
|
||||||
|
'has_admin_permissions', 'private'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
METADATA_SCHEMA = {
|
METADATA_SCHEMA = {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
@ -242,3 +315,14 @@ class BuildTriggerHandler(object):
|
||||||
prepared.tags = [commit_sha[:7]]
|
prepared.tags = [commit_sha[:7]]
|
||||||
|
|
||||||
return prepared
|
return prepared
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def build_sources_response(cls, sources):
|
||||||
|
validate(sources, BUILD_SOURCES_SCHEMA)
|
||||||
|
return sources
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def build_namespaces_response(cls, namespaces_dict):
|
||||||
|
namespaces = list(namespaces_dict.values())
|
||||||
|
validate(namespaces, NAMESPACES_SCHEMA)
|
||||||
|
return namespaces
|
||||||
|
|
|
@ -35,7 +35,7 @@ BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'required': ['full_name'],
|
'required': ['full_name'],
|
||||||
},
|
}, # /Repository
|
||||||
'push': {
|
'push': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
@ -91,10 +91,10 @@ BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'required': ['html', 'avatar'],
|
'required': ['html', 'avatar'],
|
||||||
},
|
}, # /User
|
||||||
},
|
},
|
||||||
'required': ['username'],
|
'required': ['username'],
|
||||||
},
|
}, # /Author
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'links': {
|
'links': {
|
||||||
|
@ -111,19 +111,19 @@ BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'required': ['html'],
|
'required': ['html'],
|
||||||
},
|
}, # /Links
|
||||||
},
|
},
|
||||||
'required': ['hash', 'message', 'date'],
|
'required': ['hash', 'message', 'date'],
|
||||||
|
}, # /Target
|
||||||
},
|
},
|
||||||
|
'required': ['name', 'target'],
|
||||||
|
}, # /New
|
||||||
},
|
},
|
||||||
'required': ['target'],
|
}, # /Changes item
|
||||||
},
|
}, # /Changes
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'required': ['changes'],
|
'required': ['changes'],
|
||||||
},
|
}, # / Push
|
||||||
},
|
},
|
||||||
'actor': {
|
'actor': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
|
@ -157,9 +157,9 @@ BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'required': ['username'],
|
'required': ['username'],
|
||||||
},
|
}, # /Actor
|
||||||
'required': ['push', 'repository'],
|
'required': ['push', 'repository'],
|
||||||
}
|
} # /Root
|
||||||
|
|
||||||
BITBUCKET_COMMIT_INFO_SCHEMA = {
|
BITBUCKET_COMMIT_INFO_SCHEMA = {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
|
@ -242,7 +242,7 @@ def get_transformed_webhook_payload(bb_payload, default_branch=None):
|
||||||
config['default_branch'] = default_branch
|
config['default_branch'] = default_branch
|
||||||
config['git_url'] = 'git@bitbucket.org:%s.git' % repository_name
|
config['git_url'] = 'git@bitbucket.org:%s.git' % repository_name
|
||||||
|
|
||||||
config['commit_info.url'] = target['links.html.href']
|
config['commit_info.url'] = target['links.html.href'] or ''
|
||||||
config['commit_info.message'] = target['message']
|
config['commit_info.message'] = target['message']
|
||||||
config['commit_info.date'] = target['date']
|
config['commit_info.date'] = target['date']
|
||||||
|
|
||||||
|
@ -412,10 +412,11 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
||||||
'id': owner,
|
'id': owner,
|
||||||
'title': owner,
|
'title': owner,
|
||||||
'avatar_url': repo['logo'],
|
'avatar_url': repo['logo'],
|
||||||
'score': 0,
|
'url': 'https://bitbucket.org/%s' % (owner),
|
||||||
|
'score': 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
return list(namespaces.values())
|
return BuildTriggerHandler.build_namespaces_response(namespaces)
|
||||||
|
|
||||||
def list_build_sources_for_namespace(self, namespace):
|
def list_build_sources_for_namespace(self, namespace):
|
||||||
def repo_view(repo):
|
def repo_view(repo):
|
||||||
|
@ -436,7 +437,8 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
||||||
if not result:
|
if not result:
|
||||||
raise RepositoryReadException('Could not read repository list: ' + err_msg)
|
raise RepositoryReadException('Could not read repository list: ' + err_msg)
|
||||||
|
|
||||||
return [repo_view(repo) for repo in data if repo['owner'] == namespace]
|
repos = [repo_view(repo) for repo in data if repo['owner'] == namespace]
|
||||||
|
return BuildTriggerHandler.build_sources_response(repos)
|
||||||
|
|
||||||
def list_build_subdirs(self):
|
def list_build_subdirs(self):
|
||||||
config = self.config
|
config = self.config
|
||||||
|
@ -464,7 +466,7 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
||||||
|
|
||||||
(result, data, err_msg) = repository.get_raw_path_contents(path, revision='master')
|
(result, data, err_msg) = repository.get_raw_path_contents(path, revision='master')
|
||||||
if not result:
|
if not result:
|
||||||
raise RepositoryReadException(err_msg)
|
return None
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@ -541,7 +543,7 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
||||||
# Lookup the commit SHA for the branch.
|
# Lookup the commit SHA for the branch.
|
||||||
(result, data, _) = repository.get_branch(branch_name)
|
(result, data, _) = repository.get_branch(branch_name)
|
||||||
if not result:
|
if not result:
|
||||||
raise TriggerStartException('Could not find branch commit SHA')
|
raise TriggerStartException('Could not find branch in repository')
|
||||||
|
|
||||||
return data['target']['hash']
|
return data['target']['hash']
|
||||||
|
|
||||||
|
@ -549,7 +551,7 @@ class BitbucketBuildTrigger(BuildTriggerHandler):
|
||||||
# Lookup the commit SHA for the tag.
|
# Lookup the commit SHA for the tag.
|
||||||
(result, data, _) = repository.get_tag(tag_name)
|
(result, data, _) = repository.get_tag(tag_name)
|
||||||
if not result:
|
if not result:
|
||||||
raise TriggerStartException('Could not find tag commit SHA')
|
raise TriggerStartException('Could not find tag in repository')
|
||||||
|
|
||||||
return data['target']['hash']
|
return data['target']['hash']
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,6 @@ from buildtrigger.bitbuckethandler import (BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA as b
|
||||||
from buildtrigger.githubhandler import (GITHUB_WEBHOOK_PAYLOAD_SCHEMA as gh_schema,
|
from buildtrigger.githubhandler import (GITHUB_WEBHOOK_PAYLOAD_SCHEMA as gh_schema,
|
||||||
get_transformed_webhook_payload as gh_payload)
|
get_transformed_webhook_payload as gh_payload)
|
||||||
|
|
||||||
from buildtrigger.bitbuckethandler import (BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA as bb_schema,
|
|
||||||
get_transformed_webhook_payload as bb_payload)
|
|
||||||
|
|
||||||
from buildtrigger.gitlabhandler import (GITLAB_WEBHOOK_PAYLOAD_SCHEMA as gl_schema,
|
from buildtrigger.gitlabhandler import (GITLAB_WEBHOOK_PAYLOAD_SCHEMA as gl_schema,
|
||||||
get_transformed_webhook_payload as gl_payload)
|
get_transformed_webhook_payload as gl_payload)
|
||||||
|
|
||||||
|
@ -162,7 +159,7 @@ class CustomBuildTrigger(BuildTriggerHandler):
|
||||||
def handle_trigger_request(self, request):
|
def handle_trigger_request(self, request):
|
||||||
payload = request.data
|
payload = request.data
|
||||||
if not payload:
|
if not payload:
|
||||||
raise InvalidPayloadException()
|
raise InvalidPayloadException('Missing expected payload')
|
||||||
|
|
||||||
logger.debug('Payload %s', payload)
|
logger.debug('Payload %s', payload)
|
||||||
|
|
||||||
|
@ -186,7 +183,10 @@ class CustomBuildTrigger(BuildTriggerHandler):
|
||||||
'git_url': config['build_source'],
|
'git_url': config['build_source'],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
return self.prepare_build(metadata, is_manual=True)
|
return self.prepare_build(metadata, is_manual=True)
|
||||||
|
except ValidationError as ve:
|
||||||
|
raise TriggerStartException(ve.message)
|
||||||
|
|
||||||
def activate(self, standard_webhook_url):
|
def activate(self, standard_webhook_url):
|
||||||
config = self.config
|
config = self.config
|
||||||
|
|
|
@ -11,7 +11,7 @@ from github import (Github, UnknownObjectException, GithubException,
|
||||||
|
|
||||||
from jsonschema import validate
|
from jsonschema import validate
|
||||||
|
|
||||||
from app import github_trigger
|
from app import app, github_trigger
|
||||||
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
||||||
TriggerDeactivationException, TriggerStartException,
|
TriggerDeactivationException, TriggerStartException,
|
||||||
EmptyRepositoryException, ValidationRequestException,
|
EmptyRepositoryException, ValidationRequestException,
|
||||||
|
@ -261,6 +261,7 @@ class GithubBuildTrigger(BuildTriggerHandler):
|
||||||
raise TriggerDeactivationException(msg)
|
raise TriggerDeactivationException(msg)
|
||||||
|
|
||||||
# Remove the webhook.
|
# Remove the webhook.
|
||||||
|
if 'hook_id' in config:
|
||||||
try:
|
try:
|
||||||
hook = repo.get_hook(config['hook_id'])
|
hook = repo.get_hook(config['hook_id'])
|
||||||
hook.delete()
|
hook.delete()
|
||||||
|
@ -285,6 +286,7 @@ class GithubBuildTrigger(BuildTriggerHandler):
|
||||||
'id': usr.login,
|
'id': usr.login,
|
||||||
'title': usr.name or usr.login,
|
'title': usr.name or usr.login,
|
||||||
'avatar_url': usr.avatar_url,
|
'avatar_url': usr.avatar_url,
|
||||||
|
'url': usr.html_url,
|
||||||
'score': usr.plan.private_repos if usr.plan else 0,
|
'score': usr.plan.private_repos if usr.plan else 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +300,7 @@ class GithubBuildTrigger(BuildTriggerHandler):
|
||||||
'score': org.plan.private_repos if org.plan else 0,
|
'score': org.plan.private_repos if org.plan else 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
return list(namespaces.values())
|
return BuildTriggerHandler.build_namespaces_response(namespaces)
|
||||||
|
|
||||||
@_catch_ssl_errors
|
@_catch_ssl_errors
|
||||||
def list_build_sources_for_namespace(self, namespace):
|
def list_build_sources_for_namespace(self, namespace):
|
||||||
|
@ -315,15 +317,19 @@ class GithubBuildTrigger(BuildTriggerHandler):
|
||||||
|
|
||||||
gh_client = self._get_client()
|
gh_client = self._get_client()
|
||||||
usr = gh_client.get_user()
|
usr = gh_client.get_user()
|
||||||
|
|
||||||
if namespace == usr.login:
|
if namespace == usr.login:
|
||||||
return [repo_view(repo) for repo in usr.get_repos() if repo.owner.login == namespace]
|
repos = [repo_view(repo) for repo in usr.get_repos() if repo.owner.login == namespace]
|
||||||
|
return BuildTriggerHandler.build_sources_response(repos)
|
||||||
|
|
||||||
|
try:
|
||||||
org = gh_client.get_organization(namespace)
|
org = gh_client.get_organization(namespace)
|
||||||
if org is None:
|
if org is None:
|
||||||
return []
|
return []
|
||||||
|
except GithubException:
|
||||||
|
return []
|
||||||
|
|
||||||
return [repo_view(repo) for repo in org.get_repos(type='member')]
|
repos = [repo_view(repo) for repo in org.get_repos(type='member')]
|
||||||
|
return BuildTriggerHandler.build_sources_response(repos)
|
||||||
|
|
||||||
|
|
||||||
@_catch_ssl_errors
|
@_catch_ssl_errors
|
||||||
|
@ -479,8 +485,11 @@ class GithubBuildTrigger(BuildTriggerHandler):
|
||||||
raise TriggerStartException(msg)
|
raise TriggerStartException(msg)
|
||||||
|
|
||||||
def get_branch_sha(branch_name):
|
def get_branch_sha(branch_name):
|
||||||
|
try:
|
||||||
branch = repo.get_branch(branch_name)
|
branch = repo.get_branch(branch_name)
|
||||||
return branch.commit.sha
|
return branch.commit.sha
|
||||||
|
except GithubException:
|
||||||
|
raise TriggerStartException('Could not find branch in repository')
|
||||||
|
|
||||||
def get_tag_sha(tag_name):
|
def get_tag_sha(tag_name):
|
||||||
tags = {tag.name: tag for tag in repo.get_tags()}
|
tags = {tag.name: tag for tag in repo.get_tags()}
|
||||||
|
@ -515,9 +524,21 @@ class GithubBuildTrigger(BuildTriggerHandler):
|
||||||
|
|
||||||
# This is for GitHub's probing/testing.
|
# This is for GitHub's probing/testing.
|
||||||
if 'zen' in payload:
|
if 'zen' in payload:
|
||||||
raise ValidationRequestException()
|
raise SkipRequestException()
|
||||||
|
|
||||||
# Lookup the default branch for the repository.
|
# Lookup the default branch for the repository.
|
||||||
|
if 'repository' not in payload:
|
||||||
|
raise ValidationRequestException("Missing 'repository' on request")
|
||||||
|
|
||||||
|
if 'owner' not in payload['repository']:
|
||||||
|
raise ValidationRequestException("Missing 'owner' on repository")
|
||||||
|
|
||||||
|
if 'name' not in payload['repository']['owner']:
|
||||||
|
raise ValidationRequestException("Missing owner 'name' on repository")
|
||||||
|
|
||||||
|
if 'name' not in payload['repository']:
|
||||||
|
raise ValidationRequestException("Missing 'name' on repository")
|
||||||
|
|
||||||
default_branch = None
|
default_branch = None
|
||||||
lookup_user = None
|
lookup_user = None
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -48,6 +48,9 @@ GITLAB_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||||
'items': {
|
'items': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
'id': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
'url': {
|
'url': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
},
|
},
|
||||||
|
@ -67,7 +70,7 @@ GITLAB_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||||
'required': ['email'],
|
'required': ['email'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'required': ['url', 'message', 'timestamp'],
|
'required': ['id', 'url', 'message', 'timestamp'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -282,10 +285,11 @@ class GitLabBuildTrigger(BuildTriggerHandler):
|
||||||
'id': namespace['path'],
|
'id': namespace['path'],
|
||||||
'title': namespace['name'],
|
'title': namespace['name'],
|
||||||
'avatar_url': repo['owner']['avatar_url'],
|
'avatar_url': repo['owner']['avatar_url'],
|
||||||
'score': 0,
|
'score': 1,
|
||||||
|
'url': gl_client.host + '/' + namespace['path'],
|
||||||
}
|
}
|
||||||
|
|
||||||
return list(namespaces.values())
|
return BuildTriggerHandler.build_namespaces_response(namespaces)
|
||||||
|
|
||||||
@_catch_timeouts
|
@_catch_timeouts
|
||||||
def list_build_sources_for_namespace(self, namespace):
|
def list_build_sources_for_namespace(self, namespace):
|
||||||
|
@ -309,7 +313,8 @@ class GitLabBuildTrigger(BuildTriggerHandler):
|
||||||
|
|
||||||
gl_client = self._get_authorized_client()
|
gl_client = self._get_authorized_client()
|
||||||
repositories = _paginated_iterator(gl_client.getprojects, RepositoryReadException)
|
repositories = _paginated_iterator(gl_client.getprojects, RepositoryReadException)
|
||||||
return [repo_view(repo) for repo in repositories if repo['namespace']['path'] == namespace]
|
repos = [repo_view(repo) for repo in repositories if repo['namespace']['path'] == namespace]
|
||||||
|
return BuildTriggerHandler.build_sources_response(repos)
|
||||||
|
|
||||||
@_catch_timeouts
|
@_catch_timeouts
|
||||||
def list_build_subdirs(self):
|
def list_build_subdirs(self):
|
||||||
|
@ -486,18 +491,18 @@ class GitLabBuildTrigger(BuildTriggerHandler):
|
||||||
def get_tag_sha(tag_name):
|
def get_tag_sha(tag_name):
|
||||||
tags = gl_client.getrepositorytags(repo['id'])
|
tags = gl_client.getrepositorytags(repo['id'])
|
||||||
if tags is False:
|
if tags is False:
|
||||||
raise TriggerStartException('Could not find tags')
|
raise TriggerStartException('Could not find tag in repository')
|
||||||
|
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
if tag['name'] == tag_name:
|
if tag['name'] == tag_name:
|
||||||
return tag['commit']['id']
|
return tag['commit']['id']
|
||||||
|
|
||||||
raise TriggerStartException('Could not find commit')
|
raise TriggerStartException('Could not find tag in repository')
|
||||||
|
|
||||||
def get_branch_sha(branch_name):
|
def get_branch_sha(branch_name):
|
||||||
branch = gl_client.getbranch(repo['id'], branch_name)
|
branch = gl_client.getbranch(repo['id'], branch_name)
|
||||||
if branch is False:
|
if branch is False:
|
||||||
raise TriggerStartException('Could not find branch')
|
raise TriggerStartException('Could not find branch in repository')
|
||||||
|
|
||||||
return branch['commit']['id']
|
return branch['commit']['id']
|
||||||
|
|
||||||
|
|
0
buildtrigger/test/__init__.py
Normal file
0
buildtrigger/test/__init__.py
Normal file
158
buildtrigger/test/bitbucketmock.py
Normal file
158
buildtrigger/test/bitbucketmock.py
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from mock import Mock
|
||||||
|
|
||||||
|
from buildtrigger.bitbuckethandler import BitbucketBuildTrigger
|
||||||
|
from util.morecollections import AttrDict
|
||||||
|
|
||||||
|
def get_bitbucket_trigger(subdir=''):
|
||||||
|
trigger_obj = AttrDict(dict(auth_token='foobar', id='sometrigger'))
|
||||||
|
trigger = BitbucketBuildTrigger(trigger_obj, {
|
||||||
|
'build_source': 'foo/bar',
|
||||||
|
'subdir': subdir,
|
||||||
|
'username': 'knownuser'
|
||||||
|
})
|
||||||
|
|
||||||
|
trigger._get_client = get_mock_bitbucket
|
||||||
|
return trigger
|
||||||
|
|
||||||
|
def get_repo_path_contents(path, revision):
|
||||||
|
data = {
|
||||||
|
'files': [{'path': 'Dockerfile'}],
|
||||||
|
}
|
||||||
|
|
||||||
|
return (True, data, None)
|
||||||
|
|
||||||
|
def get_raw_path_contents(path, revision):
|
||||||
|
if path == '/Dockerfile':
|
||||||
|
return (True, 'hello world', None)
|
||||||
|
|
||||||
|
if path == 'somesubdir/Dockerfile':
|
||||||
|
return (True, 'hi universe', None)
|
||||||
|
|
||||||
|
return (False, None, None)
|
||||||
|
|
||||||
|
def get_branches_and_tags():
|
||||||
|
data = {
|
||||||
|
'branches': [{'name': 'master'}, {'name': 'otherbranch'}],
|
||||||
|
'tags': [{'name': 'sometag'}, {'name': 'someothertag'}],
|
||||||
|
}
|
||||||
|
return (True, data, None)
|
||||||
|
|
||||||
|
def get_branches():
|
||||||
|
return (True, {'master': {}, 'otherbranch': {}}, None)
|
||||||
|
|
||||||
|
def get_tags():
|
||||||
|
return (True, {'sometag': {}, 'someothertag': {}}, None)
|
||||||
|
|
||||||
|
def get_branch(branch_name):
|
||||||
|
if branch_name != 'master':
|
||||||
|
return (False, None, None)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'target': {
|
||||||
|
'hash': 'aaaaaaa',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return (True, data, None)
|
||||||
|
|
||||||
|
def get_tag(tag_name):
|
||||||
|
if tag_name != 'sometag':
|
||||||
|
return (False, None, None)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'target': {
|
||||||
|
'hash': 'aaaaaaa',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return (True, data, None)
|
||||||
|
|
||||||
|
def get_changeset_mock(commit_sha):
|
||||||
|
if commit_sha != 'aaaaaaa':
|
||||||
|
return (False, None, 'Not found')
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'node': 'aaaaaaa',
|
||||||
|
'message': 'some message',
|
||||||
|
'timestamp': 'now',
|
||||||
|
'raw_author': 'foo@bar.com',
|
||||||
|
}
|
||||||
|
|
||||||
|
return (True, data, None)
|
||||||
|
|
||||||
|
def get_changesets():
|
||||||
|
changesets_mock = Mock()
|
||||||
|
changesets_mock.get = Mock(side_effect=get_changeset_mock)
|
||||||
|
return changesets_mock
|
||||||
|
|
||||||
|
def get_deploykeys():
|
||||||
|
deploykeys_mock = Mock()
|
||||||
|
deploykeys_mock.create = Mock(return_value=(True, {'pk': 'someprivatekey'}, None))
|
||||||
|
deploykeys_mock.delete = Mock(return_value=(True, {}, None))
|
||||||
|
return deploykeys_mock
|
||||||
|
|
||||||
|
def get_webhooks():
|
||||||
|
webhooks_mock = Mock()
|
||||||
|
webhooks_mock.create = Mock(return_value=(True, {'uuid': 'someuuid'}, None))
|
||||||
|
webhooks_mock.delete = Mock(return_value=(True, {}, None))
|
||||||
|
return webhooks_mock
|
||||||
|
|
||||||
|
def get_repo_mock(name):
|
||||||
|
if name != 'bar':
|
||||||
|
return None
|
||||||
|
|
||||||
|
repo_mock = Mock()
|
||||||
|
repo_mock.get_main_branch = Mock(return_value=(True, {'name': 'master'}, None))
|
||||||
|
repo_mock.get_path_contents = Mock(side_effect=get_repo_path_contents)
|
||||||
|
repo_mock.get_raw_path_contents = Mock(side_effect=get_raw_path_contents)
|
||||||
|
repo_mock.get_branches_and_tags = Mock(side_effect=get_branches_and_tags)
|
||||||
|
repo_mock.get_branches = Mock(side_effect=get_branches)
|
||||||
|
repo_mock.get_tags = Mock(side_effect=get_tags)
|
||||||
|
repo_mock.get_branch = Mock(side_effect=get_branch)
|
||||||
|
repo_mock.get_tag = Mock(side_effect=get_tag)
|
||||||
|
|
||||||
|
repo_mock.changesets = Mock(side_effect=get_changesets)
|
||||||
|
repo_mock.deploykeys = Mock(side_effect=get_deploykeys)
|
||||||
|
repo_mock.webhooks = Mock(side_effect=get_webhooks)
|
||||||
|
return repo_mock
|
||||||
|
|
||||||
|
def get_repositories_mock():
|
||||||
|
repos_mock = Mock()
|
||||||
|
repos_mock.get = Mock(side_effect=get_repo_mock)
|
||||||
|
return repos_mock
|
||||||
|
|
||||||
|
def get_namespace_mock(namespace):
|
||||||
|
namespace_mock = Mock()
|
||||||
|
namespace_mock.repositories = Mock(side_effect=get_repositories_mock)
|
||||||
|
return namespace_mock
|
||||||
|
|
||||||
|
def get_repo(namespace, name):
|
||||||
|
return {
|
||||||
|
'owner': namespace,
|
||||||
|
'logo': 'avatarurl',
|
||||||
|
'slug': name,
|
||||||
|
'description': 'some %s repo' % (name),
|
||||||
|
'utc_last_updated': str(datetime.utcfromtimestamp(0)),
|
||||||
|
'read_only': namespace != 'knownuser',
|
||||||
|
'is_private': name == 'somerepo',
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_visible_repos():
|
||||||
|
repos = [
|
||||||
|
get_repo('knownuser', 'somerepo'),
|
||||||
|
get_repo('someorg', 'somerepo'),
|
||||||
|
get_repo('someorg', 'anotherrepo'),
|
||||||
|
]
|
||||||
|
return (True, repos, None)
|
||||||
|
|
||||||
|
def get_authed_mock(token, secret):
|
||||||
|
authed_mock = Mock()
|
||||||
|
authed_mock.for_namespace = Mock(side_effect=get_namespace_mock)
|
||||||
|
authed_mock.get_visible_repositories = Mock(side_effect=get_visible_repos)
|
||||||
|
return authed_mock
|
||||||
|
|
||||||
|
def get_mock_bitbucket():
|
||||||
|
bitbucket_mock = Mock()
|
||||||
|
bitbucket_mock.get_authorized_client = Mock(side_effect=get_authed_mock)
|
||||||
|
return bitbucket_mock
|
173
buildtrigger/test/githubmock.py
Normal file
173
buildtrigger/test/githubmock.py
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from mock import Mock
|
||||||
|
|
||||||
|
from github import GithubException
|
||||||
|
|
||||||
|
from buildtrigger.githubhandler import GithubBuildTrigger
|
||||||
|
from util.morecollections import AttrDict
|
||||||
|
|
||||||
|
def get_github_trigger(subdir=''):
|
||||||
|
trigger_obj = AttrDict(dict(auth_token='foobar', id='sometrigger'))
|
||||||
|
trigger = GithubBuildTrigger(trigger_obj, {'build_source': 'foo', 'subdir': subdir})
|
||||||
|
trigger._get_client = get_mock_github
|
||||||
|
return trigger
|
||||||
|
|
||||||
|
def get_mock_github():
|
||||||
|
def get_commit_mock(commit_sha):
|
||||||
|
if commit_sha == 'aaaaaaa':
|
||||||
|
commit_mock = Mock()
|
||||||
|
commit_mock.sha = commit_sha
|
||||||
|
commit_mock.html_url = 'http://url/to/commit'
|
||||||
|
commit_mock.last_modified = 'now'
|
||||||
|
|
||||||
|
commit_mock.commit = Mock()
|
||||||
|
commit_mock.commit.message = 'some cool message'
|
||||||
|
|
||||||
|
commit_mock.committer = Mock()
|
||||||
|
commit_mock.committer.login = 'someuser'
|
||||||
|
commit_mock.committer.avatar_url = 'avatarurl'
|
||||||
|
commit_mock.committer.html_url = 'htmlurl'
|
||||||
|
|
||||||
|
commit_mock.author = Mock()
|
||||||
|
commit_mock.author.login = 'someuser'
|
||||||
|
commit_mock.author.avatar_url = 'avatarurl'
|
||||||
|
commit_mock.author.html_url = 'htmlurl'
|
||||||
|
return commit_mock
|
||||||
|
|
||||||
|
raise GithubException(None, None)
|
||||||
|
|
||||||
|
def get_branch_mock(branch_name):
|
||||||
|
if branch_name == 'master':
|
||||||
|
branch_mock = Mock()
|
||||||
|
branch_mock.commit = Mock()
|
||||||
|
branch_mock.commit.sha = 'aaaaaaa'
|
||||||
|
return branch_mock
|
||||||
|
|
||||||
|
raise GithubException(None, None)
|
||||||
|
|
||||||
|
def get_repo_mock(namespace, name):
|
||||||
|
repo_mock = Mock()
|
||||||
|
repo_mock.owner = Mock()
|
||||||
|
repo_mock.owner.login = namespace
|
||||||
|
|
||||||
|
repo_mock.full_name = '%s/%s' % (namespace, name)
|
||||||
|
repo_mock.name = name
|
||||||
|
repo_mock.description = 'some %s repo' % (name)
|
||||||
|
repo_mock.pushed_at = datetime.utcfromtimestamp(0)
|
||||||
|
repo_mock.html_url = 'https://bitbucket.org/%s/%s' % (namespace, name)
|
||||||
|
repo_mock.private = name == 'somerepo'
|
||||||
|
repo_mock.permissions = Mock()
|
||||||
|
repo_mock.permissions.admin = namespace == 'knownuser'
|
||||||
|
return repo_mock
|
||||||
|
|
||||||
|
def get_user_repos_mock():
|
||||||
|
return [get_repo_mock('knownuser', 'somerepo')]
|
||||||
|
|
||||||
|
def get_org_repos_mock(type='all'):
|
||||||
|
return [get_repo_mock('someorg', 'somerepo'), get_repo_mock('someorg', 'anotherrepo')]
|
||||||
|
|
||||||
|
def get_orgs_mock():
|
||||||
|
return [get_org_mock('someorg')]
|
||||||
|
|
||||||
|
def get_user_mock(username='knownuser'):
|
||||||
|
if username == 'knownuser':
|
||||||
|
user_mock = Mock()
|
||||||
|
user_mock.name = username
|
||||||
|
user_mock.plan = Mock()
|
||||||
|
user_mock.plan.private_repos = 1
|
||||||
|
user_mock.login = username
|
||||||
|
user_mock.html_url = 'https://bitbucket.org/%s' % (username)
|
||||||
|
user_mock.avatar_url = 'avatarurl'
|
||||||
|
user_mock.get_repos = Mock(side_effect=get_user_repos_mock)
|
||||||
|
user_mock.get_orgs = Mock(side_effect=get_orgs_mock)
|
||||||
|
return user_mock
|
||||||
|
|
||||||
|
raise GithubException(None, None)
|
||||||
|
|
||||||
|
def get_org_mock(namespace):
|
||||||
|
if namespace == 'someorg':
|
||||||
|
org_mock = Mock()
|
||||||
|
org_mock.get_repos = Mock(side_effect=get_org_repos_mock)
|
||||||
|
org_mock.login = namespace
|
||||||
|
org_mock.html_url = 'https://bitbucket.org/%s' % (namespace)
|
||||||
|
org_mock.avatar_url = 'avatarurl'
|
||||||
|
org_mock.name = namespace
|
||||||
|
org_mock.plan = Mock()
|
||||||
|
org_mock.plan.private_repos = 2
|
||||||
|
return org_mock
|
||||||
|
|
||||||
|
raise GithubException(None, None)
|
||||||
|
|
||||||
|
def get_tags_mock():
|
||||||
|
sometag = Mock()
|
||||||
|
sometag.name = 'sometag'
|
||||||
|
sometag.commit = get_commit_mock('aaaaaaa')
|
||||||
|
|
||||||
|
someothertag = Mock()
|
||||||
|
someothertag.name = 'someothertag'
|
||||||
|
someothertag.commit = get_commit_mock('aaaaaaa')
|
||||||
|
return [sometag, someothertag]
|
||||||
|
|
||||||
|
def get_branches_mock():
|
||||||
|
master = Mock()
|
||||||
|
master.name = 'master'
|
||||||
|
master.commit = get_commit_mock('aaaaaaa')
|
||||||
|
|
||||||
|
otherbranch = Mock()
|
||||||
|
otherbranch.name = 'otherbranch'
|
||||||
|
otherbranch.commit = get_commit_mock('aaaaaaa')
|
||||||
|
return [master, otherbranch]
|
||||||
|
|
||||||
|
def get_file_contents_mock(filepath):
|
||||||
|
if filepath == '/Dockerfile':
|
||||||
|
m = Mock()
|
||||||
|
m.content = 'hello world'
|
||||||
|
return m
|
||||||
|
|
||||||
|
if filepath == 'somesubdir/Dockerfile':
|
||||||
|
m = Mock()
|
||||||
|
m.content = 'hi universe'
|
||||||
|
return m
|
||||||
|
|
||||||
|
raise GithubException(None, None)
|
||||||
|
|
||||||
|
def get_git_tree_mock(commit_sha, recursive=False):
|
||||||
|
first_file = Mock()
|
||||||
|
first_file.type = 'blob'
|
||||||
|
first_file.path = 'Dockerfile'
|
||||||
|
|
||||||
|
second_file = Mock()
|
||||||
|
second_file.type = 'other'
|
||||||
|
second_file.path = '/some/Dockerfile'
|
||||||
|
|
||||||
|
third_file = Mock()
|
||||||
|
third_file.type = 'blob'
|
||||||
|
third_file.path = 'somesubdir/Dockerfile'
|
||||||
|
|
||||||
|
t = Mock()
|
||||||
|
|
||||||
|
if commit_sha == 'aaaaaaa':
|
||||||
|
t.tree = [
|
||||||
|
first_file, second_file, third_file,
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
t.tree = []
|
||||||
|
|
||||||
|
return t
|
||||||
|
|
||||||
|
repo_mock = Mock()
|
||||||
|
repo_mock.default_branch = 'master'
|
||||||
|
repo_mock.ssh_url = 'ssh_url'
|
||||||
|
|
||||||
|
repo_mock.get_branch = Mock(side_effect=get_branch_mock)
|
||||||
|
repo_mock.get_tags = Mock(side_effect=get_tags_mock)
|
||||||
|
repo_mock.get_branches = Mock(side_effect=get_branches_mock)
|
||||||
|
repo_mock.get_commit = Mock(side_effect=get_commit_mock)
|
||||||
|
repo_mock.get_file_contents = Mock(side_effect=get_file_contents_mock)
|
||||||
|
repo_mock.get_git_tree = Mock(side_effect=get_git_tree_mock)
|
||||||
|
|
||||||
|
gh_mock = Mock()
|
||||||
|
gh_mock.get_repo = Mock(return_value=repo_mock)
|
||||||
|
gh_mock.get_user = Mock(side_effect=get_user_mock)
|
||||||
|
gh_mock.get_organization = Mock(side_effect=get_org_mock)
|
||||||
|
return gh_mock
|
186
buildtrigger/test/gitlabmock.py
Normal file
186
buildtrigger/test/gitlabmock.py
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from mock import Mock
|
||||||
|
|
||||||
|
from buildtrigger.gitlabhandler import GitLabBuildTrigger
|
||||||
|
from util.morecollections import AttrDict
|
||||||
|
|
||||||
|
def get_gitlab_trigger(subdir=''):
|
||||||
|
trigger_obj = AttrDict(dict(auth_token='foobar', id='sometrigger'))
|
||||||
|
trigger = GitLabBuildTrigger(trigger_obj, {
|
||||||
|
'build_source': 'foo/bar',
|
||||||
|
'subdir': subdir,
|
||||||
|
'username': 'knownuser'
|
||||||
|
})
|
||||||
|
|
||||||
|
trigger._get_authorized_client = get_mock_gitlab
|
||||||
|
return trigger
|
||||||
|
|
||||||
|
def adddeploykey_mock(project_id, name, public_key):
|
||||||
|
return {'id': 'foo'}
|
||||||
|
|
||||||
|
def addprojecthook_mock(project_id, webhook_url, push=False):
|
||||||
|
return {'id': 'foo'}
|
||||||
|
|
||||||
|
def get_currentuser_mock():
|
||||||
|
return {
|
||||||
|
'username': 'knownuser'
|
||||||
|
}
|
||||||
|
|
||||||
|
def project(namespace, name):
|
||||||
|
return {
|
||||||
|
'id': '%s/%s' % (namespace, name),
|
||||||
|
'default_branch': 'master',
|
||||||
|
'namespace': {
|
||||||
|
'id': namespace,
|
||||||
|
'path': namespace,
|
||||||
|
'name': namespace,
|
||||||
|
},
|
||||||
|
'path': name,
|
||||||
|
'path_with_namespace': '%s/%s' % (namespace, name),
|
||||||
|
'description': 'some %s repo' % name,
|
||||||
|
'last_activity_at': str(datetime.utcfromtimestamp(0)),
|
||||||
|
'web_url': 'https://bitbucket.org/%s/%s' % (namespace, name),
|
||||||
|
'ssh_url_to_repo': 'git://%s/%s' % (namespace, name),
|
||||||
|
'public': name != 'somerepo',
|
||||||
|
'permissions': {
|
||||||
|
'project_access': {
|
||||||
|
'access_level': 50 if namespace == 'knownuser' else 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'owner': {
|
||||||
|
'avatar_url': 'avatarurl',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getprojects_mock(page=1, per_page=100):
|
||||||
|
return [
|
||||||
|
project('knownuser', 'somerepo'),
|
||||||
|
project('someorg', 'somerepo'),
|
||||||
|
project('someorg', 'anotherrepo'),
|
||||||
|
]
|
||||||
|
|
||||||
|
def getproject_mock(project_name):
|
||||||
|
if project_name == 'knownuser/somerepo':
|
||||||
|
return project('knownuser', 'somerepo')
|
||||||
|
|
||||||
|
if project_name == 'foo/bar':
|
||||||
|
return project('foo', 'bar')
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def getbranches_mock(project_id):
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'name': 'master',
|
||||||
|
'commit': {
|
||||||
|
'id': 'aaaaaaa',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'otherbranch',
|
||||||
|
'commit': {
|
||||||
|
'id': 'aaaaaaa',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
def getrepositorytags_mock(project_id):
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'name': 'sometag',
|
||||||
|
'commit': {
|
||||||
|
'id': 'aaaaaaa',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'someothertag',
|
||||||
|
'commit': {
|
||||||
|
'id': 'aaaaaaa',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
def getrepositorytree_mock(project_id, ref_name='master'):
|
||||||
|
return [
|
||||||
|
{'name': 'README'},
|
||||||
|
{'name': 'Dockerfile'},
|
||||||
|
]
|
||||||
|
|
||||||
|
def getrepositorycommit_mock(project_id, commit_sha):
|
||||||
|
if commit_sha != 'aaaaaaa':
|
||||||
|
return False
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': 'aaaaaaa',
|
||||||
|
'message': 'some message',
|
||||||
|
'committed_date': 'now',
|
||||||
|
}
|
||||||
|
|
||||||
|
def getusers_mock(search=None):
|
||||||
|
if search == 'knownuser':
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'username': 'knownuser',
|
||||||
|
'avatar_url': 'avatarurl',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getbranch_mock(repo_id, branch):
|
||||||
|
if branch != 'master' and branch != 'otherbranch':
|
||||||
|
return False
|
||||||
|
|
||||||
|
return {
|
||||||
|
'name': branch,
|
||||||
|
'commit': {
|
||||||
|
'id': 'aaaaaaa',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def gettag_mock(repo_id, tag):
|
||||||
|
if tag != 'sometag' and tag != 'someothertag':
|
||||||
|
return False
|
||||||
|
|
||||||
|
return {
|
||||||
|
'name': tag,
|
||||||
|
'commit': {
|
||||||
|
'id': 'aaaaaaa',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getrawfile_mock(repo_id, branch_name, path):
|
||||||
|
if path == '/Dockerfile':
|
||||||
|
return 'hello world'
|
||||||
|
|
||||||
|
if path == 'somesubdir/Dockerfile':
|
||||||
|
return 'hi universe'
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_mock_gitlab():
|
||||||
|
mock_gitlab = Mock()
|
||||||
|
mock_gitlab.host = 'https://bitbucket.org'
|
||||||
|
|
||||||
|
mock_gitlab.currentuser = Mock(side_effect=get_currentuser_mock)
|
||||||
|
mock_gitlab.getusers = Mock(side_effect=getusers_mock)
|
||||||
|
|
||||||
|
mock_gitlab.getprojects = Mock(side_effect=getprojects_mock)
|
||||||
|
mock_gitlab.getproject = Mock(side_effect=getproject_mock)
|
||||||
|
mock_gitlab.getbranches = Mock(side_effect=getbranches_mock)
|
||||||
|
|
||||||
|
mock_gitlab.getbranch = Mock(side_effect=getbranch_mock)
|
||||||
|
mock_gitlab.gettag = Mock(side_effect=gettag_mock)
|
||||||
|
|
||||||
|
mock_gitlab.getrepositorytags = Mock(side_effect=getrepositorytags_mock)
|
||||||
|
mock_gitlab.getrepositorytree = Mock(side_effect=getrepositorytree_mock)
|
||||||
|
mock_gitlab.getrepositorycommit = Mock(side_effect=getrepositorycommit_mock)
|
||||||
|
|
||||||
|
mock_gitlab.getrawfile = Mock(side_effect=getrawfile_mock)
|
||||||
|
|
||||||
|
mock_gitlab.adddeploykey = Mock(side_effect=adddeploykey_mock)
|
||||||
|
mock_gitlab.addprojecthook = Mock(side_effect=addprojecthook_mock)
|
||||||
|
mock_gitlab.deletedeploykey = Mock(return_value=True)
|
||||||
|
mock_gitlab.deleteprojecthook = Mock(return_value=True)
|
||||||
|
return mock_gitlab
|
91
buildtrigger/test/test_bitbuckethandler.py
Normal file
91
buildtrigger/test/test_bitbuckethandler.py
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import json
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from buildtrigger.test.bitbucketmock import get_bitbucket_trigger
|
||||||
|
from buildtrigger.triggerutil import (SkipRequestException, ValidationRequestException,
|
||||||
|
InvalidPayloadException)
|
||||||
|
from endpoints.building import PreparedBuild
|
||||||
|
from util.morecollections import AttrDict
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def bitbucket_trigger():
|
||||||
|
return get_bitbucket_trigger()
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_build_subdirs(bitbucket_trigger):
|
||||||
|
assert bitbucket_trigger.list_build_subdirs() == ['']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('subdir, contents', [
|
||||||
|
('', 'hello world'),
|
||||||
|
('somesubdir', 'hi universe'),
|
||||||
|
('unknownpath', None),
|
||||||
|
])
|
||||||
|
def test_load_dockerfile_contents(subdir, contents):
|
||||||
|
trigger = get_bitbucket_trigger(subdir)
|
||||||
|
assert trigger.load_dockerfile_contents() == contents
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('payload, expected_error, expected_message', [
|
||||||
|
('{}', InvalidPayloadException, "'push' is a required property"),
|
||||||
|
|
||||||
|
# Valid payload:
|
||||||
|
('''{
|
||||||
|
"push": {
|
||||||
|
"changes": [{
|
||||||
|
"new": {
|
||||||
|
"name": "somechange",
|
||||||
|
"target": {
|
||||||
|
"hash": "aaaaaaa",
|
||||||
|
"message": "foo",
|
||||||
|
"date": "now",
|
||||||
|
"links": {
|
||||||
|
"html": {
|
||||||
|
"href": "somelink"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"full_name": "foo/bar"
|
||||||
|
}
|
||||||
|
}''', None, None),
|
||||||
|
|
||||||
|
# Skip message:
|
||||||
|
('''{
|
||||||
|
"push": {
|
||||||
|
"changes": [{
|
||||||
|
"new": {
|
||||||
|
"name": "somechange",
|
||||||
|
"target": {
|
||||||
|
"hash": "aaaaaaa",
|
||||||
|
"message": "[skip build] foo",
|
||||||
|
"date": "now",
|
||||||
|
"links": {
|
||||||
|
"html": {
|
||||||
|
"href": "somelink"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"full_name": "foo/bar"
|
||||||
|
}
|
||||||
|
}''', SkipRequestException, ''),
|
||||||
|
])
|
||||||
|
def test_handle_trigger_request(bitbucket_trigger, payload, expected_error, expected_message):
|
||||||
|
def get_payload():
|
||||||
|
return json.loads(payload)
|
||||||
|
|
||||||
|
request = AttrDict(dict(get_json=get_payload))
|
||||||
|
|
||||||
|
if expected_error is not None:
|
||||||
|
with pytest.raises(expected_error) as ipe:
|
||||||
|
bitbucket_trigger.handle_trigger_request(request)
|
||||||
|
assert ipe.value.message == expected_message
|
||||||
|
else:
|
||||||
|
assert isinstance(bitbucket_trigger.handle_trigger_request(request), PreparedBuild)
|
51
buildtrigger/test/test_customhandler.py
Normal file
51
buildtrigger/test/test_customhandler.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from buildtrigger.customhandler import CustomBuildTrigger
|
||||||
|
from buildtrigger.triggerutil import (InvalidPayloadException, SkipRequestException,
|
||||||
|
TriggerStartException)
|
||||||
|
from endpoints.building import PreparedBuild
|
||||||
|
from util.morecollections import AttrDict
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('payload, expected_error, expected_message', [
|
||||||
|
('', InvalidPayloadException, 'Missing expected payload'),
|
||||||
|
('{}', InvalidPayloadException, "'commit' is a required property"),
|
||||||
|
|
||||||
|
('{"commit": "foo", "ref": "refs/heads/something", "default_branch": "baz"}',
|
||||||
|
InvalidPayloadException, "u'foo' does not match '^([A-Fa-f0-9]{7,})$'"),
|
||||||
|
|
||||||
|
('{"commit": "11d6fbc", "ref": "refs/heads/something", "default_branch": "baz"}', None, None),
|
||||||
|
('''{
|
||||||
|
"commit": "11d6fbc",
|
||||||
|
"ref": "refs/heads/something",
|
||||||
|
"default_branch": "baz",
|
||||||
|
"commit_info": {
|
||||||
|
"message": "[skip build]",
|
||||||
|
"url": "http://foo.bar",
|
||||||
|
"date": "NOW"
|
||||||
|
}
|
||||||
|
}''', SkipRequestException, ''),
|
||||||
|
])
|
||||||
|
def test_handle_trigger_request(payload, expected_error, expected_message):
|
||||||
|
trigger = CustomBuildTrigger(None, {'build_source': 'foo'})
|
||||||
|
request = AttrDict(dict(data=payload))
|
||||||
|
|
||||||
|
if expected_error is not None:
|
||||||
|
with pytest.raises(expected_error) as ipe:
|
||||||
|
trigger.handle_trigger_request(request)
|
||||||
|
assert ipe.value.message == expected_message
|
||||||
|
else:
|
||||||
|
assert isinstance(trigger.handle_trigger_request(request), PreparedBuild)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('run_parameters, expected_error, expected_message', [
|
||||||
|
({}, TriggerStartException, 'missing required parameter'),
|
||||||
|
({'commit_sha': 'foo'}, TriggerStartException, "'foo' does not match '^([A-Fa-f0-9]{7,})$'"),
|
||||||
|
({'commit_sha': '11d6fbc'}, None, None),
|
||||||
|
])
|
||||||
|
def test_manual_start(run_parameters, expected_error, expected_message):
|
||||||
|
trigger = CustomBuildTrigger(None, {'build_source': 'foo'})
|
||||||
|
if expected_error is not None:
|
||||||
|
with pytest.raises(expected_error) as ipe:
|
||||||
|
trigger.manual_start(run_parameters)
|
||||||
|
assert ipe.value.message == expected_message
|
||||||
|
else:
|
||||||
|
assert isinstance(trigger.manual_start(run_parameters), PreparedBuild)
|
125
buildtrigger/test/test_githosthandler.py
Normal file
125
buildtrigger/test/test_githosthandler.py
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from buildtrigger.triggerutil import TriggerStartException
|
||||||
|
from buildtrigger.test.bitbucketmock import get_bitbucket_trigger
|
||||||
|
from buildtrigger.test.githubmock import get_github_trigger
|
||||||
|
from buildtrigger.test.gitlabmock import get_gitlab_trigger
|
||||||
|
from endpoints.building import PreparedBuild
|
||||||
|
|
||||||
|
# Note: This test suite executes a common set of tests against all the trigger types specified
|
||||||
|
# in this fixture. Each trigger's mock is expected to return the same data for all of these calls.
|
||||||
|
@pytest.fixture(params=[get_github_trigger(), get_bitbucket_trigger(), get_gitlab_trigger()])
|
||||||
|
def githost_trigger(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('run_parameters, expected_error, expected_message', [
|
||||||
|
# No branch or tag specified: use the commit of the default branch.
|
||||||
|
({}, None, None),
|
||||||
|
|
||||||
|
# Invalid branch.
|
||||||
|
({'refs': {'kind': 'branch', 'name': 'invalid'}}, TriggerStartException,
|
||||||
|
'Could not find branch in repository'),
|
||||||
|
|
||||||
|
# Invalid tag.
|
||||||
|
({'refs': {'kind': 'tag', 'name': 'invalid'}}, TriggerStartException,
|
||||||
|
'Could not find tag in repository'),
|
||||||
|
|
||||||
|
# Valid branch.
|
||||||
|
({'refs': {'kind': 'branch', 'name': 'master'}}, None, None),
|
||||||
|
|
||||||
|
# Valid tag.
|
||||||
|
({'refs': {'kind': 'tag', 'name': 'sometag'}}, None, None),
|
||||||
|
])
|
||||||
|
def test_manual_start(run_parameters, expected_error, expected_message, githost_trigger):
|
||||||
|
if expected_error is not None:
|
||||||
|
with pytest.raises(expected_error) as ipe:
|
||||||
|
githost_trigger.manual_start(run_parameters)
|
||||||
|
assert ipe.value.message == expected_message
|
||||||
|
else:
|
||||||
|
assert isinstance(githost_trigger.manual_start(run_parameters), PreparedBuild)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('name, expected', [
|
||||||
|
('refs', [
|
||||||
|
{'kind': 'branch', 'name': 'master'},
|
||||||
|
{'kind': 'branch', 'name': 'otherbranch'},
|
||||||
|
{'kind': 'tag', 'name': 'sometag'},
|
||||||
|
{'kind': 'tag', 'name': 'someothertag'},
|
||||||
|
]),
|
||||||
|
('tag_name', set(['sometag', 'someothertag'])),
|
||||||
|
('branch_name', set(['master', 'otherbranch'])),
|
||||||
|
('invalid', None)
|
||||||
|
])
|
||||||
|
def test_list_field_values(name, expected, githost_trigger):
|
||||||
|
if expected is None:
|
||||||
|
assert githost_trigger.list_field_values(name) is None
|
||||||
|
elif isinstance(expected, set):
|
||||||
|
assert set(githost_trigger.list_field_values(name)) == set(expected)
|
||||||
|
else:
|
||||||
|
assert githost_trigger.list_field_values(name) == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_build_source_namespaces(githost_trigger):
|
||||||
|
namespaces_expected = [
|
||||||
|
{
|
||||||
|
'personal': True,
|
||||||
|
'score': 1,
|
||||||
|
'avatar_url': 'avatarurl',
|
||||||
|
'id': 'knownuser',
|
||||||
|
'title': 'knownuser',
|
||||||
|
'url': 'https://bitbucket.org/knownuser',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'score': 2,
|
||||||
|
'title': 'someorg',
|
||||||
|
'personal': False,
|
||||||
|
'url': 'https://bitbucket.org/someorg',
|
||||||
|
'avatar_url': 'avatarurl',
|
||||||
|
'id': 'someorg'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
found = githost_trigger.list_build_source_namespaces()
|
||||||
|
found.sort()
|
||||||
|
|
||||||
|
namespaces_expected.sort()
|
||||||
|
assert found == namespaces_expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('namespace, expected', [
|
||||||
|
('', []),
|
||||||
|
('unknown', []),
|
||||||
|
|
||||||
|
('knownuser', [
|
||||||
|
{
|
||||||
|
'last_updated': 0, 'name': 'somerepo',
|
||||||
|
'url': 'https://bitbucket.org/knownuser/somerepo', 'private': True,
|
||||||
|
'full_name': 'knownuser/somerepo', 'has_admin_permissions': True,
|
||||||
|
'description': 'some somerepo repo'
|
||||||
|
}]),
|
||||||
|
|
||||||
|
('someorg', [
|
||||||
|
{
|
||||||
|
'last_updated': 0, 'name': 'somerepo',
|
||||||
|
'url': 'https://bitbucket.org/someorg/somerepo', 'private': True,
|
||||||
|
'full_name': 'someorg/somerepo', 'has_admin_permissions': False,
|
||||||
|
'description': 'some somerepo repo'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'last_updated': 0, 'name': 'anotherrepo',
|
||||||
|
'url': 'https://bitbucket.org/someorg/anotherrepo', 'private': False,
|
||||||
|
'full_name': 'someorg/anotherrepo', 'has_admin_permissions': False,
|
||||||
|
'description': 'some anotherrepo repo'
|
||||||
|
}]),
|
||||||
|
])
|
||||||
|
def test_list_build_sources_for_namespace(namespace, expected, githost_trigger):
|
||||||
|
assert githost_trigger.list_build_sources_for_namespace(namespace) == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_activate(githost_trigger):
|
||||||
|
_, private_key = githost_trigger.activate('http://some/url')
|
||||||
|
assert 'private_key' in private_key
|
||||||
|
|
||||||
|
|
||||||
|
def test_deactivate(githost_trigger):
|
||||||
|
githost_trigger.deactivate()
|
89
buildtrigger/test/test_githubhandler.py
Normal file
89
buildtrigger/test/test_githubhandler.py
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import json
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from buildtrigger.test.githubmock import get_github_trigger
|
||||||
|
from buildtrigger.triggerutil import SkipRequestException, ValidationRequestException
|
||||||
|
from endpoints.building import PreparedBuild
|
||||||
|
from util.morecollections import AttrDict
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def github_trigger():
|
||||||
|
return get_github_trigger()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('payload, expected_error, expected_message', [
|
||||||
|
('{"zen": true}', SkipRequestException, ""),
|
||||||
|
|
||||||
|
('{}', ValidationRequestException, "Missing 'repository' on request"),
|
||||||
|
('{"repository": "foo"}', ValidationRequestException, "Missing 'owner' on repository"),
|
||||||
|
|
||||||
|
# Valid payload:
|
||||||
|
('''{
|
||||||
|
"repository": {
|
||||||
|
"owner": {
|
||||||
|
"name": "someguy"
|
||||||
|
},
|
||||||
|
"name": "somerepo",
|
||||||
|
"ssh_url": "someurl"
|
||||||
|
},
|
||||||
|
"ref": "refs/tags/foo",
|
||||||
|
"head_commit": {
|
||||||
|
"id": "11d6fbc",
|
||||||
|
"url": "http://some/url",
|
||||||
|
"message": "some message",
|
||||||
|
"timestamp": "NOW"
|
||||||
|
}
|
||||||
|
}''', None, None),
|
||||||
|
|
||||||
|
# Skip message:
|
||||||
|
('''{
|
||||||
|
"repository": {
|
||||||
|
"owner": {
|
||||||
|
"name": "someguy"
|
||||||
|
},
|
||||||
|
"name": "somerepo",
|
||||||
|
"ssh_url": "someurl"
|
||||||
|
},
|
||||||
|
"ref": "refs/tags/foo",
|
||||||
|
"head_commit": {
|
||||||
|
"id": "11d6fbc",
|
||||||
|
"url": "http://some/url",
|
||||||
|
"message": "[skip build]",
|
||||||
|
"timestamp": "NOW"
|
||||||
|
}
|
||||||
|
}''', SkipRequestException, ''),
|
||||||
|
])
|
||||||
|
def test_handle_trigger_request(github_trigger, payload, expected_error, expected_message):
|
||||||
|
def get_payload():
|
||||||
|
return json.loads(payload)
|
||||||
|
|
||||||
|
request = AttrDict(dict(get_json=get_payload))
|
||||||
|
|
||||||
|
if expected_error is not None:
|
||||||
|
with pytest.raises(expected_error) as ipe:
|
||||||
|
github_trigger.handle_trigger_request(request)
|
||||||
|
assert ipe.value.message == expected_message
|
||||||
|
else:
|
||||||
|
assert isinstance(github_trigger.handle_trigger_request(request), PreparedBuild)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('subdir, contents', [
|
||||||
|
('', 'hello world'),
|
||||||
|
('somesubdir', 'hi universe'),
|
||||||
|
('unknownpath', None),
|
||||||
|
])
|
||||||
|
def test_load_dockerfile_contents(subdir, contents):
|
||||||
|
trigger = get_github_trigger(subdir)
|
||||||
|
assert trigger.load_dockerfile_contents() == contents
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('username, expected_response', [
|
||||||
|
('unknownuser', None),
|
||||||
|
('knownuser', {'html_url': 'https://bitbucket.org/knownuser', 'avatar_url': 'avatarurl'}),
|
||||||
|
])
|
||||||
|
def test_lookup_user(username, expected_response, github_trigger):
|
||||||
|
assert github_trigger.lookup_user(username) == expected_response
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_build_subdirs(github_trigger):
|
||||||
|
assert github_trigger.list_build_subdirs() == ['', 'somesubdir']
|
90
buildtrigger/test/test_gitlabhandler.py
Normal file
90
buildtrigger/test/test_gitlabhandler.py
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import json
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from buildtrigger.test.gitlabmock import get_gitlab_trigger
|
||||||
|
from buildtrigger.triggerutil import (SkipRequestException, ValidationRequestException,
|
||||||
|
InvalidPayloadException)
|
||||||
|
from endpoints.building import PreparedBuild
|
||||||
|
from util.morecollections import AttrDict
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def gitlab_trigger():
|
||||||
|
return get_gitlab_trigger()
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_build_subdirs(gitlab_trigger):
|
||||||
|
assert gitlab_trigger.list_build_subdirs() == ['']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('subdir, contents', [
|
||||||
|
('', 'hello world'),
|
||||||
|
('somesubdir', 'hi universe'),
|
||||||
|
('unknownpath', None),
|
||||||
|
])
|
||||||
|
def test_load_dockerfile_contents(subdir, contents):
|
||||||
|
trigger = get_gitlab_trigger(subdir)
|
||||||
|
assert trigger.load_dockerfile_contents() == contents
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('email, expected_response', [
|
||||||
|
('unknown@email.com', None),
|
||||||
|
('knownuser', {'username': 'knownuser', 'html_url': 'https://bitbucket.org/knownuser',
|
||||||
|
'avatar_url': 'avatarurl'}),
|
||||||
|
])
|
||||||
|
def test_lookup_user(email, expected_response, gitlab_trigger):
|
||||||
|
assert gitlab_trigger.lookup_user(email) == expected_response
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('payload, expected_error, expected_message', [
|
||||||
|
('{}', SkipRequestException, ''),
|
||||||
|
|
||||||
|
# Valid payload:
|
||||||
|
('''{
|
||||||
|
"object_kind": "push",
|
||||||
|
"ref": "refs/heads/master",
|
||||||
|
"checkout_sha": "aaaaaaa",
|
||||||
|
"repository": {
|
||||||
|
"git_ssh_url": "foobar"
|
||||||
|
},
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"id": "aaaaaaa",
|
||||||
|
"url": "someurl",
|
||||||
|
"message": "hello there!",
|
||||||
|
"timestamp": "now"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}''', None, None),
|
||||||
|
|
||||||
|
# Skip message:
|
||||||
|
('''{
|
||||||
|
"object_kind": "push",
|
||||||
|
"ref": "refs/heads/master",
|
||||||
|
"checkout_sha": "aaaaaaa",
|
||||||
|
"repository": {
|
||||||
|
"git_ssh_url": "foobar"
|
||||||
|
},
|
||||||
|
"commits": [
|
||||||
|
{
|
||||||
|
"id": "aaaaaaa",
|
||||||
|
"url": "someurl",
|
||||||
|
"message": "[skip build] hello there!",
|
||||||
|
"timestamp": "now"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}''', SkipRequestException, ''),
|
||||||
|
])
|
||||||
|
def test_handle_trigger_request(gitlab_trigger, payload, expected_error, expected_message):
|
||||||
|
def get_payload():
|
||||||
|
return json.loads(payload)
|
||||||
|
|
||||||
|
request = AttrDict(dict(get_json=get_payload))
|
||||||
|
|
||||||
|
if expected_error is not None:
|
||||||
|
with pytest.raises(expected_error) as ipe:
|
||||||
|
gitlab_trigger.handle_trigger_request(request)
|
||||||
|
assert ipe.value.message == expected_message
|
||||||
|
else:
|
||||||
|
assert isinstance(gitlab_trigger.handle_trigger_request(request), PreparedBuild)
|
||||||
|
|
||||||
|
|
Reference in a new issue