コード例 #1
0
class UpdateNameTestCase(TestCase):

    def setUp(self):
        self.alice = User(username="******")
        self.alice.set_password("password")
        self.alice.save()

        self.check = Check(user=self.alice)
        self.check.save()

    def test_it_works(self):
        url = "/checks/%s/name/" % self.check.code
        payload = {"name": "Alice Was Here"}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 302

        check = Check.objects.get(code=self.check.code)
        assert check.name == "Alice Was Here"

    def test_it_checks_ownership(self):

        charlie = User(username="******")
        charlie.set_password("password")
        charlie.save()

        url = "/checks/%s/name/" % self.check.code
        payload = {"name": "Charlie Sent This"}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 403

    def test_it_handles_bad_uuid(self):
        url = "/checks/not-uuid/name/"
        payload = {"name": "Alice Was Here"}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 400

    def test_it_handles_missing_uuid(self):
        # Valid UUID but there is no check for it:
        url = "/checks/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/name/"
        payload = {"name": "Alice Was Here"}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 404

    def test_it_sanitizes_tags(self):
        url = "/checks/%s/name/" % self.check.code
        payload = {"tags": "  foo  bar\r\t \n  baz \n"}

        self.client.login(username="******", password="******")
        self.client.post(url, data=payload)

        check = Check.objects.get(id=self.check.id)
        self.assertEqual(check.tags, "foo bar baz")
コード例 #2
0
ファイル: test_ping.py プロジェクト: bobocola/healthchecks
    def test_post_works(self):
        check = Check()
        check.save()

        csrf_client = Client(enforce_csrf_checks=True)
        r = csrf_client.post("/ping/%s/" % check.code)
        assert r.status_code == 200
コード例 #3
0
    def test_it_works(self):
        check = Check()
        check.save()

        payload = [{
            "event": "inbound",
            "msg": {
                "raw_msg": "This is raw message",
                "to": [
                    ["*****@*****.**", "Somebody"],
                    ["*****@*****.**" % check.code, "Healthchecks"]
                ]
            }
        }]

        data = {"mandrill_events": json.dumps(payload)}
        r = self.client.post("/handle_email/", data=data)
        assert r.status_code == 200

        same_check = Check.objects.get(code=check.code)
        assert same_check.status == "up"

        pings = list(Ping.objects.all())
        assert pings[0].scheme == "email"
        assert pings[0].body == "This is raw message"
コード例 #4
0
    def test_status_works_with_grace_period(self):
        check = Check()
        check.status = "up"
        check.last_ping = timezone.now() - timedelta(days=1, minutes=30)

        self.assertTrue(check.in_grace_period())
        self.assertEqual(check.get_status(), "up")
コード例 #5
0
ファイル: views.py プロジェクト: cogzidel/healthchecks
def checks(request):
    if request.method == "GET":
        q = Check.objects.filter(user=request.user)

        tags = set(request.GET.getlist("tag"))
        for tag in tags:
            # approximate filtering by tags
            q = q.filter(tags__contains=tag)

        checks = []
        for check in q:
            # precise, final filtering
            if not tags or check.matches_tag_set(tags):
                checks.append(check.to_dict())

        return JsonResponse({"checks": checks})

    elif request.method == "POST":
        created = False
        check = _lookup(request.user, request.json)
        if check is None:
            num_checks = Check.objects.filter(user=request.user).count()
            if num_checks >= request.user.profile.check_limit:
                return HttpResponseForbidden()

            check = Check(user=request.user)
            created = True

        _update(check, request.json)

        return JsonResponse(check.to_dict(), status=201 if created else 200)

    # If request is neither GET nor POST, return "405 Method not allowed"
    return HttpResponse(status=405)
コード例 #6
0
ファイル: views.py プロジェクト: Persistent13/healthchecks
def checks(request):
    if request.method == "GET":
        q = Check.objects.filter(user=request.user)
        doc = {"checks": [check.to_dict() for check in q]}
        return JsonResponse(doc)

    elif request.method == "POST":
        check = Check(user=request.user)
        check.name = str(request.json.get("name", ""))
        check.tags = str(request.json.get("tags", ""))
        if "timeout" in request.json:
            check.timeout = td(seconds=request.json["timeout"])
        if "grace" in request.json:
            check.grace = td(seconds=request.json["grace"])

        check.save()

        # This needs to be done after saving the check, because of
        # the M2M relation between checks and channels:
        if request.json.get("channels") == "*":
            check.assign_all_channels()

        return JsonResponse(check.to_dict(), status=201)

    # If request is neither GET nor POST, return "405 Method not allowed"
    return HttpResponse(status=405)
コード例 #7
0
ファイル: test_ping.py プロジェクト: bobocola/healthchecks
    def test_head_works(self):
        check = Check()
        check.save()

        csrf_client = Client(enforce_csrf_checks=True)
        r = csrf_client.head("/ping/%s/" % check.code)
        assert r.status_code == 200
        assert Ping.objects.count() == 1
コード例 #8
0
    def test_it_strips_tags(self):
        check = Check()

        check.tags = " foo  bar "
        self.assertEquals(check.tags_list(), ["foo", "bar"])

        check.tags = " "
        self.assertEquals(check.tags_list(), [])
コード例 #9
0
    def test_it_handles_grace_period(self):
        check = Check(user=self.alice, status="up")
        # 1 day 30 minutes after ping the check is in grace period:
        check.last_ping = timezone.now() - timedelta(days=1, minutes=30)
        check.save()

        # Expect no exceptions--
        handle_one(check)
コード例 #10
0
ファイル: test_pause.py プロジェクト: haswalt/healthchecks
    def test_it_validates_ownership(self):
        check = Check(user=self.bob, status="up")
        check.save()

        url = "/api/v1/checks/%s/pause" % check.code
        r = self.client.post(url, "", content_type="application/json",
                             HTTP_X_API_KEY="abc")

        self.assertEqual(r.status_code, 400)
コード例 #11
0
ファイル: views.py プロジェクト: haswalt/healthchecks
def add_check(request):
    assert request.method == "POST"

    check = Check(user=request.team.user)
    check.save()

    check.assign_all_channels()

    return redirect("hc-checks")
コード例 #12
0
    def test_it_shows_only_users_checks(self):
        bobs_check = Check(user=self.bob, name="Bob 1")
        bobs_check.save()

        r = self.get()
        data = r.json()
        self.assertEqual(len(data["checks"]), 2)
        for check in data["checks"]:
            self.assertNotEqual(check["name"], "Bob 1")
コード例 #13
0
    def test_unique_accepts_only_whitelisted_values(self):
        existing = Check(user=self.alice, name="Foo")
        existing.save()

        self.post({
            "api_key": "abc",
            "name": "Foo",
            "unique": ["status"]
        }, expected_fragment="unexpected value")
コード例 #14
0
ファイル: test_notify.py プロジェクト: diwu1989/healthchecks
class NotifyTestCase(TestCase):

    def _setup_data(self, channel_kind, channel_value, email_verified=True):
        self.alice = User(username="******")
        self.alice.save()

        self.check = Check()
        self.check.status = "down"
        self.check.save()

        self.channel = Channel(user=self.alice)
        self.channel.kind = channel_kind
        self.channel.value = channel_value
        self.channel.email_verified = email_verified
        self.channel.save()
        self.channel.checks.add(self.check)

    @patch("hc.api.models.requests.get")
    def test_webhook(self, mock_get):
        self._setup_data("webhook", "http://example")
        mock_get.return_value.status_code = 200

        self.channel.notify(self.check)
        mock_get.assert_called_with(u"http://example", timeout=5)

    @patch("hc.api.models.requests.get", side_effect=ReadTimeout)
    def test_webhooks_handle_timeouts(self, mock_get):
        self._setup_data("webhook", "http://example")
        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

    def test_email(self):
        self._setup_data("email", "*****@*****.**")
        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        # And email should have been sent
        self.assertEqual(len(mail.outbox), 1)

    def test_it_skips_unverified_email(self):
        self._setup_data("email", "*****@*****.**", email_verified=False)
        self.channel.notify(self.check)

        assert Notification.objects.count() == 0
        self.assertEqual(len(mail.outbox), 0)

    @patch("hc.api.models.requests.post")
    def test_pd(self, mock_post):
        self._setup_data("pd", "123")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        assert "trigger" in kwargs["data"]
コード例 #15
0
    def test_it_switches(self):
        c = Check(user=self.alice, name="This belongs to Alice")
        c.save()

        self.client.login(username="******", password="******")

        url = "/accounts/switch_team/%s/" % self.alice.username
        r = self.client.get(url, follow=True)

        self.assertContains(r, "This belongs to Alice")
