webhooks/beeminder: Mock time.time() to avoid race.

Rewritten in significant part by tabbott to actually be correct.

One particularly nasty thing the original webhook integration did is
do `current_time = time.time()` at the top of the `view.py` function
-- that means that code ran at import time, not runtime.
This commit is contained in:
Ricky 2018-03-07 23:48:23 +05:30 committed by Tim Abbott
parent 713ca1e5f2
commit 7c830c5767
2 changed files with 31 additions and 27 deletions

View File

@ -1,40 +1,42 @@
# -*- coding: utf-8 -*-
import time
from typing import Text
from unittest.mock import patch
from typing import Text, Any
from zerver.lib.test_classes import WebhookTestCase
class BeeminderHookTests(WebhookTestCase):
STREAM_NAME = 'beeminder'
URL_TEMPLATE = u"/api/v1/external/beeminder?api_key={api_key}&email=AARON@zulip.com"
def test_beeminder_derail(self) -> None:
# Important: These checks need to be in the test to avoid a race.
# TODO: We should mock time.time for these tests
current_time = float(time.time())
time_delta_in_hours = (float(1517759100) - current_time)/float(3600)
@patch('zerver.webhooks.beeminder.view.time.time')
def test_beeminder_derail(self, time: Any) -> None:
time.return_value = 1517739100 # 5.6 hours from fixture value
expected_subject = u"beekeeper"
expected_message = u"Hello **aaron**! I am the Beeminder bot! :octopus: \n You are going to derail \
from goal **gainweight** in **{:0.1f} hours** \n You need **+2 in 7 days (60)** to avoid derailing \n * Pledge: **0$** :relieved:".format(time_delta_in_hours)
expected_message = '\n'.join([
'Hello **aaron**! I am the Beeminder bot! :octopus:',
' You are going to derail from goal **gainweight** in **{:0.1f} hours**'.format(5.6),
' You need **+2 in 7 days (60)** to avoid derailing',
' * Pledge: **0$** :relieved:'
])
self.send_and_test_stream_message('derail',
expected_subject,
expected_message,
content_type="application/x-www-form-urlencoded")
def test_beeminder_derail_pm(self) -> None:
# Important: These checks need to be in the test to avoid a race.
# TODO: We should mock time.time for these tests
current_time = float(time.time())
time_delta_in_hours = (float(1517759100) - current_time)/float(3600)
@patch('zerver.webhooks.beeminder.view.time.time')
def test_beeminder_derail_pm(self, time: Any) -> None:
time.return_value = 1517739100 # 5.6 hours from fixture value
self.url = self.build_webhook_url(
email="AARON@zulip.com",
username="aaron",
user_ip="127.0.0.1"
)
expected_message = u"I am the Beeminder bot! :octopus: \n You are going to derail from \
goal **gainweight** in **{:0.1f} hours** \n You need **+2 in 7 days (60)** to avoid derailing \n * Pledge: **5$**:worried:".format(time_delta_in_hours)
expected_message = '\n'.join([
'I am the Beeminder bot! :octopus:',
' You are going to derail from goal **gainweight** in **{:0.1f} hours**'.format(5.6),
' You need **+2 in 7 days (60)** to avoid derailing',
' * Pledge: **5$**:worried:'
])
self.send_and_test_private_message('derail_pm',
expected_message,
content_type="application/json")

View File

@ -7,13 +7,17 @@ from zerver.lib.request import REQ, has_request_variables
from zerver.lib.response import json_success
from zerver.models import UserProfile, get_user_profile_by_email
import time
current_time = time.time()
def get_user_name(email: str) -> str:
profile = get_user_profile_by_email(email)
name = profile.short_name
return name
def get_time(payload: Dict[str, Any]) -> Any:
losedate = payload["goal"]["losedate"]
time_remaining = (losedate - time.time())/3600
return time_remaining
@api_key_only_webhook_view("beeminder")
@has_request_variables
def api_beeminder_webhook(request: HttpRequest, user_profile: UserProfile,
@ -24,10 +28,9 @@ def api_beeminder_webhook(request: HttpRequest, user_profile: UserProfile,
secret = payload["goal"]["secret"]
goal_name = payload["goal"]["slug"]
losedate = payload["goal"]["losedate"]
limsum = payload["goal"]["limsum"]
pledge = payload["goal"]["pledge"]
time_remain = (losedate - current_time)/3600 # time in hours
time_remain = get_time(payload) # time in hours
# To show user's probable reaction by looking at pledge amount
if pledge > 0:
expression = ':worried:'
@ -36,10 +39,9 @@ def api_beeminder_webhook(request: HttpRequest, user_profile: UserProfile,
if not secret:
# In this case notifications will be sent to stream
name = get_user_name(email)
body = u"Hello **{}**! I am the Beeminder bot! :octopus: \n You are going to derail \
from goal **{}** in **{:0.1f} hours** \n You need **{}** to avoid derailing \n * Pledge: **{}$** {}"
body = u"Hello **{}**! I am the Beeminder bot! :octopus:\n You are going to derail \
from goal **{}** in **{:0.1f} hours**\n You need **{}** to avoid derailing\n * Pledge: **{}$** {}"
body = body.format(name, goal_name, time_remain, limsum, pledge, expression)
check_send_stream_message(user_profile, request.client, stream, topic, body)
return json_success()
@ -47,8 +49,8 @@ def api_beeminder_webhook(request: HttpRequest, user_profile: UserProfile,
else:
# In this case PM will be sent to user
p = get_user_profile_by_email(email)
body = u"I am the Beeminder bot! :octopus: \n You are going to derail from \
goal **{}** in **{:0.1f} hours** \n You need **{}** to avoid derailing \n * Pledge: **{}$**{}"
body = u"I am the Beeminder bot! :octopus:\n You are going to derail from \
goal **{}** in **{:0.1f} hours**\n You need **{}** to avoid derailing\n * Pledge: **{}$**{}"
body = body.format(goal_name, time_remain, limsum, pledge, expression)
check_send_private_message(user_profile, request.client, p, body)
return json_success()