github: more complete mocking for app setup
This mocks the /app/installations and /installations/repositories GitHub API calls to better validate the GitHub project initalization in the driver. It implements enough that we can use GithubClientManager:_prime_installation_map() directly and better tests the token issuing, etc. Change-Id: I608f1540ef33b1a95595393e546afba308fef66achanges/53/810553/3
parent
b3eb94e3b2
commit
403d1c2882
|
@ -6,3 +6,4 @@ PyMySQL
|
|||
psycopg2-binary
|
||||
beautifulsoup4
|
||||
graphene
|
||||
requests_mock
|
||||
|
|
|
@ -67,6 +67,7 @@ import yaml
|
|||
import paramiko
|
||||
import prometheus_client.exposition
|
||||
import sqlalchemy
|
||||
import requests_mock
|
||||
|
||||
from zuul.driver.sql.sqlconnection import DatabaseSession
|
||||
from zuul import model
|
||||
|
@ -2742,6 +2743,8 @@ class FakeGithubClientManager(GithubClientManager):
|
|||
github_class = tests.fakegithub.FakeGithubClient
|
||||
github_enterprise_class = tests.fakegithub.FakeGithubEnterpriseClient
|
||||
|
||||
log = logging.getLogger("zuul.test.FakeGithubClientManager")
|
||||
|
||||
def __init__(self, connection_config):
|
||||
super().__init__(connection_config)
|
||||
self.record_clients = False
|
||||
|
@ -2769,19 +2772,93 @@ class FakeGithubClientManager(GithubClientManager):
|
|||
return client
|
||||
|
||||
def _prime_installation_map(self):
|
||||
# Only valid if installed as a github app
|
||||
if not self.app_id:
|
||||
return
|
||||
|
||||
# simulate one installation per org
|
||||
orgs = {}
|
||||
latest_inst_id = 0
|
||||
for repo in self.github_data.repos:
|
||||
inst_id = orgs.get(repo[0])
|
||||
if not inst_id:
|
||||
latest_inst_id += 1
|
||||
inst_id = latest_inst_id
|
||||
orgs[repo[0]] = inst_id
|
||||
self.installation_map['/'.join(repo)] = inst_id
|
||||
# github_data.repos is a hash like
|
||||
# { ('org', 'project1'): <dataobj>
|
||||
# ('org', 'project2'): <dataobj>,
|
||||
# ('org2', 'project1'): <dataobj>, ... }
|
||||
#
|
||||
# we don't care about the value. index by org, e.g.
|
||||
#
|
||||
# {
|
||||
# 'org': ('project1', 'project2')
|
||||
# 'org2': ('project1', 'project2')
|
||||
# }
|
||||
orgs = defaultdict(list)
|
||||
project_id = 1
|
||||
for org, project in self.github_data.repos:
|
||||
# Each entry is in the format for "repositories" response
|
||||
# of GET /installation/repositories
|
||||
orgs[org].append({
|
||||
'id': project_id,
|
||||
'name': project,
|
||||
'full_name': '%s/%s' % (org, project)
|
||||
# note, lots of other stuff that's not relevant
|
||||
})
|
||||
project_id += 1
|
||||
|
||||
self.log.debug("GitHub installation mapped to: %s" % orgs)
|
||||
|
||||
# Mock response to GET /app/installations
|
||||
app_json = []
|
||||
app_projects = []
|
||||
app_id = 1
|
||||
for org, projects in orgs.items():
|
||||
# We respond as if each org is a different app instance
|
||||
#
|
||||
# Below we will be sent the app_id in a token to query
|
||||
# what projects this app exports. Keep the projects in a
|
||||
# sequential list so we can just look up "projects for app
|
||||
# X" == app_projects[X]
|
||||
app_projects.append(projects)
|
||||
app_json.append(
|
||||
{
|
||||
'id': app_id,
|
||||
# Acutally none of this matters, and there's lots
|
||||
# more in a real response. Padded out just for
|
||||
# example sake.
|
||||
'account': {
|
||||
'login': org,
|
||||
'id': 1234,
|
||||
'type': 'User',
|
||||
},
|
||||
'permissions': {
|
||||
'checks': 'write',
|
||||
'metadata': 'read',
|
||||
'contents': 'read'
|
||||
},
|
||||
'events': ['push',
|
||||
'pull_request'
|
||||
],
|
||||
'suspended_at': None,
|
||||
'suspended_by': None,
|
||||
}
|
||||
)
|
||||
app_id += 1
|
||||
|
||||
# TODO(ianw) : we could exercise the pagination paths ...
|
||||
with requests_mock.Mocker() as m:
|
||||
m.get('%s/app/installations' % self.base_url, json=app_json)
|
||||
|
||||
def repositories_callback(request, context):
|
||||
# FakeGithubSession gives us an auth token "token
|
||||
# token-X" where "X" corresponds to the app id we want
|
||||
# the projects for. apps start at id "1", so the projects
|
||||
# to return for this call are app_projects[token-1]
|
||||
token = int(request.headers['Authorization'][12:])
|
||||
projects = app_projects[token - 1]
|
||||
return {
|
||||
'total_count': len(projects),
|
||||
'repositories': projects
|
||||
}
|
||||
m.get('%s/installation/repositories?per_page=100' % self.base_url,
|
||||
json=repositories_callback)
|
||||
|
||||
# everything mocked now, call real implementation
|
||||
super()._prime_installation_map()
|
||||
|
||||
|
||||
class FakeGithubConnection(githubconnection.GithubConnection):
|
||||
|
|
|
@ -641,8 +641,9 @@ class FakeGithubSession(object):
|
|||
if re.match(r'.*/app/installations/.*/access_tokens', url):
|
||||
expiry = (datetime.datetime.now(utc) + datetime.timedelta(
|
||||
minutes=60)).replace(microsecond=0).isoformat()
|
||||
install_id = url.split('/')[-2]
|
||||
data = {
|
||||
'token': 'fake',
|
||||
'token': 'token-%s' % install_id,
|
||||
'expires_at': expiry,
|
||||
}
|
||||
return FakeResponse(data, 201)
|
||||
|
@ -767,6 +768,12 @@ class FakeGithubData(object):
|
|||
self.reports = []
|
||||
self.fail_check_run_creation = False
|
||||
|
||||
def __repr__(self):
|
||||
return ("pull_requests:%s repos:%s reports:%s "
|
||||
"fail_check_run_creation:%s" % (
|
||||
self.pull_requests, self.repos, self.reports,
|
||||
self.fail_check_run_creation))
|
||||
|
||||
|
||||
class FakeGithubClient(object):
|
||||
|
||||
|
|
Loading…
Reference in New Issue