diff --git a/tests/test_commands.py b/tests/test_commands.py index 6bafb4f..5710c11 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -124,6 +124,7 @@ def test_with_multiword_term(self, mock_commands): def test_quote(): with patch('tululbot.commands.quote_engine') as mock_engine: + mock_engine.refresh_cache_if_applicable.return_value = False mock_engine.retrieve_random.return_value = 'some random quote' rv = quote() assert rv == 'some random quote' diff --git a/tests/test_quoteengine.py b/tests/test_quoteengine.py new file mode 100644 index 0000000..7382f39 --- /dev/null +++ b/tests/test_quoteengine.py @@ -0,0 +1,87 @@ +import json +from unittest.mock import patch + +import pytest + +from tululbot.utils import QuoteEngine + +generic_quote_1 = ( + '--- ' + 'quotes: ' + ' - q_no: 1' + ' quote: "Kenapa kalo mau sahur tiba2 ngantuk? Karena Anda belum punya cinta yang menemani Anda sahur."' + ' author: Anang Ferdi' + ' author_bio: Dokter cinta veteran' + ' tags: ' + ' - cinta' + ' - sahur' + ' - ramadhan' +) + +generic_quote_2 = ( + '---' + 'quotes:' + ' - q_no: 3' + ' quote: "Cinta itu bisa dibagi dengan 0 ga sih?"' + ' author: Adrian Nuradiansyah' + ' author_bio: Awesome Akhi' + ' tags: ' + ' - cinta' + ' - geek' + ' - matematika ' +) + +@pytest.fixture +def qe(): + return QuoteEngine() + +def test_refresh_time(qe): + with patch('tululbot.tils.datetime.datetime') as mock_dt: + with patch('tululbot.utils.requests') as mock_requests: + class FakeResponse: + pass + + response1 = FakeResponse() + response1.status_code = 200 + response1.text = ( + '{' + ' "commit": {' + ' "sha": "beefbeef"' + ' }' + '}' + ) + response1.ok = True + + response2 = FakeResponse() + response2.status_code = 200 + response2.text = ( + '' + '
' + '

Snowden is former CIA employee.

' + '
' + '' + ) + + mock_requests.get.side_effect = { + qe._git_branch_check_url: response1 + } + + qe.refresh_interval = 5 + + mock_dt.now.return_value = datetime(2009, 1, 6, 15, 8, 30) + qe._last_refresh = datetime(2000, 1, 6, 15, 8, 24) + + result = qe.refresh_url_if_applicable + assert result == True + result = qe.refresh_url_if_applicable + assert result == False + result = qe.refresh_url_if_applicable + assert result == False + + qe._last_refresh = datetime(2000, 1, 6, 15, 8, 24) + + result = qe.refresh_url_if_applicable + assert result == True + result = qe.refresh_url_if_applicable + assert result == False + diff --git a/tululbot/commands.py b/tululbot/commands.py index aacd376..30d8651 100644 --- a/tululbot/commands.py +++ b/tululbot/commands.py @@ -24,7 +24,6 @@ dispatcher = CommandDispatcher() quote_engine = QuoteEngine() -quote_engine.refresh_cache() @dispatcher.command(r'^/leli (?P.+)$') @@ -95,6 +94,7 @@ def search_on_google(): @dispatcher.command(r'^/quote$') def quote(): + quote_engine.refresh_cache_if_applicable() return quote_engine.retrieve_random() diff --git a/tululbot/utils.py b/tululbot/utils.py index 57bcfc8..192c94f 100644 --- a/tululbot/utils.py +++ b/tululbot/utils.py @@ -1,3 +1,4 @@ +import datetime import random import requests @@ -7,12 +8,24 @@ class QuoteEngine: def __init__(self): - self._quote_url = 'https://cdn.rawgit.com/tulul/tulul-quotes/master/quote.yaml' # noqa # Note: rawgit does not have 100% uptime, but at # least they're not throttling us. + self._quote_url = 'https://cdn.rawgit.com/tulul/tulul-quotes/master/quote.yaml' # noqa + + # Why not using rawgit's development endpoint? Because + # they can't promise 100% uptime on the development endpoint. + # Meanwhile, production endpoint's uptime is better because + # it is served by MaxCDN + self._git_branch_check_url = 'https://api.github.com/repos/tulul/tulul-quotes/branches/master' # noqa self._cache = [] + # URI refresh per interval + self._refresh_interval = 5 * 60 + # Dummy date. Must be old enough (just to trigger) + # the uri must be refreshed on the first call + self._last_refresh = datetime(2009, 1, 6, 15, 8, 24, 78915) + def retrieve_random(self): cache = self._cache return self.format_quote(random.choice(cache)) @@ -20,9 +33,37 @@ def retrieve_random(self): def format_quote(self, q): return '{q[quote]} - {q[author]}, {q[author_bio]}'.format(q=q) - def refresh_cache(self): + def refresh_cache_if_applicable(self): + if (!self.refresh_url_if_applicable()): + return False + body = requests.get(self._quote_url).text - # What if previosuly we have the cache, but this time + # What if previously we have the cache, but this time # when we try to get new cache, the network occurs error? # We will think about "don't refresh if error" later. self._cache = yaml.load(body)['quotes'] + + return True + + def refresh_url_if_applicable(self): + now = datetime.datetime.now() + delta = now - self._last_refresh + + if (delta.seconds < self._refresh_interval): + return False + + req = requests.get(self._git_branch_check_url) + + # Don't care broken request + if (!x.ok): + return False + + json = req.get_json() + sha = json['commit']['sha'] + self._quote_url = 'https://cdn.rawgit.com/tulul/tulul-quotes/%s/quote.yaml'.format(sha) + + self._refresh_interval = now + + return True + +