0
0
Fork 0
mirror of https://github.com/healthchecks/healthchecks.git synced 2025-04-07 22:25:35 +00:00

Improve type hints

This commit is contained in:
Pēteris Caune 2023-08-29 19:10:27 +03:00
parent aaa172cd28
commit 2b73ddde17
No known key found for this signature in database
GPG key ID: E28D7679E9A9EDE2
59 changed files with 661 additions and 632 deletions

View file

@ -13,7 +13,7 @@ from statsd.defaults.env import statsd
from hc.api.models import Check, Flip
def notify(flip_id, stdout):
def notify(flip_id: int, stdout) -> None:
flip = Flip.objects.get(id=flip_id)
check = flip.owner
@ -71,7 +71,7 @@ class Command(BaseCommand):
help="Send alerts synchronously, without using threads",
)
def process_one_flip(self, use_threads=True):
def process_one_flip(self, use_threads: bool = True) -> bool:
"""Find unprocessed flip, send notifications."""
q = Flip.objects.filter(processed=None)
@ -95,7 +95,7 @@ class Command(BaseCommand):
return True
def handle_going_down(self):
def handle_going_down(self) -> bool:
"""Process a single check going down."""
now_value = now()

View file

@ -33,7 +33,7 @@ class Command(BaseCommand):
help="Keep running indefinitely in a 300 second wait loop",
)
def handle_one_report(self):
def handle_one_report(self) -> bool:
report_due = Q(next_report_date__lt=now())
report_not_scheduled = Q(next_report_date__isnull=True)
@ -68,7 +68,7 @@ class Command(BaseCommand):
return True
def handle_one_nag(self):
def handle_one_nag(self) -> bool:
now_value = now()
q = Profile.objects.filter(next_nag_date__lt=now_value)
q = q.exclude(nag_period=NO_NAG)

View file