コード例 #16
0
    def test_it_works(self):
        check = Check(user=self.alice)
        check.last_ping_body = "this is body"
        check.save()

        Ping.objects.create(owner=check)

        self.client.login(username="******", password="******")
        r = self.client.post("/checks/%s/last_ping/" % check.code)
        self.assertContains(r, "this is body", status_code=200)
コード例 #17
0
class UpdateTimeoutTestCase(BaseTestCase):

    def setUp(self):
        super(UpdateTimeoutTestCase, self).setUp()
        self.check = Check(user=self.alice)
        self.check.save()

    def test_it_works(self):
        url = "/checks/%s/timeout/" % self.check.code
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        self.assertRedirects(r, "/checks/")

        check = Check.objects.get(code=self.check.code)
        assert check.timeout.total_seconds() == 3600
        assert check.grace.total_seconds() == 60

    def test_team_access_works(self):
        url = "/checks/%s/timeout/" % self.check.code
        payload = {"timeout": 7200, "grace": 60}

        # Logging in as bob, not alice. Bob has team access so this
        # should work.
        self.client.login(username="******", password="******")
        self.client.post(url, data=payload)

        check = Check.objects.get(code=self.check.code)
        assert check.timeout.total_seconds() == 7200

    def test_it_handles_bad_uuid(self):
        url = "/checks/not-uuid/timeout/"
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 400

    def test_it_handles_missing_uuid(self):
        # Valid UUID but there is no check for it:
        url = "/checks/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/timeout/"
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 404

    def test_it_checks_ownership(self):
        url = "/checks/%s/timeout/" % self.check.code
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 403
コード例 #18
0
class MyChecksTestCase(BaseTestCase):

    def setUp(self):
        super(MyChecksTestCase, self).setUp()
        self.check = Check(user=self.alice, name="Alice Was Here")
        self.check.save()

    def test_it_works(self):
        self.client.login(username="******", password="******")
        r = self.client.get("/checks/")
        self.assertContains(r, "Alice Was Here", status_code=200)
コード例 #19
0
ファイル: test_ping.py プロジェクト: bobocola/healthchecks
    def test_it_truncates_long_ua(self):
        ua = "01234567890" * 30

        check = Check()
        check.save()
        r = self.client.get("/ping/%s/" % check.code, HTTP_USER_AGENT=ua)
        assert r.status_code == 200

        pings = list(Ping.objects.all())
        assert len(pings[0].ua) == 200
        assert ua.startswith(pings[0].ua)
コード例 #20
0
ファイル: views.py プロジェクト: cogzidel/healthchecks
def add_check(request):
    num_checks = Check.objects.filter(user=request.team.user).count()
    if num_checks >= request.team.check_limit:
        return HttpResponseBadRequest()

    check = Check(user=request.team.user)
    check.save()

    check.assign_all_channels()

    return redirect("hc-checks")
コード例 #21
0
ファイル: test_ping.py プロジェクト: bobocola/healthchecks
    def test_it_works(self):
        check = Check()
        check.save()

        r = self.client.get("/ping/%s/" % check.code)
        assert r.status_code == 200

        same_check = Check.objects.get(code=check.code)
        assert same_check.status == "up"

        pings = list(Ping.objects.all())
        assert pings[0].scheme == "http"
コード例 #22
0
ファイル: test_pause.py プロジェクト: haswalt/healthchecks
    def test_it_works(self):
        check = Check(user=self.alice, status="up")
        check.save()

        url = "/api/v1/checks/%s/pause" % check.code
        r = self.client.post(url, "", content_type="application/json",
                             HTTP_X_API_KEY="abc")

        self.assertEqual(r.status_code, 200)

        check.refresh_from_db()
        self.assertEqual(check.status, "paused")
コード例 #23
0
ファイル: test_ping.py プロジェクト: bobocola/healthchecks
    def test_it_handles_120_char_ua(self):
        ua = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) "
              "AppleWebKit/537.36 (KHTML, like Gecko) "
              "Chrome/44.0.2403.89 Safari/537.36")

        check = Check()
        check.save()
        r = self.client.get("/ping/%s/" % check.code, HTTP_USER_AGENT=ua)
        assert r.status_code == 200

        pings = list(Ping.objects.all())
        assert pings[0].ua == ua
コード例 #24
0
ファイル: test_profile.py プロジェクト: cogzidel/healthchecks
    def test_it_skips_nag_if_none_down(self):
        check = Check(name="Test Check", user=self.alice)
        check.last_ping = now()
        check.save()

        self.profile.nag_period = td(hours=1)
        self.profile.save()

        sent = self.profile.send_report(nag=True)
        self.assertFalse(sent)

        self.assertEqual(len(mail.outbox), 0)
コード例 #25
0
    def test_it_sends_report(self):
        check = Check(name="Test Check", user=self.alice)
        check.save()

        self.alice.profile.send_report()

        # And an email should have been sent
        self.assertEqual(len(mail.outbox), 1)
        message = mail.outbox[0]

        self.assertEqual(message.subject, 'Monthly Report')
        self.assertIn("Test Check", message.body)
コード例 #26
0
    def setUp(self):
        super(ListChecksTestCase, self).setUp()

        self.a1 = Check(user=self.alice, name="Alice 1")
        self.a1.timeout = td(seconds=3600)
        self.a1.grace = td(seconds=900)
        self.a1.save()

        self.a2 = Check(user=self.alice, name="Alice 2")
        self.a2.timeout = td(seconds=86400)
        self.a2.grace = td(seconds=3600)
        self.a2.save()
コード例 #27
0
ファイル: views.py プロジェクト: haswalt/healthchecks
def _welcome_check(request):
    check = None
    if "welcome_code" in request.session:
        code = request.session["welcome_code"]
        check = Check.objects.filter(code=code).first()

    if check is None:
        check = Check()
        check.save()
        request.session["welcome_code"] = str(check.code)

    return check
コード例 #28
0
class RemoveCheckTestCase(BaseTestCase):

    def setUp(self):
        super(RemoveCheckTestCase, self).setUp()
        self.check = Check(user=self.alice)
        self.check.save()

    def test_it_works(self):
        url = "/checks/%s/remove/" % self.check.code

        self.client.login(username="******", password="******")
        r = self.client.post(url)
        self.assertRedirects(r, "/checks/")

        assert Check.objects.count() == 0

    def test_team_access_works(self):
        url = "/checks/%s/remove/" % self.check.code

        # Logging in as bob, not alice. Bob has team access so this
        # should work.
        self.client.login(username="******", password="******")
        self.client.post(url)
        assert Check.objects.count() == 0

    def test_it_handles_bad_uuid(self):
        url = "/checks/not-uuid/remove/"

        self.client.login(username="******", password="******")
        r = self.client.post(url)
        assert r.status_code == 400

    def test_it_checks_owner(self):
        url = "/checks/%s/remove/" % self.check.code

        self.client.login(username="******", password="******")
        r = self.client.post(url)
        assert r.status_code == 403

    def test_it_handles_missing_uuid(self):
        # Valid UUID but there is no check for it:
        url = "/checks/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/remove/"

        self.client.login(username="******", password="******")
        r = self.client.post(url)
        assert r.status_code == 404

    def test_it_rejects_get(self):
        url = "/checks/%s/remove/" % self.check.code
        self.client.login(username="******", password="******")
        r = self.client.get(url)
        self.assertEqual(r.status_code, 405)
コード例 #29
0
class UpdateTimeoutTestCase(TestCase):

    def setUp(self):
        self.alice = User(username="******")
        self.alice.set_password("password")
        self.alice.save()

        self.check = Check(user=self.alice)
        self.check.save()

    def test_it_works(self):
        url = "/checks/%s/timeout/" % self.check.code
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 302

        check = Check.objects.get(code=self.check.code)
        assert check.timeout.total_seconds() == 3600
        assert check.grace.total_seconds() == 60

    def test_it_handles_bad_uuid(self):
        url = "/checks/not-uuid/timeout/"
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 400

    def test_it_handles_missing_uuid(self):
        # Valid UUID but there is no check for it:
        url = "/checks/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/timeout/"
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 404

    def test_it_checks_ownership(self):
        charlie = User(username="******")
        charlie.set_password("password")
        charlie.save()

        url = "/checks/%s/timeout/" % self.check.code
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 403
コード例 #30
0
class MyChecksTestCase(TestCase):

    def setUp(self):
        self.alice = User(username="******")
        self.alice.set_password("password")
        self.alice.save()

        self.check = Check(user=self.alice, name="Alice Was Here")
        self.check.save()

    def test_it_works(self):
        self.client.login(username="******", password="******")
        r = self.client.get("/checks/")
        self.assertContains(r, "Alice Was Here", status_code=200)
コード例 #31
0
    def test_it_works_synchronously(self, mock_notify):
        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)
        check.save()

        call_command("sendalerts",
                     loop=False,
                     use_threads=False,
                     stdout=StringIO())

        # It should call `notify` instead of `notify_on_thread`
        self.assertTrue(mock_notify.called)
