From 3069bd854c2486f9230fce65527da5c972f34828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Ganczarek?= Date: Sun, 20 Oct 2019 22:40:45 +0200 Subject: [PATCH 1/2] Pro's solution --- lottery/api.py | 23 +++++++++++++++++++++++ lottery/logic.py | 12 ++++++++++++ lottery/lottery.py | 16 ---------------- lottery/ports.py | 28 ++++++++++++++++++++++++++++ run.sh | 2 +- tests/test_logic.py | 36 ++++++++++++++++++++++++++++++++++++ tests/test_lottery.py | 35 ----------------------------------- tests/test_ports.py | 34 ++++++++++++++++++++++++++++++++++ 8 files changed, 134 insertions(+), 52 deletions(-) create mode 100644 lottery/api.py create mode 100644 lottery/logic.py delete mode 100644 lottery/lottery.py create mode 100644 lottery/ports.py create mode 100644 tests/test_logic.py delete mode 100644 tests/test_lottery.py create mode 100644 tests/test_ports.py diff --git a/lottery/api.py b/lottery/api.py new file mode 100644 index 0000000..897b11d --- /dev/null +++ b/lottery/api.py @@ -0,0 +1,23 @@ +import random + +from flask import Flask + +from .logic import LotteryLogic +from .ports import APIPort, FilePort, RandomPort + +APP = Flask(__name__) +HOSTNAME = "https://httpbin.org/post" +LOG_FILENAME = "ticket.log" +MAX_TICKET_NUMBER = 1000 + + +@APP.route("/") +def lottery(): + logic = LotteryLogic( + api_port=APIPort(host=HOSTNAME), + file_port=FilePort(LOG_FILENAME), + random_port=RandomPort(random, MAX_TICKET_NUMBER), + ) + ticket = logic.generate_ticket() + logic.save_ticket(ticket) + return {"ticket_no": ticket} diff --git a/lottery/logic.py b/lottery/logic.py new file mode 100644 index 0000000..abf8990 --- /dev/null +++ b/lottery/logic.py @@ -0,0 +1,12 @@ +class LotteryLogic: + def __init__(self, api_port, file_port, random_port): + self._api_port = api_port + self._file_port = file_port + self._random_port = random_port + + def generate_ticket(self): + return self._random_port.generate() + + def save_ticket(self, ticket): + self._file_port.save(ticket) + self._api_port.send(ticket) diff --git a/lottery/lottery.py b/lottery/lottery.py deleted file mode 100644 index 3521dd8..0000000 --- a/lottery/lottery.py +++ /dev/null @@ -1,16 +0,0 @@ -from random import randint - -import requests -from flask import Flask - -APP = Flask(__name__) - - -@APP.route('/') -def lottery(): - ticket_no = randint(1, 1000) - with open('ticket.log', 'a') as ticket_log: - ticket_log.write(f'{ticket_no}\n') - data = {'ticket_no': ticket_no} - requests.post('https://httpbin.org/post', data=data) - return data diff --git a/lottery/ports.py b/lottery/ports.py new file mode 100644 index 0000000..40f806a --- /dev/null +++ b/lottery/ports.py @@ -0,0 +1,28 @@ +import requests + + +class RandomPort: + def __init__(self, random, max_number): + self._random = random + self._max_number = max_number + + def generate(self): + return self._random.randint(1, self._max_number) + + +class FilePort: + def __init__(self, filename): + self._filename = filename + + def save(self, ticket): + with open(self._filename, "a") as file_desc: + file_desc.write(f"{ticket}\n") + + +class APIPort: + def __init__(self, host): + self._host = host + self.last_response = None + + def send(self, ticket): + self.last_response = requests.post(self._host, data={"ticket_no": ticket}) diff --git a/run.sh b/run.sh index 17bbf30..19aa68c 100644 --- a/run.sh +++ b/run.sh @@ -1 +1 @@ -env FLASK_APP=lottery.lottery flask run +env FLASK_APP=lottery.api flask run diff --git a/tests/test_logic.py b/tests/test_logic.py new file mode 100644 index 0000000..542bf4f --- /dev/null +++ b/tests/test_logic.py @@ -0,0 +1,36 @@ +from unittest.mock import Mock + +from pytest import fixture + +from lottery.logic import LotteryLogic +from lottery.ports import APIPort, FilePort, RandomPort + + +@fixture +def dummy_random(): + return Mock(RandomPort, generate=Mock(return_value=527)) + + +@fixture +def dummy_api(): + return Mock(APIPort) + + +@fixture +def dummy_file(): + return Mock(FilePort) + + +def test_generate_ticket(dummy_random): + logic = LotteryLogic(api_port=None, file_port=None, random_port=dummy_random) + + assert logic.generate_ticket() == 527 + + +def test_send(dummy_api, dummy_file): + logic = LotteryLogic(api_port=dummy_api, file_port=dummy_file, random_port=None) + + logic.save_ticket(527) + + dummy_api.send.assert_called_with(527) + dummy_file.save.assert_called_with(527) diff --git a/tests/test_lottery.py b/tests/test_lottery.py deleted file mode 100644 index 6833614..0000000 --- a/tests/test_lottery.py +++ /dev/null @@ -1,35 +0,0 @@ -from unittest.mock import patch - -from pytest import fixture - -from lottery.lottery import lottery - - -@fixture(autouse=True, scope='module') -def mocked_open(): - with patch('lottery.lottery.open') as mock: - yield mock - - -@fixture(autouse=True, scope='module') -def mocked_requests(): - with patch('lottery.lottery.requests') as mock: - yield mock - - -@fixture(autouse=True, scope='module') -def mocked_randint(): - with patch('lottery.lottery.randint') as mock: - mock.return_value = 546 - yield mock - - -def test_lottery(mocked_open, mocked_randint, mocked_requests): - assert lottery() == {'ticket_no': 546} - mocked_randint.assert_called_with(1, 1000) - mocked_open.assert_called_with('ticket.log', 'a') - mocked_open().__enter__().write.assert_called_with('546\n') - mocked_requests.post.assert_called_with( - 'https://httpbin.org/post', - data={'ticket_no': 546}, - ) diff --git a/tests/test_ports.py b/tests/test_ports.py new file mode 100644 index 0000000..a0c5dd2 --- /dev/null +++ b/tests/test_ports.py @@ -0,0 +1,34 @@ +from random import Random +from tempfile import mkstemp + +from pytest import mark + +from lottery.ports import APIPort, FilePort, RandomPort + + +def test_random_port(): + port = RandomPort(Random(10000), 1000) + + assert port.generate() == 592 + + +@mark.integration +def test_file_port(): + __, path = mkstemp() + port = FilePort(path) + + port.save(123) + port.save(987) + + with open(path, "r") as desc: + assert desc.read() == "123\n987\n" + + +@mark.integration +def test_api_port(): + port = APIPort("https://httpbin.org/post") + + port.send(123) + + assert port.last_response.status_code == 200 + assert port.last_response.json()["form"] == {"ticket_no": "123"} From d270e2d34617a0a7cd9717eb313c7e5e21bf7903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Ganczarek?= Date: Mon, 21 Oct 2019 10:41:08 +0200 Subject: [PATCH 2/2] Improvements --- lottery/api.py | 18 ++++++++++-------- lottery/entities.py | 8 ++++++++ lottery/logic.py | 17 ++++++++++++----- lottery/ports.py | 6 ++++-- tests/test_logic.py | 8 ++++---- tests/test_ports.py | 15 ++++++++++----- 6 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 lottery/entities.py diff --git a/lottery/api.py b/lottery/api.py index 897b11d..57a4686 100644 --- a/lottery/api.py +++ b/lottery/api.py @@ -1,8 +1,9 @@ import random +from dataclasses import asdict from flask import Flask -from .logic import LotteryLogic +from .logic import TicketGenerator, TicketSaver from .ports import APIPort, FilePort, RandomPort APP = Flask(__name__) @@ -13,11 +14,12 @@ @APP.route("/") def lottery(): - logic = LotteryLogic( - api_port=APIPort(host=HOSTNAME), - file_port=FilePort(LOG_FILENAME), - random_port=RandomPort(random, MAX_TICKET_NUMBER), + ticket_generator = TicketGenerator( + random_port=RandomPort(random, MAX_TICKET_NUMBER) ) - ticket = logic.generate_ticket() - logic.save_ticket(ticket) - return {"ticket_no": ticket} + ticket_saver = TicketSaver( + api_port=APIPort(host=HOSTNAME), file_port=FilePort(LOG_FILENAME) + ) + ticket = ticket_generator.generate_ticket() + ticket_saver.save_ticket(ticket) + return asdict(ticket) diff --git a/lottery/entities.py b/lottery/entities.py new file mode 100644 index 0000000..6d43a91 --- /dev/null +++ b/lottery/entities.py @@ -0,0 +1,8 @@ +from dataclasses import dataclass, field +from datetime import datetime + + +@dataclass +class Ticket: + ticket_id: int + creation_time: datetime = field(default_factory=datetime.now) diff --git a/lottery/logic.py b/lottery/logic.py index abf8990..087fc84 100644 --- a/lottery/logic.py +++ b/lottery/logic.py @@ -1,11 +1,18 @@ -class LotteryLogic: - def __init__(self, api_port, file_port, random_port): - self._api_port = api_port - self._file_port = file_port +from .entities import Ticket + + +class TicketGenerator: + def __init__(self, random_port): self._random_port = random_port def generate_ticket(self): - return self._random_port.generate() + return Ticket(ticket_id=self._random_port.generate()) + + +class TicketSaver: + def __init__(self, api_port, file_port): + self._api_port = api_port + self._file_port = file_port def save_ticket(self, ticket): self._file_port.save(ticket) diff --git a/lottery/ports.py b/lottery/ports.py index 40f806a..177cade 100644 --- a/lottery/ports.py +++ b/lottery/ports.py @@ -1,3 +1,5 @@ +from dataclasses import asdict, astuple + import requests @@ -16,7 +18,7 @@ def __init__(self, filename): def save(self, ticket): with open(self._filename, "a") as file_desc: - file_desc.write(f"{ticket}\n") + file_desc.write("{0},{1}\n".format(*astuple(ticket))) class APIPort: @@ -25,4 +27,4 @@ def __init__(self, host): self.last_response = None def send(self, ticket): - self.last_response = requests.post(self._host, data={"ticket_no": ticket}) + self.last_response = requests.post(self._host, data=asdict(ticket)) diff --git a/tests/test_logic.py b/tests/test_logic.py index 542bf4f..2014c47 100644 --- a/tests/test_logic.py +++ b/tests/test_logic.py @@ -2,7 +2,7 @@ from pytest import fixture -from lottery.logic import LotteryLogic +from lottery.logic import TicketGenerator, TicketSaver from lottery.ports import APIPort, FilePort, RandomPort @@ -22,13 +22,13 @@ def dummy_file(): def test_generate_ticket(dummy_random): - logic = LotteryLogic(api_port=None, file_port=None, random_port=dummy_random) + logic = TicketGenerator(random_port=dummy_random) - assert logic.generate_ticket() == 527 + assert logic.generate_ticket().ticket_id == 527 def test_send(dummy_api, dummy_file): - logic = LotteryLogic(api_port=dummy_api, file_port=dummy_file, random_port=None) + logic = TicketSaver(api_port=dummy_api, file_port=dummy_file) logic.save_ticket(527) diff --git a/tests/test_ports.py b/tests/test_ports.py index a0c5dd2..5227960 100644 --- a/tests/test_ports.py +++ b/tests/test_ports.py @@ -1,8 +1,10 @@ +from datetime import datetime from random import Random from tempfile import mkstemp from pytest import mark +from lottery.entities import Ticket from lottery.ports import APIPort, FilePort, RandomPort @@ -17,18 +19,21 @@ def test_file_port(): __, path = mkstemp() port = FilePort(path) - port.save(123) - port.save(987) + port.save(Ticket(ticket_id=123, creation_time=datetime(2020, 10, 11, 10, 22, 1))) + port.save(Ticket(ticket_id=987, creation_time=datetime(2020, 10, 11, 11, 22, 1))) with open(path, "r") as desc: - assert desc.read() == "123\n987\n" + assert desc.read() == "123,2020-10-11 10:22:01\n987,2020-10-11 11:22:01\n" @mark.integration def test_api_port(): port = APIPort("https://httpbin.org/post") - port.send(123) + port.send(Ticket(ticket_id=123, creation_time=datetime(2020, 10, 11, 10, 22, 1))) assert port.last_response.status_code == 200 - assert port.last_response.json()["form"] == {"ticket_no": "123"} + assert port.last_response.json()["form"] == { + "creation_time": "2020-10-11 10:22:01", + "ticket_id": "123", + }