@ -780,7 +780,7 @@ class Channel(models.Model):
_, cls = TRANSPORTS[self.kind]
return cls(self)
def notify(self, check, is_test=False):
def notify(self, check: Check, is_test: bool = False) -> str:
if self.transport.is_noop(check):
return "no-op"
@ -1108,7 +1108,7 @@ class Flip(models.Model):
"up": 1 if self.new_status == "up" else 0,
}
def select_channels(self):
def select_channels(self) -> list[Channel]:
"""Return a list of channels that need to be notified.
* Exclude all channels for new->up and paused->up transitions.
@ -1170,7 +1170,7 @@ class TokenBucket(models.Model):
return TokenBucket.authorize(value, 20, 3600)
@staticmethod
def authorize_login_email(email):
def authorize_login_email(email: str) -> bool:
# remove dots and alias:
mailbox, domain = email.split("@")
mailbox = mailbox.replace(".", "")

View file

@ -5,7 +5,7 @@ from hc.test import BaseTestCase
class ApiAdminTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project, tags="foo bar")
@ -13,7 +13,7 @@ class ApiAdminTestCase(BaseTestCase):
self.alice.is_superuser = True
self.alice.save()
def test_it_shows_channel_list_with_pushbullet(self):
def test_it_shows_channel_list_with_pushbullet(self) -> None:
self.client.login(username="alice@example.org", password="password")
Channel.objects.create(

View file

@ -11,7 +11,7 @@ from hc.test import BaseTestCase
class BadgeTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project, tags="foo bar")
@ -23,30 +23,30 @@ class BadgeTestCase(BaseTestCase):
self.with_late_url = "/badge/%s/%s/foo.json" % (self.project.badge_key, sig)
self.shields_url = "/badge/%s/%s-2/foo.shields" % (self.project.badge_key, sig)
def test_it_rejects_bad_signature(self):
def test_it_rejects_bad_signature(self) -> None:
r = self.client.get("/badge/%s/12345678/foo.svg" % self.project.badge_key)
self.assertEqual(r.status_code, 404)
def test_it_returns_svg(self):
def test_it_returns_svg(self) -> None:
r = self.client.get(self.svg_url)
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
self.assertIn("no-cache", r["Cache-Control"])
self.assertContains(r, "#4c1")
def test_it_rejects_bad_format(self):
def test_it_rejects_bad_format(self) -> None:
r = self.client.get(self.json_url + "foo")
self.assertEqual(r.status_code, 404)
def test_it_handles_options(self):
def test_it_handles_options(self) -> None:
r = self.client.options(self.svg_url)
self.assertEqual(r.status_code, 204)
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
def test_it_handles_new(self):
def test_it_handles_new(self) -> None:
doc = self.client.get(self.json_url).json()
self.assertEqual(doc, {"status": "up", "total": 1, "grace": 0, "down": 0})
def test_it_ignores_started_when_down(self):
def test_it_ignores_started_when_down(self) -> None:
self.check.last_start = now()
self.check.status = "down"
self.check.save()
@ -54,7 +54,7 @@ class BadgeTestCase(BaseTestCase):
doc = self.client.get(self.json_url).json()
self.assertEqual(doc, {"status": "down", "total": 1, "grace": 0, "down": 1})
def test_it_treats_late_as_up(self):
def test_it_treats_late_as_up(self) -> None:
self.check.last_ping = now() - td(days=1, minutes=10)
self.check.status = "up"
self.check.save()
@ -62,7 +62,7 @@ class BadgeTestCase(BaseTestCase):
doc = self.client.get(self.json_url).json()
self.assertEqual(doc, {"status": "up", "total": 1, "grace": 1, "down": 0})
def test_it_handles_special_characters(self):
def test_it_handles_special_characters(self) -> None:
self.check.tags = "db@dc1"
self.check.save()
@ -73,7 +73,7 @@ class BadgeTestCase(BaseTestCase):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
def test_late_mode_returns_late_status(self):
def test_late_mode_returns_late_status(self) -> None:
self.check.last_ping = now() - td(days=1, minutes=10)
self.check.status = "up"
self.check.save()
@ -81,7 +81,7 @@ class BadgeTestCase(BaseTestCase):
doc = self.client.get(self.with_late_url).json()
self.assertEqual(doc, {"status": "late", "total": 1, "grace": 1, "down": 0})
def test_late_mode_ignores_started_when_late(self):
def test_late_mode_ignores_started_when_late(self) -> None:
self.check.last_start = now()
self.check.last_ping = now() - td(days=1, minutes=10)
self.check.status = "up"
@ -90,7 +90,7 @@ class BadgeTestCase(BaseTestCase):
doc = self.client.get(self.with_late_url).json()
self.assertEqual(doc, {"status": "late", "total": 1, "grace": 1, "down": 0})
def test_it_returns_shields_json(self):
def test_it_returns_shields_json(self) -> None:
doc = self.client.get(self.shields_url).json()
self.assertEqual(
doc,

View file

@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class BounceTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project, status="up")
@ -29,7 +29,12 @@ class BounceTestCase(BaseTestCase):
self.url = "/api/v2/bounces/"
def post(self, status="5.0.0", to_local=None, diagnostic_code=""):
def post(
self,
status: str = "5.0.0",
to_local: str | None = None,
diagnostic_code: str = "",
):
if to_local is None:
to_local = sign_bounce_id("n.%s" % self.n.code)
@ -68,7 +73,7 @@ To: foo@example.com
return self.csrf_client.post(self.url, msg, content_type="text/plain")
def test_it_handles_permanent_notification_bounce(self):
def test_it_handles_permanent_notification_bounce(self) -> None:
r = self.post()
self.assertEqual(r.status_code, 200)
@ -82,7 +87,7 @@ To: foo@example.com
)
self.assertTrue(self.channel.disabled)
def test_it_handles_transient_notification_bounce(self):
def test_it_handles_transient_notification_bounce(self) -> None:
r = self.post(status="4.0.0")
self.assertEqual(r.status_code, 200)
self.assertEqual(r.content.decode(), "OK")
@ -96,12 +101,12 @@ To: foo@example.com
)
self.assertFalse(self.channel.disabled)
def test_it_handles_notification_non_bounce(self):
def test_it_handles_notification_non_bounce(self) -> None:
r = self.post(status="2.0.0")
self.assertEqual(r.status_code, 200)
self.assertEqual(r.content.decode(), "OK (ignored)")
def test_it_handles_bad_signature(self):
def test_it_handles_bad_signature(self) -> None:
with override_settings(SECRET_KEY="wrong-signing-key"):
to_local = sign_bounce_id("n.%s" % self.n.code)
@ -109,7 +114,7 @@ To: foo@example.com
self.assertEqual(r.status_code, 200)
self.assertEqual(r.content.decode(), "OK (bad signature)")
def test_it_handles_expired_signature(self):
def test_it_handles_expired_signature(self) -> None:
with patch("hc.lib.signing.time") as mock_time:
mock_time.time.return_value = time.time() - 3600 * 48 - 1
to_local = sign_bounce_id("n.%s" % self.n.code)
@ -118,14 +123,14 @@ To: foo@example.com
self.assertEqual(r.status_code, 200)
self.assertEqual(r.content.decode(), "OK (bad signature)")
def test_it_checks_notification_age(self):
def test_it_checks_notification_age(self) -> None:
self.n.created = now() - td(hours=49)
self.n.save()
r = self.post()
self.assertEqual(r.status_code, 200)
self.assertEqual(r.content.decode(), "OK (notification not found)")
def test_it_handles_permanent_report_bounce(self):
def test_it_handles_permanent_report_bounce(self) -> None:
to_local = sign_bounce_id("r.alice")
r = self.post(to_local=to_local)
self.assertEqual(r.status_code, 200)
@ -135,7 +140,7 @@ To: foo@example.com
self.assertEqual(self.profile.reports, "off")
self.assertEqual(self.profile.nag_period, td())
def test_it_handles_transient_report_bounce(self):
def test_it_handles_transient_report_bounce(self) -> None:
to_local = sign_bounce_id("r.alice")
r = self.post(status="4.0.0", to_local=to_local)
self.assertEqual(r.status_code, 200)
@ -144,13 +149,13 @@ To: foo@example.com
self.profile.refresh_from_db()
self.assertEqual(self.profile.reports, "monthly")
def test_it_handles_bad_username(self):
def test_it_handles_bad_username(self) -> None:
to_local = sign_bounce_id("r.doesnotexist")
r = self.post(to_local=to_local)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.content.decode(), "OK (user not found)")
def test_it_logs_diagnostic_code(self):
def test_it_logs_diagnostic_code(self) -> None:
diagnostic_code = (
"Diagnostic-Code: smtp; 451 4.0.0 No usable MXs, last err: try again later"
)
@ -168,7 +173,7 @@ To: foo@example.com
self.channel.refresh_from_db()
self.assertEqual(self.channel.last_error, expected)
def test_it_truncates_long_diagnostic_code(self):
def test_it_truncates_long_diagnostic_code(self) -> None:
diagnostic_code = "Diagnostic-Code: smtp; 451 4.0.0 " + "foo " * 100
r = self.post(status="4.0.0", diagnostic_code=diagnostic_code)

View file

@ -7,7 +7,7 @@ from hc.test import BaseTestCase
class ChannelModelTestCase(BaseTestCase):
def test_webhook_spec_handles_mixed(self):
def test_webhook_spec_handles_mixed(self) -> None:
c = Channel(kind="webhook")
c.value = json.dumps(
{
@ -42,18 +42,18 @@ class ChannelModelTestCase(BaseTestCase):
},
)
def test_it_handles_legacy_opsgenie_value(self):
def test_it_handles_legacy_opsgenie_value(self) -> None:
c = Channel(kind="opsgenie", value="foo123")
self.assertEqual(c.opsgenie_key, "foo123")
self.assertEqual(c.opsgenie_region, "us")
def test_it_handles_json_opsgenie_value(self):
def test_it_handles_json_opsgenie_value(self) -> None:
c = Channel(kind="opsgenie")
c.value = json.dumps({"key": "abc", "region": "eu"})
self.assertEqual(c.opsgenie_key, "abc")
self.assertEqual(c.opsgenie_region, "eu")
def test_it_handles_legacy_sms_json_value(self):
def test_it_handles_legacy_sms_json_value(self) -> None:
c = Channel(kind="sms", value=json.dumps({"value": "+123123123"}))
self.assertTrue(c.sms_notify_down)
self.assertFalse(c.sms_notify_up)

View file

@ -9,34 +9,34 @@ from hc.api.models import Check
class CheckModelTestCase(TestCase):
def test_it_handles_new_check(self):
def test_it_handles_new_check(self) -> None:
check = Check()
self.assertEqual(check.going_down_after(), None)
def test_it_handles_paused_check(self):
def test_it_handles_paused_check(self) -> None:
check = Check(status="paused")
check.last_ping = now() - td(days=2)
self.assertEqual(check.going_down_after(), None)
def test_it_handles_up(self):
def test_it_handles_up(self) -> None:
check = Check(status="up")
check.last_ping = now() - td(hours=1)
expected_aa = check.last_ping + td(days=1, hours=1)
self.assertEqual(check.going_down_after(), expected_aa)
def test_it_handles_paused_then_started_check(self):
def test_it_handles_paused_then_started_check(self) -> None:
check = Check(status="paused")
check.last_start = now() - td(days=2)
expected_aa = check.last_start + td(hours=1)
self.assertEqual(check.going_down_after(), expected_aa)
def test_it_handles_down(self):
def test_it_handles_down(self) -> None:
check = Check(status="down")
check.last_ping = now() - td(hours=1)
self.assertEqual(check.going_down_after(), None)
def test_it_handles_down_then_started_check(self):
def test_it_handles_down_then_started_check(self) -> None:
check = Check(status="down")
check.last_start = now() - td(minutes=10)

View file

@ -16,7 +16,7 @@ MOCK_NOW = Mock(return_value=CURRENT_TIME)
class CheckModelTestCase(BaseTestCase):
def test_it_strips_tags(self):
def test_it_strips_tags(self) -> None:
check = Check()
check.tags = " foo bar "
@ -25,24 +25,24 @@ class CheckModelTestCase(BaseTestCase):
check.tags = " "
self.assertEqual(check.tags_list(), [])
def test_get_status_handles_new_check(self):
def test_get_status_handles_new_check(self) -> None:
check = Check()
self.assertEqual(check.get_status(), "new")
def test_status_works_with_grace_period(self):
def test_status_works_with_grace_period(self) -> None:
check = Check()
check.status = "up"
check.last_ping = now() - td(days=1, minutes=30)
self.assertEqual(check.get_status(), "grace")
def test_get_status_handles_paused_check(self):
def test_get_status_handles_paused_check(self) -> None:
check = Check()
check.status = "paused"
check.last_ping = now() - td(days=1, minutes=30)
self.assertEqual(check.get_status(), "paused")
def test_status_works_with_cron_syntax(self):
def test_status_works_with_cron_syntax(self) -> None:
dt = make_aware(datetime(2000, 1, 1), timezone=timezone.utc)
# Expect ping every midnight, default grace is 1 hour
@ -67,7 +67,7 @@ class CheckModelTestCase(BaseTestCase):
mock_now.return_value = dt + td(days=1, minutes=60)
self.assertEqual(check.get_status(), "down")
def test_status_works_with_timezone(self):
def test_status_works_with_timezone(self) -> None:
dt = make_aware(datetime(2000, 1, 1), timezone=timezone.utc)
# Expect ping every day at 10am, default grace is 1 hour
@ -93,21 +93,21 @@ class CheckModelTestCase(BaseTestCase):
mock_now.return_value = dt + td(days=1, minutes=60)
self.assertEqual(check.get_status(), "down")
def test_get_status_handles_past_grace(self):
def test_get_status_handles_past_grace(self) -> None:
check = Check()
check.status = "up"
check.last_ping = now() - td(days=2)
self.assertEqual(check.get_status(), "down")
def test_get_status_obeys_down_status(self):
def test_get_status_obeys_down_status(self) -> None:
check = Check()
check.status = "down"
check.last_ping = now() - td(minutes=1)
self.assertEqual(check.get_status(), "down")
def test_get_status_handles_started(self):
def test_get_status_handles_started(self) -> None:
check = Check()
check.last_ping = now() - td(hours=2)
# Last start was 5 minutes ago, display status should be "started"
@ -116,7 +116,7 @@ class CheckModelTestCase(BaseTestCase):
check.status = status
self.assertEqual(check.get_status(with_started=True), "started")
def test_get_status_handles_down_then_started_and_expired(self):
def test_get_status_handles_down_then_started_and_expired(self) -> None:
check = Check(status="down")
# Last ping was 2 days ago
check.last_ping = now() - td(days=2)
@ -126,7 +126,7 @@ class CheckModelTestCase(BaseTestCase):
self.assertEqual(check.get_status(with_started=True), "down")
self.assertEqual(check.get_status(), "down")
def test_get_status_handles_up_then_started(self):
def test_get_status_handles_up_then_started(self) -> None:
check = Check(status="up")
# Last ping was 2 hours ago, so is still up
check.last_ping = now() - td(hours=2)
@ -137,7 +137,7 @@ class CheckModelTestCase(BaseTestCase):
# A started check still is considered "up":
self.assertEqual(check.get_status(), "up")
def test_get_status_handles_up_then_started_and_expired(self):
def test_get_status_handles_up_then_started_and_expired(self) -> None:
check = Check(status="up")
# Last ping was 3 hours ago, so is still up
check.last_ping = now() - td(hours=3)
@ -147,7 +147,7 @@ class CheckModelTestCase(BaseTestCase):
self.assertEqual(check.get_status(with_started=True), "down")
self.assertEqual(check.get_status(), "down")
def test_get_status_handles_paused_then_started_and_expired(self):
def test_get_status_handles_paused_then_started_and_expired(self) -> None:
check = Check(status="paused")
# Last start was 2 hours ago - the check is past its grace time
check.last_start = now() - td(hours=2)
@ -155,14 +155,14 @@ class CheckModelTestCase(BaseTestCase):
self.assertEqual(check.get_status(with_started=True), "down")
self.assertEqual(check.get_status(), "down")
def test_get_status_handles_started_and_mia(self):
def test_get_status_handles_started_and_mia(self) -> None:
check = Check()
check.last_start = now() - td(hours=2)
self.assertEqual(check.get_status(with_started=True), "down")
self.assertEqual(check.get_status(), "down")
def test_next_ping_with_cron_syntax(self):
def test_next_ping_with_cron_syntax(self) -> None:
dt = make_aware(datetime(2000, 1, 1), timezone=timezone.utc)
# Expect ping every round hour
@ -179,7 +179,7 @@ class CheckModelTestCase(BaseTestCase):
@patch("hc.api.models.now", MOCK_NOW)
@patch("hc.lib.date.now", MOCK_NOW)
def test_downtimes_handles_no_flips(self):
def test_downtimes_handles_no_flips(self) -> None:
check = Check(project=self.project)
check.created = datetime(2019, 1, 1, tzinfo=timezone.utc)
check.save()
@ -203,7 +203,7 @@ class CheckModelTestCase(BaseTestCase):
@patch("hc.api.models.now", MOCK_NOW)
@patch("hc.lib.date.now", MOCK_NOW)
def test_downtimes_handles_currently_down_check(self):
def test_downtimes_handles_currently_down_check(self) -> None:
check = Check(project=self.project, status="down")
check.created = datetime(2019, 1, 1, tzinfo=timezone.utc)
check.save()
@ -215,7 +215,7 @@ class CheckModelTestCase(BaseTestCase):
@patch("hc.api.models.now", MOCK_NOW)
@patch("hc.lib.date.now", MOCK_NOW)
def test_downtimes_handles_flip_one_day_ago(self):
def test_downtimes_handles_flip_one_day_ago(self) -> None:
check = Check.objects.create(project=self.project, status="down")
check.created = datetime(2019, 1, 1, tzinfo=timezone.utc)
@ -237,7 +237,7 @@ class CheckModelTestCase(BaseTestCase):
@patch("hc.api.models.now", MOCK_NOW)
@patch("hc.lib.date.now", MOCK_NOW)
def test_downtimes_handles_flip_two_months_ago(self):
def test_downtimes_handles_flip_two_months_ago(self) -> None:
check = Check.objects.create(project=self.project, status="down")
check.created = datetime(2019, 1, 1, tzinfo=timezone.utc)
@ -267,7 +267,7 @@ class CheckModelTestCase(BaseTestCase):
@patch("hc.api.models.now", MOCK_NOW)
@patch("hc.lib.date.now", MOCK_NOW)
def test_downtimes_handles_non_utc_timezone(self):
def test_downtimes_handles_non_utc_timezone(self) -> None:
check = Check.objects.create(project=self.project, status="down")
check.created = datetime(2019, 1, 1, tzinfo=timezone.utc)
@ -292,7 +292,7 @@ class CheckModelTestCase(BaseTestCase):
@patch("hc.api.models.now", MOCK_NOW)
@patch("hc.lib.date.now", MOCK_NOW)
def test_downtimes_handles_months_when_check_did_not_exist(self):
def test_downtimes_handles_months_when_check_did_not_exist(self) -> None:
check = Check(project=self.project)
check.created = datetime(2020, 1, 1, 9, tzinfo=timezone.utc)
check.save()
@ -312,7 +312,7 @@ class CheckModelTestCase(BaseTestCase):
self.assertEqual(jan[2], 0)
@override_settings(S3_BUCKET=None)
def test_it_prunes(self):
def test_it_prunes(self) -> None:
check = Check.objects.create(project=self.project, n_pings=101)
Ping.objects.create(owner=check, n=101)
Ping.objects.create(owner=check, n=1)
@ -332,7 +332,7 @@ class CheckModelTestCase(BaseTestCase):
@override_settings(S3_BUCKET="test-bucket")
@patch("hc.api.models.remove_objects")
def test_it_prunes_object_storage(self, remove_objects):
def test_it_prunes_object_storage(self, remove_objects: Mock) -> None:
check = Check.objects.create(project=self.project, n_pings=101)
Ping.objects.create(owner=check, n=101)
Ping.objects.create(owner=check, n=1, object_size=1000)

View file

@ -11,7 +11,12 @@ from hc.test import BaseTestCase
class CreateCheckTestCase(BaseTestCase):
URL = "/api/v1/checks/"
def post(self, data, expect_fragment=None, v=1):
def post(
self,
data: dict[str, str | int | bool | list[str]],
expect_fragment: str | None = None,
v: int = 1,
):
if "api_key" not in data:
data["api_key"] = "X" * 32
@ -23,7 +28,7 @@ class CreateCheckTestCase(BaseTestCase):
return r
def test_it_works(self):
def test_it_works(self) -> None:
r = self.post(
{
"name": "Foo",
@ -75,12 +80,12 @@ class CreateCheckTestCase(BaseTestCase):
self.assertEqual(check.grace.total_seconds(), 60)
self.assertEqual(check.project, self.project)
def test_it_handles_options(self):
def test_it_handles_options(self) -> None:
r = self.client.options(self.URL)
self.assertEqual(r.status_code, 204)
self.assertIn("POST", r["Access-Control-Allow-Methods"])
def test_30_days_works(self):
def test_30_days_works(self) -> None:
r = self.post({"name": "Foo", "timeout": 2592000, "grace": 2592000})
self.assertEqual(r.status_code, 201)
@ -89,7 +94,7 @@ class CreateCheckTestCase(BaseTestCase):
self.assertEqual(check.timeout.total_seconds(), 2592000)
self.assertEqual(check.grace.total_seconds(), 2592000)
def test_it_accepts_api_key_in_header(self):
def test_it_accepts_api_key_in_header(self) -> None:
payload = {"name": "Foo"}
r = self.client.post(
self.URL, payload, content_type="application/json", HTTP_X_API_KEY="X" * 32
@ -97,7 +102,7 @@ class CreateCheckTestCase(BaseTestCase):
self.assertEqual(r.status_code, 201)
def test_it_assigns_channels(self):
def test_it_assigns_channels(self) -> None:
channel = Channel.objects.create(project=self.project)
r = self.post({"channels": "*"})
@ -106,7 +111,7 @@ class CreateCheckTestCase(BaseTestCase):
check = Check.objects.get()
self.assertEqual(check.channel_set.get(), channel)
def test_it_sets_channel_by_name(self):
def test_it_sets_channel_by_name(self) -> None:
channel = Channel.objects.create(project=self.project, name="alerts")
r = self.post({"channels": "alerts"})
@ -116,7 +121,7 @@ class CreateCheckTestCase(BaseTestCase):
assigned_channel = check.channel_set.get()
self.assertEqual(assigned_channel, channel)
def test_it_sets_channel_by_name_formatted_as_uuid(self):
def test_it_sets_channel_by_name_formatted_as_uuid(self) -> None:
name = "102eaa82-a274-4b15-a499-c1bb6bbcd7b6"
channel = Channel.objects.create(project=self.project, name=name)
@ -127,7 +132,7 @@ class CreateCheckTestCase(BaseTestCase):
assigned_channel = check.channel_set.get()
self.assertEqual(assigned_channel, channel)
def test_it_handles_channel_lookup_by_name_with_no_results(self):
def test_it_handles_channel_lookup_by_name_with_no_results(self) -> None:
r = self.post({"channels": "abc"})
self.assertEqual(r.status_code, 400)
self.assertEqual(r.json()["error"], "invalid channel identifier: abc")
@ -135,7 +140,7 @@ class CreateCheckTestCase(BaseTestCase):
# The check should not have been saved
self.assertFalse(Check.objects.exists())
def test_it_handles_channel_lookup_by_name_with_multiple_results(self):
def test_it_handles_channel_lookup_by_name_with_multiple_results(self) -> None:
Channel.objects.create(project=self.project, name="foo")
Channel.objects.create(project=self.project, name="foo")
@ -147,7 +152,7 @@ class CreateCheckTestCase(BaseTestCase):
# The check should not have been saved
self.assertFalse(Check.objects.exists())
def test_it_rejects_multiple_empty_channel_names(self):
def test_it_rejects_multiple_empty_channel_names(self) -> None:
Channel.objects.create(project=self.project, name="")
r = self.post({"channels": ","})
@ -157,7 +162,7 @@ class CreateCheckTestCase(BaseTestCase):
# The check should not have been saved
self.assertFalse(Check.objects.exists())
def test_it_supports_unique_name(self):
def test_it_supports_unique_name(self) -> None:
check = Check.objects.create(project=self.project, name="Foo")
r = self.post({"name": "Foo", "tags": "bar", "unique": ["name"]})
@ -172,7 +177,7 @@ class CreateCheckTestCase(BaseTestCase):
check.refresh_from_db()
self.assertEqual(check.tags, "bar")
def test_it_supports_unique_slug(self):
def test_it_supports_unique_slug(self) -> None:
check = Check.objects.create(project=self.project, slug="foo")
r = self.post({"slug": "foo", "tags": "bar", "unique": ["slug"]})
@ -187,7 +192,7 @@ class CreateCheckTestCase(BaseTestCase):
check.refresh_from_db()
self.assertEqual(check.tags, "bar")
def test_it_supports_unique_tags(self):
def test_it_supports_unique_tags(self) -> None:
Check.objects.create(project=self.project, tags="foo")
r = self.post({"tags": "foo", "unique": ["tags"]})
@ -198,7 +203,7 @@ class CreateCheckTestCase(BaseTestCase):
# And there should be only one check in the database:
self.assertEqual(Check.objects.count(), 1)
def test_it_supports_unique_timeout(self):
def test_it_supports_unique_timeout(self) -> None:
Check.objects.create(project=self.project, timeout=td(seconds=123))
r = self.post({"timeout": 123, "unique": ["timeout"]})
@ -209,7 +214,7 @@ class CreateCheckTestCase(BaseTestCase):
# And there should be only one check in the database:
self.assertEqual(Check.objects.count(), 1)
def test_it_supports_unique_grace(self):
def test_it_supports_unique_grace(self) -> None:
Check.objects.create(project=self.project, grace=td(seconds=123))
r = self.post({"grace": 123, "unique": ["grace"]})
@ -220,7 +225,7 @@ class CreateCheckTestCase(BaseTestCase):
# And there should be only one check in the database:
self.assertEqual(Check.objects.count(), 1)
def test_it_handles_empty_unique_parameter(self):
def test_it_handles_empty_unique_parameter(self) -> None:
check = Check.objects.create(project=self.project)
r = self.post({"name": "Hello", "unique": []})
@ -232,50 +237,50 @@ class CreateCheckTestCase(BaseTestCase):
check.refresh_from_db()
self.assertEqual(check.name, "")
def test_it_handles_missing_request_body(self):
def test_it_handles_missing_request_body(self) -> None:
r = self.client.post(self.URL, content_type="application/json")
self.assertEqual(r.status_code, 401)
self.assertEqual(r.json()["error"], "missing api key")
def test_it_handles_invalid_json(self):
def test_it_handles_invalid_json(self) -> None:
r = self.client.post(
self.URL, "this is not json", content_type="application/json"
)
self.assertEqual(r.status_code, 400)
self.assertEqual(r.json()["error"], "could not parse request body")
def test_it_rejects_wrong_api_key(self):
def test_it_rejects_wrong_api_key(self) -> None:
r = self.post({"api_key": "Y" * 32})
self.assertEqual(r.status_code, 401)
def test_it_rejects_small_timeout(self):
def test_it_rejects_small_timeout(self) -> None:
self.post({"timeout": 0}, expect_fragment="timeout is too small")
def test_it_rejects_large_timeout(self):
def test_it_rejects_large_timeout(self) -> None:
self.post({"timeout": 31536001}, expect_fragment="timeout is too large")
def test_it_rejects_non_number_timeout(self):
def test_it_rejects_non_number_timeout(self) -> None:
self.post({"timeout": "oops"}, expect_fragment="timeout is not a number")
def test_it_rejects_non_string_name(self):
def test_it_rejects_non_string_name(self) -> None:
self.post({"name": False}, expect_fragment="name is not a string")
def test_it_rejects_long_name(self):
def test_it_rejects_long_name(self) -> None:
self.post({"name": "01234567890" * 20}, expect_fragment="name is too long")
def test_unique_accepts_only_specific_values(self):
def test_unique_accepts_only_specific_values(self) -> None:
self.post(
{"name": "Foo", "unique": ["status"]},
expect_fragment="unexpected value",
)
def test_it_rejects_bad_unique_values(self):
def test_it_rejects_bad_unique_values(self) -> None:
self.post(
{"name": "Foo", "unique": "not a list"},
expect_fragment="not an array",
)
def test_it_supports_cron_syntax(self):
def test_it_supports_cron_syntax(self) -> None:
r = self.post({"schedule": "5 * * * *", "tz": "Europe/Riga", "grace": 60})
self.assertEqual(r.status_code, 201)
@ -287,80 +292,80 @@ class CreateCheckTestCase(BaseTestCase):
self.assertTrue("timeout" not in doc)
def test_it_validates_cron_expression(self):
def test_it_validates_cron_expression(self) -> None:
r = self.post({"schedule": "bad-expression", "tz": "Europe/Riga", "grace": 60})
self.assertEqual(r.status_code, 400)
def test_it_validates_timezone(self):
def test_it_validates_timezone(self) -> None:
r = self.post({"schedule": "* * * * *", "tz": "not-a-timezone", "grace": 60})
self.assertEqual(r.status_code, 400)
def test_it_sets_default_timeout(self):
def test_it_sets_default_timeout(self) -> None:
r = self.post({})
self.assertEqual(r.status_code, 201)
doc = r.json()
self.assertEqual(doc["timeout"], 86400)
def test_it_obeys_check_limit(self):
def test_it_obeys_check_limit(self) -> None:
self.profile.check_limit = 0
self.profile.save()
r = self.post({})
self.assertEqual(r.status_code, 403)
def test_it_rejects_readonly_key(self):
def test_it_rejects_readonly_key(self) -> None:
self.project.api_key_readonly = "R" * 32
self.project.save()
r = self.post({"api_key": "R" * 32, "name": "Foo"})
self.assertEqual(r.status_code, 401)
def test_it_sets_manual_resume(self):
def test_it_sets_manual_resume(self) -> None:
r = self.post({"manual_resume": True})
self.assertEqual(r.status_code, 201)
check = Check.objects.get()
self.assertTrue(check.manual_resume)
def test_it_rejects_non_boolean_manual_resume(self):
def test_it_rejects_non_boolean_manual_resume(self) -> None:
r = self.post({"manual_resume": "surprise"})
self.assertEqual(r.status_code, 400)
def test_it_sets_methods(self):
def test_it_sets_methods(self) -> None:
r = self.post({"methods": "POST"})
self.assertEqual(r.status_code, 201)
check = Check.objects.get()
self.assertEqual(check.methods, "POST")
def test_it_rejects_bad_methods_value(self):
def test_it_rejects_bad_methods_value(self) -> None:
r = self.post({"methods": "bad-value"})
self.assertEqual(r.status_code, 400)
def test_it_sets_success_kw(self):
def test_it_sets_success_kw(self) -> None:
r = self.post({"subject": "SUCCESS,COMPLETE"})
self.assertEqual(r.status_code, 201)
check = Check.objects.get()
self.assertTrue(check.filter_subject)
self.assertEqual(check.success_kw, "SUCCESS,COMPLETE")
def test_it_sets_failure_kw(self):
def test_it_sets_failure_kw(self) -> None:
r = self.post({"subject_fail": "FAILED,FAILURE"})
self.assertEqual(r.status_code, 201)
check = Check.objects.get()
self.assertTrue(check.filter_subject)
self.assertEqual(check.failure_kw, "FAILED,FAILURE")
def test_it_rejects_non_string_subject(self):
def test_it_rejects_non_string_subject(self) -> None:
self.post({"subject": False}, expect_fragment="subject is not a string")
def test_it_rejects_non_string_subject_fail(self):
def test_it_rejects_non_string_subject_fail(self) -> None:
msg = "subject_fail is not a string"
self.post({"subject_fail": False}, expect_fragment=msg)
def test_v2_reports_started_separately(self):
def test_v2_reports_started_separately(self) -> None:
Check.objects.create(project=self.project, name="X", last_start=now())
r = self.post({"name": "X", "unique": ["name"]}, v=2)
@ -371,7 +376,7 @@ class CreateCheckTestCase(BaseTestCase):
self.assertEqual(doc["status"], "new")
self.assertTrue(doc["started"])
def test_v3_saves_slug(self):
def test_v3_saves_slug(self) -> None:
r = self.post({"name": "Foo", "slug": "custom-slug"}, v=3)
self.assertEqual(r.status_code, 201)
@ -379,14 +384,14 @@ class CreateCheckTestCase(BaseTestCase):
self.assertEqual(check.name, "Foo")
self.assertEqual(check.slug, "custom-slug")
def test_v3_does_not_autogenerate_slug(self):
def test_v3_does_not_autogenerate_slug(self) -> None:
r = self.post({"name": "Foo"}, v=3)
self.assertEqual(r.status_code, 201)
check = Check.objects.get()
self.assertEqual(check.slug, "")
def test_it_handles_invalid_slug(self):
def test_it_handles_invalid_slug(self) -> None:
for slug in ["Uppercase", "special!", "look spaces"]:
r = self.post({"name": "Foo", "slug": "Hey!"}, v=3)
self.assertEqual(r.status_code, 400)

View file

@ -7,13 +7,13 @@ from hc.test import BaseTestCase
class DeleteCheckTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project)
self.url = f"/api/v2/checks/{self.check.code}"
self.urlv1 = f"/api/v1/checks/{self.check.code}"
def test_it_works(self):
def test_it_works(self) -> None:
r = self.client.delete(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 200)
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
@ -21,21 +21,21 @@ class DeleteCheckTestCase(BaseTestCase):
# It should be gone--
self.assertFalse(Check.objects.filter(code=self.check.code).exists())
def test_it_handles_missing_check(self):
def test_it_handles_missing_check(self) -> None:
self.check.delete()
r = self.client.delete(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 404)
def test_it_handles_options(self):
def test_it_handles_options(self) -> None:
r = self.client.options(self.url)
self.assertEqual(r.status_code, 204)
self.assertIn("DELETE", r["Access-Control-Allow-Methods"])
def test_it_handles_missing_api_key(self):
def test_it_handles_missing_api_key(self) -> None:
r = self.client.delete(self.url)
self.assertContains(r, "missing api key", status_code=401)
def test_v1_reports_status_started(self):
def test_v1_reports_status_started(self) -> None:
self.check.last_start = now()
self.check.save()
@ -44,7 +44,7 @@ class DeleteCheckTestCase(BaseTestCase):
self.assertEqual(doc["status"], "started")
self.assertTrue(doc["started"])
def test_v2_reports_started_separately(self):
def test_v2_reports_started_separately(self) -> None:
self.check.last_start = now()
self.check.save()

View file

@ -9,7 +9,7 @@ from hc.test import BaseTestCase
class FlipModelTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project)
self.channel = Channel.objects.create(project=self.project, kind="email")
@ -20,30 +20,30 @@ class FlipModelTestCase(BaseTestCase):
self.flip.old_status = "up"
self.flip.new_status = "down"
def test_select_channels_works(self):
def test_select_channels_works(self) -> None:
channels = self.flip.select_channels()
self.assertEqual(channels, [self.channel])
def test_select_channels_handles_noop(self):
def test_select_channels_handles_noop(self) -> None:
self.channel.value = json.dumps({"down": False})
self.channel.save()
channels = self.flip.select_channels()
self.assertEqual(channels, [])
def test_select_channels_validates_new_status(self):
def test_select_channels_validates_new_status(self) -> None:
self.flip.new_status = "paused"
with self.assertRaises(NotImplementedError):
self.flip.select_channels()
def test_send_alerts_handles_new_up_transition(self):
def test_send_alerts_handles_new_up_transition(self) -> None:
self.flip.old_status = "new"
self.flip.new_status = "up"
channels = self.flip.select_channels()
self.assertEqual(channels, [])
def test_it_skips_disabled_channels(self):
def test_it_skips_disabled_channels(self) -> None:
self.channel.disabled = True
self.channel.save()

View file

@ -7,7 +7,7 @@ from hc.test import BaseTestCase
class GetBadgesTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.a1 = Check(project=self.project, name="Alice 1")
@ -20,11 +20,10 @@ class GetBadgesTestCase(BaseTestCase):
self.url = "/api/v1/badges/"
def get(self, api_key="X" * 32, qs=""):
def get(self, api_key: str = "X" * 32, qs: str = ""):
return self.client.get(self.url + qs, HTTP_X_API_KEY=api_key)
def test_it_works(self):
def test_it_works(self) -> None:
r = self.get()
self.assertEqual(r.status_code, 200)
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
@ -33,17 +32,17 @@ class GetBadgesTestCase(BaseTestCase):
self.assertTrue("foo" in doc["badges"])
self.assertTrue("svg" in doc["badges"]["foo"])
def test_readonly_key_is_allowed(self):
def test_readonly_key_is_allowed(self) -> None:
self.project.api_key_readonly = "R" * 32
self.project.save()
r = self.get(api_key=self.project.api_key_readonly)
self.assertEqual(r.status_code, 200)
def test_it_rejects_post(self):
def test_it_rejects_post(self) -> None:
r = self.csrf_client.post(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 405)
def test_it_handles_missing_api_key(self):
def test_it_handles_missing_api_key(self) -> None:
r = self.client.get(self.url)
self.assertContains(r, "missing api key", status_code=401)

View file

@ -1,6 +1,7 @@
from __future__ import annotations
from datetime import timedelta as td
from uuid import UUID
from django.utils.timezone import now
@ -9,7 +10,7 @@ from hc.test import BaseTestCase
class GetCheckTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.now = now().replace(microsecond=0)
@ -32,11 +33,11 @@ class GetCheckTestCase(BaseTestCase):
self.c1 = Channel.objects.create(project=self.project)
self.a1.channel_set.add(self.c1)
def get(self, code, api_key="X" * 32, v=1):
def get(self, code: UUID | str, api_key: str = "X" * 32, v: int = 1):
url = f"/api/v{v}/checks/{code}"
return self.client.get(url, HTTP_X_API_KEY=api_key)
def test_it_works(self):
def test_it_works(self) -> None:
r = self.get(self.a1.code)
self.assertEqual(r.status_code, 200)
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
@ -64,16 +65,16 @@ class GetCheckTestCase(BaseTestCase):
self.assertTrue(doc["filter_subject"])
self.assertFalse(doc["filter_body"])
def test_it_handles_invalid_uuid(self):
def test_it_handles_invalid_uuid(self) -> None:
r = self.get("not-an-uuid")
self.assertEqual(r.status_code, 404)
def test_it_handles_missing_check(self):
def test_it_handles_missing_check(self) -> None:
made_up_code = "07c2f548-9850-4b27-af5d-6c9dc157ec02"
r = self.get(made_up_code)
self.assertEqual(r.status_code, 404)
def test_it_handles_unique_key(self):
def test_it_handles_unique_key(self) -> None:
r = self.get(self.a1.unique_key)
self.assertEqual(r.status_code, 200)
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
@ -90,11 +91,11 @@ class GetCheckTestCase(BaseTestCase):
self.assertEqual(doc["channels"], str(self.c1.code))
self.assertEqual(doc["desc"], "This is description")
def test_it_rejects_post_unique_key(self):
def test_it_rejects_post_unique_key(self) -> None:
r = self.csrf_client.post(f"/api/v1/checks/{self.a1.unique_key}")
self.assertEqual(r.status_code, 405)
def test_readonly_key_works(self):
def test_readonly_key_works(self) -> None:
self.project.api_key_readonly = "R" * 32
self.project.save()
@ -105,7 +106,7 @@ class GetCheckTestCase(BaseTestCase):
for key in ("ping_url", "update_url", "pause_url", "resume_url"):
self.assertNotContains(r, key)
def test_v1_reports_status_started(self):
def test_v1_reports_status_started(self) -> None:
self.a1.last_start = now()
self.a1.save()
@ -116,7 +117,7 @@ class GetCheckTestCase(BaseTestCase):
self.assertEqual(doc["status"], "started")
self.assertTrue(doc["started"])
def test_v2_reports_started_separately(self):
def test_v2_reports_started_separately(self) -> None:
self.a1.last_start = now()
self.a1.save()
@ -127,7 +128,7 @@ class GetCheckTestCase(BaseTestCase):
self.assertEqual(doc["status"], "new")
self.assertTrue(doc["started"])
def test_v1_by_unique_key_reports_status_started(self):
def test_v1_by_unique_key_reports_status_started(self) -> None:
self.a1.last_start = now()
self.a1.save()
@ -136,7 +137,7 @@ class GetCheckTestCase(BaseTestCase):
self.assertEqual(doc["status"], "started")
self.assertTrue(doc["started"])
def test_v2_by_unique_key_reports_started_separately(self):
def test_v2_by_unique_key_reports_started_separately(self) -> None:
self.a1.last_start = now()
self.a1.save()

View file

@ -9,7 +9,7 @@ from hc.test import BaseTestCase
class GetFlipsTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.a1 = Check(project=self.project, name="Alice 1")
@ -30,11 +30,10 @@ class GetFlipsTestCase(BaseTestCase):
self.url = f"/api/v1/checks/{self.a1.code}/flips/"
def get(self, api_key="X" * 32, qs=""):
def get(self, api_key: str = "X" * 32, qs: str = ""):
return self.client.get(self.url + qs, HTTP_X_API_KEY=api_key)
def test_it_works(self):
def test_it_works(self) -> None:
r = self.get()
self.assertEqual(r.status_code, 200)
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
@ -47,7 +46,7 @@ class GetFlipsTestCase(BaseTestCase):
self.assertEqual(flip["timestamp"], "2020-06-01T12:24:32+00:00")
self.assertEqual(flip["up"], 1)
def test_it_works_with_unique_key(self):
def test_it_works_with_unique_key(self) -> None:
url = f"/api/v1/checks/{self.a1.unique_key}/flips/"
r = self.client.get(url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 200)
@ -55,47 +54,47 @@ class GetFlipsTestCase(BaseTestCase):
doc = r.json()
self.assertEqual(len(doc["flips"]), 1)
def test_readonly_key_is_allowed(self):
def test_readonly_key_is_allowed(self) -> None:
self.project.api_key_readonly = "R" * 32
self.project.save()
r = self.get(api_key=self.project.api_key_readonly)
self.assertEqual(r.status_code, 200)
def test_it_rejects_post(self):
def test_it_rejects_post(self) -> None:
r = self.csrf_client.post(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 405)
def test_it_rejects_non_integer_start(self):
def test_it_rejects_non_integer_start(self) -> None:
r = self.get(qs="?start=abc")
self.assertEqual(r.status_code, 400)
def test_it_rejects_negative_start(self):
def test_it_rejects_negative_start(self) -> None:
r = self.get(qs="?start=-123")
self.assertEqual(r.status_code, 400)
def test_it_filters_by_start(self):
def test_it_filters_by_start(self) -> None:
r = self.get(qs="?start=1591014300") # 2020-06-01 12:25:00
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json(), {"flips": []})
def test_it_filters_by_end(self):
def test_it_filters_by_end(self) -> None:
r = self.get(qs="?end=1591014180") # 2020-06-01 12:23:00
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json(), {"flips": []})
def test_it_rejects_huge_start(self):
def test_it_rejects_huge_start(self) -> None:
r = self.get(qs="?start=12345678901234567890")
self.assertEqual(r.status_code, 400)
def test_it_rejects_negative_seconds(self):
def test_it_rejects_negative_seconds(self) -> None:
r = self.get(qs="?seconds=-123")
self.assertEqual(r.status_code, 400)
def test_it_rejects_huge_seconds(self):
def test_it_rejects_huge_seconds(self) -> None:
r = self.get(qs="?seconds=12345678901234567890")
self.assertEqual(r.status_code, 400)
def test_it_handles_missing_api_key(self):
def test_it_handles_missing_api_key(self) -> None:
r = self.client.get(self.url)
self.assertContains(r, "missing api key", status_code=401)

View file

@ -5,7 +5,7 @@ from hc.test import BaseTestCase
class GetPingBodyTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project, name="Alice 1")
@ -20,35 +20,35 @@ class GetPingBodyTestCase(BaseTestCase):
self.url = f"/api/v1/checks/{self.check.code}/pings/1/body"
def test_it_works(self):
def test_it_works(self) -> None:
r = self.client.get(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.headers["Content-Type"], "text/plain")
self.assertEqual(r.content, b"Foo\nBar\nBaz")
def test_it_handles_missing_api_key(self):
def test_it_handles_missing_api_key(self) -> None:
r = self.client.get(self.url)
self.assertContains(r, "missing api key", status_code=401)
def test_readonly_key_is_not_allowed(self):
def test_readonly_key_is_not_allowed(self) -> None:
self.project.api_key_readonly = "R" * 32
self.project.save()
r = self.client.get(self.url, HTTP_X_API_KEY="R" * 32)
self.assertEqual(r.status_code, 401)
def test_it_rejects_post(self):
def test_it_rejects_post(self) -> None:
r = self.client.post(self.url, HTTP_X_API_KEY=self.project.api_key)
self.assertEqual(r.status_code, 405)
def test_it_rejects_charlies_key(self):
def test_it_rejects_charlies_key(self) -> None:
self.charlies_project.api_key = "C" * 32
self.charlies_project.save()
r = self.client.get(self.url, HTTP_X_API_KEY="C" * 32)
self.assertEqual(r.status_code, 403)
def test_it_checks_n_threshold(self):
def test_it_checks_n_threshold(self) -> None:
self.check.n_pings = 101
self.check.save()
@ -56,14 +56,14 @@ class GetPingBodyTestCase(BaseTestCase):
r = self.client.get(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 404)
def test_it_handles_no_body(self):
def test_it_handles_no_body(self) -> None:
self.ping.body_raw = None
self.ping.save()
r = self.client.get(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 404)
def test_it_returns_original_bytes(self):
def test_it_returns_original_bytes(self) -> None:
self.ping.body_raw = b"Hello\x01\x99World"
self.ping.save()

View file

@ -12,7 +12,7 @@ EPOCH = datetime(2020, 1, 1, tzinfo=timezone.utc)
class GetPingsTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.a1 = Check(project=self.project, name="Alice 1")
@ -34,10 +34,10 @@ class GetPingsTestCase(BaseTestCase):
self.url = "/api/v1/checks/%s/pings/" % self.a1.code
def get(self, api_key="X" * 32):
def get(self, api_key: str = "X" * 32):
return self.csrf_client.get(self.url, HTTP_X_API_KEY=api_key)
def test_it_works(self):
def test_it_works(self) -> None:
r = self.get()
self.assertEqual(r.status_code, 200)
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
@ -52,22 +52,22 @@ class GetPingsTestCase(BaseTestCase):
self.assertEqual(ping["method"], "get")
self.assertEqual(ping["ua"], "foo-agent")
def test_readonly_key_is_not_allowed(self):
def test_readonly_key_is_not_allowed(self) -> None:
self.project.api_key_readonly = "R" * 32
self.project.save()
r = self.get(api_key=self.project.api_key_readonly)
self.assertEqual(r.status_code, 401)
def test_it_rejects_post(self):
def test_it_rejects_post(self) -> None:
r = self.csrf_client.post(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 405)
def test_it_handles_missing_api_key(self):
def test_it_handles_missing_api_key(self) -> None:
r = self.client.get(self.url)
self.assertContains(r, "missing api key", status_code=401)
def test_it_calculates_overlapping_durations(self):
def test_it_calculates_overlapping_durations(self) -> None:
self.ping.delete()
m = td(minutes=1)
@ -82,7 +82,7 @@ class GetPingsTestCase(BaseTestCase):
self.assertEqual(doc["pings"][0]["duration"], 300.0)
self.assertEqual(doc["pings"][1]["duration"], 120.0)
def test_it_disables_duration_calculation(self):
def test_it_disables_duration_calculation(self) -> None:
self.ping.delete()
# Set up a worst case scenario where each success ping has an unique rid,
# and there are no "start" pings:
@ -95,7 +95,7 @@ class GetPingsTestCase(BaseTestCase):
for d in doc["pings"]:
self.assertNotIn("duration", d)
def test_it_handles_rid(self):
def test_it_handles_rid(self) -> None:
self.ping.rid = uuid4()
self.ping.save()

View file

@ -5,7 +5,7 @@ from hc.test import BaseTestCase
class ListChannelsTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.c1 = Channel(project=self.project)
@ -18,7 +18,7 @@ class ListChannelsTestCase(BaseTestCase):
def get(self):
return self.client.get(self.url, HTTP_X_API_KEY="X" * 32)
def test_it_works(self):
def test_it_works(self) -> None:
r = self.get()
self.assertEqual(r.status_code, 200)
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
@ -31,12 +31,12 @@ class ListChannelsTestCase(BaseTestCase):
self.assertEqual(c["kind"], "email")
self.assertEqual(c["name"], "Email to Alice")
def test_it_handles_options(self):
def test_it_handles_options(self) -> None:
r = self.client.options(self.url)
self.assertEqual(r.status_code, 204)
self.assertIn("GET", r["Access-Control-Allow-Methods"])
def test_it_shows_only_users_channels(self):
def test_it_shows_only_users_channels(self) -> None:
Channel.objects.create(project=self.bobs_project, kind="email", name="Bob")
r = self.get()
@ -45,10 +45,10 @@ class ListChannelsTestCase(BaseTestCase):
for c in data["channels"]:
self.assertNotEqual(c["name"], "Bob")
def test_it_handles_missing_api_key(self):
def test_it_handles_missing_api_key(self) -> None:
r = self.client.get(self.url)
self.assertContains(r, "missing api key", status_code=401)
def test_it_rejects_post(self):
def test_it_rejects_post(self) -> None:
r = self.csrf_client.post(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 405)

View file

@ -10,7 +10,7 @@ from hc.test import BaseTestCase
class ListChecksTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.now = now().replace(microsecond=0)
@ -36,10 +36,10 @@ class ListChecksTestCase(BaseTestCase):
self.c1 = Channel.objects.create(project=self.project)
self.a1.channel_set.add(self.c1)
def get(self, v=1):
def get(self, v: int = 1):
return self.client.get(f"/api/v{v}/checks/", HTTP_X_API_KEY="X" * 32)
def test_it_works(self):
def test_it_works(self) -> None:
# Expect 3 queries:
# * check API key
# * retrieve checks
@ -84,12 +84,12 @@ class ListChecksTestCase(BaseTestCase):
self.assertEqual(a2["last_ping"], self.now.isoformat())
self.assertEqual(a2["next_ping"], next_ping.isoformat())
def test_it_handles_options(self):
def test_it_handles_options(self) -> None:
r = self.client.options("/api/v1/checks/")
self.assertEqual(r.status_code, 204)
self.assertIn("GET", r["Access-Control-Allow-Methods"])
def test_it_shows_only_users_checks(self):
def test_it_shows_only_users_checks(self) -> None:
Check.objects.create(project=self.bobs_project, name="Bob 1")
r = self.get()
@ -98,7 +98,7 @@ class ListChecksTestCase(BaseTestCase):
for check in data["checks"]:
self.assertNotEqual(check["name"], "Bob 1")
def test_it_works_with_tags_param(self):
def test_it_works_with_tags_param(self) -> None:
r = self.client.get("/api/v1/checks/?tag=a2-tag", HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 200)
@ -111,7 +111,7 @@ class ListChecksTestCase(BaseTestCase):
self.assertEqual(check["name"], "Alice 2")
self.assertEqual(check["tags"], "a2-tag")
def test_it_filters_with_multiple_tags_param(self):
def test_it_filters_with_multiple_tags_param(self) -> None:
r = self.client.get(
"/api/v1/checks/?tag=a1-tag&tag=a1-additional-tag", HTTP_X_API_KEY="X" * 32
)
@ -126,7 +126,7 @@ class ListChecksTestCase(BaseTestCase):
self.assertEqual(check["name"], "Alice 1")
self.assertEqual(check["tags"], "a1-tag a1-additional-tag")
def test_it_does_not_match_tag_partially(self):
def test_it_does_not_match_tag_partially(self) -> None:
r = self.client.get("/api/v1/checks/?tag=tag", HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 200)
@ -134,7 +134,7 @@ class ListChecksTestCase(BaseTestCase):
self.assertTrue("checks" in doc)
self.assertEqual(len(doc["checks"]), 0)
def test_non_existing_tags_filter_returns_empty_result(self):
def test_non_existing_tags_filter_returns_empty_result(self) -> None:
r = self.client.get(
"/api/v1/checks/?tag=non_existing_tag_with_no_checks",
HTTP_X_API_KEY="X" * 32,
@ -145,7 +145,7 @@ class ListChecksTestCase(BaseTestCase):
self.assertTrue("checks" in doc)
self.assertEqual(len(doc["checks"]), 0)
def test_readonly_key_works(self):
def test_readonly_key_works(self) -> None:
self.project.api_key_readonly = "R" * 32
self.project.save()
@ -158,7 +158,7 @@ class ListChecksTestCase(BaseTestCase):
# When using readonly keys, the ping URLs should not be exposed:
self.assertNotContains(r, self.a1.url())
def test_v1_reports_status_started(self):
def test_v1_reports_status_started(self) -> None:
self.a1.last_start = now()
self.a1.save()
self.a2.delete()
@ -170,7 +170,7 @@ class ListChecksTestCase(BaseTestCase):
self.assertEqual(a1["status"], "started")
self.assertTrue(a1["started"])
def test_v2_reports_started_separately(self):
def test_v2_reports_started_separately(self) -> None:
self.a1.last_start = now()
self.a1.save()
self.a2.delete()
@ -181,7 +181,7 @@ class ListChecksTestCase(BaseTestCase):
self.assertEqual(a1["status"], "new")
self.assertTrue(a1["started"])
def test_it_works_with_slug_param(self):
def test_it_works_with_slug_param(self) -> None:
r = self.client.get("/api/v1/checks/?slug=alice-1", HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 200)

View file

@ -11,7 +11,7 @@ from hc.test import BaseTestCase
class MetricsTestCase(BaseTestCase):
url = "/api/v1/metrics/"
def test_it_returns_num_unprocessed_flips(self):
def test_it_returns_num_unprocessed_flips(self) -> None:
check = Check.objects.create(project=self.project, status="down")
flip = Flip(owner=check)
flip.created = now()
@ -25,34 +25,34 @@ class MetricsTestCase(BaseTestCase):
doc = r.json()
self.assertEqual(doc["num_unprocessed_flips"], 1)
def test_it_returns_max_ping_id(self):
def test_it_returns_max_ping_id(self) -> None:
check = Check.objects.create(project=self.project, status="down")
Ping.objects.create(owner=check, n=1)
last_ping = Ping.objects.last()
ping = Ping.objects.create(owner=check, n=1)
r = self.client.get(self.url, HTTP_X_METRICS_KEY="foo")
self.assertEqual(r.status_code, 200)
doc = r.json()
self.assertEqual(doc["max_ping_id"], last_ping.id)
self.assertEqual(doc["max_ping_id"], ping.id)
def test_it_returns_max_notification_id(self):
def test_it_returns_max_notification_id(self) -> None:
check = Check.objects.create(project=self.project, status="down")
channel = Channel.objects.create(project=self.project, kind="email")
Notification.objects.create(owner=check, channel=channel, check_status="down")
last_notification = Notification.objects.last()
n = Notification.objects.create(
owner=check, channel=channel, check_status="down"
)
r = self.client.get(self.url, HTTP_X_METRICS_KEY="foo")
self.assertEqual(r.status_code, 200)
doc = r.json()
self.assertEqual(doc["max_notification_id"], last_notification.id)
self.assertEqual(doc["max_notification_id"], n.id)
@override_settings(METRICS_KEY=None)
def test_it_handles_unset_metrics_key(self):
def test_it_handles_unset_metrics_key(self) -> None:
r = self.client.get(self.url, HTTP_X_METRICS_KEY="foo")
self.assertEqual(r.status_code, 403)
def test_it_handles_incorrect_metrics_key(self):
def test_it_handles_incorrect_metrics_key(self) -> None:
r = self.client.get(self.url, HTTP_X_METRICS_KEY="bar")
self.assertEqual(r.status_code, 403)

View file

@ -7,7 +7,7 @@ from hc.test import BaseTestCase
class NotificationStatusTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project, status="up")
@ -23,7 +23,7 @@ class NotificationStatusTestCase(BaseTestCase):
self.url = "/api/v1/notifications/%s/status" % self.n.code
def test_it_handles_twilio_failed_status(self):
def test_it_handles_twilio_failed_status(self) -> None:
r = self.csrf_client.post(self.url, {"MessageStatus": "failed"})
self.assertEqual(r.status_code, 200)
@ -34,7 +34,7 @@ class NotificationStatusTestCase(BaseTestCase):
self.assertEqual(self.channel.last_error, "Delivery failed (status=failed).")
self.assertTrue(self.channel.email_verified)
def test_it_handles_twilio_undelivered_status(self):
def test_it_handles_twilio_undelivered_status(self) -> None:
r = self.csrf_client.post(self.url, {"MessageStatus": "undelivered"})
self.assertEqual(r.status_code, 200)
@ -44,7 +44,7 @@ class NotificationStatusTestCase(BaseTestCase):
self.channel.refresh_from_db()
self.assertIn("status=undelivered", self.channel.last_error)
def test_it_handles_twilio_delivered_status(self):
def test_it_handles_twilio_delivered_status(self) -> None:
r = self.csrf_client.post(self.url, {"MessageStatus": "delivered"})
self.assertEqual(r.status_code, 200)
@ -54,7 +54,7 @@ class NotificationStatusTestCase(BaseTestCase):
self.channel.refresh_from_db()
self.assertEqual(self.channel.last_error, "")
def test_it_checks_ttl(self):
def test_it_checks_ttl(self) -> None:
self.n.created = self.n.created - td(minutes=61)
self.n.save()
@ -65,17 +65,17 @@ class NotificationStatusTestCase(BaseTestCase):
self.n.refresh_from_db()
self.assertEqual(self.n.error, "")
def test_it_handles_missing_notification(self):
def test_it_handles_missing_notification(self) -> None:
fake_code = "07c2f548-9850-4b27-af5d-6c9dc157ec02"
url = f"/api/v1/notifications/{fake_code}/status"
r = self.csrf_client.post(url, {"MessageStatus": "failed"})
self.assertEqual(r.status_code, 200)
def test_it_requires_post(self):
def test_it_requires_post(self) -> None:
r = self.csrf_client.get(self.url)
self.assertEqual(r.status_code, 405)
def test_it_handles_error_key(self):
def test_it_handles_error_key(self) -> None:
r = self.csrf_client.post(self.url, {"error": "Something went wrong."})
self.assertEqual(r.status_code, 200)
@ -86,7 +86,7 @@ class NotificationStatusTestCase(BaseTestCase):
self.assertEqual(self.channel.last_error, "Something went wrong.")
self.assertTrue(self.channel.email_verified)
def test_it_handles_mark_disabled_key(self):
def test_it_handles_mark_disabled_key(self) -> None:
payload = {"error": "Received complaint.", "mark_disabled": "1"}
r = self.csrf_client.post(self.url, payload)
@ -97,7 +97,7 @@ class NotificationStatusTestCase(BaseTestCase):
self.assertTrue(self.channel.email_verified)
self.assertTrue(self.channel.disabled)
def test_it_handles_twilio_call_status_failed(self):
def test_it_handles_twilio_call_status_failed(self) -> None:
r = self.csrf_client.post(self.url, {"CallStatus": "failed"})
self.assertEqual(r.status_code, 200)

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.core import mail
from django.test.utils import override_settings
@ -15,7 +15,9 @@ from hc.test import BaseTestCase
class NotifyTestCase(BaseTestCase):
def _setup_data(self, kind, value, status="down", email_verified=True):
def _setup_data(
self, kind: str, value: str, status: str = "down", email_verified: bool = True
) -> None:
self.check = Check(project=self.project)
self.check.status = status
self.check.last_ping = now() - td(minutes=61)
@ -29,7 +31,7 @@ class NotifyTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_pagerteam(self, mock_post):
def test_pagerteam(self, mock_post: Mock) -> None:
self._setup_data("pagerteam", "123")
self.channel.notify(self.check)
@ -37,7 +39,7 @@ class NotifyTestCase(BaseTestCase):
self.assertEqual(Notification.objects.count(), 0)
@patch("hc.api.transports.curl.request")
def test_hipchat(self, mock_post):
def test_hipchat(self, mock_post: Mock) -> None:
self._setup_data("hipchat", "123")
self.channel.notify(self.check)
@ -45,7 +47,7 @@ class NotifyTestCase(BaseTestCase):
self.assertEqual(Notification.objects.count(), 0)
@patch("hc.api.transports.curl.request")
def test_call(self, mock_post):
def test_call(self, mock_post: Mock) -> None:
self.profile.call_limit = 1
self.profile.save()
@ -65,7 +67,7 @@ class NotifyTestCase(BaseTestCase):
self.assertTrue(payload["StatusCallback"].endswith(callback_path))
@patch("hc.api.transports.curl.request")
def test_call_limit(self, mock_post):
def test_call_limit(self, mock_post: Mock) -> None:
# At limit already:
self.profile.call_limit = 50
self.profile.last_call_date = now()
@ -89,7 +91,7 @@ class NotifyTestCase(BaseTestCase):
self.assertEqual(email.subject, "Monthly Phone Call Limit Reached")
@patch("hc.api.transports.curl.request")
def test_call_limit_reset(self, mock_post):
def test_call_limit_reset(self, mock_post: Mock) -> None:
# At limit, but also into a new month
self.profile.call_limit = 50
self.profile.calls_sent = 50
@ -102,7 +104,7 @@ class NotifyTestCase(BaseTestCase):
self.channel.notify(self.check)
mock_post.assert_called_once()
def test_not_implemented(self):
def test_not_implemented(self) -> None:
self._setup_data("webhook", "http://example")
self.channel.kind = "invalid"
@ -111,7 +113,7 @@ class NotifyTestCase(BaseTestCase):
@patch("hc.api.transports.os.system")
@override_settings(SHELL_ENABLED=True)
def test_shell(self, mock_system):
def test_shell(self, mock_system: Mock) -> None:
definition = {"cmd_down": "logger hello", "cmd_up": ""}
self._setup_data("shell", json.dumps(definition))
mock_system.return_value = 0
@ -121,7 +123,7 @@ class NotifyTestCase(BaseTestCase):
@patch("hc.api.transports.os.system")
@override_settings(SHELL_ENABLED=True)
def test_shell_handles_nonzero_exit_code(self, mock_system):
def test_shell_handles_nonzero_exit_code(self, mock_system: Mock) -> None:
definition = {"cmd_down": "logger hello", "cmd_up": ""}
self._setup_data("shell", json.dumps(definition))
mock_system.return_value = 123
@ -132,7 +134,7 @@ class NotifyTestCase(BaseTestCase):
@patch("hc.api.transports.os.system")
@override_settings(SHELL_ENABLED=True)
def test_shell_supports_variables(self, mock_system):
def test_shell_supports_variables(self, mock_system: Mock) -> None:
definition = {"cmd_down": "logger $NAME is $STATUS ($TAG1)", "cmd_up": ""}
self._setup_data("shell", json.dumps(definition))
mock_system.return_value = 0
@ -146,7 +148,7 @@ class NotifyTestCase(BaseTestCase):
@patch("hc.api.transports.os.system")
@override_settings(SHELL_ENABLED=False)
def test_shell_disabled(self, mock_system):
def test_shell_disabled(self, mock_system: Mock) -> None:
definition = {"cmd_down": "logger hello", "cmd_up": ""}
self._setup_data("shell", json.dumps(definition))

View file

@ -22,7 +22,7 @@ except ImportError:
@skipIf(not have_apprise, "apprise not installed")
class NotifyAppriseTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -38,7 +38,7 @@ class NotifyAppriseTestCase(BaseTestCase):
@patch("apprise.Apprise")
@override_settings(APPRISE_ENABLED=True)
def test_apprise_enabled(self, mock_apprise):
def test_apprise_enabled(self, mock_apprise: Mock) -> None:
mock_aobj = Mock()
mock_aobj.add.return_value = True
mock_aobj.notify.return_value = True
@ -51,7 +51,7 @@ class NotifyAppriseTestCase(BaseTestCase):
@patch("apprise.Apprise")
@override_settings(APPRISE_ENABLED=False)
def test_apprise_disabled(self, mock_apprise):
def test_apprise_disabled(self, mock_apprise: Mock) -> None:
self.channel.notify(self.check)
n = Notification.objects.get()

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.utils.timezone import now
@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class NotifyDiscordTestCase(BaseTestCase):
def _setup_data(self, value, status="down"):
def _setup_data(self, value: str, status: str = "down") -> None:
self.check = Check(project=self.project)
self.check.status = status
self.check.last_ping = now() - td(minutes=61)
@ -30,7 +30,7 @@ class NotifyDiscordTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
v = json.dumps({"webhook": {"url": "https://example.org"}})
self._setup_data(v)
mock_post.return_value.status_code = 200
@ -46,7 +46,7 @@ class NotifyDiscordTestCase(BaseTestCase):
self.assertEqual(fields["Last Ping"], "Success, an hour ago")
@patch("hc.api.transports.curl.request")
def test_it_rewrites_discordapp_com(self, mock_post):
def test_it_rewrites_discordapp_com(self, mock_post: Mock) -> None:
v = json.dumps({"webhook": {"url": "https://discordapp.com/foo"}})
self._setup_data(v)
mock_post.return_value.status_code = 200
@ -60,7 +60,7 @@ class NotifyDiscordTestCase(BaseTestCase):
self.assertEqual(url, "https://discord.com/foo/slack")
@patch("hc.api.transports.curl.request")
def test_it_handles_last_ping_failure(self, mock_post):
def test_it_handles_last_ping_failure(self, mock_post: Mock) -> None:
v = json.dumps({"webhook": {"url": "123"}})
self._setup_data(v)
mock_post.return_value.status_code = 200

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.conf import settings
from django.core import mail
@ -16,7 +16,7 @@ from hc.test import BaseTestCase
class NotifyEmailTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -42,7 +42,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@override_settings(DEFAULT_FROM_EMAIL="alerts@example.org")
def test_it_works(self):
def test_it_works(self) -> None:
self.channel.notify(self.check)
n = Notification.objects.get()
@ -99,7 +99,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertNotIn(str(self.check.code), email.body)
@override_settings(DEFAULT_FROM_EMAIL='"Alerts" <alerts@example.org>')
def test_it_message_id_generation_handles_angle_brackets(self):
def test_it_message_id_generation_handles_angle_brackets(self) -> None:
self.channel.notify(self.check)
email = mail.outbox[0]
@ -107,7 +107,7 @@ class NotifyEmailTestCase(BaseTestCase):
@override_settings(S3_BUCKET="test-bucket")
@patch("hc.api.models.get_object")
def test_it_loads_body_from_object_storage(self, get_object):
def test_it_loads_body_from_object_storage(self, get_object: Mock) -> None:
get_object.return_value = b"Body Line 1\nBody Line 2"
self.ping.object_size = 1000
@ -124,7 +124,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertEqual(code, self.check.code)
self.assertEqual(n, 1)
def test_it_shows_cron_schedule(self):
def test_it_shows_cron_schedule(self) -> None:
self.check.kind = "cron"
self.check.schedule = "0 18-23,0-8 * * *"
self.check.tz = "Europe/Riga"
@ -140,7 +140,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertIn("<code>0 18-23,0-8 * * *</code>", html)
self.assertIn("Europe/Riga", html)
def test_it_truncates_long_body(self):
def test_it_truncates_long_body(self) -> None:
self.ping.body = "X" * 10000 + ", and the rest gets cut off"
self.ping.save()
@ -153,7 +153,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertIn("[truncated]", html)
self.assertNotIn("the rest gets cut off", html)
def test_it_handles_missing_ping_object(self):
def test_it_handles_missing_ping_object(self) -> None:
self.ping.delete()
self.channel.notify(self.check)
@ -163,7 +163,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertIn("Daily Backup", html)
def test_it_handles_missing_profile(self):
def test_it_handles_missing_profile(self) -> None:
self.channel.value = "alice+notifications@example.org"
self.channel.save()
@ -178,7 +178,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertNotIn("Projects Overview", email.body)
self.assertNotIn("Projects Overview", html)
def test_email_transport_handles_json_value(self):
def test_email_transport_handles_json_value(self) -> None:
payload = {"value": "alice@example.org", "up": True, "down": True}
self.channel.value = json.dumps(payload)
self.channel.save()
@ -191,7 +191,7 @@ class NotifyEmailTestCase(BaseTestCase):
email = mail.outbox[0]
self.assertEqual(email.to[0], "alice@example.org")
def test_it_reports_unverified_email(self):
def test_it_reports_unverified_email(self) -> None:
self.channel.email_verified = False
self.channel.save()
@ -201,7 +201,7 @@ class NotifyEmailTestCase(BaseTestCase):
n = Notification.objects.get()
self.assertEqual(n.error, "Email not verified")
def test_email_checks_up_down_flags(self):
def test_email_checks_up_down_flags(self) -> None:
payload = {"value": "alice@example.org", "up": True, "down": False}
self.channel.value = json.dumps(payload)
self.channel.save()
@ -212,7 +212,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertEqual(Notification.objects.count(), 0)
self.assertEqual(len(mail.outbox), 0)
def test_email_handles_amperstand(self):
def test_email_handles_amperstand(self) -> None:
self.check.name = "Foo & Bar"
self.check.save()
@ -223,7 +223,7 @@ class NotifyEmailTestCase(BaseTestCase):
@override_settings(S3_BUCKET="test-bucket")
@patch("hc.api.models.get_object")
def test_it_handles_pending_body(self, get_object):
def test_it_handles_pending_body(self, get_object: Mock) -> None:
get_object.return_value = None
self.ping.object_size = 1000
@ -239,7 +239,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertIn("The request body data is being processed", email.body)
self.assertIn("The request body data is being processed", html)
def test_it_shows_ignored_nonzero_exitstatus(self):
def test_it_shows_ignored_nonzero_exitstatus(self) -> None:
self.ping.kind = "ign"
self.ping.exitstatus = 123
self.ping.save()
@ -251,7 +251,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertIn("Ignored", email.body)
self.assertIn("Ignored", html)
def test_it_handles_last_ping_log(self):
def test_it_handles_last_ping_log(self) -> None:
self.ping.kind = "log"
self.ping.save()
@ -263,7 +263,7 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertIn("Log", html)
@override_settings(EMAIL_MAIL_FROM_TMPL="%s@bounces.example.org")
def test_it_sets_custom_mail_from(self):
def test_it_sets_custom_mail_from(self) -> None:
self.channel.notify(self.check)
email = mail.outbox[0]

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.utils.timezone import now
@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class NotifyGotidyTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -29,7 +29,7 @@ class NotifyGotidyTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -40,7 +40,7 @@ class NotifyGotidyTestCase(BaseTestCase):
self.assertIn(self.check.cloaked_url(), payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_shows_all_other_checks_up_note(self, mock_post):
def test_it_shows_all_other_checks_up_note(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
other = Check(project=self.project)
@ -55,7 +55,7 @@ class NotifyGotidyTestCase(BaseTestCase):
self.assertIn("All the other checks are up.", payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_lists_other_down_checks(self, mock_post):
def test_it_lists_other_down_checks(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
other = Check(project=self.project)
@ -72,7 +72,7 @@ class NotifyGotidyTestCase(BaseTestCase):
self.assertIn(other.cloaked_url(), payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_does_not_show_more_than_10_other_checks(self, mock_post):
def test_it_does_not_show_more_than_10_other_checks(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
for i in range(0, 11):

View file

@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.utils.timezone import now
@ -12,7 +12,7 @@ from hc.test import BaseTestCase
class NotifyLineTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -28,7 +28,7 @@ class NotifyLineTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -40,7 +40,7 @@ class NotifyLineTestCase(BaseTestCase):
self.assertIn("""The check "Foo" is DOWN""", params["message"])
@patch("hc.api.transports.curl.request")
def test_it_does_not_escape_message(self, mock_post):
def test_it_does_not_escape_message(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.name = "Foo & Bar"
self.check.status = "up"

View file

@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class NotifyMattermostTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -32,7 +32,7 @@ class NotifyMattermostTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -46,14 +46,14 @@ class NotifyMattermostTestCase(BaseTestCase):
self.assertEqual(fields["Last Ping"], "Success, an hour ago")
@override_settings(MATTERMOST_ENABLED=False)
def test_it_requires_mattermost_enabled(self):
def test_it_requires_mattermost_enabled(self) -> None:
self.channel.notify(self.check)
n = Notification.objects.get()
self.assertEqual(n.error, "Mattermost notifications are not enabled.")
@patch("hc.api.transports.curl.request")
def test_it_does_not_disable_channel_on_404(self, mock_post):
def test_it_does_not_disable_channel_on_404(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 404
self.channel.notify(self.check)
@ -62,7 +62,7 @@ class NotifyMattermostTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver")
@patch("hc.api.transports.curl.request")
def test_it_shows_last_ping_body(self, mock_post):
def test_it_shows_last_ping_body(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.body_raw = b"Hello World"
@ -77,7 +77,7 @@ class NotifyMattermostTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver")
@patch("hc.api.transports.curl.request")
def test_it_shows_truncated_last_ping_body(self, mock_post):
def test_it_shows_truncated_last_ping_body(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.body_raw = b"Hello World" * 1000
@ -92,7 +92,7 @@ class NotifyMattermostTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver")
@patch("hc.api.transports.curl.request")
def test_it_skips_last_ping_body_containing_backticks(self, mock_post):
def test_it_skips_last_ping_body_with_backticks(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.body_raw = b"Hello ``` World"

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -14,7 +14,7 @@ from hc.test import BaseTestCase
class NotifyMsTeamsTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -33,7 +33,7 @@ class NotifyMsTeamsTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.name = "_underscores_ & more"
@ -57,7 +57,7 @@ class NotifyMsTeamsTestCase(BaseTestCase):
self.assertNotIn(str(self.check.code), serialized)
@patch("hc.api.transports.curl.request")
def test_it_shows_schedule_and_tz(self, mock_post):
def test_it_shows_schedule_and_tz(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.kind = "cron"
self.check.tz = "Europe/Riga"
@ -70,7 +70,7 @@ class NotifyMsTeamsTestCase(BaseTestCase):
self.assertEqual(facts["Time Zone:"], "Europe/Riga")
@patch("hc.api.transports.curl.request")
def test_it_escapes_stars_in_schedule(self, mock_post):
def test_it_escapes_stars_in_schedule(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.kind = "cron"
@ -83,14 +83,14 @@ class NotifyMsTeamsTestCase(BaseTestCase):
self.assertEqual(facts["Schedule:"], "\u034f* \u034f* \u034f* \u034f* \u034f*")
@override_settings(MSTEAMS_ENABLED=False)
def test_it_requires_msteams_enabled(self):
def test_it_requires_msteams_enabled(self) -> None:
self.channel.notify(self.check)
n = Notification.objects.get()
self.assertEqual(n.error, "MS Teams notifications are not enabled.")
@patch("hc.api.transports.curl.request")
def test_it_handles_last_ping_fail(self, mock_post):
def test_it_handles_last_ping_fail(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.kind = "fail"
@ -104,7 +104,7 @@ class NotifyMsTeamsTestCase(BaseTestCase):
self.assertEqual(facts["Last Ping:"], "Failure, an hour ago")
@patch("hc.api.transports.curl.request")
def test_it_handles_last_ping_log(self, mock_post):
def test_it_handles_last_ping_log(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.kind = "log"
@ -117,7 +117,7 @@ class NotifyMsTeamsTestCase(BaseTestCase):
self.assertEqual(facts["Last Ping:"], "Log, an hour ago")
@patch("hc.api.transports.curl.request")
def test_it_shows_ignored_nonzero_exitstatus(self, mock_post):
def test_it_shows_ignored_nonzero_exitstatus(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.kind = "ign"
@ -132,7 +132,7 @@ class NotifyMsTeamsTestCase(BaseTestCase):
self.assertEqual(facts["Last Ping:"], "Ignored, an hour ago")
@patch("hc.api.transports.curl.request")
def test_it_shows_last_ping_body(self, mock_post):
def test_it_shows_last_ping_body(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.body_raw = b"Hello World"
@ -146,7 +146,7 @@ class NotifyMsTeamsTestCase(BaseTestCase):
self.assertIn("```\nHello World\n```", section["text"])
@patch("hc.api.transports.curl.request")
def test_it_shows_truncated_last_ping_body(self, mock_post):
def test_it_shows_truncated_last_ping_body(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.body_raw = b"Hello World" * 1000
@ -160,7 +160,7 @@ class NotifyMsTeamsTestCase(BaseTestCase):
self.assertIn("[truncated]", section["text"])
@patch("hc.api.transports.curl.request")
def test_it_skips_last_ping_body_containing_backticks(self, mock_post):
def test_it_skips_last_ping_body_with_backticks(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.body_raw = b"Hello ``` World"

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.utils.timezone import now
@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class NotifyNtfyTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -43,7 +43,7 @@ class NotifyNtfyTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -61,7 +61,7 @@ class NotifyNtfyTestCase(BaseTestCase):
self.assertNotIn("All the other checks are up.", payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_shows_schedule_and_tz(self, mock_post):
def test_it_shows_schedule_and_tz(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.kind = "cron"
@ -74,7 +74,7 @@ class NotifyNtfyTestCase(BaseTestCase):
self.assertIn("Time Zone: Europe/Riga", payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_shows_all_other_checks_up_note(self, mock_post):
def test_it_shows_all_other_checks_up_note(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
other = Check(project=self.project)
@ -89,7 +89,7 @@ class NotifyNtfyTestCase(BaseTestCase):
self.assertIn("All the other checks are up.", payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_lists_other_down_checks(self, mock_post):
def test_it_lists_other_down_checks(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
other = Check(project=self.project)
@ -105,7 +105,7 @@ class NotifyNtfyTestCase(BaseTestCase):
self.assertIn("Foobar", payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_does_not_show_more_than_10_other_checks(self, mock_post):
def test_it_does_not_show_more_than_10_other_checks(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
for i in range(0, 11):
@ -122,7 +122,7 @@ class NotifyNtfyTestCase(BaseTestCase):
self.assertIn("11 other checks are also down.", payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_uses_access_token(self, mock_post):
def test_it_uses_access_token(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.value = json.dumps(

View file

@ -14,7 +14,9 @@ from hc.test import BaseTestCase
class NotifyOpsGenieTestCase(BaseTestCase):
def _setup_data(self, value, status="down", email_verified=True):
def _setup_data(
self, value: str, status: str = "down", email_verified: bool = True
) -> None:
self.check = Check(project=self.project)
self.check.status = status
self.check.last_ping = now() - td(minutes=61)
@ -28,7 +30,7 @@ class NotifyOpsGenieTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_opsgenie_with_legacy_value(self, mock_post):
def test_opsgenie_with_legacy_value(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 202
@ -43,7 +45,7 @@ class NotifyOpsGenieTestCase(BaseTestCase):
self.assertIn("DOWN", payload["message"])
@patch("hc.api.transports.curl.request")
def test_opsgenie_up(self, mock_post):
def test_opsgenie_up(self, mock_post: Mock) -> None:
self._setup_data("123", status="up")
mock_post.return_value.status_code = 202
@ -56,7 +58,7 @@ class NotifyOpsGenieTestCase(BaseTestCase):
self.assertTrue(str(self.check.code) in url)
@patch("hc.api.transports.curl.request")
def test_opsgenie_with_json_value(self, mock_post):
def test_opsgenie_with_json_value(self, mock_post: Mock) -> None:
self._setup_data(json.dumps({"key": "456", "region": "eu"}))
mock_post.return_value.status_code = 202
@ -69,7 +71,7 @@ class NotifyOpsGenieTestCase(BaseTestCase):
self.assertIn("api.eu.opsgenie.com", url)
@patch("hc.api.transports.curl.request")
def test_opsgenie_returns_error(self, mock_post):
def test_opsgenie_returns_error(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 403
mock_post.return_value.json.return_value = {"message": "Nice try"}
@ -79,7 +81,7 @@ class NotifyOpsGenieTestCase(BaseTestCase):
self.assertEqual(n.error, 'Received status code 403 with a message: "Nice try"')
@patch("hc.api.transports.curl.request")
def test_it_handles_non_json_error_response(self, mock_post):
def test_it_handles_non_json_error_response(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 403
mock_post.return_value.json = Mock(side_effect=ValueError)
@ -89,7 +91,7 @@ class NotifyOpsGenieTestCase(BaseTestCase):
self.assertEqual(n.error, "Received status code 403")
@override_settings(OPSGENIE_ENABLED=False)
def test_it_requires_opsgenie_enabled(self):
def test_it_requires_opsgenie_enabled(self) -> None:
self._setup_data("123")
self.channel.notify(self.check)

View file

@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class NotifyPagertreeTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -28,7 +28,7 @@ class NotifyPagertreeTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -38,14 +38,14 @@ class NotifyPagertreeTestCase(BaseTestCase):
self.assertEqual(payload["event_type"], "trigger")
@override_settings(PAGERTREE_ENABLED=False)
def test_it_requires_pagertree_enabled(self):
def test_it_requires_pagertree_enabled(self) -> None:
self.channel.notify(self.check)
n = Notification.objects.get()
self.assertEqual(n.error, "PagerTree notifications are not enabled.")
@patch("hc.api.transports.curl.request")
def test_it_does_not_escape_title(self, mock_post):
def test_it_does_not_escape_title(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.name = "Foo & Bar"

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -14,7 +14,9 @@ from hc.test import BaseTestCase
class NotifyPdTestCase(BaseTestCase):
def _setup_data(self, value, status="down", email_verified=True):
def _setup_data(
self, value: str, status: str = "down", email_verified: bool = True
) -> None:
self.check = Check(project=self.project)
self.check.name = "Foo"
self.check.desc = "Description goes here"
@ -30,7 +32,7 @@ class NotifyPdTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 200
@ -44,7 +46,7 @@ class NotifyPdTestCase(BaseTestCase):
self.assertEqual(payload["service_key"], "123")
@patch("hc.api.transports.curl.request")
def test_it_shows_schedule_and_tz(self, mock_post):
def test_it_shows_schedule_and_tz(self, mock_post: Mock) -> None:
self._setup_data("123")
self.check.kind = "cron"
self.check.tz = "Europe/Riga"
@ -57,7 +59,7 @@ class NotifyPdTestCase(BaseTestCase):
self.assertEqual(payload["details"]["Time zone"], "Europe/Riga")
@patch("hc.api.transports.curl.request")
def test_pd_complex(self, mock_post):
def test_pd_complex(self, mock_post: Mock) -> None:
self._setup_data(json.dumps({"service_key": "456"}))
mock_post.return_value.status_code = 200
@ -69,7 +71,7 @@ class NotifyPdTestCase(BaseTestCase):
self.assertEqual(payload["service_key"], "456")
@override_settings(PD_ENABLED=False)
def test_it_requires_pd_enabled(self):
def test_it_requires_pd_enabled(self) -> None:
self._setup_data("123")
self.channel.notify(self.check)
@ -77,7 +79,7 @@ class NotifyPdTestCase(BaseTestCase):
self.assertEqual(n.error, "PagerDuty notifications are not enabled.")
@patch("hc.api.transports.curl.request")
def test_it_does_not_escape_description(self, mock_post):
def test_it_does_not_escape_description(self, mock_post: Mock) -> None:
self._setup_data("123")
self.check.name = "Foo & Bar"
self.check.save()

View file

@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.utils.timezone import now
@ -12,7 +12,7 @@ from hc.test import BaseTestCase
class NotifyPushbulletTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -28,7 +28,7 @@ class NotifyPushbulletTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -42,7 +42,7 @@ class NotifyPushbulletTestCase(BaseTestCase):
self.assertEqual(kwargs["headers"]["Access-Token"], "fake-token")
@patch("hc.api.transports.curl.request")
def test_it_escapes_body(self, mock_post):
def test_it_escapes_body(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.name = "Foo & Bar"
self.check.save()

View file

@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -15,7 +15,9 @@ API = "https://api.pushover.net/1"
class NotifyPushoverTestCase(BaseTestCase):
def _setup_data(self, value, status="down", email_verified=True):
def _setup_data(
self, value: str, status: str = "down", email_verified: bool = True
) -> None:
self.check = Check(project=self.project)
self.check.name = "Foo"
self.check.status = status
@ -30,7 +32,7 @@ class NotifyPushoverTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
self._setup_data("123|0")
mock_post.return_value.status_code = 200
@ -49,7 +51,7 @@ class NotifyPushoverTestCase(BaseTestCase):
self.assertEqual(payload["tags"], self.check.unique_key)
@patch("hc.api.transports.curl.request")
def test_it_supports_up_priority(self, mock_post):
def test_it_supports_up_priority(self, mock_post: Mock) -> None:
self._setup_data("123|0|2", status="up")
mock_post.return_value.status_code = 200
@ -64,7 +66,7 @@ class NotifyPushoverTestCase(BaseTestCase):
@override_settings(SECRET_KEY="test-secret")
@patch("hc.api.transports.curl.request")
def test_it_obeys_rate_limit(self, mock_post):
def test_it_obeys_rate_limit(self, mock_post: Mock) -> None:
self._setup_data("123|0")
# "c0ca..." is sha1("123test-secret")
@ -77,7 +79,7 @@ class NotifyPushoverTestCase(BaseTestCase):
self.assertEqual(n.error, "Rate limit exceeded")
@patch("hc.api.transports.curl.request")
def test_it_cancels_emergency_notification(self, mock_post):
def test_it_cancels_emergency_notification(self, mock_post: Mock) -> None:
self._setup_data("123|2|0", status="up")
mock_post.return_value.status_code = 200
@ -95,7 +97,7 @@ class NotifyPushoverTestCase(BaseTestCase):
self.assertIn("UP", payload["title"])
@patch("hc.api.transports.curl.request")
def test_it_shows_all_other_checks_up_note(self, mock_post):
def test_it_shows_all_other_checks_up_note(self, mock_post: Mock) -> None:
self._setup_data("123|0")
mock_post.return_value.status_code = 200
@ -111,7 +113,7 @@ class NotifyPushoverTestCase(BaseTestCase):
self.assertIn("All the other checks are up.", payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_lists_other_down_checks(self, mock_post):
def test_it_lists_other_down_checks(self, mock_post: Mock) -> None:
self._setup_data("123|0")
mock_post.return_value.status_code = 200
@ -129,7 +131,7 @@ class NotifyPushoverTestCase(BaseTestCase):
self.assertIn(other.cloaked_url(), payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_does_not_show_more_than_10_other_checks(self, mock_post):
def test_it_does_not_show_more_than_10_other_checks(self, mock_post: Mock) -> None:
self._setup_data("123|0")
mock_post.return_value.status_code = 200
@ -147,7 +149,7 @@ class NotifyPushoverTestCase(BaseTestCase):
self.assertIn("11 other checks are also down.", payload["message"])
@patch("hc.api.transports.curl.request")
def test_it_does_not_escape_title(self, mock_post):
def test_it_does_not_escape_title(self, mock_post: Mock) -> None:
self._setup_data("123|0")
self.check.name = "Foo & Bar"
self.check.save()
@ -159,7 +161,7 @@ class NotifyPushoverTestCase(BaseTestCase):
self.assertEqual(payload["title"], "Foo & Bar is DOWN")
@patch("hc.api.transports.curl.request")
def test_it_handles_disabled_priority(self, mock_post):
def test_it_handles_disabled_priority(self, mock_post: Mock) -> None:
self._setup_data("123|-3")
self.channel.notify(self.check)
@ -167,7 +169,7 @@ class NotifyPushoverTestCase(BaseTestCase):
mock_post.assert_not_called()
@patch("hc.api.transports.curl.request")
def test_it_handles_disabled_up_priority(self, mock_post):
def test_it_handles_disabled_up_priority(self, mock_post: Mock) -> None:
self._setup_data("123|0|-3", status="up")
self.channel.notify(self.check)

View file

@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class NotifyRocketChatTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -32,7 +32,7 @@ class NotifyRocketChatTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -46,14 +46,14 @@ class NotifyRocketChatTestCase(BaseTestCase):
self.assertEqual(fields["Last Ping"], "Success, an hour ago")
@override_settings(ROCKETCHAT_ENABLED=False)
def test_it_requires_rocketchat_enabled(self):
def test_it_requires_rocketchat_enabled(self) -> None:
self.channel.notify(self.check)
n = Notification.objects.get()
self.assertEqual(n.error, "Rocket.Chat notifications are not enabled.")
@patch("hc.api.transports.curl.request")
def test_it_does_not_disable_channel_on_404(self, mock_post):
def test_it_does_not_disable_channel_on_404(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 404
self.channel.notify(self.check)
@ -62,7 +62,7 @@ class NotifyRocketChatTestCase(BaseTestCase):
self.assertEqual(self.channel.last_error, "Received status code 404")
@patch("hc.api.transports.curl.request")
def test_it_shows_schedule_and_tz(self, mock_post):
def test_it_shows_schedule_and_tz(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.kind = "cron"
self.check.tz = "Europe/Riga"
@ -75,7 +75,7 @@ class NotifyRocketChatTestCase(BaseTestCase):
self.assertEqual(fields["Time Zone"], "Europe/Riga")
@patch("hc.api.transports.curl.request")
def test_it_shows_last_ping_body_size(self, mock_post):
def test_it_shows_last_ping_body_size(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.n = 123
@ -91,7 +91,7 @@ class NotifyRocketChatTestCase(BaseTestCase):
self.assertIn("#ping-123", fields["Last Ping Body"])
@patch("hc.api.transports.curl.request")
def test_it_shows_last_ping_body_one_byte(self, mock_post):
def test_it_shows_last_ping_body_one_byte(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.n = 123
@ -106,7 +106,7 @@ class NotifyRocketChatTestCase(BaseTestCase):
self.assertIn("1 byte,", fields["Last Ping Body"])
@patch("hc.api.transports.curl.request")
def test_it_shows_ping_kind_fail(self, mock_post):
def test_it_shows_ping_kind_fail(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.kind = "fail"
@ -120,7 +120,7 @@ class NotifyRocketChatTestCase(BaseTestCase):
self.assertEqual("Failure, an hour ago", fields["Last Ping"])
@patch("hc.api.transports.curl.request")
def test_it_shows_nonzero_exitstatus(self, mock_post):
def test_it_shows_nonzero_exitstatus(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.kind = "fail"
@ -135,7 +135,7 @@ class NotifyRocketChatTestCase(BaseTestCase):
self.assertEqual("Exit status 123, an hour ago", fields["Last Ping"])
@patch("hc.api.transports.curl.request")
def test_it_shows_ignored_nonzero_exitstatus(self, mock_post):
def test_it_shows_ignored_nonzero_exitstatus(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.kind = "ign"

View file

@ -5,7 +5,7 @@ from __future__ import annotations
import json
import logging
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.core import mail
from django.test.utils import override_settings
@ -23,7 +23,7 @@ class MockSocket(object):
self.req = None
self.outbox = b""
def settimeout(self, seconds):
def settimeout(self, seconds: int) -> None:
pass
def connect(self, address):
@ -47,7 +47,7 @@ class MockSocket(object):
return head
def setup_mock(socket, response_tmpl, side_effect=None):
def setup_mock(socket: Mock, response_tmpl, side_effect=None) -> MockSocket:
# A mock of socket.socket object
socketobj = MockSocket(response_tmpl, side_effect)
@ -60,7 +60,7 @@ def setup_mock(socket, response_tmpl, side_effect=None):
@override_settings(SIGNAL_CLI_SOCKET="/tmp/socket")
class NotifySignalTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -85,7 +85,7 @@ class NotifySignalTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.socket.socket")
def test_it_works(self, socket):
def test_it_works(self, socket) -> None:
socketobj = setup_mock(socket, {})
self.channel.notify(self.check)
@ -110,7 +110,7 @@ class NotifySignalTestCase(BaseTestCase):
self.assertNotIn("All the other checks are up.", params["message"])
@patch("hc.api.transports.socket.socket")
def test_it_shows_schedule_and_tz(self, socket):
def test_it_shows_schedule_and_tz(self, socket: Mock) -> None:
socketobj = setup_mock(socket, {})
self.check.kind = "cron"
@ -123,7 +123,7 @@ class NotifySignalTestCase(BaseTestCase):
self.assertIn("Time Zone: Europe/Riga", params["message"])
@patch("hc.api.transports.socket.socket")
def test_it_handles_special_characters(self, socket):
def test_it_handles_special_characters(self, socket: Mock) -> None:
socketobj = setup_mock(socket, {})
self.project.name = "Alice & Friends"
@ -146,7 +146,7 @@ class NotifySignalTestCase(BaseTestCase):
@override_settings(SIGNAL_CLI_SOCKET="example.org:1234")
@patch("hc.api.transports.socket.socket")
def test_it_handles_host_port(self, socket):
def test_it_handles_host_port(self, socket: Mock) -> None:
socketobj = setup_mock(socket, {})
self.channel.notify(self.check)
@ -156,7 +156,7 @@ class NotifySignalTestCase(BaseTestCase):
self.assertEqual(n.error, "")
@patch("hc.api.transports.socket.socket")
def test_it_obeys_down_flag(self, socket):
def test_it_obeys_down_flag(self, socket: Mock) -> None:
payload = {"value": "+123456789", "up": True, "down": False}
self.channel.value = json.dumps(payload)
self.channel.save()
@ -168,7 +168,7 @@ class NotifySignalTestCase(BaseTestCase):
socket.assert_not_called()
@patch("hc.api.transports.socket.socket")
def test_it_requires_signal_cli_socket(self, socket):
def test_it_requires_signal_cli_socket(self, socket: Mock) -> None:
with override_settings(SIGNAL_CLI_SOCKET=None):
self.channel.notify(self.check)
@ -177,7 +177,7 @@ class NotifySignalTestCase(BaseTestCase):
socket.assert_not_called()
@patch("hc.api.transports.socket.socket")
def test_it_does_not_escape_special_characters(self, socket):
def test_it_does_not_escape_special_characters(self, socket: Mock) -> None:
socketobj = setup_mock(socket, {})
self.check.name = "Foo & Bar"
@ -189,7 +189,7 @@ class NotifySignalTestCase(BaseTestCase):
@override_settings(SECRET_KEY="test-secret")
@patch("hc.api.transports.socket.socket")
def test_it_obeys_rate_limit(self, socket):
def test_it_obeys_rate_limit(self, socket: Mock) -> None:
# "2862..." is sha1("+123456789test-secret")
obj = TokenBucket(value="signal-2862991ccaa15c8856e7ee0abaf3448fb3c292e0")
obj.tokens = 0
@ -201,7 +201,7 @@ class NotifySignalTestCase(BaseTestCase):
socket.assert_not_called()
@patch("hc.api.transports.socket.socket")
def test_it_shows_all_other_checks_up_note(self, socket):
def test_it_shows_all_other_checks_up_note(self, socket: Mock) -> None:
socketobj = setup_mock(socket, {})
other = Check(project=self.project)
@ -216,7 +216,7 @@ class NotifySignalTestCase(BaseTestCase):
self.assertIn("All the other checks are up.", message)
@patch("hc.api.transports.socket.socket")
def test_it_lists_other_down_checks(self, socket):
def test_it_lists_other_down_checks(self, socket: Mock) -> None:
socketobj = setup_mock(socket, {})
other = Check(project=self.project)
@ -232,7 +232,7 @@ class NotifySignalTestCase(BaseTestCase):
self.assertIn("Foobar & Co", message)
@patch("hc.api.transports.socket.socket")
def test_it_does_not_show_more_than_10_other_checks(self, socket):
def test_it_does_not_show_more_than_10_other_checks(self, socket: Mock) -> None:
socketobj = setup_mock(socket, {})
for i in range(0, 11):
@ -249,7 +249,7 @@ class NotifySignalTestCase(BaseTestCase):
self.assertIn("11 other checks are also down.", message)
@patch("hc.api.transports.socket.socket")
def test_it_handles_unregistered_failure(self, socket):
def test_it_handles_unregistered_failure(self, socket: Mock) -> None:
msg = {
"error": {
"code": -1,
@ -274,7 +274,7 @@ class NotifySignalTestCase(BaseTestCase):
self.assertEqual(n.error, "Recipient not found")
@patch("hc.api.transports.socket.socket")
def test_it_handles_error_code(self, socket):
def test_it_handles_error_code(self, socket: Mock) -> None:
setup_mock(socket, {"error": {"code": 123}})
self.channel.notify(self.check)
@ -283,7 +283,7 @@ class NotifySignalTestCase(BaseTestCase):
self.assertEqual(n.error, "signal-cli call failed (123)")
@patch("hc.api.transports.socket.socket")
def test_it_handles_oserror(self, socket):
def test_it_handles_oserror(self, socket: Mock) -> None:
setup_mock(socket, {}, side_effect=OSError("oops"))
logging.disable(logging.CRITICAL)
@ -294,7 +294,7 @@ class NotifySignalTestCase(BaseTestCase):
self.assertEqual(n.error, "signal-cli call failed (oops)")
@patch("hc.api.transports.socket.socket")
def test_it_checks_jsonrpc_id(self, socket):
def test_it_checks_jsonrpc_id(self, socket: Mock) -> None:
socketobj = setup_mock(socket, {})
# Add a message with an unexpected id in the outbox.
# The socket reader should skip over it.
@ -310,7 +310,7 @@ class NotifySignalTestCase(BaseTestCase):
@override_settings(ADMINS=[("Admin", "admin@example.org")])
@patch("hc.api.transports.socket.socket")
def test_it_handles_rate_limit_failure(self, socket):
def test_it_handles_rate_limit_failure(self, socket: Mock) -> None:
msg = {
"error": {
"code": -1,
@ -359,7 +359,7 @@ class NotifySignalTestCase(BaseTestCase):
self.assertIn("The check <b>Foo &amp; Co</b> is <b>DOWN</b>.", html)
@patch("hc.api.transports.socket.socket")
def test_it_handles_null_data(self, socket):
def test_it_handles_null_data(self, socket: Mock) -> None:
msg = {
"error": {
"code": -32602,

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -15,7 +15,9 @@ from hc.test import BaseTestCase
class NotifySlackTestCase(BaseTestCase):
def _setup_data(self, value, status="down", email_verified=True):
def _setup_data(
self, value: str, status: str = "down", email_verified: bool = True
) -> None:
self.check = Check(project=self.project)
self.check.name = "Foobar"
self.check.status = status
@ -35,7 +37,7 @@ class NotifySlackTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver", SITE_LOGO_URL=None)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
self._setup_data("https://example.org")
mock_post.return_value.status_code = 200
@ -57,7 +59,7 @@ class NotifySlackTestCase(BaseTestCase):
self.assertIn("http://testserver/static/img/logo.png", serialized)
@patch("hc.api.transports.curl.request")
def test_slack_with_complex_value(self, mock_post):
def test_slack_with_complex_value(self, mock_post: Mock) -> None:
v = json.dumps({"incoming_webhook": {"url": "123"}})
self._setup_data(v)
mock_post.return_value.status_code = 200
@ -69,7 +71,7 @@ class NotifySlackTestCase(BaseTestCase):
self.assertEqual(url, "123")
@patch("hc.api.transports.curl.request")
def test_it_handles_500(self, mock_post):
def test_it_handles_500(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 500
@ -79,7 +81,7 @@ class NotifySlackTestCase(BaseTestCase):
self.assertEqual(n.error, "Received status code 500")
@patch("hc.api.transports.curl.request", side_effect=CurlError("Timed out"))
def test_it_handles_timeout(self, mock_post):
def test_it_handles_timeout(self, mock_post: Mock) -> None:
self._setup_data("123")
self.channel.notify(self.check)
@ -90,7 +92,7 @@ class NotifySlackTestCase(BaseTestCase):
self.assertEqual(mock_post.call_count, 3)
@patch("hc.api.transports.curl.request")
def test_it_shows_schedule_and_tz(self, mock_post):
def test_it_shows_schedule_and_tz(self, mock_post: Mock) -> None:
self._setup_data("123")
self.check.kind = "cron"
self.check.tz = "Europe/Riga"
@ -105,7 +107,7 @@ class NotifySlackTestCase(BaseTestCase):
self.assertEqual(fields["Time Zone"], "Europe/Riga")
@patch("hc.api.transports.curl.request")
def test_slack_with_tabs_in_schedule(self, mock_post):
def test_slack_with_tabs_in_schedule(self, mock_post: Mock) -> None:
self._setup_data("123")
self.check.kind = "cron"
self.check.schedule = "*\t* * * *"
@ -117,7 +119,7 @@ class NotifySlackTestCase(BaseTestCase):
mock_post.assert_called_once()
@override_settings(SLACK_ENABLED=False)
def test_it_requires_slack_enabled(self):
def test_it_requires_slack_enabled(self) -> None:
self._setup_data("123")
self.channel.notify(self.check)
@ -125,7 +127,7 @@ class NotifySlackTestCase(BaseTestCase):
self.assertEqual(n.error, "Slack notifications are not enabled.")
@patch("hc.api.transports.curl.request")
def test_it_does_not_retry_404(self, mock_post):
def test_it_does_not_retry_404(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 404
@ -136,7 +138,7 @@ class NotifySlackTestCase(BaseTestCase):
self.assertEqual(mock_post.call_count, 1)
@patch("hc.api.transports.curl.request")
def test_it_disables_channel_on_404(self, mock_post):
def test_it_disables_channel_on_404(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 404
@ -146,7 +148,7 @@ class NotifySlackTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver")
@patch("hc.api.transports.curl.request")
def test_it_handles_last_ping_fail(self, mock_post):
def test_it_handles_last_ping_fail(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 200
@ -162,7 +164,7 @@ class NotifySlackTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver")
@patch("hc.api.transports.curl.request")
def test_it_shows_nonzero_exit_status(self, mock_post):
def test_it_shows_nonzero_exit_status(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 200
@ -177,7 +179,7 @@ class NotifySlackTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver")
@patch("hc.api.transports.curl.request")
def test_it_handles_last_ping_log(self, mock_post):
def test_it_handles_last_ping_log(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 200
@ -192,7 +194,7 @@ class NotifySlackTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver")
@patch("hc.api.transports.curl.request")
def test_it_shows_ignored_nonzero_exitstatus(self, mock_post):
def test_it_shows_ignored_nonzero_exitstatus(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 200
@ -209,7 +211,7 @@ class NotifySlackTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver")
@patch("hc.api.transports.curl.request")
def test_it_shows_last_ping_body(self, mock_post):
def test_it_shows_last_ping_body(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 200
@ -225,7 +227,7 @@ class NotifySlackTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver")
@patch("hc.api.transports.curl.request")
def test_it_shows_truncated_last_ping_body(self, mock_post):
def test_it_shows_truncated_last_ping_body(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 200
@ -241,7 +243,7 @@ class NotifySlackTestCase(BaseTestCase):
@override_settings(SITE_ROOT="http://testserver")
@patch("hc.api.transports.curl.request")
def test_it_skips_last_ping_body_containing_backticks(self, mock_post):
def test_it_skips_last_ping_body_with_backticks(self, mock_post: Mock) -> None:
self._setup_data("123")
mock_post.return_value.status_code = 200

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.core import mail
from django.test.utils import override_settings
@ -15,7 +15,7 @@ from hc.test import BaseTestCase
class NotifySmsTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -31,7 +31,7 @@ class NotifySmsTestCase(BaseTestCase):
@override_settings(TWILIO_FROM="+000", TWILIO_MESSAGING_SERVICE_SID=None)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
self.check.last_ping = now() - td(hours=2)
mock_post.return_value.status_code = 200
@ -53,7 +53,7 @@ class NotifySmsTestCase(BaseTestCase):
@override_settings(TWILIO_MESSAGING_SERVICE_SID="dummy-sid")
@patch("hc.api.transports.curl.request")
def test_it_uses_messaging_service(self, mock_post):
def test_it_uses_messaging_service(self, mock_post: Mock) -> None:
self.check.last_ping = now() - td(hours=2)
mock_post.return_value.status_code = 200
@ -64,7 +64,7 @@ class NotifySmsTestCase(BaseTestCase):
self.assertFalse("From" in payload)
@patch("hc.api.transports.curl.request")
def test_it_enforces_limit(self, mock_post):
def test_it_enforces_limit(self, mock_post: Mock) -> None:
# At limit already:
self.profile.last_sms_date = now()
self.profile.sms_sent = 50
@ -84,7 +84,7 @@ class NotifySmsTestCase(BaseTestCase):
self.assertEqual(email.subject, "Monthly SMS Limit Reached")
@patch("hc.api.transports.curl.request")
def test_it_resets_limit_next_month(self, mock_post):
def test_it_resets_limit_next_month(self, mock_post: Mock) -> None:
# At limit, but also into a new month
self.profile.sms_sent = 50
self.profile.last_sms_date = now() - td(days=100)
@ -96,7 +96,7 @@ class NotifySmsTestCase(BaseTestCase):
mock_post.assert_called_once()
@patch("hc.api.transports.curl.request")
def test_it_does_not_escape_special_characters(self, mock_post):
def test_it_does_not_escape_special_characters(self, mock_post: Mock) -> None:
self.check.name = "Foo > Bar & Co"
self.check.last_ping = now() - td(hours=2)
@ -108,7 +108,7 @@ class NotifySmsTestCase(BaseTestCase):
self.assertIn("Foo > Bar & Co", payload["Body"])
@patch("hc.api.transports.curl.request")
def test_it_handles_disabled_down_notification(self, mock_post):
def test_it_handles_disabled_down_notification(self, mock_post: Mock) -> None:
payload = {"value": "+123123123", "up": True, "down": False}
self.channel.value = json.dumps(payload)
@ -116,7 +116,7 @@ class NotifySmsTestCase(BaseTestCase):
mock_post.assert_not_called()
@patch("hc.api.transports.curl.request")
def test_it_sends_up_notification(self, mock_post):
def test_it_sends_up_notification(self, mock_post: Mock) -> None:
payload = {"value": "+123123123", "up": True, "down": False}
self.channel.value = json.dumps(payload)

View file

@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class NotifySpikeTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -29,7 +29,7 @@ class NotifySpikeTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -40,13 +40,13 @@ class NotifySpikeTestCase(BaseTestCase):
self.assertEqual(payload["title"], "Foo is DOWN")
@override_settings(SPIKE_ENABLED=False)
def test_it_requires_spike_enabled(self):
def test_it_requires_spike_enabled(self) -> None:
self.channel.notify(self.check)
n = Notification.objects.get()
self.assertEqual(n.error, "Spike notifications are not enabled.")
@patch("hc.api.transports.curl.request")
def test_it_does_not_escape(self, mock_post):
def test_it_does_not_escape(self, mock_post: Mock) -> None:
self.check.name = "Foo & Bar"
self.check.status = "up"
self.check.save()

View file

@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class NotifyTelegramTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -35,7 +35,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -59,7 +59,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertNotIn("All the other checks are up.", payload["text"])
@patch("hc.api.transports.curl.request")
def test_it_sends_to_thread(self, mock_post):
def test_it_sends_to_thread(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.value = json.dumps({"id": 123, "thread_id": 456})
@ -72,7 +72,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertEqual(payload["message_thread_id"], 456)
@patch("hc.api.transports.curl.request")
def test_it_shows_cron_schedule(self, mock_post):
def test_it_shows_cron_schedule(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.kind = "cron"
@ -89,7 +89,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertIn("<b>Time Zone:</b> Europe/Riga\n", payload["text"])
@patch("hc.api.transports.curl.request")
def test_it_returns_error(self, mock_post):
def test_it_returns_error(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 400
mock_post.return_value.json.return_value = {"description": "Hi"}
@ -98,7 +98,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertEqual(n.error, 'Received status code 400 with a message: "Hi"')
@patch("hc.api.transports.curl.request")
def test_it_handles_non_json_error(self, mock_post):
def test_it_handles_non_json_error(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 400
mock_post.return_value.json = Mock(side_effect=ValueError)
@ -107,7 +107,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertEqual(n.error, "Received status code 400")
@patch("hc.api.transports.curl.request")
def test_it_handles_group_supergroup_migration(self, mock_post):
def test_it_handles_group_supergroup_migration(self, mock_post: Mock) -> None:
error_response = Mock(status_code=400)
error_response.json.return_value = {
"description": "Hello",
@ -127,7 +127,7 @@ class NotifyTelegramTestCase(BaseTestCase):
n = Notification.objects.get()
self.assertEqual(n.error, "")
def test_telegram_obeys_rate_limit(self):
def test_telegram_obeys_rate_limit(self) -> None:
TokenBucket.objects.create(value="tg-123", tokens=0)
self.channel.notify(self.check)
@ -135,7 +135,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertEqual(n.error, "Rate limit exceeded")
@patch("hc.api.transports.curl.request")
def test_it_shows_all_other_checks_up_note(self, mock_post):
def test_it_shows_all_other_checks_up_note(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
other = Check(project=self.project)
@ -150,7 +150,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertIn("All the other checks are up.", payload["text"])
@patch("hc.api.transports.curl.request")
def test_it_lists_other_down_checks(self, mock_post):
def test_it_lists_other_down_checks(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
other = Check(project=self.project)
@ -167,7 +167,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertIn(other.cloaked_url(), payload["text"])
@patch("hc.api.transports.curl.request")
def test_it_does_not_show_more_than_10_other_checks(self, mock_post):
def test_it_does_not_show_more_than_10_other_checks(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
for i in range(0, 11):
@ -184,7 +184,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertIn("11 other checks are also down.", payload["text"])
@patch("hc.api.transports.curl.request")
def test_it_disables_channel_on_403_group_deleted(self, mock_post):
def test_it_disables_channel_on_403_group_deleted(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 403
mock_post.return_value.json.return_value = {
"description": "Forbidden: the group chat was deleted"
@ -195,7 +195,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertTrue(self.channel.disabled)
@patch("hc.api.transports.curl.request")
def test_it_disables_channel_on_403_bot_blocked(self, mock_post):
def test_it_disables_channel_on_403_bot_blocked(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 403
mock_post.return_value.json.return_value = {
"description": "Forbidden: bot was blocked by the user"
@ -206,7 +206,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertTrue(self.channel.disabled)
@patch("hc.api.transports.curl.request")
def test_it_shows_last_ping_body(self, mock_post):
def test_it_shows_last_ping_body(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.body_raw = b"Hello World"
@ -219,7 +219,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertIn("Hello World", payload["text"])
@patch("hc.api.transports.curl.request")
def test_it_shows_truncated_last_ping_body(self, mock_post):
def test_it_shows_truncated_last_ping_body(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.body_raw = b"Hello World" * 100
@ -231,7 +231,7 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertIn("[truncated]", payload["text"])
@patch("hc.api.transports.curl.request")
def test_it_escapes_html(self, mock_post):
def test_it_escapes_html(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.ping.body_raw = b"<b>bold</b>\nfoo & bar"

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -13,7 +13,7 @@ from hc.test import BaseTestCase
@override_settings(TRELLO_APP_KEY="fake-trello-app-key")
class NotifyTrelloTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -36,7 +36,7 @@ class NotifyTrelloTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -50,7 +50,7 @@ class NotifyTrelloTestCase(BaseTestCase):
self.assertEqual(params["token"], "fake-token")
@patch("hc.api.transports.curl.request")
def test_it_shows_schedule_and_tz(self, mock_post):
def test_it_shows_schedule_and_tz(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.kind = "cron"
self.check.tz = "Europe/Riga"
@ -64,7 +64,7 @@ class NotifyTrelloTestCase(BaseTestCase):
self.assertIn("**Time Zone:** Europe/Riga", params["desc"])
@patch("hc.api.transports.curl.request")
def test_it_does_not_escape_name(self, mock_post):
def test_it_does_not_escape_name(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.name = "Foo & Bar"

View file

@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class NotifyVictorOpsTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -28,7 +28,7 @@ class NotifyVictorOpsTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -38,14 +38,14 @@ class NotifyVictorOpsTestCase(BaseTestCase):
self.assertEqual(payload["message_type"], "CRITICAL")
@override_settings(VICTOROPS_ENABLED=False)
def test_it_requires_victorops_enabled(self):
def test_it_requires_victorops_enabled(self) -> None:
self.channel.notify(self.check)
n = Notification.objects.get()
self.assertEqual(n.error, "Splunk On-Call notifications are not enabled.")
@patch("hc.api.transports.curl.request")
def test_it_does_not_escape_description(self, mock_post):
def test_it_does_not_escape_description(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.name = "Foo & Bar"

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
@ -15,7 +15,9 @@ from hc.test import BaseTestCase
class NotifyWebhookTestCase(BaseTestCase):
def _setup_data(self, value, status="down", email_verified=True):
def _setup_data(
self, value: str, status: str = "down", email_verified: bool = True
) -> None:
self.check = Check(project=self.project)
self.check.status = status
self.check.last_ping = now() - td(minutes=61)
@ -29,7 +31,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.channel.checks.add(self.check)
@patch("hc.api.transports.curl.request")
def test_webhook(self, mock_get):
def test_webhook(self, mock_get: Mock) -> None:
definition = {
"method_down": "GET",
"url_down": "http://example",
@ -49,7 +51,7 @@ class NotifyWebhookTestCase(BaseTestCase):
)
@patch("hc.api.transports.curl.request", side_effect=CurlError("Foo failed"))
def test_webhooks_handle_curl_errors(self, mock_get):
def test_webhooks_handle_curl_errors(self, mock_get: Mock) -> None:
definition = {
"method_down": "GET",
"url_down": "http://example",
@ -70,7 +72,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(self.channel.last_error, "Foo failed")
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_500(self, mock_get):
def test_webhooks_handle_500(self, mock_get: Mock) -> None:
definition = {
"method_down": "GET",
"url_down": "http://example",
@ -90,7 +92,9 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(n.error, "Received status code 500")
@patch("hc.api.transports.curl.request", side_effect=CurlError("Foo failed"))
def test_webhooks_dont_retry_when_sending_test_notifications(self, mock_get):
def test_webhooks_dont_retry_when_sending_test_notifications(
self, mock_get: Mock
) -> None:
definition = {
"method_down": "GET",
"url_down": "http://example",
@ -108,7 +112,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(n.error, "Foo failed")
@patch("hc.api.transports.curl.request")
def test_webhooks_support_variables(self, mock_get):
def test_webhooks_support_variables(self, mock_get: Mock) -> None:
definition = {
"method_down": "GET",
"url_down": "http://host/$CODE/$STATUS/$TAG1/$TAG2/?name=$NAME",
@ -132,7 +136,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(kwargs["timeout"], 10)
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_variable_variables(self, mock_get):
def test_webhooks_handle_variable_variables(self, mock_get: Mock) -> None:
definition = {
"method_down": "GET",
"url_down": "http://host/$$NAMETAG1",
@ -151,7 +155,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(url, "http://host/$TAG1")
@patch("hc.api.transports.curl.request")
def test_webhooks_support_post(self, mock_request):
def test_webhooks_support_post(self, mock_request: Mock) -> None:
definition = {
"method_down": "POST",
"url_down": "http://example.com",
@ -172,7 +176,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertTrue(payload.startswith("The Time Is 2"))
@patch("hc.api.transports.curl.request")
def test_webhooks_dollarsign_escaping(self, mock_get):
def test_webhooks_dollarsign_escaping(self, mock_get: Mock) -> None:
# If name or tag contains what looks like a variable reference,
# that should be left alone:
definition = {
@ -193,7 +197,7 @@ class NotifyWebhookTestCase(BaseTestCase):
mock_get.assert_called_with("get", url, headers={}, timeout=10)
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_up_events(self, mock_get):
def test_webhooks_handle_up_events(self, mock_get: Mock) -> None:
definition = {
"method_up": "GET",
"url_up": "http://bar",
@ -207,7 +211,7 @@ class NotifyWebhookTestCase(BaseTestCase):
mock_get.assert_called_with("get", "http://bar", headers={}, timeout=10)
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_noop_up_events(self, mock_get):
def test_webhooks_handle_noop_up_events(self, mock_get: Mock) -> None:
definition = {
"method_up": "GET",
"url_up": "",
@ -222,7 +226,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(Notification.objects.count(), 0)
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_unicode_post_body(self, mock_request):
def test_webhooks_handle_unicode_post_body(self, mock_request: Mock) -> None:
definition = {
"method_down": "POST",
"url_down": "http://foo.com",
@ -240,7 +244,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertIsInstance(payload, bytes)
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_post_headers(self, mock_request):
def test_webhooks_handle_post_headers(self, mock_request: Mock) -> None:
definition = {
"method_down": "POST",
"url_down": "http://foo.com",
@ -257,7 +261,7 @@ class NotifyWebhookTestCase(BaseTestCase):
)
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_get_headers(self, mock_request):
def test_webhooks_handle_get_headers(self, mock_request: Mock) -> None:
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
@ -274,7 +278,7 @@ class NotifyWebhookTestCase(BaseTestCase):
)
@patch("hc.api.transports.curl.request")
def test_webhooks_allow_user_agent_override(self, mock_request):
def test_webhooks_allow_user_agent_override(self, mock_request: Mock) -> None:
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
@ -291,7 +295,7 @@ class NotifyWebhookTestCase(BaseTestCase):
)
@patch("hc.api.transports.curl.request")
def test_webhooks_support_variables_in_headers(self, mock_request):
def test_webhooks_support_variables_in_headers(self, mock_request: Mock) -> None:
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
@ -311,7 +315,7 @@ class NotifyWebhookTestCase(BaseTestCase):
)
@override_settings(WEBHOOKS_ENABLED=False)
def test_it_requires_webhooks_enabled(self):
def test_it_requires_webhooks_enabled(self) -> None:
definition = {
"method_down": "GET",
"url_down": "http://example",
@ -326,7 +330,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(n.error, "Webhook notifications are not enabled.")
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_non_ascii_in_headers(self, mock_request):
def test_webhooks_handle_non_ascii_in_headers(self, mock_request: Mock) -> None:
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
@ -343,7 +347,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(headers["X-Foo"], "b&#257;r")
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_latin1_in_headers(self, mock_request):
def test_webhooks_handle_latin1_in_headers(self, mock_request: Mock) -> None:
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
@ -360,7 +364,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(headers["X-Foo"], "½")
@patch("hc.api.transports.curl.request")
def test_webhooks_support_json_variable(self, mock_post):
def test_webhooks_support_json_variable(self, mock_post: Mock) -> None:
definition = {
"method_down": "POST",
"url_down": "http://example.org",
@ -380,7 +384,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(body["name"], "Hello World")
@patch("hc.api.transports.curl.request")
def test_webhooks_support_body_variable_in_body(self, mock_post):
def test_webhooks_support_body_variable_in_body(self, mock_post: Mock) -> None:
definition = {
"method_down": "POST",
"url_down": "http://example.org",
@ -401,7 +405,9 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(payload, ping_body)
@patch("hc.api.transports.curl.request")
def test_webhooks_dont_support_body_variable_in_url_and_headers(self, mock_post):
def test_webhooks_dont_support_body_variable_in_url_and_headers(
self, mock_post: Mock
) -> None:
definition = {
"method_down": "POST",
"url_down": "http://example.org/$BODY",
@ -424,7 +430,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(headers["User-Agent"], "$BODY")
@patch("hc.api.transports.curl.request")
def test_webhooks_support_exitstatus_variable(self, mock_post):
def test_webhooks_support_exitstatus_variable(self, mock_post: Mock) -> None:
definition = {
"method_down": "POST",
"url_down": "http://example.org",
@ -440,8 +446,8 @@ class NotifyWebhookTestCase(BaseTestCase):
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_exitstatus_variable_with_last_ping_missing(
self, mock_post
):
self, mock_post: Mock
) -> None:
definition = {
"method_down": "POST",
"url_down": "http://example.org",
@ -456,7 +462,7 @@ class NotifyWebhookTestCase(BaseTestCase):
self.assertEqual(payload, b"Exit status -1")
@patch("hc.api.transports.curl.request")
def test_webhooks_handle_null_exitstatus(self, mock_post):
def test_webhooks_handle_null_exitstatus(self, mock_post: Mock) -> None:
definition = {
"method_down": "POST",
"url_down": "http://example.org",

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from django.core import mail
from django.test.utils import override_settings
@ -15,7 +15,7 @@ from hc.test import BaseTestCase
class NotifyWhatsAppTestCase(BaseTestCase):
def _setup_data(self, notify_up=True, notify_down=True):
def _setup_data(self, notify_up: bool = True, notify_down: bool = True) -> None:
self.check = Check(project=self.project)
self.check.status = "down"
self.check.last_ping = now() - td(minutes=61)
@ -30,7 +30,7 @@ class NotifyWhatsAppTestCase(BaseTestCase):
@override_settings(TWILIO_FROM="+000", TWILIO_MESSAGING_SERVICE_SID=None)
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self._setup_data()
@ -50,7 +50,7 @@ class NotifyWhatsAppTestCase(BaseTestCase):
@override_settings(TWILIO_MESSAGING_SERVICE_SID="dummy-sid")
@patch("hc.api.transports.curl.request")
def test_it_uses_messaging_service(self, mock_post):
def test_it_uses_messaging_service(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self._setup_data()
@ -61,7 +61,7 @@ class NotifyWhatsAppTestCase(BaseTestCase):
self.assertFalse("From" in payload)
@patch("hc.api.transports.curl.request")
def test_it_obeys_up_down_flags(self, mock_post):
def test_it_obeys_up_down_flags(self, mock_post: Mock) -> None:
self._setup_data(notify_down=False)
self.check.last_ping = now() - td(hours=2)
@ -70,7 +70,7 @@ class NotifyWhatsAppTestCase(BaseTestCase):
mock_post.assert_not_called()
@patch("hc.api.transports.curl.request")
def test_it_enforces_limit(self, mock_post):
def test_it_enforces_limit(self, mock_post: Mock) -> None:
# At limit already:
self.profile.last_sms_date = now()
self.profile.sms_sent = 50
@ -92,7 +92,7 @@ class NotifyWhatsAppTestCase(BaseTestCase):
self.assertEqual(email.subject, "Monthly WhatsApp Limit Reached")
@patch("hc.api.transports.curl.request")
def test_it_does_not_escape_special_characters(self, mock_post):
def test_it_does_not_escape_special_characters(self, mock_post: Mock) -> None:
self._setup_data()
self.check.name = "Foo > Bar & Co"

View file

@ -14,7 +14,7 @@ from hc.test import BaseTestCase
class NotifyZulipTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -29,7 +29,7 @@ class NotifyZulipTestCase(BaseTestCase):
self.channel.save()
self.channel.checks.add(self.check)
def definition(self, **kwargs):
def definition(self, **kwargs: str) -> dict[str, str]:
d = {
"bot_email": "bot@example.org",
"api_key": "fake-key",
@ -40,7 +40,7 @@ class NotifyZulipTestCase(BaseTestCase):
return d
@patch("hc.api.transports.curl.request")
def test_it_works(self, mock_post):
def test_it_works(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.channel.notify(self.check)
@ -57,7 +57,7 @@ class NotifyZulipTestCase(BaseTestCase):
self.assertNotIn(str(self.check.code), serialized)
@patch("hc.api.transports.curl.request")
def test_it_uses_custom_topic(self, mock_post):
def test_it_uses_custom_topic(self, mock_post: Mock) -> None:
self.channel.value = json.dumps(self.definition(topic="foo"))
self.channel.save()
@ -68,7 +68,7 @@ class NotifyZulipTestCase(BaseTestCase):
self.assertEqual(payload["topic"], "foo")
@patch("hc.api.transports.curl.request")
def test_it_returns_error(self, mock_post):
def test_it_returns_error(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 403
mock_post.return_value.json.return_value = {"msg": "Nice try"}
@ -78,7 +78,7 @@ class NotifyZulipTestCase(BaseTestCase):
self.assertEqual(n.error, 'Received status code 403 with a message: "Nice try"')
@patch("hc.api.transports.curl.request")
def test_it_handles_non_json_error_response(self, mock_post):
def test_it_handles_non_json_error_response(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 403
mock_post.return_value.json = Mock(side_effect=ValueError)
@ -87,7 +87,7 @@ class NotifyZulipTestCase(BaseTestCase):
self.assertEqual(n.error, "Received status code 403")
@patch("hc.api.transports.curl.request")
def test_it_uses_site_parameter(self, mock_post):
def test_it_uses_site_parameter(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
definition = {
"bot_email": "bot@example.org",
@ -108,14 +108,14 @@ class NotifyZulipTestCase(BaseTestCase):
self.assertIn("DOWN", payload["topic"])
@override_settings(ZULIP_ENABLED=False)
def test_it_requires_zulip_enabled(self):
def test_it_requires_zulip_enabled(self) -> None:
self.channel.notify(self.check)
n = Notification.objects.get()
self.assertEqual(n.error, "Zulip notifications are not enabled.")
@patch("hc.api.transports.curl.request")
def test_it_does_not_escape_topic(self, mock_post):
def test_it_does_not_escape_topic(self, mock_post: Mock) -> None:
mock_post.return_value.status_code = 200
self.check.name = "Foo & Bar"

View file

@ -9,14 +9,14 @@ from hc.test import BaseTestCase
class PauseTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project, status="up")
self.url = f"/api/v2/checks/{self.check.code}/pause"
self.urlv1 = f"/api/v2/checks/{self.check.code}/pause"
def test_it_works(self):
def test_it_works(self) -> None:
r = self.csrf_client.post(
self.url, "", content_type="application/json", HTTP_X_API_KEY="X" * 32
)
@ -34,7 +34,7 @@ class PauseTestCase(BaseTestCase):
# should be marked as processed from the beginning, so sendalerts ignores it
self.assertTrue(flip.processed)
def test_it_accepts_api_key_in_post_body(self):
def test_it_accepts_api_key_in_post_body(self) -> None:
payload = {"api_key": "X" * 32}
r = self.csrf_client.post(self.url, payload, content_type="application/json")
self.assertEqual(r.status_code, 200)
@ -42,16 +42,16 @@ class PauseTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.status, "paused")
def test_it_handles_options(self):
def test_it_handles_options(self) -> None:
r = self.client.options(self.url)
self.assertEqual(r.status_code, 204)
self.assertIn("POST", r["Access-Control-Allow-Methods"])
def test_it_only_allows_post(self):
def test_it_only_allows_post(self) -> None:
r = self.client.get(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 405)
def test_it_validates_ownership(self):
def test_it_validates_ownership(self) -> None:
check = Check.objects.create(project=self.bobs_project, status="up")
url = f"/api/v1/checks/{check.code}/pause"
@ -61,7 +61,7 @@ class PauseTestCase(BaseTestCase):
self.assertEqual(r.status_code, 403)
def test_it_validates_uuid(self):
def test_it_validates_uuid(self) -> None:
url = "/api/v1/checks/not-uuid/pause"
r = self.client.post(
url, "", content_type="application/json", HTTP_X_API_KEY="X" * 32
@ -69,7 +69,7 @@ class PauseTestCase(BaseTestCase):
self.assertEqual(r.status_code, 404)
def test_it_handles_missing_check(self):
def test_it_handles_missing_check(self) -> None:
url = "/api/v1/checks/07c2f548-9850-4b27-af5d-6c9dc157ec02/pause"
r = self.client.post(
url, "", content_type="application/json", HTTP_X_API_KEY="X" * 32
@ -77,7 +77,7 @@ class PauseTestCase(BaseTestCase):
self.assertEqual(r.status_code, 404)
def test_it_clears_last_start_alert_after(self):
def test_it_clears_last_start_alert_after(self) -> None:
self.check.last_start = now()
self.check.alert_after = self.check.last_start + td(hours=1)
self.check.save()
@ -93,7 +93,7 @@ class PauseTestCase(BaseTestCase):
self.assertEqual(self.check.last_start, None)
self.assertEqual(self.check.alert_after, None)
def test_it_clears_next_nag_date(self):
def test_it_clears_next_nag_date(self) -> None:
self.profile.nag_period = td(hours=1)
self.profile.next_nag_date = now() + td(minutes=30)
self.profile.save()
@ -105,7 +105,7 @@ class PauseTestCase(BaseTestCase):
self.profile.refresh_from_db()
self.assertIsNone(self.profile.next_nag_date)
def test_it_rejects_non_dict_post_body(self):
def test_it_rejects_non_dict_post_body(self) -> None:
r = self.csrf_client.post(self.url, "123", content_type="application/json")
self.assertEqual(r.status_code, 400)
self.assertEqual(

View file

@ -1,7 +1,7 @@
from __future__ import annotations
from datetime import timedelta as td
from unittest.mock import patch
from unittest.mock import Mock, patch
from uuid import uuid4
from django.test import Client
@ -14,14 +14,14 @@ from hc.test import BaseTestCase
@override_settings(S3_BUCKET=None)
class PingTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project)
self.url = f"/ping/{self.check.code}"
self.project.api_key = "X" * 32
@override_settings(PING_BODY_LIMIT=10000)
def test_it_works(self):
def test_it_works(self) -> None:
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.headers["Access-Control-Allow-Origin"], "*")
@ -38,7 +38,7 @@ class PingTestCase(BaseTestCase):
self.assertEqual(ping.created, self.check.last_ping)
self.assertIsNone(ping.exitstatus)
def test_it_changes_status_of_paused_check(self):
def test_it_changes_status_of_paused_check(self) -> None:
self.check.status = "paused"
self.check.save()
@ -48,7 +48,7 @@ class PingTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.status, "up")
def test_it_clears_last_start(self):
def test_it_clears_last_start(self) -> None:
self.check.last_start = now()
self.check.save()
@ -58,7 +58,7 @@ class PingTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.last_start, None)
def test_post_works(self):
def test_post_works(self) -> None:
csrf_client = Client(enforce_csrf_checks=True)
r = csrf_client.post(self.url, "hello world", content_type="text/plain")
self.assertEqual(r.status_code, 200)
@ -67,27 +67,27 @@ class PingTestCase(BaseTestCase):
self.assertEqual(ping.method, "POST")
self.assertEqual(bytes(ping.body_raw), b"hello world")
def test_head_works(self):
def test_head_works(self) -> None:
csrf_client = Client(enforce_csrf_checks=True)
r = csrf_client.head(self.url)
self.assertEqual(r.status_code, 200)
self.assertEqual(Ping.objects.count(), 1)
def test_it_handles_bad_uuid(self):
def test_it_handles_bad_uuid(self) -> None:
r = self.client.get("/ping/not-uuid/")
self.assertEqual(r.status_code, 404)
def test_it_rejects_alternative_uuid_formats(self):
def test_it_rejects_alternative_uuid_formats(self) -> None:
# This uuid is missing separators. uuid.UUID() would accept it.
r = self.client.get("/ping/07c2f54898504b27af5d6c9dc157ec02/")
self.assertEqual(r.status_code, 404)
def test_it_handles_missing_check(self):
def test_it_handles_missing_check(self) -> None:
r = self.client.get("/ping/07c2f548-9850-4b27-af5d-6c9dc157ec02/")
self.assertEqual(r.status_code, 404)
self.assertEqual(r.content.decode(), "not found")
def test_it_handles_120_char_ua(self):
def test_it_handles_120_char_ua(self) -> None:
ua = (
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
@ -100,7 +100,7 @@ class PingTestCase(BaseTestCase):
ping = Ping.objects.get()
self.assertEqual(ping.ua, ua)
def test_it_truncates_long_ua(self):
def test_it_truncates_long_ua(self) -> None:
ua = "01234567890" * 30
r = self.client.get(self.url, HTTP_USER_AGENT=ua)
@ -110,21 +110,21 @@ class PingTestCase(BaseTestCase):
self.assertEqual(len(ping.ua), 200)
assert ua.startswith(ping.ua)
def test_it_reads_forwarded_ip(self):
def test_it_reads_forwarded_ip(self) -> None:
ip = "1.1.1.1"
r = self.client.get(self.url, HTTP_X_FORWARDED_FOR=ip)
ping = Ping.objects.get()
self.assertEqual(r.status_code, 200)
self.assertEqual(ping.remote_addr, "1.1.1.1")
def test_it_reads_forwarded_ipv6_ip(self):
def test_it_reads_forwarded_ipv6_ip(self) -> None:
ip = "2001::1"
r = self.client.get(self.url, HTTP_X_FORWARDED_FOR=ip)
ping = Ping.objects.get()
self.assertEqual(r.status_code, 200)
self.assertEqual(ping.remote_addr, "2001::1")
def test_it_reads_first_forwarded_ip(self):
def test_it_reads_first_forwarded_ip(self) -> None:
ip = "1.1.1.1, 2.2.2.2"
r = self.client.get(
self.url,
@ -135,7 +135,7 @@ class PingTestCase(BaseTestCase):
self.assertEqual(r.status_code, 200)
self.assertEqual(ping.remote_addr, "1.1.1.1")
def test_it_handles_forwarded_ip_plus_port(self):
def test_it_handles_forwarded_ip_plus_port(self) -> None:
ip = "1.1.1.1:1234"
r = self.client.get(
self.url,
@ -146,17 +146,17 @@ class PingTestCase(BaseTestCase):
self.assertEqual(r.status_code, 200)
self.assertEqual(ping.remote_addr, "1.1.1.1")
def test_it_reads_forwarded_protocol(self):
def test_it_reads_forwarded_protocol(self) -> None:
r = self.client.get(self.url, HTTP_X_FORWARDED_PROTO="https")
ping = Ping.objects.get()
self.assertEqual(r.status_code, 200)
self.assertEqual(ping.scheme, "https")
def test_it_never_caches(self):
def test_it_never_caches(self) -> None:
r = self.client.get(self.url)
assert "no-cache" in r.get("Cache-Control")
def test_it_updates_confirmation_flag(self):
def test_it_updates_confirmation_flag(self) -> None:
payload = "Please Confirm ..."
r = self.client.post(self.url, data=payload, content_type="text/plain")
self.assertEqual(r.status_code, 200)
@ -164,7 +164,7 @@ class PingTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertTrue(self.check.has_confirmation_link)
def test_fail_endpoint_works(self):
def test_fail_endpoint_works(self) -> None:
r = self.client.get(self.url + "/fail")
self.assertEqual(r.status_code, 200)
@ -179,7 +179,7 @@ class PingTestCase(BaseTestCase):
self.assertEqual(flip.owner, self.check)
self.assertEqual(flip.new_status, "down")
def test_start_endpoint_works(self):
def test_start_endpoint_works(self) -> None:
last_ping = now() - td(hours=2)
self.check.last_ping = last_ping
self.check.save()
@ -194,7 +194,7 @@ class PingTestCase(BaseTestCase):
ping = Ping.objects.get()
self.assertEqual(ping.kind, "start")
def test_start_does_not_change_status_of_paused_check(self):
def test_start_does_not_change_status_of_paused_check(self) -> None:
self.check.status = "paused"
self.check.save()
@ -205,7 +205,7 @@ class PingTestCase(BaseTestCase):
self.assertTrue(self.check.last_start)
self.assertEqual(self.check.status, "paused")
def test_start_sets_last_start_rid(self):
def test_start_sets_last_start_rid(self) -> None:
rid = uuid4()
r = self.client.get(self.url + f"/start?rid={rid}")
self.assertEqual(r.status_code, 200)
@ -214,7 +214,7 @@ class PingTestCase(BaseTestCase):
self.assertTrue(self.check.last_start)
self.assertEqual(self.check.last_start_rid, rid)
def test_it_sets_last_duration(self):
def test_it_sets_last_duration(self) -> None:
self.check.last_start = now() - td(seconds=10)
self.check.save()
@ -224,7 +224,7 @@ class PingTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertTrue(self.check.last_duration.total_seconds() >= 10)
def test_it_does_not_update_last_ping_on_rid_mismatch(self):
def test_it_does_not_update_last_ping_on_rid_mismatch(self) -> None:
t = now() - td(seconds=10)
self.check.last_start = t
self.check.last_start_rid = uuid4()
@ -239,7 +239,7 @@ class PingTestCase(BaseTestCase):
# last_duration should be not set
self.assertIsNone(self.check.last_duration)
def test_it_clears_last_ping_and_sets_last_duration_if_rid_matches(self):
def test_it_clears_last_ping_and_sets_last_duration_if_rid_matches(self) -> None:
self.check.last_start = now() - td(seconds=10)
self.check.last_start_rid = uuid4()
self.check.save()
@ -251,7 +251,7 @@ class PingTestCase(BaseTestCase):
self.assertIsNone(self.check.last_start)
self.assertTrue(self.check.last_duration.total_seconds() >= 10)
def test_it_clears_last_ping_if_rid_is_absent(self):
def test_it_clears_last_ping_if_rid_is_absent(self) -> None:
self.check.last_start = now() - td(seconds=10)
self.check.last_start_rid = uuid4()
self.check.save()
@ -263,7 +263,7 @@ class PingTestCase(BaseTestCase):
self.assertIsNone(self.check.last_start)
self.assertIsNone(self.check.last_duration)
def test_it_clears_last_ping_on_failure(self):
def test_it_clears_last_ping_on_failure(self) -> None:
self.check.last_start = now() - td(seconds=10)
self.check.last_start_rid = uuid4()
self.check.save()
@ -275,7 +275,7 @@ class PingTestCase(BaseTestCase):
self.assertIsNone(self.check.last_start)
self.assertIsNone(self.check.last_duration)
def test_it_requires_post(self):
def test_it_requires_post(self) -> None:
self.check.methods = "POST"
self.check.save()
@ -291,7 +291,7 @@ class PingTestCase(BaseTestCase):
self.assertEqual(ping.kind, "ign")
@override_settings(PING_BODY_LIMIT=5)
def test_it_chops_long_body(self):
def test_it_chops_long_body(self) -> None:
r = self.client.post(self.url, "hello world", content_type="text/plain")
self.assertEqual(r.headers["Ping-Body-Limit"], "5")
@ -300,14 +300,14 @@ class PingTestCase(BaseTestCase):
self.assertEqual(bytes(ping.body_raw), b"hello")
@override_settings(PING_BODY_LIMIT=None)
def test_it_allows_unlimited_body(self):
def test_it_allows_unlimited_body(self) -> None:
r = self.client.post(self.url, "A" * 20000, content_type="text/plain")
self.assertNotIn("Ping-Body-Limit", r.headers)
ping = Ping.objects.get()
self.assertEqual(len(ping.body_raw), 20000)
def test_it_handles_manual_resume_flag(self):
def test_it_handles_manual_resume_flag(self) -> None:
self.check.status = "paused"
self.check.manual_resume = True
self.check.save()
@ -322,7 +322,7 @@ class PingTestCase(BaseTestCase):
self.assertEqual(ping.scheme, "http")
self.assertEqual(ping.kind, "ign")
def test_zero_exit_status_works(self):
def test_zero_exit_status_works(self) -> None:
r = self.client.get(self.url + "/0")
self.assertEqual(r.status_code, 200)
@ -333,7 +333,7 @@ class PingTestCase(BaseTestCase):
self.assertEqual(ping.kind, None)
self.assertEqual(ping.exitstatus, 0)
def test_nonzero_exit_status_works(self):
def test_nonzero_exit_status_works(self) -> None:
r = self.client.get(self.url + "/123")
self.assertEqual(r.status_code, 200)
@ -344,11 +344,11 @@ class PingTestCase(BaseTestCase):
self.assertEqual(ping.kind, "fail")
self.assertEqual(ping.exitstatus, 123)
def test_it_rejects_exit_status_over_255(self):
def test_it_rejects_exit_status_over_255(self) -> None:
r = self.client.get(self.url + "/256")
self.assertEqual(r.status_code, 400)
def test_it_accepts_bad_unicode(self):
def test_it_accepts_bad_unicode(self) -> None:
csrf_client = Client(enforce_csrf_checks=True)
r = csrf_client.post(self.url, b"Hello \xe9 World", content_type="text/plain")
self.assertEqual(r.status_code, 200)
@ -359,7 +359,7 @@ class PingTestCase(BaseTestCase):
@override_settings(S3_BUCKET="test-bucket", PING_BODY_LIMIT=None)
@patch("hc.api.models.put_object")
def test_it_uploads_body_to_s3(self, put_object):
def test_it_uploads_body_to_s3(self, put_object: Mock) -> None:
r = self.client.post(self.url, b"a" * 101, content_type="text/plain")
self.assertEqual(r.status_code, 200)
@ -372,7 +372,7 @@ class PingTestCase(BaseTestCase):
self.assertEqual(n, 1)
self.assertEqual(data, b"a" * 101)
def test_log_endpoint_works(self):
def test_log_endpoint_works(self) -> None:
r = self.client.post(self.url + "/log", "hello", content_type="text/plain")
self.assertEqual(r.status_code, 200)
@ -387,7 +387,7 @@ class PingTestCase(BaseTestCase):
self.assertFalse(Flip.objects.exists())
def test_it_saves_run_id(self):
def test_it_saves_run_id(self) -> None:
rid = uuid4()
r = self.client.get(self.url + f"/start?rid={rid}")
self.assertEqual(r.status_code, 200)
@ -395,7 +395,7 @@ class PingTestCase(BaseTestCase):
ping = Ping.objects.get()
self.assertEqual(ping.rid, rid)
def test_it_handles_invalid_rid(self):
def test_it_handles_invalid_rid(self) -> None:
samples = ["12345", "684e2e73-017e-465f-8149-d70b7c5aaa490"]
for sample in samples:
r = self.client.get(self.url + f"/start?rid={sample}")

View file

@ -7,12 +7,12 @@ from hc.test import BaseTestCase
class PingBySlugTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project, name="foo", slug="foo")
self.url = f"/ping/{self.project.ping_key}/foo"
def test_it_works(self):
def test_it_works(self) -> None:
r = self.client.get(self.url)
self.assertEqual(r.content, b"OK")
self.assertEqual(r.status_code, 200)
@ -21,7 +21,7 @@ class PingBySlugTestCase(BaseTestCase):
ping = Ping.objects.get()
self.assertEqual(ping.kind, None)
def test_post_works(self):
def test_post_works(self) -> None:
csrf_client = Client(enforce_csrf_checks=True)
r = csrf_client.post(self.url, "hello world", content_type="text/plain")
self.assertEqual(r.status_code, 200)
@ -30,31 +30,31 @@ class PingBySlugTestCase(BaseTestCase):
self.assertEqual(ping.method, "POST")
self.assertEqual(bytes(ping.body_raw), b"hello world")
def test_head_works(self):
def test_head_works(self) -> None:
csrf_client = Client(enforce_csrf_checks=True)
r = csrf_client.head(self.url)
self.assertEqual(r.status_code, 200)
self.assertEqual(Ping.objects.count(), 1)
def test_it_never_caches(self):
def test_it_never_caches(self) -> None:
r = self.client.get(self.url)
assert "no-cache" in r.get("Cache-Control")
def test_fail_endpoint_works(self):
def test_fail_endpoint_works(self) -> None:
r = self.client.get(self.url + "/fail")
self.assertEqual(r.status_code, 200)
ping = Ping.objects.get()
self.assertEqual(ping.kind, "fail")
def test_start_endpoint_works(self):
def test_start_endpoint_works(self) -> None:
r = self.client.get(self.url + "/start")
self.assertEqual(r.status_code, 200)
ping = Ping.objects.get()
self.assertEqual(ping.kind, "start")
def test_zero_exit_status_works(self):
def test_zero_exit_status_works(self) -> None:
r = self.client.get(self.url + "/0")
self.assertEqual(r.status_code, 200)
@ -62,7 +62,7 @@ class PingBySlugTestCase(BaseTestCase):
self.assertEqual(ping.kind, None)
self.assertEqual(ping.exitstatus, 0)
def test_nonzero_exit_status_works(self):
def test_nonzero_exit_status_works(self) -> None:
r = self.client.get(self.url + "/123")
self.assertEqual(r.status_code, 200)
@ -70,18 +70,18 @@ class PingBySlugTestCase(BaseTestCase):
self.assertEqual(ping.kind, "fail")
self.assertEqual(ping.exitstatus, 123)
def test_it_handles_duplicates(self):
def test_it_handles_duplicates(self) -> None:
# Another check with the same slug:
Check.objects.create(project=self.project, name="foo", slug="foo")
r = self.client.get(self.url)
self.assertEqual(r.status_code, 409)
def test_it_handles_wrong_ping_key(self):
def test_it_handles_wrong_ping_key(self) -> None:
r = self.client.get("/ping/rrrrrrrrrrrrrrrrrrrrrr/foo")
self.assertEqual(r.status_code, 404)
def test_it_auto_provisions_missing_check(self):
def test_it_auto_provisions_missing_check(self) -> None:
self.check.delete()
channel = Channel.objects.create(project=self.project)
@ -96,7 +96,7 @@ class PingBySlugTestCase(BaseTestCase):
# It should assign all channels to the new check
self.assertEqual(check.channel_set.get(), channel)
def test_auto_provisioning_is_off_by_default(self):
def test_auto_provisioning_is_off_by_default(self) -> None:
self.check.delete()
r = self.client.get(self.url)
self.assertEqual(r.status_code, 404)

View file

@ -11,24 +11,25 @@ EPOCH = datetime(2020, 1, 1, tzinfo=timezone.utc)
class PingModelTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project)
def test_it_calculates_duration(self):
def test_it_calculates_duration(self) -> None:
Ping.objects.create(owner=self.check, created=EPOCH, kind="start")
p2 = Ping.objects.create(owner=self.check, created=EPOCH + td(minutes=5))
assert p2.duration
self.assertEqual(p2.duration.total_seconds(), 300)
def test_it_handles_no_adjacent_start_event(self):
def test_it_handles_no_adjacent_start_event(self) -> None:
Ping.objects.create(owner=self.check, created=EPOCH, kind="start")
Ping.objects.create(owner=self.check, created=EPOCH + td(minutes=5))
p3 = Ping.objects.create(owner=self.check, created=EPOCH + td(minutes=10))
self.assertIsNone(p3.duration)
def test_it_runs_no_queries_for_the_first_ping(self):
def test_it_runs_no_queries_for_the_first_ping(self) -> None:
p = Ping.objects.create(owner=self.check, created=EPOCH, n=1)
with self.assertNumQueries(0):
self.assertIsNone(p.duration)

View file

@ -10,7 +10,7 @@ from hc.test import BaseTestCase
class PruneNotificationsTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check(project=self.project)
@ -21,7 +21,7 @@ class PruneNotificationsTestCase(BaseTestCase):
self.channel = Channel.objects.create(project=self.project)
self.check.channel_set.add(self.channel)
def test_it_works(self):
def test_it_works(self) -> None:
p = Ping(owner=self.check)
p.created = now()
p.save()
@ -34,6 +34,6 @@ class PruneNotificationsTestCase(BaseTestCase):
self.assertIn("Pruned 1 notifications", output)
self.assertFalse(Notification.objects.exists())
def test_it_handles_missing_pings(self):
def test_it_handles_missing_pings(self) -> None:
output = Command().handle()
self.assertIn("Pruned 0 notifications", output)

View file

@ -5,13 +5,13 @@ from hc.test import BaseTestCase
class ResumeTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project, status="paused")
self.url = f"/api/v1/checks/{self.check.code}/resume"
def test_it_works(self):
def test_it_works(self) -> None:
r = self.csrf_client.post(
self.url, "", content_type="application/json", HTTP_X_API_KEY="X" * 32
)
@ -31,7 +31,7 @@ class ResumeTestCase(BaseTestCase):
# should be marked as processed from the beginning, so sendalerts ignores it
self.assertTrue(flip.processed)
def test_it_handles_not_paused_checks(self):
def test_it_handles_not_paused_checks(self) -> None:
self.check.status = "up"
self.check.save()
@ -41,7 +41,7 @@ class ResumeTestCase(BaseTestCase):
self.assertEqual(r.status_code, 409)
def test_it_accepts_api_key_in_post_body(self):
def test_it_accepts_api_key_in_post_body(self) -> None:
payload = {"api_key": "X" * 32}
r = self.csrf_client.post(self.url, payload, content_type="application/json")
self.assertEqual(r.status_code, 200)
@ -49,16 +49,16 @@ class ResumeTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.status, "new")
def test_it_handles_options(self):
def test_it_handles_options(self) -> None:
r = self.client.options(self.url)
self.assertEqual(r.status_code, 204)
self.assertIn("POST", r["Access-Control-Allow-Methods"])
def test_it_only_allows_post(self):
def test_it_only_allows_post(self) -> None:
r = self.client.get(self.url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 405)
def test_it_validates_ownership(self):
def test_it_validates_ownership(self) -> None:
check = Check.objects.create(project=self.bobs_project, status="up")
url = f"/api/v1/checks/{check.code}/resume"
r = self.client.post(
@ -67,7 +67,7 @@ class ResumeTestCase(BaseTestCase):
self.assertEqual(r.status_code, 403)
def test_it_validates_uuid(self):
def test_it_validates_uuid(self) -> None:
url = "/api/v1/checks/not-uuid/resume"
r = self.client.post(
url, "", content_type="application/json", HTTP_X_API_KEY="X" * 32
@ -75,7 +75,7 @@ class ResumeTestCase(BaseTestCase):
self.assertEqual(r.status_code, 404)
def test_it_handles_missing_check(self):
def test_it_handles_missing_check(self) -> None:
url = "/api/v1/checks/07c2f548-9850-4b27-af5d-6c9dc157ec02/resume"
r = self.client.post(
url, "", content_type="application/json", HTTP_X_API_KEY="X" * 32
@ -83,7 +83,7 @@ class ResumeTestCase(BaseTestCase):
self.assertEqual(r.status_code, 404)
def test_it_rejects_non_dict_post_body(self):
def test_it_rejects_non_dict_post_body(self) -> None:
r = self.csrf_client.post(self.url, "123", content_type="application/json")
self.assertEqual(r.status_code, 400)
self.assertEqual(

View file

@ -13,7 +13,7 @@ from hc.test import BaseTestCase
class SendAlertsTestCase(BaseTestCase):
def test_it_handles_grace_period(self):
def test_it_handles_grace_period(self) -> None:
check = Check(project=self.project, status="up")
# 1 day 30 minutes after ping the check is in grace period:
check.last_ping = now() - td(days=1, minutes=30)
@ -26,7 +26,7 @@ class SendAlertsTestCase(BaseTestCase):
self.assertEqual(check.status, "up")
self.assertEqual(Flip.objects.count(), 0)
def test_it_creates_a_flip_when_check_goes_down(self):
def test_it_creates_a_flip_when_check_goes_down(self) -> None:
check = Check(project=self.project, status="up")
check.last_ping = now() - td(days=2)
check.alert_after = check.last_ping + td(days=1, hours=1)
@ -49,7 +49,7 @@ class SendAlertsTestCase(BaseTestCase):
self.assertEqual(check.alert_after, None)
@patch("hc.api.management.commands.sendalerts.notify_on_thread")
def test_it_processes_flip(self, mock_notify):
def test_it_processes_flip(self, mock_notify: Mock) -> None:
check = Check(project=self.project, status="up")
check.last_ping = now()
check.alert_after = check.last_ping + td(days=1, hours=1)
@ -73,7 +73,7 @@ class SendAlertsTestCase(BaseTestCase):
mock_notify.assert_called_once()
@patch("hc.api.management.commands.sendalerts.notify_on_thread")
def test_it_updates_alert_after(self, mock_notify):
def test_it_updates_alert_after(self, mock_notify: Mock) -> None:
check = Check(project=self.project, status="up")
check.last_ping = now() - td(hours=1)
check.alert_after = check.last_ping
@ -93,7 +93,7 @@ class SendAlertsTestCase(BaseTestCase):
self.assertEqual(Flip.objects.count(), 0)
@patch("hc.api.management.commands.sendalerts.notify")
def test_it_works_synchronously(self, mock_notify):
def test_it_works_synchronously(self, mock_notify: Mock) -> None:
check = Check(project=self.project, status="up")
check.last_ping = now() - td(days=2)
check.alert_after = check.last_ping + td(days=1, hours=1)
@ -104,7 +104,7 @@ class SendAlertsTestCase(BaseTestCase):
# It should call `notify` instead of `notify_on_thread`
mock_notify.assert_called_once()
def test_it_sets_next_nag_date(self):
def test_it_sets_next_nag_date(self) -> None:
self.profile.nag_period = td(hours=1)
self.profile.save()
@ -130,7 +130,7 @@ class SendAlertsTestCase(BaseTestCase):
self.bobs_profile.refresh_from_db()
self.assertIsNotNone(self.bobs_profile.next_nag_date)
def test_it_clears_next_nag_date(self):
def test_it_clears_next_nag_date(self) -> None:
self.profile.nag_period = td(hours=1)
self.profile.next_nag_date = now() - td(minutes=30)
self.profile.save()
@ -158,7 +158,7 @@ class SendAlertsTestCase(BaseTestCase):
self.bobs_profile.refresh_from_db()
self.assertIsNone(self.bobs_profile.next_nag_date)
def test_it_does_not_touch_already_set_next_nag_dates(self):
def test_it_does_not_touch_already_set_next_nag_dates(self) -> None:
original_nag_date = now() - td(minutes=30)
self.profile.nag_period = td(hours=1)
self.profile.next_nag_date = original_nag_date

View file

@ -43,7 +43,7 @@ Mychecks
@patch("hc.accounts.models.now", MOCK_NOW)
@patch("hc.api.management.commands.sendreports.now", MOCK_NOW)
class SendReportsTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
# Make alice eligible for a monthly report:
@ -66,7 +66,7 @@ class SendReportsTestCase(BaseTestCase):
self.check.status = "down"
self.check.save()
def test_it_sends_monthly_report(self):
def test_it_sends_monthly_report(self) -> None:
cmd = Command(stdout=Mock())
cmd.pause = Mock() # don't pause for 1s
@ -74,6 +74,7 @@ class SendReportsTestCase(BaseTestCase):
self.assertTrue(found)
self.profile.refresh_from_db()
assert self.profile.next_report_date
self.assertEqual(self.profile.next_report_date.date(), date(2020, 2, 1))
self.assertEqual(self.profile.next_report_date.day, 1)
self.assertEqual(len(mail.outbox), 1)
@ -90,7 +91,7 @@ class SendReportsTestCase(BaseTestCase):
self.assertIn("Nov. 2019", html)
self.assertIn("Dec. 2019", html)
def test_it_sends_weekly_report(self):
def test_it_sends_weekly_report(self) -> None:
self.profile.reports = "weekly"
self.profile.save()
@ -108,7 +109,7 @@ class SendReportsTestCase(BaseTestCase):
self.assertIn("Dec 30 - Jan 5", html)
self.assertIn("Jan 6 - Jan 12", html)
def test_it_handles_positive_utc_offset(self):
def test_it_handles_positive_utc_offset(self) -> None:
self.profile.reports = "weekly"
self.profile.tz = "America/New_York"
self.profile.save()
@ -127,7 +128,7 @@ class SendReportsTestCase(BaseTestCase):
self.assertIn("Dec 30 - Jan 5", html)
self.assertNotIn("Jan 6 - Jan 12", html)
def test_it_handles_negative_utc_offset(self):
def test_it_handles_negative_utc_offset(self) -> None:
self.profile.reports = "weekly"
self.profile.tz = "Asia/Tokyo"
self.profile.save()
@ -144,14 +145,14 @@ class SendReportsTestCase(BaseTestCase):
self.assertIn("Dec 30 - Jan 5", html)
self.assertIn("Jan 6 - Jan 12", html)
def test_it_obeys_next_report_date(self):
def test_it_obeys_next_report_date(self) -> None:
self.profile.next_report_date = CURRENT_TIME + td(days=1)
self.profile.save()
found = Command().handle_one_report()
self.assertFalse(found)
def test_it_fills_blank_next_monthly_report_date(self):
def test_it_fills_blank_next_monthly_report_date(self) -> None:
self.profile.next_report_date = None
self.profile.save()
@ -159,10 +160,11 @@ class SendReportsTestCase(BaseTestCase):
self.assertTrue(found)
self.profile.refresh_from_db()
assert self.profile.next_report_date
self.assertEqual(self.profile.next_report_date.date(), date(2020, 2, 1))
self.assertEqual(len(mail.outbox), 0)
def test_it_fills_blank_next_weekly_report_date(self):
def test_it_fills_blank_next_weekly_report_date(self) -> None:
self.profile.reports = "weekly"
self.profile.next_report_date = None
self.profile.save()
@ -174,14 +176,14 @@ class SendReportsTestCase(BaseTestCase):
self.assertEqual(self.profile.next_report_date.date(), date(2020, 1, 20))
self.assertEqual(len(mail.outbox), 0)
def test_it_obeys_reports_off(self):
def test_it_obeys_reports_off(self) -> None:
self.profile.reports = "off"
self.profile.save()
found = Command().handle_one_report()
self.assertFalse(found)
def test_it_requires_pinged_checks(self):
def test_it_requires_pinged_checks(self) -> None:
self.check.delete()
found = Command().handle_one_report()
@ -190,7 +192,7 @@ class SendReportsTestCase(BaseTestCase):
# No email should have been sent:
self.assertEqual(len(mail.outbox), 0)
def test_it_sends_nag(self):
def test_it_sends_nag(self) -> None:
cmd = Command(stdout=Mock())
cmd.pause = Mock() # don't pause for 1s
@ -198,6 +200,7 @@ class SendReportsTestCase(BaseTestCase):
self.assertTrue(found)
self.profile.refresh_from_db()
assert self.profile.next_nag_date
self.assertTrue(self.profile.next_nag_date > CURRENT_TIME)
self.assertEqual(len(mail.outbox), 1)
@ -208,7 +211,7 @@ class SendReportsTestCase(BaseTestCase):
self.assertEqual(email.body, NAG_TEXT)
def test_it_obeys_next_nag_date(self):
def test_it_obeys_next_nag_date(self) -> None:
self.profile.next_nag_date = CURRENT_TIME + td(days=1)
self.profile.save()
@ -216,7 +219,7 @@ class SendReportsTestCase(BaseTestCase):
found = Command().handle_one_nag()
self.assertFalse(found)
def test_it_obeys_nag_period(self):
def test_it_obeys_nag_period(self) -> None:
self.profile.nag_period = td()
self.profile.save()
@ -224,7 +227,7 @@ class SendReportsTestCase(BaseTestCase):
found = Command().handle_one_nag()
self.assertFalse(found)
def test_nags_require_down_checks(self):
def test_nags_require_down_checks(self) -> None:
self.check.status = "up"
self.check.save()
@ -238,7 +241,7 @@ class SendReportsTestCase(BaseTestCase):
self.profile.refresh_from_db()
self.assertIsNone(self.profile.next_nag_date)
def test_nags_skip_up_checks(self):
def test_nags_skip_up_checks(self) -> None:
check2 = Check(project=self.project, last_ping=now())
check2.name = "Foobar"
check2.status = "up"
@ -258,7 +261,7 @@ class SendReportsTestCase(BaseTestCase):
self.assertNotIn("Foobar", html)
@override_settings(EMAIL_MAIL_FROM_TMPL="%s@bounces.example.org")
def test_it_sets_custom_mail_from(self):
def test_it_sets_custom_mail_from(self) -> None:
cmd = Command(stdout=Mock())
cmd.pause = Mock() # don't pause for 1s

View file

@ -39,12 +39,12 @@ Content-Transfer-Encoding: 8bit
@override_settings(S3_BUCKET=None)
class SmtpdTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project)
self.email = "%s@does.not.matter" % self.check.code
def test_it_works(self):
def test_it_works(self) -> None:
_process_message("1.2.3.4", "foo@example.org", self.email, b"hello world")
ping = Ping.objects.latest("id")
@ -53,7 +53,7 @@ class SmtpdTestCase(BaseTestCase):
self.assertEqual(bytes(ping.body_raw), b"hello world")
self.assertEqual(ping.kind, None)
def test_it_handles_success_filter_match(self):
def test_it_handles_success_filter_match(self) -> None:
self.check.filter_subject = True
self.check.success_kw = "SUCCESS"
self.check.save()
@ -66,7 +66,7 @@ class SmtpdTestCase(BaseTestCase):
self.assertEqual(ping.ua, "Email from foo@example.org")
self.assertEqual(ping.kind, None)
def test_it_handles_success_filter_match_in_body(self):
def test_it_handles_success_filter_match_in_body(self) -> None:
self.check.filter_body = True
self.check.success_kw = "SUCCESS"
self.check.save()
@ -78,7 +78,7 @@ class SmtpdTestCase(BaseTestCase):
ping = Ping.objects.latest("id")
self.assertEqual(ping.kind, None)
def test_it_handles_success_body_filter_match_in_html_body(self):
def test_it_handles_success_body_filter_match_in_html_body(self) -> None:
self.check.filter_body = True
self.check.success_kw = "SUCCESS"
self.check.save()
@ -89,7 +89,7 @@ class SmtpdTestCase(BaseTestCase):
ping = Ping.objects.latest("id")
self.assertEqual(ping.kind, None)
def test_it_handles_success_filter_miss(self):
def test_it_handles_success_filter_miss(self) -> None:
self.check.filter_body = True
self.check.success_kw = "SUCCESS"
self.check.save()
@ -100,7 +100,7 @@ class SmtpdTestCase(BaseTestCase):
ping = Ping.objects.latest("id")
self.assertEqual(ping.kind, "ign")
def test_it_handles_failure_filter_match(self):
def test_it_handles_failure_filter_match(self) -> None:
self.check.filter_subject = True
self.check.failure_kw = "FAIL"
self.check.save()
@ -113,7 +113,7 @@ class SmtpdTestCase(BaseTestCase):
self.assertEqual(ping.ua, "Email from foo@example.org")
self.assertEqual(ping.kind, "fail")
def test_it_handles_failure_filter_miss(self):
def test_it_handles_failure_filter_miss(self) -> None:
self.check.filter_subject = True
self.check.failure_kw = "FAIL"
self.check.save()
@ -126,7 +126,7 @@ class SmtpdTestCase(BaseTestCase):
self.assertEqual(ping.ua, "Email from foo@example.org")
self.assertEqual(ping.kind, "ign")
def test_it_handles_multiple_success_keywords(self):
def test_it_handles_multiple_success_keywords(self) -> None:
self.check.filter_subject = True
self.check.success_kw = "SUCCESS, OK"
self.check.save()
@ -139,7 +139,7 @@ class SmtpdTestCase(BaseTestCase):
self.assertEqual(ping.ua, "Email from foo@example.org")
self.assertEqual(ping.kind, None)
def test_it_handles_multiple_failure_keywords(self):
def test_it_handles_multiple_failure_keywords(self) -> None:
self.check.filter_subject = True
self.check.failure_kw = "FAIL, WARNING"
self.check.save()
@ -152,7 +152,7 @@ class SmtpdTestCase(BaseTestCase):
self.assertEqual(ping.ua, "Email from foo@example.org")
self.assertEqual(ping.kind, "fail")
def test_it_handles_failure_before_success(self):
def test_it_handles_failure_before_success(self) -> None:
self.check.filter_subject = True
self.check.success_kw = "SUCCESS"
self.check.failure_kw = "FAIL"
@ -167,7 +167,7 @@ class SmtpdTestCase(BaseTestCase):
self.assertEqual(ping.ua, "Email from foo@example.org")
self.assertEqual(ping.kind, "fail")
def test_it_handles_start_filter_match(self):
def test_it_handles_start_filter_match(self) -> None:
self.check.filter_subject = True
self.check.start_kw = "START"
self.check.save()
@ -180,7 +180,7 @@ class SmtpdTestCase(BaseTestCase):
self.assertEqual(ping.ua, "Email from foo@example.org")
self.assertEqual(ping.kind, "start")
def test_it_handles_encoded_subject(self):
def test_it_handles_encoded_subject(self) -> None:
self.check.filter_subject = True
self.check.success_kw = "SUCCESS"
self.check.save()
@ -193,7 +193,7 @@ class SmtpdTestCase(BaseTestCase):
self.assertEqual(ping.ua, "Email from foo@example.org")
self.assertEqual(ping.kind, None)
async def test_it_handles_multiple_recipients(self):
async def test_it_handles_multiple_recipients(self) -> None:
class Session:
peer = ["1.2.3.4"]

View file

@ -6,7 +6,7 @@ from hc.test import BaseTestCase
class StatusTestCase(BaseTestCase):
url = "/api/v1/status/"
def test_it_works(self):
def test_it_works(self) -> None:
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)

View file

@ -14,7 +14,7 @@ ALICE_HASH = "d60db3b2343e713a4de3e92d4eb417e4f05f06ab"
@override_settings(SECRET_KEY="test-secret")
class TokenBucketTestCase(BaseTestCase):
def test_it_works(self):
def test_it_works(self) -> None:
r = TokenBucket.authorize_login_email("alice@example.org")
self.assertTrue(r)
@ -22,13 +22,13 @@ class TokenBucketTestCase(BaseTestCase):
self.assertEqual(obj.tokens, 0.95)
self.assertEqual(obj.value, "em-" + ALICE_HASH)
def test_it_handles_insufficient_tokens(self):
def test_it_handles_insufficient_tokens(self) -> None:
TokenBucket.objects.create(value="em-" + ALICE_HASH, tokens=0.04)
r = TokenBucket.authorize_login_email("alice@example.org")
self.assertFalse(r)
def test_it_tops_up(self):
def test_it_tops_up(self) -> None:
obj = TokenBucket(value="em-" + ALICE_HASH)
obj.tokens = 0
obj.updated = now() - td(minutes=30)
@ -40,7 +40,7 @@ class TokenBucketTestCase(BaseTestCase):
obj.refresh_from_db()
self.assertAlmostEqual(obj.tokens, 0.45, places=4)
def test_it_normalizes_email(self):
def test_it_normalizes_email(self) -> None:
emails = ("alice+alias@example.org", "a.li.ce@example.org")
for email in emails:

View file

@ -10,15 +10,15 @@ from hc.test import BaseTestCase
class UpdateCheckTestCase(BaseTestCase):
def setUp(self):
def setUp(self) -> None:
super().setUp()
self.check = Check.objects.create(project=self.project)
def post(self, code, data, v=1):
def post(self, code: uuid.UUID | str, data, v: int = 1):
url = f"/api/v{v}/checks/{code}"
return self.csrf_client.post(url, data, content_type="application/json")
def test_it_works(self):
def test_it_works(self) -> None:
self.check.last_ping = now()
self.check.status = "up"
self.check.save()
@ -61,12 +61,12 @@ class UpdateCheckTestCase(BaseTestCase):
expected_aa = self.check.last_ping + td(seconds=3600 + 60)
self.assertEqual(self.check.alert_after, expected_aa)
def test_it_handles_options(self):
def test_it_handles_options(self) -> None:
r = self.client.options("/api/v1/checks/%s" % self.check.code)
self.assertEqual(r.status_code, 204)
self.assertIn("POST", r["Access-Control-Allow-Methods"])
def test_it_unassigns_channels(self):
def test_it_unassigns_channels(self) -> None:
Channel.objects.create(project=self.project)
self.check.assign_all_channels()
@ -76,22 +76,22 @@ class UpdateCheckTestCase(BaseTestCase):
check = Check.objects.get()
self.assertEqual(check.channel_set.count(), 0)
def test_it_handles_invalid_uuid(self):
def test_it_handles_invalid_uuid(self) -> None:
r = self.post("not-an-uuid", {"api_key": "X" * 32})
self.assertEqual(r.status_code, 404)
def test_it_handles_missing_check(self):
def test_it_handles_missing_check(self) -> None:
made_up_code = "07c2f548-9850-4b27-af5d-6c9dc157ec02"
r = self.post(made_up_code, {"api_key": "X" * 32})
self.assertEqual(r.status_code, 404)
def test_it_validates_ownership(self):
def test_it_validates_ownership(self) -> None:
check = Check.objects.create(project=self.bobs_project, status="up")
r = self.post(check.code, {"api_key": "X" * 32})
self.assertEqual(r.status_code, 403)
def test_it_updates_cron_to_simple(self):
def test_it_updates_cron_to_simple(self) -> None:
self.check.kind = "cron"
self.check.schedule = "5 * * * *"
self.check.save()
@ -102,7 +102,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.kind, "simple")
def test_it_sets_single_channel(self):
def test_it_sets_single_channel(self) -> None:
channel = Channel.objects.create(project=self.project)
# Create another channel so we can test that only the first one
# gets assigned:
@ -116,9 +116,9 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 1)
self.assertEqual(self.check.channel_set.first().code, channel.code)
self.assertEqual(self.check.channel_set.get().code, channel.code)
def test_it_sets_the_channel_only_once(self):
def test_it_sets_the_channel_only_once(self) -> None:
channel = Channel.objects.create(project=self.project)
duplicates = "%s,%s" % (channel.code, channel.code)
r = self.post(self.check.code, {"api_key": "X" * 32, "channels": duplicates})
@ -127,7 +127,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 1)
def test_it_sets_channel_by_name(self):
def test_it_sets_channel_by_name(self) -> None:
channel = Channel.objects.create(project=self.project, name="alerts")
r = self.post(self.check.code, {"api_key": "X" * 32, "channels": "alerts"})
@ -136,9 +136,9 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 1)
self.assertEqual(self.check.channel_set.first().code, channel.code)
self.assertEqual(self.check.channel_set.get().code, channel.code)
def test_it_sets_channel_by_name_formatted_as_uuid(self):
def test_it_sets_channel_by_name_formatted_as_uuid(self) -> None:
name = "102eaa82-a274-4b15-a499-c1bb6bbcd7b6"
channel = Channel.objects.create(project=self.project, name=name)
@ -147,9 +147,9 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 1)
self.assertEqual(self.check.channel_set.first().code, channel.code)
self.assertEqual(self.check.channel_set.get().code, channel.code)
def test_it_handles_comma_separated_channel_codes(self):
def test_it_handles_comma_separated_channel_codes(self) -> None:
c1 = Channel.objects.create(project=self.project)
c2 = Channel.objects.create(project=self.project)
r = self.post(
@ -162,7 +162,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 2)
def test_it_handles_asterix(self):
def test_it_handles_asterix(self) -> None:
Channel.objects.create(project=self.project)
Channel.objects.create(project=self.project)
r = self.post(self.check.code, {"api_key": "X" * 32, "channels": "*"})
@ -172,7 +172,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 2)
def test_it_ignores_channels_if_channels_key_missing(self):
def test_it_ignores_channels_if_channels_key_missing(self) -> None:
Channel.objects.create(project=self.project)
self.check.assign_all_channels()
@ -181,7 +181,7 @@ class UpdateCheckTestCase(BaseTestCase):
check = Check.objects.get()
self.assertEqual(check.channel_set.count(), 1)
def test_it_rejects_bad_channel_code(self):
def test_it_rejects_bad_channel_code(self) -> None:
payload = {"api_key": "X" * 32, "channels": "abc", "name": "New Name"}
r = self.post(
self.check.code,
@ -194,7 +194,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.name, "")
def test_it_rejects_missing_channel(self):
def test_it_rejects_missing_channel(self) -> None:
code = str(uuid.uuid4())
r = self.post(self.check.code, {"api_key": "X" * 32, "channels": code})
@ -204,7 +204,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 0)
def test_it_rejects_channel_from_another_project(self):
def test_it_rejects_channel_from_another_project(self) -> None:
charlies_channel = Channel.objects.create(project=self.charlies_project)
code = str(charlies_channel.code)
@ -216,7 +216,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 0)
def test_it_handles_channel_lookup_by_name_with_no_results(self):
def test_it_handles_channel_lookup_by_name_with_no_results(self) -> None:
r = self.post(self.check.code, {"api_key": "X" * 32, "channels": "foo"})
self.assertEqual(r.status_code, 400)
@ -225,7 +225,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 0)
def test_it_handles_channel_lookup_by_name_with_multiple_results(self):
def test_it_handles_channel_lookup_by_name_with_multiple_results(self) -> None:
Channel.objects.create(project=self.project, name="foo")
Channel.objects.create(project=self.project, name="foo")
@ -237,7 +237,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 0)
def test_it_rejects_multiple_empty_channel_names(self):
def test_it_rejects_multiple_empty_channel_names(self) -> None:
Channel.objects.create(project=self.project, name="")
r = self.post(self.check.code, {"api_key": "X" * 32, "channels": ","})
@ -247,17 +247,17 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.channel_set.count(), 0)
def test_it_rejects_non_string_channels_key(self):
def test_it_rejects_non_string_channels_key(self) -> None:
r = self.post(self.check.code, {"api_key": "X" * 32, "channels": None})
self.assertEqual(r.status_code, 400)
def test_it_rejects_non_string_desc(self):
def test_it_rejects_non_string_desc(self) -> None:
r = self.post(self.check.code, {"api_key": "X" * 32, "desc": 123})
self.assertEqual(r.status_code, 400)
def test_it_validates_cron_expression(self):
def test_it_validates_cron_expression(self) -> None:
self.check.kind = "cron"
self.check.schedule = "5 * * * *"
self.check.save()
@ -271,21 +271,21 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.schedule, "5 * * * *")
def test_it_rejects_readonly_key(self):
def test_it_rejects_readonly_key(self) -> None:
self.project.api_key_readonly = "R" * 32
self.project.save()
r = self.post(self.check.code, {"api_key": "R" * 32, "name": "Foo"})
self.assertEqual(r.status_code, 401)
def test_it_sets_manual_resume_to_true(self):
def test_it_sets_manual_resume_to_true(self) -> None:
r = self.post(self.check.code, {"api_key": "X" * 32, "manual_resume": True})
self.assertEqual(r.status_code, 200)
self.check.refresh_from_db()
self.assertTrue(self.check.manual_resume)
def test_it_sets_manual_resume_to_false(self):
def test_it_sets_manual_resume_to_false(self) -> None:
self.check.manual_resume = True
self.check.save()
@ -295,14 +295,14 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertFalse(self.check.manual_resume)
def test_it_sets_methods(self):
def test_it_sets_methods(self) -> None:
r = self.post(self.check.code, {"api_key": "X" * 32, "methods": "POST"})
self.assertEqual(r.status_code, 200)
self.check.refresh_from_db()
self.assertEqual(self.check.methods, "POST")
def test_it_clears_methods(self):
def test_it_clears_methods(self) -> None:
self.check.methods = "POST"
self.check.save()
@ -313,7 +313,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.methods, "")
def test_it_leaves_methods_unchanged(self):
def test_it_leaves_methods_unchanged(self) -> None:
self.check.methods = "POST"
self.check.save()
@ -324,11 +324,11 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.methods, "POST")
def test_it_rejects_bad_methods_value(self):
def test_it_rejects_bad_methods_value(self) -> None:
r = self.post(self.check.code, {"api_key": "X" * 32, "methods": "bad-value"})
self.assertEqual(r.status_code, 400)
def test_it_sets_success_kw(self):
def test_it_sets_success_kw(self) -> None:
r = self.post(
self.check.code, {"api_key": "X" * 32, "subject": "SUCCESS,COMPLETE"}
)
@ -338,7 +338,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.assertTrue(self.check.filter_subject)
self.assertEqual(self.check.success_kw, "SUCCESS,COMPLETE")
def test_it_sets_failure_kw(self):
def test_it_sets_failure_kw(self) -> None:
payload = {"api_key": "X" * 32, "subject_fail": "FAILED,FAILURE"}
r = self.post(self.check.code, payload)
self.assertEqual(r.status_code, 200)
@ -347,7 +347,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.assertTrue(self.check.filter_subject)
self.assertEqual(self.check.failure_kw, "FAILED,FAILURE")
def test_it_unsets_filter_subject_flag(self):
def test_it_unsets_filter_subject_flag(self) -> None:
self.check.filter_subject = True
self.check.success_kw = "SUCCESS"
self.check.save()
@ -359,7 +359,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.assertFalse(self.check.filter_subject)
self.assertEqual(self.check.success_kw, "")
def test_it_accepts_60_days_timeout(self):
def test_it_accepts_60_days_timeout(self) -> None:
payload = {"api_key": "X" * 32, "timeout": 60 * 24 * 3600}
r = self.post(self.check.code, payload)
self.assertEqual(r.status_code, 200)
@ -367,12 +367,12 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.timeout.total_seconds(), 60 * 24 * 3600)
def test_it_rejects_out_of_range_timeout(self):
def test_it_rejects_out_of_range_timeout(self) -> None:
payload = {"api_key": "X" * 32, "timeout": 500 * 24 * 3600}
r = self.post(self.check.code, payload)
self.assertEqual(r.status_code, 400)
def test_it_prioritizes_filter_subject_field(self):
def test_it_prioritizes_filter_subject_field(self) -> None:
payload = {"api_key": "X" * 32, "subject": "SUCCESS", "filter_subject": False}
r = self.post(self.check.code, payload)
self.assertEqual(r.status_code, 200)
@ -381,7 +381,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.assertFalse(self.check.filter_subject)
self.assertEqual(self.check.success_kw, "SUCCESS")
def test_v1_reports_status_started(self):
def test_v1_reports_status_started(self) -> None:
self.check.last_start = now()
self.check.save()
@ -390,7 +390,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.assertEqual(doc["status"], "started")
self.assertTrue(doc["started"])
def test_v2_reports_started_separately(self):
def test_v2_reports_started_separately(self) -> None:
self.check.last_start = now()
self.check.save()
@ -399,7 +399,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.assertEqual(doc["status"], "new")
self.assertTrue(doc["started"])
def test_v3_saves_slug(self):
def test_v3_saves_slug(self) -> None:
payload = {"slug": "updated-slug", "api_key": "X" * 32}
r = self.post(self.check.code, payload, v=3)
self.assertEqual(r.status_code, 200)
@ -407,7 +407,7 @@ class UpdateCheckTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.slug, "updated-slug")
def test_v3_does_not_autogenerate_slug(self):
def test_v3_does_not_autogenerate_slug(self) -> None:
self.check.slug = "foo"
self.check.save()

View file

@ -33,9 +33,9 @@ class HexTimestampSigner(Signer):
return value
def sign_bounce_id(s):
def sign_bounce_id(s: str) -> str:
return HexTimestampSigner(sep=".", algorithm="sha1").sign(s)
def unsign_bounce_id(s, max_age):
def unsign_bounce_id(s: str, max_age: int) -> str:
return HexTimestampSigner(sep=".", algorithm="sha1").unsign(s, max_age=max_age)