コード例 #32
0
    def test_it_works(self):
        check = Check(user=self.alice, status="up")
        check.save()

        url = "/api/v1/checks/%s/pause" % check.code
        r = self.client.post(url, "", content_type="application/json",
                             HTTP_X_API_KEY="X" * 32)

        self.assertEqual(r.status_code, 200)
        self.assertEqual(r["Access-Control-Allow-Origin"], "*")

        check.refresh_from_db()
        self.assertEqual(check.status, "paused")
コード例 #33
0
class StatusSingleTestCase(BaseTestCase):
    def setUp(self):
        super(StatusSingleTestCase, self).setUp()
        self.check = Check(project=self.project, name="Alice Was Here")
        self.check.save()

    def test_it_works(self):
        self.client.login(username="******", password="******")
        r = self.client.get("/checks/%s/status/" % self.check.code)
        doc = r.json()

        self.assertEqual(doc["status"], "new")
        self.assertTrue("never received a ping" in doc["status_text"])
        self.assertTrue("not received any pings yet" in doc["events"])

    def test_it_returns_events(self):
        p = Ping.objects.create(owner=self.check, ua="test-user-agent", n=1)
        self.check.status = "up"
        self.check.last_ping = p.created
        self.check.save()

        self.client.login(username="******", password="******")
        r = self.client.get("/checks/%s/status/" % self.check.code)
        doc = r.json()

        self.assertEqual(doc["status"], "up")
        self.assertEqual(doc["updated"], p.created.strftime("%s.%f"))
        self.assertTrue("test-user-agent" in doc["events"])

    def test_it_omits_events(self):
        p = Ping.objects.create(owner=self.check, ua="test-user-agent", n=1)
        self.check.status = "up"
        self.check.last_ping = p.created
        self.check.save()

        timestamp = p.created.strftime("%s.%f")
        url = "/checks/%s/status/?u=%s" % (self.check.code, timestamp)

        self.client.login(username="******", password="******")
        r = self.client.get(url)
        doc = r.json()

        self.assertFalse("events" in doc)

    def test_it_allows_cross_team_access(self):
        self.bobs_profile.current_project = None
        self.bobs_profile.save()

        self.client.login(username="******", password="******")
        r = self.client.get("/checks/%s/status/" % self.check.code)
        self.assertEqual(r.status_code, 200)
コード例 #34
0
    def test_it_does_not_show_more_than_10_other_checks(self, mock_post):
        self._setup_data("123|0")
        mock_post.return_value.status_code = 200

        for i in range(0, 11):
            other = Check(project=self.project)
            other.name = f"Foobar #{i}"
            other.status = "down"
            other.last_ping = now() - td(minutes=61)
            other.save()

        self.channel.notify(self.check)

        args, kwargs = mock_post.call_args
        payload = kwargs["data"]
        self.assertNotIn("Foobar", payload["message"])
        self.assertIn("11 other checks are also down.", payload["message"])
コード例 #35
0
    def test_status_works_with_cron_syntax(self):
        dt = timezone.make_aware(datetime(2000, 1, 1), timezone=timezone.utc)

        # Expect ping every midnight, default grace is 1 hour
        check = Check()
        check.kind = "cron"
        check.schedule = "0 0 * * *"
        check.status = "up"
        check.last_ping = dt

        with patch("hc.api.models.now") as mock_now:
            # 23:59pm
            mock_now.return_value = dt + td(hours=23, minutes=59)
            self.assertEqual(check.get_status(), "up")

        with patch("hc.api.models.now") as mock_now:
            # 00:00am
            mock_now.return_value = dt + td(days=1)
            self.assertEqual(check.get_status(), "grace")

        with patch("hc.api.models.now") as mock_now:
            # 1:30am
            mock_now.return_value = dt + td(days=1, minutes=60)
            self.assertEqual(check.get_status(), "down")
コード例 #36
0
class PauseTestCase(BaseTestCase):

    def setUp(self):
        super(PauseTestCase, self).setUp()
        self.check = Check(user=self.alice, status="up")
        self.check.save()

        self.url = "/checks/%s/pause/" % self.check.code

    def test_it_pauses(self):
        self.client.login(username="******", password="******")
        r = self.client.post(self.url)
        self.assertRedirects(r, "/checks/")

        self.check.refresh_from_db()
        self.assertEqual(self.check.status, "paused")

    def test_it_rejects_get(self):
        self.client.login(username="******", password="******")
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 405)

    def test_it_allows_cross_team_access(self):
        self.bobs_profile.current_team = None
        self.bobs_profile.save()

        self.client.login(username="******", password="******")
        r = self.client.post(self.url)
        self.assertRedirects(r, "/checks/")

    def test_it_clears_last_start_alert_after(self):
        self.check.last_start = now()
        self.check.alert_after = self.check.last_start + td(hours=1)
        self.check.save()

        self.client.login(username="******", password="******")
        self.client.post(self.url)

        self.check.refresh_from_db()
        self.assertEqual(self.check.last_start, None)
        self.assertEqual(self.check.alert_after, None)
コード例 #37
0
    def test_it_creates_a_flip_when_check_goes_down(self):
        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)
        check.save()

        result = Command().handle_going_down()

        # If it finds work, it should return True
        self.assertTrue(result)

        # It should create a flip object
        flip = Flip.objects.get()
        self.assertEqual(flip.owner_id, check.id)
        self.assertEqual(flip.created, check.alert_after)
        self.assertEqual(flip.new_status, "down")

        # It should change stored status to "down", and clear out alert_after
        check.refresh_from_db()
        self.assertEqual(check.status, "down")
        self.assertEqual(check.alert_after, None)
コード例 #38
0
    def test_it_updates_members_next_nag_date(self):
        self.bobs_profile.nag_period = timedelta(hours=1)
        self.bobs_profile.save()

        check = Check(user=self.alice, status="down")
        check.last_ping = now() - timedelta(days=2)
        check.alert_after = check.get_alert_after()
        check.save()

        flip = Flip(owner=check, created=check.last_ping)
        flip.old_status = "up"
        flip.new_status = "down"
        flip.save()

        notify(flip.id, Mock())

        self.bobs_profile.refresh_from_db()
        self.assertIsNotNone(self.bobs_profile.next_nag_date)
コード例 #39
0
    def test_it_works(self):
        check = Check(user=self.alice, status="up")
        check.save()

        url = "/api/v1/checks/%s/pause" % check.code
        r = self.client.post(url,
                             "",
                             content_type="application/json",
                             HTTP_X_API_KEY="abc")

        # Assert the expected status code and check's status
        self.assertEqual(r.status_code, 200)
        # Assert the new database value of status is paused
        check.refresh_from_db()
        self.assertEqual(check.status, "paused")
コード例 #40
0
    def test_it_accepts_n(self):
        check = Check(user=self.alice)
        check.save()

        # remote_addr, scheme, method, ua, body:
        check.ping("1.2.3.4", "http", "post", "tester", "foo-123")
        check.ping("1.2.3.4", "http", "post", "tester", "bar-456")

        self.client.login(username="******", password="******")

        r = self.client.get("/checks/%s/pings/1/" % check.code)
        self.assertContains(r, "foo-123", status_code=200)

        r = self.client.get("/checks/%s/pings/2/" % check.code)
        self.assertContains(r, "bar-456", status_code=200)
コード例 #41
0
    def test_it_does_not_touch_already_set_next_nag_dates(self):
        original_nag_date = now() - timedelta(minutes=30)
        self.profile.nag_period = timedelta(hours=1)
        self.profile.next_nag_date = original_nag_date
        self.profile.save()

        check = Check(user=self.alice, status="down")
        check.last_ping = now() - timedelta(days=2)
        check.alert_after = check.get_alert_after()
        check.save()

        flip = Flip(owner=check, created=check.last_ping)
        flip.old_status = "up"
        flip.new_status = "down"
        flip.save()

        notify(flip.id, Mock())

        self.profile.refresh_from_db()
        self.assertEqual(self.profile.next_nag_date, original_nag_date)
コード例 #42
0
ファイル: test_sendalerts.py プロジェクト: andela/hc-ravens
    def test_it_handles_few(self, mock):
        yesterday = timezone.now() - timedelta(days=1)
        names = ["Check %d" % d for d in range(0,10)]
        
        for name in names:
            check = Check(user=self.alice, name=name)
            check.alert_after = yesterday
            check.status = "up"
            check.save()

        result = Command().handle_many()
        self.assertEqual(result, True)

        handled_names = []
        for args, kwargs in mock.call_args_list:
            handled_names.append(args[0].name)   
        self.assertEqual(len(names), len(handled_names))
