# Path: api/app.py
# def create_app(settings_obj=None) -> web.Application:
#     """App factory. Sets up routes and all plugins.
# 
#     :param settings_obj: Object containing optional configuration overrides. May be
#         a Python module or class with uppercased variables.
#     """
#     config = Config()
#     config.from_object(settings)
#     if settings_obj:  # Update with overrides
#         config.from_object(settings_obj)
#     app = web.Application(debug=config.DEBUG)
#     # Store uppercased configuration variables on app
#     app.update(config)
# 
#     # Set up routes
#     routes.setup(app)
#     # Set up cache
#     cache.setup(app)
#     # Use content negotiation middleware to render JSON responses
#     negotiation.setup(app)
#     # Normalize paths: https://aiohttp-utils.readthedocs.org/en/latest/modules/path_norm.html
#     path_norm.setup(app)
#     # Set up Github client
#     github_plugin.setup(app)
# 
#     return app
# 
# Path: api/github/client.py
# class GitHubClient:
# 
#     def __init__(self, client_id: str, client_secret: str, cache: BaseCache=None):
#         self.client_id = client_id
#         self.client_secret = client_secret
#         if not cache:
#             logger.warning('No caching enabled for GitHubClient')
#         self.cache = cache
# 
#     async def make_request(self, url, method='get', cache_key: str=None, **kwargs):
#         url = url.lstrip('/')
# 
#         kwargs.setdefault('params', {})
#         kwargs['params'].update({
#             'client_id': self.client_id,
#             'client_secret': self.client_secret,
#         })
# 
#         kwargs.setdefault('headers', {})
#         kwargs['headers'].update({
#             'User-Agent': 'sir',
#             'Accept': 'application/vnd.github.v3+json',
#         })
# 
#         if self.cache:
#             etag = (self.cache.get(cache_key) or {}).get('etag')
#             if etag:
#                 kwargs['headers']['If-None-Match'] = etag
# 
#         full_url = '{API_URL}/{url}'.format(API_URL=API_URL, url=url)
#         async with aiohttp.request(method.upper(), full_url, **kwargs) as resp:
#             if resp.status >= 400:
#                 json = await resp.json()
#                 raise GitHubClientError(json, resp)
#             else:
#                 if self.cache and resp.status == HTTPStatus.NOT_MODIFIED:
#                     logger.info('Cache HIT for {}; Etag: {}'.format(url, etag))
#                     return self.cache.get(cache_key)['response']
#                 else:
#                     json = await resp.json()
#                     new_etag = resp.headers.get('ETag')
#                     logger.info('Cache MISS for {}'.format(url))
#                     if new_etag and self.cache:
#                         cache_payload = {
#                             'etag': new_etag,
#                             'response': json
#                         }
#                         logger.info('Caching etag and response: {}'.format(new_etag))
#                         self.cache.set(cache_key, cache_payload)
#                     return json
# 
#     async def user_repos(self, username: str, sort: str='pushed'):
#         cache_key = get_cache_key('user_repos', {
#             'username': username,
#             'sort': sort
#         })
#         return await self.make_request(
#             '/users/{username}/repos'.format(username=username),
#             params={'sort': sort},
#             cache_key=cache_key
#         )
# 
#     async def repo_tags(self, username: str, repo: str):
#         cache_key = get_cache_key('repo_tags', {
#             'username': username,
#             'repo': repo
#         })
#         return await self.make_request(
#             '/repos/{username}/{repo}/tags'.format(
#                 username=username, repo=repo
#             ),
#             cache_key=cache_key
#         )
# 
#     async def compare(self, username: str, repo: str, base: str, head='HEAD'):
#         cache_key = get_cache_key('compare', {
#             'username': username,
#             'repo': repo,
#             'base': base,
#             'head': head,
#         })
#         return await self.make_request(
#             '/repos/{username}/{repo}/compare/{base}...{head}'.format(**locals()),
#             cache_key=cache_key
#         )
# 
#     async def rate_limit(self):
#         return await self.make_request('/rate_limit')
# 
# Path: api/github/plugin.py
# APP_CLIENT_KEY = 'github_client'


import asyncio
import os
import sys
import pytest
from decorator import decorator
from api.app import create_app
from api.github.client import GitHubClient
from api.github.plugin import APP_CLIENT_KEY as GITHUB_APP_KEY
sys.path.insert(0, os.path.join(HERE, '..', '..'))


class TestConfig:
    ENV = 'testing'
    DEBUG = True
    CACHE = {'STRATEGY': 'simple', 'PARAMS': {}}


class BaseMockGitHubClient(GitHubClient):

    # Stub out request method
    async def make_request(self, **kwargs):
        pass

    async def repo_tags(self, username, repo):
        raise NotImplementedError

    async def user_repos(self, username, sort='pushed'):
        raise NotImplementedError

    async def compare(self, username: str, repo: str, base: str, head='HEAD'):
        raise NotImplementedError

    async def rate_limit(self):
        raise NotImplementedError

@pytest.fixture()
def make_app():
    def maker(github=None):

