forked from Wavyzz/py-healthchecks.io
add some client work
This commit is contained in:
@@ -32,7 +32,7 @@ repos:
|
||||
entry: flake8
|
||||
language: system
|
||||
types: [python]
|
||||
exclude: "tests/*"
|
||||
exclude: "^(tests/*|noxfile.py)"
|
||||
require_serial: true
|
||||
- id: pyupgrade
|
||||
name: pyupgrade
|
||||
|
||||
12
noxfile.py
12
noxfile.py
@@ -26,6 +26,7 @@ python_versions = ["3.10", "3.9", "3.8", "3.7"]
|
||||
nox.needs_version = ">= 2021.6.6"
|
||||
nox.options.sessions = (
|
||||
"pre-commit",
|
||||
"bandit",
|
||||
"safety",
|
||||
"mypy",
|
||||
"tests",
|
||||
@@ -139,13 +140,18 @@ def safety(session: Session) -> None:
|
||||
@session(python=python_versions)
|
||||
def mypy(session: Session) -> None:
|
||||
"""Type-check using mypy."""
|
||||
args = session.posargs or ["src", "docs/conf.py"]
|
||||
args = session.posargs or ["src"]
|
||||
session.install(".")
|
||||
session.install("mypy", "pytest")
|
||||
session.install(*mypy_type_packages)
|
||||
session.run("mypy", *args)
|
||||
if not session.posargs:
|
||||
session.run("mypy", f"--python-executable={sys.executable}", "noxfile.py")
|
||||
|
||||
|
||||
@session(python=python_versions[0])
|
||||
def bandit(session: Session) -> None:
|
||||
"""Run bandit security tests"""
|
||||
args = session.posargs or ["-r", "./src"]
|
||||
session.run("bandit", *args)
|
||||
|
||||
|
||||
@session(python=python_versions)
|
||||
|
||||
65
poetry.lock
generated
65
poetry.lock
generated
@@ -752,6 +752,28 @@ toml = "*"
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-async"
|
||||
version = "0.1.1"
|
||||
description = "pytest-async - Run your coroutine in event loop without decorator"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "pytest-asyncio"
|
||||
version = "0.16.0"
|
||||
description = "Pytest support for asyncio."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">= 3.6"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=5.4.0"
|
||||
|
||||
[package.extras]
|
||||
testing = ["coverage", "hypothesis (>=5.7.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
version = "3.0.0"
|
||||
@@ -767,6 +789,20 @@ pytest = ">=4.6"
|
||||
[package.extras]
|
||||
testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-mock"
|
||||
version = "3.6.1"
|
||||
description = "Thin-wrapper around the mock package for easier use with pytest"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=5.0"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox", "pytest-asyncio"]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.8.2"
|
||||
@@ -842,6 +878,17 @@ urllib3 = ">=1.21.1,<1.27"
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
|
||||
use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
|
||||
|
||||
[[package]]
|
||||
name = "respx"
|
||||
version = "0.19.0"
|
||||
description = "A utility for mocking out the Python HTTPX and HTTP Core libraries."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
httpx = ">=0.21.0"
|
||||
|
||||
[[package]]
|
||||
name = "restructuredtext-lint"
|
||||
version = "1.3.2"
|
||||
@@ -1231,7 +1278,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "062c8a09c8967fafd0410a702d9bdc17ef0a260965b65345c6058a3a134a05a3"
|
||||
content-hash = "4609efb7758ecc2785e3e38e1b1bad140bd0311d253ffebd86b9e5d4e4d45766"
|
||||
|
||||
[metadata.files]
|
||||
alabaster = [
|
||||
@@ -1637,10 +1684,22 @@ pytest = [
|
||||
{file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
|
||||
{file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
|
||||
]
|
||||
pytest-async = [
|
||||
{file = "pytest_async-0.1.1-py3-none-any.whl", hash = "sha256:11cc41eef82592951d56c2bb9b0e6ab21b2f0f00663e78d95694a80d965be930"},
|
||||
{file = "pytest_async-0.1.1.tar.gz", hash = "sha256:0d6ffd3ebac2f3aa47d606dbae1984750268a89dc8caf4a908ba61c60299cdfd"},
|
||||
]
|
||||
pytest-asyncio = [
|
||||
{file = "pytest-asyncio-0.16.0.tar.gz", hash = "sha256:7496c5977ce88c34379df64a66459fe395cd05543f0a2f837016e7144391fcfb"},
|
||||
{file = "pytest_asyncio-0.16.0-py3-none-any.whl", hash = "sha256:5f2a21273c47b331ae6aa5b36087047b4899e40f03f18397c0e65fa5cca54e9b"},
|
||||
]
|
||||
pytest-cov = [
|
||||
{file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
|
||||
{file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
|
||||
]
|
||||
pytest-mock = [
|
||||
{file = "pytest-mock-3.6.1.tar.gz", hash = "sha256:40217a058c52a63f1042f0784f62009e976ba824c418cced42e88d5f40ab0e62"},
|
||||
{file = "pytest_mock-3.6.1-py3-none-any.whl", hash = "sha256:30c2f2cc9759e76eee674b81ea28c9f0b94f8f0445a1b87762cadf774f0df7e3"},
|
||||
]
|
||||
python-dateutil = [
|
||||
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
|
||||
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
|
||||
@@ -1772,6 +1831,10 @@ requests = [
|
||||
{file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"},
|
||||
{file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"},
|
||||
]
|
||||
respx = [
|
||||
{file = "respx-0.19.0-py2.py3-none-any.whl", hash = "sha256:1ac1cc99bf892ffd3e33108ae43d71d8309a58ac226965f4bd81ec055600f265"},
|
||||
{file = "respx-0.19.0.tar.gz", hash = "sha256:4a09e15803c7450d45303520ec528794c9fd77b05984263bc83b78aabbb39413"},
|
||||
]
|
||||
restructuredtext-lint = [
|
||||
{file = "restructuredtext_lint-1.3.2.tar.gz", hash = "sha256:d3b10a1fe2ecac537e51ae6d151b223b78de9fafdd50e5eb6b08c243df173c80"},
|
||||
]
|
||||
|
||||
@@ -50,6 +50,10 @@ furo = ">=2021.11.12"
|
||||
pytest-cov = "^3.0.0"
|
||||
types-croniter = "^1.0.3"
|
||||
types-pytz = "^2021.3.1"
|
||||
pytest_async = "^0.1.1"
|
||||
pytest-asyncio = "^0.16.0"
|
||||
respx = "^0.19.0"
|
||||
pytest-mock = "^3.6.1"
|
||||
|
||||
[tool.coverage.paths]
|
||||
source = ["src", "*/site-packages"]
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
"""Py Healthchecks.Io."""
|
||||
|
||||
VERSION = "0.1"
|
||||
|
||||
2
src/healthchecks_io/client/__init__.py
Normal file
2
src/healthchecks_io/client/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
"""healthchecks_io clients."""
|
||||
from .asyncclient import AsyncClient # noqa: F401
|
||||
146
src/healthchecks_io/client/_abstract.py
Normal file
146
src/healthchecks_io/client/_abstract.py
Normal file
@@ -0,0 +1,146 @@
|
||||
from abc import ABC
|
||||
from abc import abstractmethod
|
||||
from json import dumps
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from urllib.parse import parse_qsl
|
||||
from urllib.parse import ParseResult
|
||||
from urllib.parse import unquote
|
||||
from urllib.parse import urlencode
|
||||
from urllib.parse import urljoin
|
||||
from urllib.parse import urlparse
|
||||
from weakref import finalize
|
||||
|
||||
from httpx import Client
|
||||
|
||||
from healthchecks_io.schemas import checks
|
||||
|
||||
|
||||
class AbstractClient(ABC):
|
||||
"""An abstract client class that can be implemented by client classes."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_key: str,
|
||||
api_url: Optional[str] = "https://healthchecks.io/api/",
|
||||
api_version: Optional[int] = 1,
|
||||
client: Optional[Client] = None,
|
||||
) -> None:
|
||||
"""An AbstractClient that other clients can implement.
|
||||
|
||||
Args:
|
||||
api_key (str): Healthchecks.io API key
|
||||
api_url (Optional[str], optional): API URL. Defaults to "https://healthchecks.io/api/".
|
||||
api_version (Optional[int], optional): Versiopn of the api to use. Defaults to 1.
|
||||
client (Optional[Client], optional): A httpx.Client. If not
|
||||
passed in, one will be created for this object. Defaults to None.
|
||||
"""
|
||||
self._api_key = api_key
|
||||
self._client = client
|
||||
if not api_url.endswith("/"):
|
||||
api_url = f"{api_url}/"
|
||||
self._api_url = urljoin(api_url, f"v{api_version}/")
|
||||
self._finalizer = finalize(self, self._finalizer_method)
|
||||
|
||||
@abstractmethod
|
||||
def _finalizer_method(self):
|
||||
"""Finalizer method is called by weakref.finalize when the object is dereferenced to do cleanup of clients."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_checks(self, tags: Optional[List[str]]) -> List[checks.Check]:
|
||||
"""Calls the API's /checks/ endpoint to get a list of checks."""
|
||||
pass
|
||||
|
||||
def _get_api_request_url(
|
||||
self, path: str, params: Optional[Dict[str, str]] = None
|
||||
) -> str:
|
||||
"""Get a full request url for the healthchecks api.
|
||||
|
||||
Args:
|
||||
path (str): Path to request from
|
||||
params (Optional[Dict[str, str]], optional): URL Parameters. Defaults to None.
|
||||
|
||||
Returns:
|
||||
str: url
|
||||
"""
|
||||
url = urljoin(self._api_url, path)
|
||||
return self._add_url_params(url, params) if params is not None else url
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool:
|
||||
"""Is the client closed?
|
||||
|
||||
Returns:
|
||||
bool: is the client closed
|
||||
"""
|
||||
return self._client.is_closed
|
||||
|
||||
@staticmethod
|
||||
def _add_url_params(url: str, params: Dict[str, str], replace: bool = True):
|
||||
"""Add GET params to provided URL being aware of existing.
|
||||
|
||||
:param url: string of target URL
|
||||
:param params: dict containing requested params to be added
|
||||
:param replace: bool True If true, replace params if they exist with new values, otherwise append
|
||||
:return: string with updated URL
|
||||
|
||||
>> url = 'http://stackoverflow.com/test?answers=true'
|
||||
>> new_params = {'answers': False, 'data': ['some','values']}
|
||||
>> add_url_params(url, new_params)
|
||||
'http://stackoverflow.com/test?data=some&data=values&answers=false'
|
||||
"""
|
||||
# Unquoting URL first so we don't loose existing args
|
||||
url = unquote(url)
|
||||
# Extracting url info
|
||||
parsed_url = urlparse(url)
|
||||
# Extracting URL arguments from parsed URL
|
||||
get_args = parsed_url.query
|
||||
# Converting URL arguments to dict
|
||||
parsed_get_args = dict(parse_qsl(get_args))
|
||||
if replace:
|
||||
# Merging URL arguments dict with new params
|
||||
parsed_get_args.update(params)
|
||||
extra_parameters = ""
|
||||
else:
|
||||
# get all the duplicated keys from params and urlencode them, we'll concat this to the params string later
|
||||
duplicated_params = [x for x in params if x in parsed_get_args]
|
||||
# get all the args that aren't duplicated and add them to parsed_get_args
|
||||
parsed_get_args.update(
|
||||
{
|
||||
key: params[key]
|
||||
for key in [x for x in params if x not in parsed_get_args]
|
||||
}
|
||||
)
|
||||
# if we have any duplicated parameters, urlencode them, we append them later
|
||||
extra_parameters = (
|
||||
f"&{urlencode({key: params[key] for key in duplicated_params}, doseq=True)}"
|
||||
if len(duplicated_params) > 0
|
||||
else ""
|
||||
)
|
||||
|
||||
# Bool and Dict values should be converted to json-friendly values
|
||||
# you may throw this part away if you don't like it :)
|
||||
parsed_get_args.update(
|
||||
{
|
||||
k: dumps(v)
|
||||
for k, v in parsed_get_args.items()
|
||||
if isinstance(v, (bool, dict))
|
||||
}
|
||||
)
|
||||
|
||||
# Converting URL argument to proper query string
|
||||
encoded_get_args = f"{urlencode(parsed_get_args, doseq=True)}{extra_parameters}"
|
||||
# Creating new parsed result object based on provided with new
|
||||
# URL arguments. Same thing happens inside of urlparse.
|
||||
new_url = ParseResult(
|
||||
parsed_url.scheme,
|
||||
parsed_url.netloc,
|
||||
parsed_url.path,
|
||||
parsed_url.params,
|
||||
encoded_get_args,
|
||||
parsed_url.fragment,
|
||||
).geturl()
|
||||
|
||||
return new_url
|
||||
86
src/healthchecks_io/client/asyncclient.py
Normal file
86
src/healthchecks_io/client/asyncclient.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""An async healthchecks.io client."""
|
||||
import asyncio
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
from httpx import AsyncClient as HTTPXAsyncClient
|
||||
|
||||
from ._abstract import AbstractClient
|
||||
from .exceptions import HCAPIAuthError
|
||||
from .exceptions import HCAPIError
|
||||
from healthchecks_io import VERSION
|
||||
from healthchecks_io.schemas import checks
|
||||
|
||||
|
||||
class AsyncClient(AbstractClient):
|
||||
"""A Healthchecks.io client implemented using httpx's Async methods."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_key: str,
|
||||
api_url: Optional[str] = "https://healthchecks.io/api/",
|
||||
api_version: Optional[int] = 1,
|
||||
client: Optional[HTTPXAsyncClient] = None,
|
||||
) -> None:
|
||||
"""An AsyncClient can be used in code using asyncio to work with the Healthchecks.io api.
|
||||
|
||||
Args:
|
||||
api_key (str): Healthchecks.io API key
|
||||
api_url (Optional[str], optional): API URL. Defaults to "https://healthchecks.io/api/".
|
||||
api_version (Optional[int], optional): Versiopn of the api to use. Defaults to 1.
|
||||
client (Optional[HTTPXAsyncClient], optional): A httpx.Asyncclient. If not
|
||||
passed in, one will be created for this object. Defaults to None.
|
||||
"""
|
||||
if client is None:
|
||||
client = HTTPXAsyncClient()
|
||||
super().__init__(
|
||||
api_key=api_key, api_url=api_url, api_version=api_version, client=client
|
||||
)
|
||||
self._client.headers["X-Api-Key"] = self._api_key
|
||||
self._client.headers["user-agent"] = f"py-healthchecks.io/{VERSION}"
|
||||
self._client.headers["Content-type"] = "application/json"
|
||||
|
||||
def _finalizer_method(self):
|
||||
"""Calls _afinalizer_method from a sync context to work with weakref.finalizer."""
|
||||
asyncio.run(self._afinalizer_method())
|
||||
|
||||
async def _afinalizer_method(self):
|
||||
"""Finalizer coroutine that closes our client connections."""
|
||||
await self._client.aclose()
|
||||
|
||||
async def get_checks(self, tags: Optional[List[str]] = None) -> List[checks.Check]:
|
||||
"""Get a list of checks from the healthchecks api.
|
||||
|
||||
Args:
|
||||
tags (Optional[List[str]], optional): Filters the checks and returns only
|
||||
the checks that are tagged with the specified value. Defaults to None.
|
||||
|
||||
Raises:
|
||||
HCAPIAuthError: When the API returns a 401, indicates an api key issue
|
||||
HCAPIError: When the API returns anything other than a 200 or 401
|
||||
|
||||
Returns:
|
||||
List[checks.Check]: [description]
|
||||
"""
|
||||
request_url = self._get_api_request_url("checks/")
|
||||
if tags is not None:
|
||||
for tag in tags:
|
||||
request_url = self._add_url_params(
|
||||
request_url, {"tag": tag}, replace=False
|
||||
)
|
||||
|
||||
response = await self._client.get(request_url)
|
||||
|
||||
if response.status_code == 401:
|
||||
raise HCAPIAuthError("Auth failure when getting checks")
|
||||
|
||||
if response.status_code != 200:
|
||||
raise HCAPIError(
|
||||
f"Error when reaching out to HC API at {request_url}. "
|
||||
f"Status Code {response.status_code}. Response {response.text}"
|
||||
)
|
||||
|
||||
return [
|
||||
checks.Check.from_api_result(check_data)
|
||||
for check_data in response.json()["checks"]
|
||||
]
|
||||
13
src/healthchecks_io/client/exceptions.py
Normal file
13
src/healthchecks_io/client/exceptions.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""healthchecks_io exceptions."""
|
||||
|
||||
|
||||
class HCAPIError(Exception):
|
||||
"""API Exception for when we have an error with the healthchecks api."""
|
||||
|
||||
...
|
||||
|
||||
|
||||
class HCAPIAuthError(HCAPIError):
|
||||
"""Thrown when we fail to auth to the Healthchecks api."""
|
||||
|
||||
...
|
||||
@@ -0,0 +1 @@
|
||||
"""Schemas for healthchecks_io."""
|
||||
|
||||
76
tests/client/test_async.py
Normal file
76
tests/client/test_async.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import pytest
|
||||
import respx
|
||||
from httpx import AsyncClient as HTTPXAsyncClient
|
||||
from httpx import Response
|
||||
|
||||
from healthchecks_io.client import AsyncClient
|
||||
from healthchecks_io.client.exceptions import HCAPIAuthError
|
||||
from healthchecks_io.client.exceptions import HCAPIError
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.respx
|
||||
async def test_get_checks_200(fake_check_api_result, respx_mock, test_async_client):
|
||||
assert test_async_client._client is not None
|
||||
checks_url = urljoin(test_async_client._api_url, "checks/")
|
||||
respx_mock.get(checks_url).mock(
|
||||
return_value=Response(status_code=200, json={"checks": [fake_check_api_result]})
|
||||
)
|
||||
checks = await test_async_client.get_checks()
|
||||
assert len(checks) == 1
|
||||
assert checks[0].name == fake_check_api_result["name"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.respx
|
||||
async def test_get_checks_pass_in_client(fake_check_api_result, respx_mock):
|
||||
httpx_client = HTTPXAsyncClient()
|
||||
test_async_client = AsyncClient(
|
||||
api_key="test", api_url="http://localhost/api/", client=httpx_client
|
||||
)
|
||||
checks_url = urljoin(test_async_client._api_url, "checks/")
|
||||
respx_mock.get(checks_url).mock(
|
||||
return_value=Response(status_code=200, json={"checks": [fake_check_api_result]})
|
||||
)
|
||||
checks = await test_async_client.get_checks()
|
||||
assert len(checks) == 1
|
||||
assert checks[0].name == fake_check_api_result["name"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.respx
|
||||
async def test_get_checks_exceptions(
|
||||
fake_check_api_result, respx_mock, test_async_client
|
||||
):
|
||||
checks_url = urljoin(test_async_client._api_url, "checks/")
|
||||
# test exceptions
|
||||
respx_mock.get(checks_url).mock(return_value=Response(status_code=401))
|
||||
with pytest.raises(HCAPIAuthError):
|
||||
await test_async_client.get_checks()
|
||||
|
||||
respx_mock.get(checks_url).mock(return_value=Response(status_code=500))
|
||||
with pytest.raises(HCAPIError):
|
||||
await test_async_client.get_checks()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.respx
|
||||
async def test_get_checks_tags(fake_check_api_result, respx_mock, test_async_client):
|
||||
"""Test get_checks with tags"""
|
||||
checks_url = urljoin(test_async_client._api_url, "checks/")
|
||||
respx_mock.get(f"{checks_url}?tag=test&tag=test2").mock(
|
||||
return_value=Response(status_code=200, json={"checks": [fake_check_api_result]})
|
||||
)
|
||||
checks = await test_async_client.get_checks(tags=["test", "test2"])
|
||||
assert len(checks) == 1
|
||||
assert checks[0].name == fake_check_api_result["name"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
def test_finalizer_closes(test_async_client):
|
||||
"""Tests our finalizer works to close the method"""
|
||||
assert not test_async_client.is_closed
|
||||
test_async_client._finalizer_method()
|
||||
assert test_async_client.is_closed
|
||||
@@ -4,6 +4,7 @@ from typing import Union
|
||||
|
||||
import pytest
|
||||
|
||||
from healthchecks_io.client import AsyncClient
|
||||
from healthchecks_io.schemas import checks
|
||||
|
||||
|
||||
@@ -78,3 +79,10 @@ def fake_ro_check(fake_check: checks.Check):
|
||||
fake_check.update_url = None
|
||||
fake_check.pause_url = None
|
||||
yield fake_check
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_async_client():
|
||||
"""An AsyncClient for testing, set to a nonsense url so we aren't pinging healtchecks."""
|
||||
|
||||
yield AsyncClient(api_key="test", api_url="https://localhost/api")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from healthchecks_io.schemas.badges import Badges
|
||||
|
||||
|
||||
def test_badge_from_api_result():
|
||||
badges_dict = {
|
||||
"svg": "https://healthchecks.io/badge/67541b37-8b9c-4d17-b952-690eae/LOegDs5M-2/backup.svg",
|
||||
@@ -7,8 +8,8 @@ def test_badge_from_api_result():
|
||||
"json": "https://healthchecks.io/badge/67541b37-8b9c-4d17-b952-690eae/LOegDs5M-2/backup.json",
|
||||
"json3": "https://healthchecks.io/badge/67541b37-8b9c-4d17-b952-690eae/LOegDs5M/backup.json",
|
||||
"shields": "https://healthchecks.io/badge/67541b37-8b9c-4d17-b952-690eae/LOegDs5M-2/backup.shields",
|
||||
"shields3": "https://healthchecks.io/badge/67541b37-8b9c-4d17-b952-690eae/LOegDs5M/backup.shields"
|
||||
"shields3": "https://healthchecks.io/badge/67541b37-8b9c-4d17-b952-690eae/LOegDs5M/backup.shields",
|
||||
}
|
||||
this_badge = Badges.from_api_result(badges_dict)
|
||||
assert this_badge.svg == badges_dict['svg']
|
||||
assert this_badge.json_url == badges_dict['json']
|
||||
assert this_badge.svg == badges_dict["svg"]
|
||||
assert this_badge.json_url == badges_dict["json"]
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from healthchecks_io.schemas.integrations import Integration
|
||||
|
||||
|
||||
def test_badge_from_api_result():
|
||||
int_dict = {
|
||||
"id": "4ec5a071-2d08-4baa-898a-eb4eb3cd6941",
|
||||
"name": "My Work Email",
|
||||
"kind": "email"
|
||||
"kind": "email",
|
||||
}
|
||||
this_integration = Integration.from_api_result(int_dict)
|
||||
assert this_integration.id == int_dict['id']
|
||||
assert this_integration.name == int_dict['name']
|
||||
assert this_integration.id == int_dict["id"]
|
||||
assert this_integration.name == int_dict["name"]
|
||||
|
||||
Reference in New Issue
Block a user