コード例 #43
0
    def test_channel_assignment(self):

        check = Check()
        check.status = "up"
        check.user = self.alice
        check.save()

        channel = Channel(user=self.alice)
        channel.save()
        channel.checks.add(check)
        count_before = channel.checks.count()
        resp = self.post({
            "api_key": "abc",
            "channels": "*"
        })
        count_after = channel.checks.count()
        self.assertEqual((count_after - count_before), 1)
コード例 #44
0
    def test_downtimes_handles_months_when_check_did_not_exist(self):
        check = Check(project=self.project)
        check.created = datetime(2020, 1, 1, tzinfo=timezone.utc)
        check.save()

        nov, dec, jan = check.downtimes(3)

        # Nov. 2019
        self.assertIsNone(nov[1])
        self.assertIsNone(nov[2])

        # Dec. 2019
        self.assertIsNone(dec[1])
        self.assertIsNone(dec[2])

        # Jan. 2020
        self.assertEqual(jan[1], td())
        self.assertEqual(jan[2], 0)
コード例 #45
0
    def test_it_sends_nag(self):
        check = Check(name="Test Check", user=self.alice)
        check.status = "down"
        check.last_ping = now()
        check.save()

        self.profile.nag_period = td(hours=1)
        self.profile.save()

        sent = self.profile.send_report(nag=True)
        self.assertTrue(sent)

        # And an email should have been sent
        self.assertEqual(len(mail.outbox), 1)
        message = mail.outbox[0]

        self.assertEqual(message.subject, 'Reminder: 1 check still down')
        self.assertIn("Test Check", message.body)
コード例 #46
0
    def test_it_processes_flip(self, mock_notify):
        check = Check(user=self.alice, status="up")
        check.last_ping = now()
        check.alert_after = check.get_alert_after()
        check.save()

        flip = Flip(owner=check, created=check.last_ping)
        flip.old_status = "down"
        flip.new_status = "up"
        flip.save()

        result = Command().process_one_flip()

        # If it finds work, it should return True
        self.assertTrue(result)

        # It should set the processed date
        flip.refresh_from_db()
        self.assertTrue(flip.processed)

        # It should call `notify_on_thread`
        self.assertTrue(mock_notify.called)
コード例 #47
0
    def create_checks_in_db(self, checks: [dict], project_id: int):
        checks_to_create = []

        for check in checks:
            checks_to_create.append(
                Check(
                    name=check['name'],
                    tags=check['tags'],
                    desc=check['desc'],
                    grace=td(seconds=check['grace']),
                    n_pings=check['n_pings'],
                    last_ping=check['last_ping'],
                    timeout=td(seconds=check['timeout'])
                    if 'timeout' in check else DEFAULT_TIMEOUT,
                    tz=check['tz'] if 'tz' in check else 'UTC',
                    schedule=check['schedule']
                    if 'schedule' in check else "* * * * *",
                    code=check['ping_url'][check['ping_url'].find('.com/') +
                                           5:],
                    kind='simple' if 'timeout' in check else 'cron',
                    project_id=project_id,
                ))

        Check.objects.bulk_create(checks_to_create)
コード例 #48
0
    def test_it_handles_few(self, mock):
        yesterday = timezone.now() - timedelta(days=1)
        names = ["Check %d" % d for d in range(0, 10)]

        for name in names:
            check = Check(user=self.alice, name=name)
            check.alert_after = yesterday
            check.status = "up"
            check.save()

        ### Assert when Command's handle many that when handle_many should return True
        result = Command().handle_many()
        assert result, "handle_many should return True"

        handled_names = []
        for args, kwargs in mock.call_args_list:
            handled_names.append(args[0].name)

        assert set(names) == set(handled_names)
コード例 #49
0
    def test_it_handles_few(self, mock):
        alice = User(username="******")
        alice.save()

        names = ["Check %d" % d for d in range(0, 10)]

        for name in names:
            check = Check(user=alice, name=name)
            check.alert_after = datetime(2000, 1, 1)
            check.status = "up"
            check.save()

        result = handle_many()
        assert result, "handle_many should return True"

        handled_names = []
        for args, kwargs in mock.call_args_list:
            handled_names.append(args[0].name)

        assert set(names) == set(handled_names)
コード例 #50
0
    def test_downtimes_handles_no_flips(self):
        check = Check(project=self.project)
        check.created = datetime(2019, 1, 1, tzinfo=timezone.utc)
        check.save()

        nov, dec, jan = check.downtimes(3)

        # Nov. 2019
        self.assertEqual(nov[0].strftime("%m-%Y"), "11-2019")
        self.assertEqual(nov[1], td())
        self.assertEqual(nov[2], 0)

        # Dec. 2019
        self.assertEqual(dec[0].strftime("%m-%Y"), "12-2019")
        self.assertEqual(dec[1], td())
        self.assertEqual(dec[2], 0)

        # Jan. 2020
        self.assertEqual(jan[0].strftime("%m-%Y"), "01-2020")
        self.assertEqual(jan[1], td())
        self.assertEqual(jan[2], 0)
コード例 #51
0
class ListChecksTestCase(BaseTestCase):
    def setUp(self):
        super(ListChecksTestCase, self).setUp()

        self.now = now().replace(microsecond=0)

        self.a1 = Check(user=self.alice, name="Alice 1")
        self.a1.timeout = td(seconds=3600)
        self.a1.grace = td(seconds=900)
        self.a1.last_ping = self.now
        self.a1.n_pings = 1
        self.a1.status = "new"
        self.a1.tags = "a1-tag a1-additional-tag"
        self.a1.save()

        self.a2 = Check(user=self.alice, name="Alice 2")
        self.a2.timeout = td(seconds=86400)
        self.a2.grace = td(seconds=3600)
        self.a2.last_ping = self.now
        self.a2.status = "up"
        self.a2.tags = "a2-tag"
        self.a2.save()

        self.c1 = Channel.objects.create(user=self.alice)
        self.a1.channel_set.add(self.c1)

    def get(self):
        return self.client.get("/api/v1/checks/", HTTP_X_API_KEY="X" * 32)

    def test_it_works(self):
        r = self.get()
        self.assertEqual(r.status_code, 200)

        doc = r.json()
        self.assertEqual(len(doc["checks"]), 2)

        a1 = None
        a2 = None
        for check in doc["checks"]:
            if check["name"] == "Alice 1":
                a1 = check
            if check["name"] == "Alice 2":
                a2 = check

        self.assertEqual(a1["timeout"], 3600)
        self.assertEqual(a1["grace"], 900)
        self.assertEqual(a1["ping_url"], self.a1.url())
        self.assertEqual(a1["last_ping"], self.now.isoformat())
        self.assertEqual(a1["n_pings"], 1)
        self.assertEqual(a1["status"], "new")
        self.assertEqual(a1["channels"], str(self.c1.code))

        update_url = settings.SITE_ROOT + "/api/v1/checks/%s" % self.a1.code
        pause_url = update_url + "/pause"
        self.assertEqual(a1["update_url"], update_url)
        self.assertEqual(a1["pause_url"], pause_url)

        next_ping = self.now + td(seconds=3600)
        self.assertEqual(a1["next_ping"], next_ping.isoformat())

        self.assertEqual(a2["timeout"], 86400)
        self.assertEqual(a2["grace"], 3600)
        self.assertEqual(a2["ping_url"], self.a2.url())
        self.assertEqual(a2["status"], "up")

    def test_it_shows_only_users_checks(self):
        bobs_check = Check(user=self.bob, name="Bob 1")
        bobs_check.save()

        r = self.get()
        data = r.json()
        self.assertEqual(len(data["checks"]), 2)
        for check in data["checks"]:
            self.assertNotEqual(check["name"], "Bob 1")

    def test_it_accepts_api_key_from_request_body(self):
        payload = json.dumps({"api_key": "X" * 32})
        r = self.client.generic("GET",
                                "/api/v1/checks/",
                                payload,
                                content_type="application/json")

        self.assertEqual(r.status_code, 200)
        self.assertContains(r, "Alice")

    def test_it_works_with_tags_param(self):
        r = self.client.get("/api/v1/checks/?tag=a2-tag",
                            HTTP_X_API_KEY="X" * 32)
        self.assertEqual(r.status_code, 200)

        doc = r.json()
        self.assertTrue("checks" in doc)
        self.assertEqual(len(doc["checks"]), 1)

        check = doc["checks"][0]

        self.assertEqual(check["name"], "Alice 2")
        self.assertEqual(check["tags"], "a2-tag")

    def test_it_filters_with_multiple_tags_param(self):
        r = self.client.get("/api/v1/checks/?tag=a1-tag&tag=a1-additional-tag",
                            HTTP_X_API_KEY="X" * 32)
        self.assertEqual(r.status_code, 200)

        doc = r.json()
        self.assertTrue("checks" in doc)
        self.assertEqual(len(doc["checks"]), 1)

        check = doc["checks"][0]

        self.assertEqual(check["name"], "Alice 1")
        self.assertEqual(check["tags"], "a1-tag a1-additional-tag")

    def test_it_does_not_match_tag_partially(self):
        r = self.client.get("/api/v1/checks/?tag=tag", HTTP_X_API_KEY="X" * 32)
        self.assertEqual(r.status_code, 200)

        doc = r.json()
        self.assertTrue("checks" in doc)
        self.assertEqual(len(doc["checks"]), 0)

    def test_non_existing_tags_filter_returns_empty_result(self):
        r = self.client.get(
            "/api/v1/checks/?tag=non_existing_tag_with_no_checks",
            HTTP_X_API_KEY="X" * 32)
        self.assertEqual(r.status_code, 200)

        doc = r.json()
        self.assertTrue("checks" in doc)
        self.assertEqual(len(doc["checks"]), 0)

    def test_readonly_key_works(self):
        self.profile.api_key_readonly = "R" * 32
        self.profile.save()

        r = self.client.get("/api/v1/checks/", HTTP_X_API_KEY="R" * 32)
        self.assertEqual(r.status_code, 200)