# Path: api/app.py
# def create_app(settings_obj=None) -> web.Application:
#     """App factory. Sets up routes and all plugins.
# 
#     :param settings_obj: Object containing optional configuration overrides. May be
#         a Python module or class with uppercased variables.
#     """
#     config = Config()
#     config.from_object(settings)
#     if settings_obj:  # Update with overrides
#         config.from_object(settings_obj)
#     app = web.Application(debug=config.DEBUG)
#     # Store uppercased configuration variables on app
#     app.update(config)
# 
#     # Set up routes
#     routes.setup(app)
#     # Set up cache
#     cache.setup(app)
#     # Use content negotiation middleware to render JSON responses
#     negotiation.setup(app)
#     # Normalize paths: https://aiohttp-utils.readthedocs.org/en/latest/modules/path_norm.html
#     path_norm.setup(app)
#     # Set up Github client
#     github_plugin.setup(app)
# 
#     return app
# 
# Path: api/github/client.py
# class GitHubClient:
# 
#     def __init__(self, client_id: str, client_secret: str, cache: BaseCache=None):
#         self.client_id = client_id
#         self.client_secret = client_secret
#         if not cache:
#             logger.warning('No caching enabled for GitHubClient')
#         self.cache = cache
# 
#     async def make_request(self, url, method='get', cache_key: str=None, **kwargs):
#         url = url.lstrip('/')
# 
#         kwargs.setdefault('params', {})
#         kwargs['params'].update({
#             'client_id': self.client_id,
#             'client_secret': self.client_secret,
#         })
# 
#         kwargs.setdefault('headers', {})
#         kwargs['headers'].update({
#             'User-Agent': 'sir',
#             'Accept': 'application/vnd.github.v3+json',
#         })
# 
#         if self.cache:
#             etag = (self.cache.get(cache_key) or {}).get('etag')
#             if etag:
#                 kwargs['headers']['If-None-Match'] = etag
# 
#         full_url = '{API_URL}/{url}'.format(API_URL=API_URL, url=url)
#         async with aiohttp.request(method.upper(), full_url, **kwargs) as resp:
#             if resp.status >= 400:
#                 json = await resp.json()
#                 raise GitHubClientError(json, resp)
#             else:
#                 if self.cache and resp.status == HTTPStatus.NOT_MODIFIED:
#                     logger.info('Cache HIT for {}; Etag: {}'.format(url, etag))
#                     return self.cache.get(cache_key)['response']
#                 else:
#                     json = await resp.json()
#                     new_etag = resp.headers.get('ETag')
#                     logger.info('Cache MISS for {}'.format(url))
#                     if new_etag and self.cache:
#                         cache_payload = {
#                             'etag': new_etag,
#                             'response': json
#                         }
#                         logger.info('Caching etag and response: {}'.format(new_etag))
#                         self.cache.set(cache_key, cache_payload)
#                     return json
# 
#     async def user_repos(self, username: str, sort: str='pushed'):
#         cache_key = get_cache_key('user_repos', {
#             'username': username,
#             'sort': sort
#         })
#         return await self.make_request(
#             '/users/{username}/repos'.format(username=username),
#             params={'sort': sort},
#             cache_key=cache_key
#         )
# 
#     async def repo_tags(self, username: str, repo: str):
#         cache_key = get_cache_key('repo_tags', {
#             'username': username,
#             'repo': repo
#         })
#         return await self.make_request(
#             '/repos/{username}/{repo}/tags'.format(
#                 username=username, repo=repo
#             ),
#             cache_key=cache_key
#         )
# 
#     async def compare(self, username: str, repo: str, base: str, head='HEAD'):
#         cache_key = get_cache_key('compare', {
#             'username': username,
#             'repo': repo,
#             'base': base,
#             'head': head,
#         })
#         return await self.make_request(
#             '/repos/{username}/{repo}/compare/{base}...{head}'.format(**locals()),
#             cache_key=cache_key
#         )
# 
#     async def rate_limit(self):
#         return await self.make_request('/rate_limit')
# 
# Path: api/github/plugin.py
# APP_CLIENT_KEY = 'github_client'


# Path: api/tests/conftest.py
import asyncio
import os
import sys
import pytest
from decorator import decorator
from api.app import create_app
from api.github.client import GitHubClient
from api.github.plugin import APP_CLIENT_KEY as GITHUB_APP_KEY

sys.path.insert(0, os.path.join(HERE, '..', '..'))


class TestConfig:
    ENV = 'testing'
    DEBUG = True
    CACHE = {'STRATEGY': 'simple', 'PARAMS': {}}


class BaseMockGitHubClient(GitHubClient):

    # Stub out request method
    async def make_request(self, **kwargs):
        pass

    async def repo_tags(self, username, repo):
        raise NotImplementedError

    async def user_repos(self, username, sort='pushed'):
        raise NotImplementedError

    async def compare(self, username: str, repo: str, base: str, head='HEAD'):
        raise NotImplementedError

    async def rate_limit(self):
        raise NotImplementedError

@pytest.fixture()
def make_app():
    def maker(github=None):

        app = create_app(TestConfig)