コード例 #52
0
class NotifyTestCase(BaseTestCase):
    def _setup_data(self, kind, value, status="down", email_verified=True):
        self.check = Check()
        self.check.status = status
        self.check.user = self.alice
        self.check.save()

        self.channel = Channel(user=self.alice)
        self.channel.kind = kind
        self.channel.value = value
        self.channel.email_verified = email_verified
        self.channel.save()
        self.channel.checks.add(self.check)

    @patch("hc.api.transports.requests.request")
    def test_webhook(self, mock_get):
        self._setup_data("webhook", "http://example")
        mock_get.return_value.status_code = 200

        self.channel.notify(self.check)
        mock_get.assert_called_with("get",
                                    u"http://example",
                                    headers={"User-Agent": "healthchecks.io"},
                                    timeout=5)

    @patch("hc.api.transports.requests.request", side_effect=Timeout)
    def test_webhooks_handle_timeouts(self, mock_get):
        self._setup_data("webhook", "http://example")
        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Connection timed out")

    @patch("hc.api.transports.requests.request")
    def test_webhooks_ignore_up_events(self, mock_get):
        self._setup_data("webhook", "http://example", status="up")
        self.channel.notify(self.check)

        self.assertFalse(mock_get.called)
        self.assertEqual(Notification.objects.count(), 0)

    @patch("hc.api.transports.requests.request")
    def test_webhooks_support_variables(self, mock_get):
        template = "http://host/$CODE/$STATUS/$TAG1/$TAG2/?name=$NAME"
        self._setup_data("webhook", template)
        self.check.name = "Hello World"
        self.check.tags = "foo bar"
        self.check.save()

        self.channel.notify(self.check)

        url = u"http://host/%s/down/foo/bar/?name=Hello%%20World" \
            % self.check.code

        mock_get.assert_called_with("get",
                                    url,
                                    headers={"User-Agent": "healthchecks.io"},
                                    timeout=5)

    @patch("hc.api.transports.requests.request")
    def test_webhooks_dollarsign_escaping(self, mock_get):
        # If name or tag contains what looks like a variable reference,
        # that should be left alone:

        template = "http://host/$NAME"
        self._setup_data("webhook", template)
        self.check.name = "$TAG1"
        self.check.tags = "foo"
        self.check.save()

        self.channel.notify(self.check)

        url = u"http://host/%24TAG1"
        mock_get.assert_called_with("get",
                                    url,
                                    headers={"User-Agent": "healthchecks.io"},
                                    timeout=5)

    @patch("hc.api.transports.requests.request")
    def test_webhook_fires_on_up_event(self, mock_get):
        self._setup_data("webhook", "http://foo\nhttp://bar", status="up")

        self.channel.notify(self.check)

        mock_get.assert_called_with("get",
                                    "http://bar",
                                    headers={"User-Agent": "healthchecks.io"},
                                    timeout=5)

    def test_email(self):
        self._setup_data("email", "*****@*****.**")
        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "")

        # And email should have been sent
        self.assertEqual(len(mail.outbox), 1)

    def test_it_skips_unverified_email(self):
        self._setup_data("email", "*****@*****.**", email_verified=False)
        self.channel.notify(self.check)

        assert Notification.objects.count() == 1
        n = Notification.objects.first()
        self.assertEqual(n.error, "Email not verified")
        self.assertEqual(len(mail.outbox), 0)

    @override_settings(USE_PAYMENTS=True)
    def test_email_contains_upgrade_notice(self):
        self._setup_data("email", "*****@*****.**", status="up")
        self.profile.team_access_allowed = False
        self.profile.save()

        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "")

        # Check is up, payments are enabled, and the user does not have team
        # access: the email should contain upgrade note
        message = mail.outbox[0]
        html, _ = message.alternatives[0]
        assert "/pricing/" in html

    @patch("hc.api.transports.requests.request")
    def test_pd(self, mock_post):
        self._setup_data("pd", "123")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        json = kwargs["json"]
        self.assertEqual(json["event_type"], "trigger")

    @patch("hc.api.transports.requests.request")
    def test_slack(self, mock_post):
        self._setup_data("slack", "123")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        json = kwargs["json"]
        attachment = json["attachments"][0]
        fields = {f["title"]: f["value"] for f in attachment["fields"]}
        self.assertEqual(fields["Last Ping"], "Never")

    @patch("hc.api.transports.requests.request")
    def test_slack_with_complex_value(self, mock_post):
        v = json.dumps({"incoming_webhook": {"url": "123"}})
        self._setup_data("slack", v)
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        self.assertEqual(args[1], "123")

    @patch("hc.api.transports.requests.request")
    def test_slack_handles_500(self, mock_post):
        self._setup_data("slack", "123")
        mock_post.return_value.status_code = 500

        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Received status code 500")

    @patch("hc.api.transports.requests.request", side_effect=Timeout)
    def test_slack_handles_timeout(self, mock_post):
        self._setup_data("slack", "123")

        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Connection timed out")

    @patch("hc.api.transports.requests.request")
    def test_hipchat(self, mock_post):
        self._setup_data("hipchat", "123")
        mock_post.return_value.status_code = 204

        self.channel.notify(self.check)
        n = Notification.objects.first()
        self.assertEqual(n.error, "")

        args, kwargs = mock_post.call_args
        json = kwargs["json"]
        self.assertIn("DOWN", json["message"])

    @patch("hc.api.transports.requests.request")
    def test_pushover(self, mock_post):
        self._setup_data("po", "123|0")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        json = kwargs["data"]
        self.assertIn("DOWN", json["title"])

    @patch("hc.api.transports.requests.request")
    def test_victorops(self, mock_post):
        self._setup_data("victorops", "123")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        json = kwargs["json"]
        self.assertEqual(json["message_type"], "CRITICAL")

    ### Test that the web hooks handle connection errors and error 500s

    @patch("hc.api.transports.requests.request")
    def test_webhook(self, mock_get):
        self._setup_data("webhook", "http://example")
        mock_get.return_value.status_code = 500
        self.channel.notify(self.check)
        all_notifications = Notification.objects.get()
        self.assertEqual(all_notifications.error, 'Received status code 500')

    @patch("hc.api.transports.requests.request", side_effect=Timeout)
    def test_connection_time_out(self, mock_get):
        self._setup_data("webhook", "http://example")
        self.channel.notify(self.check)
        all_notifications = Notification.objects.get()
        self.assertEqual(all_notifications.error, "Connection timed out")

    @patch("hc.api.transports.requests.request", side_effect=ConnectionError)
    def test_connection_error(self, mock_get):
        self._setup_data("webhook", "http://example")
        self.channel.notify(self.check)
        all_notifications = Notification.objects.get()
        self.assertEqual(all_notifications.error, "Connection failed")
コード例 #53
0
 def setUp(self):
     super(DeleteCheckTestCase, self).setUp()
     self.check = Check(user=self.alice)
     self.check.save()
コード例 #54
0
ファイル: test_notify.py プロジェクト: sjl421/healthchecks
class NotifyTestCase(BaseTestCase):
    def _setup_data(self, kind, value, status="down", email_verified=True):
        self.check = Check()
        self.check.status = status
        self.check.user = self.alice
        self.check.last_ping = now() - td(minutes=61)
        self.check.save()

        self.channel = Channel(user=self.alice)
        self.channel.kind = kind
        self.channel.value = value
        self.channel.email_verified = email_verified
        self.channel.save()
        self.channel.checks.add(self.check)

    @patch("hc.api.transports.requests.request")
    def test_webhook(self, mock_get):
        self._setup_data("webhook", "http://example")
        mock_get.return_value.status_code = 200

        self.channel.notify(self.check)
        mock_get.assert_called_with("get",
                                    u"http://example",
                                    headers={"User-Agent": "healthchecks.io"},
                                    timeout=5)

    @patch("hc.api.transports.requests.request", side_effect=Timeout)
    def test_webhooks_handle_timeouts(self, mock_get):
        self._setup_data("webhook", "http://example")
        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Connection timed out")

    @patch("hc.api.transports.requests.request", side_effect=ConnectionError)
    def test_webhooks_handle_connection_errors(self, mock_get):
        self._setup_data("webhook", "http://example")
        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Connection failed")

    @patch("hc.api.transports.requests.request")
    def test_webhooks_ignore_up_events(self, mock_get):
        self._setup_data("webhook", "http://example", status="up")
        self.channel.notify(self.check)

        self.assertFalse(mock_get.called)
        self.assertEqual(Notification.objects.count(), 0)

    @patch("hc.api.transports.requests.request")
    def test_webhooks_handle_500(self, mock_get):
        self._setup_data("webhook", "http://example")
        mock_get.return_value.status_code = 500

        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Received status code 500")

    @patch("hc.api.transports.requests.request")
    def test_webhooks_support_variables(self, mock_get):
        template = "http://host/$CODE/$STATUS/$TAG1/$TAG2/?name=$NAME"
        self._setup_data("webhook", template)
        self.check.name = "Hello World"
        self.check.tags = "foo bar"
        self.check.save()

        self.channel.notify(self.check)

        url = u"http://host/%s/down/foo/bar/?name=Hello%%20World" \
            % self.check.code

        args, kwargs = mock_get.call_args
        self.assertEqual(args[0], "get")
        self.assertEqual(args[1], url)
        self.assertEqual(kwargs["headers"], {"User-Agent": "healthchecks.io"})
        self.assertEqual(kwargs["timeout"], 5)

    @patch("hc.api.transports.requests.request")
    def test_webhooks_support_post(self, mock_request):
        template = "http://example.com\n\nThe Time Is $NOW"
        self._setup_data("webhook", template)
        self.check.save()

        self.channel.notify(self.check)
        args, kwargs = mock_request.call_args
        self.assertEqual(args[0], "post")
        self.assertEqual(args[1], "http://example.com")

        # spaces should not have been urlencoded:
        payload = kwargs["data"].decode("utf-8")
        self.assertTrue(payload.startswith("The Time Is 2"))

    @patch("hc.api.transports.requests.request")
    def test_webhooks_dollarsign_escaping(self, mock_get):
        # If name or tag contains what looks like a variable reference,
        # that should be left alone:

        template = "http://host/$NAME"
        self._setup_data("webhook", template)
        self.check.name = "$TAG1"
        self.check.tags = "foo"
        self.check.save()

        self.channel.notify(self.check)

        url = u"http://host/%24TAG1"
        mock_get.assert_called_with("get",
                                    url,
                                    headers={"User-Agent": "healthchecks.io"},
                                    timeout=5)

    @patch("hc.api.transports.requests.request")
    def test_webhook_fires_on_up_event(self, mock_get):
        self._setup_data("webhook", "http://foo\nhttp://bar", status="up")

        self.channel.notify(self.check)

        mock_get.assert_called_with("get",
                                    "http://bar",
                                    headers={"User-Agent": "healthchecks.io"},
                                    timeout=5)

    @patch("hc.api.transports.requests.request")
    def test_webhooks_handle_unicode_post_body(self, mock_request):
        template = u"http://example.com\n\n(╯°□°)╯︵ ┻━┻"
        self._setup_data("webhook", template)
        self.check.save()

        self.channel.notify(self.check)
        args, kwargs = mock_request.call_args

        # unicode should be encoded into utf-8
        self.assertTrue(isinstance(kwargs["data"], binary_type))

    @patch("hc.api.transports.requests.request")
    def test_webhooks_handle_json_value(self, mock_request):
        definition = {"url_down": "http://foo.com"}
        self._setup_data("webhook", json.dumps(definition))
        self.channel.notify(self.check)

        headers = {"User-Agent": "healthchecks.io"}
        mock_request.assert_called_with("get",
                                        "http://foo.com",
                                        headers=headers,
                                        timeout=5)

    @patch("hc.api.transports.requests.request")
    def test_webhooks_handle_json_up_event(self, mock_request):
        definition = {"url_up": "http://bar"}

        self._setup_data("webhook", json.dumps(definition), status="up")
        self.channel.notify(self.check)

        headers = {"User-Agent": "healthchecks.io"}
        mock_request.assert_called_with("get",
                                        "http://bar",
                                        headers=headers,
                                        timeout=5)

    @patch("hc.api.transports.requests.request")
    def test_webhooks_handle_post_headers(self, mock_request):
        definition = {
            "url_down": "http://foo.com",
            "post_data": "data",
            "headers": {
                "Content-Type": "application/json"
            }
        }

        self._setup_data("webhook", json.dumps(definition))
        self.channel.notify(self.check)

        headers = {
            "User-Agent": "healthchecks.io",
            "Content-Type": "application/json"
        }
        mock_request.assert_called_with("post",
                                        "http://foo.com",
                                        data=b"data",
                                        headers=headers,
                                        timeout=5)

    @patch("hc.api.transports.requests.request")
    def test_webhooks_handle_get_headers(self, mock_request):
        definition = {
            "url_down": "http://foo.com",
            "headers": {
                "Content-Type": "application/json"
            }
        }

        self._setup_data("webhook", json.dumps(definition))
        self.channel.notify(self.check)

        headers = {
            "User-Agent": "healthchecks.io",
            "Content-Type": "application/json"
        }
        mock_request.assert_called_with("get",
                                        "http://foo.com",
                                        headers=headers,
                                        timeout=5)

    @patch("hc.api.transports.requests.request")
    def test_webhooks_allow_user_agent_override(self, mock_request):
        definition = {
            "url_down": "http://foo.com",
            "headers": {
                "User-Agent": "My-Agent"
            }
        }

        self._setup_data("webhook", json.dumps(definition))
        self.channel.notify(self.check)

        headers = {"User-Agent": "My-Agent"}
        mock_request.assert_called_with("get",
                                        "http://foo.com",
                                        headers=headers,
                                        timeout=5)

    @patch("hc.api.transports.requests.request")
    def test_webhooks_support_variables_in_headers(self, mock_request):
        definition = {
            "url_down": "http://foo.com",
            "headers": {
                "X-Message": "$NAME is DOWN"
            }
        }

        self._setup_data("webhook", json.dumps(definition))
        self.check.name = "Foo"
        self.check.save()

        self.channel.notify(self.check)

        headers = {"User-Agent": "healthchecks.io", "X-Message": "Foo is DOWN"}
        mock_request.assert_called_with("get",
                                        "http://foo.com",
                                        headers=headers,
                                        timeout=5)

    def test_email(self):
        self._setup_data("email", "*****@*****.**")
        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "")

        # And email should have been sent
        self.assertEqual(len(mail.outbox), 1)

        email = mail.outbox[0]
        self.assertTrue("X-Bounce-Url" in email.extra_headers)

    def test_it_skips_unverified_email(self):
        self._setup_data("email", "*****@*****.**", email_verified=False)
        self.channel.notify(self.check)

        # If an email is not verified, it should be skipped over
        # without logging a notification:
        self.assertEqual(Notification.objects.count(), 0)
        self.assertEqual(len(mail.outbox), 0)

    @patch("hc.api.transports.requests.request")
    def test_pd(self, mock_post):
        self._setup_data("pd", "123")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["json"]
        self.assertEqual(payload["event_type"], "trigger")
        self.assertEqual(payload["service_key"], "123")

    @patch("hc.api.transports.requests.request")
    def test_pd_complex(self, mock_post):
        self._setup_data("pd", json.dumps({"service_key": "456"}))
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["json"]
        self.assertEqual(payload["event_type"], "trigger")
        self.assertEqual(payload["service_key"], "456")

    @patch("hc.api.transports.requests.request")
    def test_pagertree(self, mock_post):
        self._setup_data("pagertree", "123")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["json"]
        self.assertEqual(payload["event_type"], "trigger")

    @patch("hc.api.transports.requests.request")
    def test_slack(self, mock_post):
        self._setup_data("slack", "123")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["json"]
        attachment = payload["attachments"][0]
        fields = {f["title"]: f["value"] for f in attachment["fields"]}
        self.assertEqual(fields["Last Ping"], "an hour ago")

    @patch("hc.api.transports.requests.request")
    def test_slack_with_complex_value(self, mock_post):
        v = json.dumps({"incoming_webhook": {"url": "123"}})
        self._setup_data("slack", v)
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        self.assertEqual(args[1], "123")

    @patch("hc.api.transports.requests.request")
    def test_slack_handles_500(self, mock_post):
        self._setup_data("slack", "123")
        mock_post.return_value.status_code = 500

        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Received status code 500")

    @patch("hc.api.transports.requests.request", side_effect=Timeout)
    def test_slack_handles_timeout(self, mock_post):
        self._setup_data("slack", "123")

        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Connection timed out")

    @patch("hc.api.transports.requests.request")
    def test_slack_with_tabs_in_schedule(self, mock_post):
        self._setup_data("slack", "123")
        self.check.kind = "cron"
        self.check.schedule = "*\t* * * *"
        self.check.save()
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        self.assertEqual(Notification.objects.count(), 1)
        self.assertTrue(mock_post.called)

    @patch("hc.api.transports.requests.request")
    def test_hipchat(self, mock_post):
        self._setup_data("hipchat", "123")
        mock_post.return_value.status_code = 204

        self.channel.notify(self.check)
        n = Notification.objects.first()
        self.assertEqual(n.error, "")

        args, kwargs = mock_post.call_args
        payload = kwargs["json"]
        self.assertIn("DOWN", payload["message"])

    @patch("hc.api.transports.requests.request")
    def test_opsgenie(self, mock_post):
        self._setup_data("opsgenie", "123")
        mock_post.return_value.status_code = 202

        self.channel.notify(self.check)
        n = Notification.objects.first()
        self.assertEqual(n.error, "")

        self.assertEqual(mock_post.call_count, 1)
        args, kwargs = mock_post.call_args
        payload = kwargs["json"]
        self.assertIn("DOWN", payload["message"])

    @patch("hc.api.transports.requests.request")
    def test_opsgenie_up(self, mock_post):
        self._setup_data("opsgenie", "123", status="up")
        mock_post.return_value.status_code = 202

        self.channel.notify(self.check)
        n = Notification.objects.first()
        self.assertEqual(n.error, "")

        self.assertEqual(mock_post.call_count, 1)
        args, kwargs = mock_post.call_args
        method, url = args
        self.assertTrue(str(self.check.code) in url)

    @patch("hc.api.transports.requests.request")
    def test_pushover(self, mock_post):
        self._setup_data("po", "123|0")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["data"]
        self.assertIn("DOWN", payload["title"])

    @patch("hc.api.transports.requests.request")
    def test_victorops(self, mock_post):
        self._setup_data("victorops", "123")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["json"]
        self.assertEqual(payload["message_type"], "CRITICAL")

    @patch("hc.api.transports.requests.request")
    def test_discord(self, mock_post):
        v = json.dumps({"webhook": {"url": "123"}})
        self._setup_data("discord", v)
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["json"]
        attachment = payload["attachments"][0]
        fields = {f["title"]: f["value"] for f in attachment["fields"]}
        self.assertEqual(fields["Last Ping"], "an hour ago")

    @patch("hc.api.transports.requests.request")
    def test_pushbullet(self, mock_post):
        self._setup_data("pushbullet", "fake-token")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        _, kwargs = mock_post.call_args
        self.assertEqual(kwargs["json"]["type"], "note")
        self.assertEqual(kwargs["headers"]["Access-Token"], "fake-token")

    @patch("hc.api.transports.requests.request")
    def test_telegram(self, mock_post):
        v = json.dumps({"id": 123})
        self._setup_data("telegram", v)
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["json"]
        self.assertEqual(payload["chat_id"], 123)
        self.assertTrue("The check" in payload["text"])

    @patch("hc.api.transports.requests.request")
    def test_sms(self, mock_post):
        self._setup_data("sms", "+1234567890")
        self.check.last_ping = now() - td(hours=2)

        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["data"]
        self.assertEqual(payload["To"], "+1234567890")
        self.assertFalse(u"\xa0" in payload["Body"])

        # sent SMS counter should go up
        self.profile.refresh_from_db()
        self.assertEqual(self.profile.sms_sent, 1)

    @patch("hc.api.transports.requests.request")
    def test_sms_handles_json_value(self, mock_post):
        value = {"label": "foo", "value": "+1234567890"}
        self._setup_data("sms", json.dumps(value))
        self.check.last_ping = now() - td(hours=2)

        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["data"]
        self.assertEqual(payload["To"], "+1234567890")

    @patch("hc.api.transports.requests.request")
    def test_sms_limit(self, mock_post):
        # At limit already:
        self.profile.last_sms_date = now()
        self.profile.sms_sent = 50
        self.profile.save()

        self._setup_data("sms", "+1234567890")

        self.channel.notify(self.check)
        self.assertFalse(mock_post.called)

        n = Notification.objects.get()
        self.assertTrue("Monthly SMS limit exceeded" in n.error)

    @patch("hc.api.transports.requests.request")
    def test_sms_limit_reset(self, mock_post):
        # At limit, but also into a new month
        self.profile.sms_sent = 50
        self.profile.last_sms_date = now() - td(days=100)
        self.profile.save()

        self._setup_data("sms", "+1234567890")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        self.assertTrue(mock_post.called)

    @patch("hc.api.transports.requests.request")
    def test_zendesk_down(self, mock_post):
        v = json.dumps({"access_token": "fake-token", "subdomain": "foo"})
        self._setup_data("zendesk", v)
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        method, url = args
        self.assertEqual(method, "post")
        self.assertTrue("foo.zendesk.com" in url)

        payload = kwargs["json"]
        self.assertEqual(payload["request"]["type"], "incident")
        self.assertTrue("down" in payload["request"]["subject"])

        headers = kwargs["headers"]
        self.assertEqual(headers["Authorization"], "Bearer fake-token")

    @patch("hc.api.transports.requests.request")
    @patch("hc.api.transports.requests.get")
    def test_zendesk_up(self, mock_get, mock_post):
        v = json.dumps({"access_token": "fake-token", "subdomain": "foo"})
        self._setup_data("zendesk", v, status="up")

        mock_post.return_value.status_code = 200
        mock_get.return_value.status_code = 200
        mock_get.return_value.json.return_value = {
            "requests": [{
                "url": "https://foo.example.org/comment",
                "description": "code is %s" % self.check.code
            }]
        }

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        self.assertTrue("foo.example.org" in args[1])

        payload = kwargs["json"]
        self.assertEqual(payload["request"]["type"], "incident")
        self.assertTrue("UP" in payload["request"]["subject"])

        headers = kwargs["headers"]
        self.assertEqual(headers["Authorization"], "Bearer fake-token")

    @patch("hc.api.transports.requests.request")
    @patch("hc.api.transports.requests.get")
    def test_zendesk_up_with_no_existing_ticket(self, mock_get, mock_post):
        v = json.dumps({"access_token": "fake-token", "subdomain": "foo"})
        self._setup_data("zendesk", v, status="up")

        mock_get.return_value.status_code = 200
        mock_get.return_value.json.return_value = {"requests": []}

        self.channel.notify(self.check)
        n = Notification.objects.get()
        self.assertEqual(n.error, "Could not find a ticket to update")

        self.assertFalse(mock_post.called)
コード例 #55
0
class NotifyTestCase(BaseTestCase):
    def _setup_data(self, kind, value, status="down", email_verified=True):
        self.check = Check(project=self.project)
        self.check.status = status
        self.check.last_ping = now() - td(minutes=61)
        self.check.save()

        self.channel = Channel(project=self.project)
        self.channel.kind = kind
        self.channel.value = value
        self.channel.email_verified = email_verified
        self.channel.save()
        self.channel.checks.add(self.check)

    @patch("hc.api.transports.requests.request")
    def test_zulip(self, mock_post):
        definition = {
            "bot_email": "*****@*****.**",
            "api_key": "fake-key",
            "mtype": "stream",
            "to": "general",
        }
        self._setup_data("zulip", json.dumps(definition))
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args

        method, url = args
        self.assertEqual(url, "https://example.org/api/v1/messages")

        payload = kwargs["data"]
        self.assertIn("DOWN", payload["topic"])

    @patch("hc.api.transports.requests.request")
    def test_zulip_returns_error(self, mock_post):
        definition = {
            "bot_email": "*****@*****.**",
            "api_key": "fake-key",
            "mtype": "stream",
            "to": "general",
        }
        self._setup_data("zulip", json.dumps(definition))
        mock_post.return_value.status_code = 403
        mock_post.return_value.json.return_value = {"msg": "Nice try"}

        self.channel.notify(self.check)

        n = Notification.objects.first()
        self.assertEqual(
            n.error, 'Received status code 403 with a message: "Nice try"')

    @patch("hc.api.transports.requests.request")
    def test_zulip_uses_site_parameter(self, mock_post):
        definition = {
            "bot_email": "*****@*****.**",
            "site": "https://custom.example.org",
            "api_key": "fake-key",
            "mtype": "stream",
            "to": "general",
        }
        self._setup_data("zulip", json.dumps(definition))
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args

        method, url = args
        self.assertEqual(url, "https://custom.example.org/api/v1/messages")

        payload = kwargs["data"]
        self.assertIn("DOWN", payload["topic"])
コード例 #56
0
    def test_it_sends_report(self):
        check = Check(name="Test Check", user=self.alice)
        check.save()

        self.alice.profile.send_report()
コード例 #57
0
 def setUp(self):
     super(PauseTestCase, self).setUp()
     self.check = Check(user=self.alice, status="up")
     self.check.save()
コード例 #58
0
ファイル: views.py プロジェクト: andela/hc-anansi
def checks(request):
    if request.method == "GET":
        q = Check.objects.filter(user=request.user)
        doc = {"checks": [check.to_dict() for check in q]}
        return JsonResponse(doc)

    elif request.method == "POST":
        check = Check(user=request.user)
        check.name = str(request.json.get("name", ""))
        check.tags = str(request.json.get("tags", ""))
        if "timeout" in request.json:
            check.timeout = td(seconds=request.json["timeout"])
        if "grace" in request.json:
            check.grace = td(seconds=request.json["grace"])

        check.save()

        # This needs to be done after saving the check, because of
        # the M2M relation between checks and channels:
        if request.json.get("channels") == "*":
            check.assign_all_channels()

        return JsonResponse(check.to_dict(), status=201)

    # If request is neither GET nor POST, return "405 Method not allowed"
    return HttpResponse(status=405)
コード例 #59
0
class NotifyTestCase(BaseTestCase):
    def _setup_data(self, value, status="down", email_verified=True):
        self.check = Check(project=self.project)
        self.check.status = status
        self.check.last_ping = now() - td(minutes=61)
        self.check.save()

        self.channel = Channel(project=self.project)
        self.channel.kind = "slack"
        self.channel.value = value
        self.channel.email_verified = email_verified
        self.channel.save()
        self.channel.checks.add(self.check)

    @patch("hc.api.transports.requests.request")
    def test_slack(self, mock_post):
        self._setup_data("123")
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        payload = kwargs["json"]
        attachment = payload["attachments"][0]
        fields = {f["title"]: f["value"] for f in attachment["fields"]}
        self.assertEqual(fields["Last Ping"], "an hour ago")

        uncloak_url = "/cloaked/%s/" % self.check.unique_key
        self.assertTrue(attachment["title_link"].endswith(uncloak_url))

    @patch("hc.api.transports.requests.request")
    def test_slack_with_complex_value(self, mock_post):
        v = json.dumps({"incoming_webhook": {"url": "123"}})
        self._setup_data(v)
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        assert Notification.objects.count() == 1

        args, kwargs = mock_post.call_args
        self.assertEqual(args[1], "123")

    @patch("hc.api.transports.requests.request")
    def test_slack_handles_500(self, mock_post):
        self._setup_data("123")
        mock_post.return_value.status_code = 500

        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Received status code 500")

    @patch("hc.api.transports.requests.request", side_effect=Timeout)
    def test_slack_handles_timeout(self, mock_post):
        self._setup_data("123")

        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Connection timed out")

    @patch("hc.api.transports.requests.request")
    def test_slack_with_tabs_in_schedule(self, mock_post):
        self._setup_data("123")
        self.check.kind = "cron"
        self.check.schedule = "*\t* * * *"
        self.check.save()
        mock_post.return_value.status_code = 200

        self.channel.notify(self.check)
        self.assertEqual(Notification.objects.count(), 1)
        self.assertTrue(mock_post.called)

    @override_settings(SLACK_ENABLED=False)
    def test_it_requires_slack_enabled(self):
        self._setup_data("123")
        self.channel.notify(self.check)

        n = Notification.objects.get()
        self.assertEqual(n.error, "Slack notifications are not enabled.")
コード例 #60
0
class UpdateTimeoutTestCase(BaseTestCase):

    def setUp(self):
        super(UpdateTimeoutTestCase, self).setUp()
        self.check = Check(project=self.project, status="up")
        self.check.last_ping = timezone.now()
        self.check.save()

        self.url = "/checks/%s/timeout/" % self.check.code

    def test_it_works(self):
        payload = {"kind": "simple", "timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(self.url, data=payload)
        self.assertRedirects(r, "/checks/")

        self.check.refresh_from_db()
        self.assertEqual(self.check.kind, "simple")
        self.assertEqual(self.check.timeout.total_seconds(), 3600)
        self.assertEqual(self.check.grace.total_seconds(), 60)

        # alert_after should be updated too
        expected_aa = self.check.last_ping + td(seconds=3600 + 60)
        self.assertEqual(self.check.alert_after, expected_aa)

    def test_it_does_not_update_status(self):
        self.check.last_ping = timezone.now() - td(days=2)
        self.check.status = "down"
        self.check.save()

        # 1 week:
        payload = {"kind": "simple", "timeout": 3600 * 24 * 7, "grace": 60}

        self.client.login(username="******", password="******")
        self.client.post(self.url, data=payload)

        self.check.refresh_from_db()
        self.assertEqual(self.check.status, "down")

    def test_it_saves_cron_expression(self):
        payload = {
            "kind": "cron",
            "schedule": "5 * * * *",
            "tz": "UTC",
            "grace": 60
        }

        self.client.login(username="******", password="******")
        r = self.client.post(self.url, data=payload)
        self.assertRedirects(r, "/checks/")

        self.check.refresh_from_db()
        self.assertEqual(self.check.kind, "cron")
        self.assertEqual(self.check.schedule, "5 * * * *")

    def test_it_validates_cron_expression(self):
        self.client.login(username="******", password="******")
        samples = ["* invalid *", "1,2 3,* * * *"]

        for sample in samples:
            payload = {
                "kind": "cron",
                "schedule": sample,
                "tz": "UTC",
                "grace": 60
            }

            r = self.client.post(self.url, data=payload)
            self.assertEqual(r.status_code, 400)

        # Check should still have its original data:
        self.check.refresh_from_db()
        self.assertEqual(self.check.kind, "simple")

    def test_it_rejects_six_field_cron_expression(self):
        payload = {
            "kind": "cron",
            "schedule": "* * * * * *",  # six fields instead of five
            "tz": "UTC",
            "grace": 60
        }

        self.client.login(username="******", password="******")
        r = self.client.post(self.url, data=payload)
        self.assertEqual(r.status_code, 400)

        # Check should still have its original data:
        self.check.refresh_from_db()
        self.assertEqual(self.check.kind, "simple")

    def test_it_validates_tz(self):
        payload = {
            "kind": "cron",
            "schedule": "* * * * *",
            "tz": "not-a-tz",
            "grace": 60
        }

        self.client.login(username="******", password="******")
        r = self.client.post(self.url, data=payload)
        self.assertEqual(r.status_code, 400)

        # Check should still have its original data:
        self.check.refresh_from_db()
        self.assertEqual(self.check.kind, "simple")

    def test_it_rejects_missing_schedule(self):
        # tz field is omitted so this should fail:
        payload = {
            "kind": "cron",
            "grace": 60,
            "tz": "UTC"
        }

        self.client.login(username="******", password="******")
        r = self.client.post(self.url, data=payload)
        self.assertEqual(r.status_code, 400)

    def test_it_rejects_missing_tz(self):
        # tz field is omitted so this should fail:
        payload = {
            "kind": "cron",
            "schedule": "* * * * *",
            "grace": 60
        }

        self.client.login(username="******", password="******")
        r = self.client.post(self.url, data=payload)
        self.assertEqual(r.status_code, 400)

    def test_team_access_works(self):
        payload = {"kind": "simple", "timeout": 7200, "grace": 60}

        # Logging in as bob, not alice. Bob has team access so this
        # should work.
        self.client.login(username="******", password="******")
        self.client.post(self.url, data=payload)

        check = Check.objects.get(code=self.check.code)
        assert check.timeout.total_seconds() == 7200

    def test_it_handles_bad_uuid(self):
        url = "/checks/not-uuid/timeout/"
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        self.assertEqual(r.status_code, 404)

    def test_it_handles_missing_uuid(self):
        # Valid UUID but there is no check for it:
        url = "/checks/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/timeout/"
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(url, data=payload)
        assert r.status_code == 404

    def test_it_checks_ownership(self):
        payload = {"timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(self.url, data=payload)
        self.assertEqual(r.status_code, 404)

    def test_it_rejects_get(self):
        self.client.login(username="******", password="******")
        r = self.client.get(self.url)
        self.assertEqual(r.status_code, 405)

    def test_it_allows_cross_team_access(self):
        self.bobs_profile.current_project = None
        self.bobs_profile.save()

        payload = {"kind": "simple", "timeout": 3600, "grace": 60}

        self.client.login(username="******", password="******")
        r = self.client.post(self.url, data=payload)
        self.assertRedirects(r, "/checks/")