diff --git a/src/pylms/__main__.py b/src/pylms/__main__.py index c123e49..9a748ee 100644 --- a/src/pylms/__main__.py +++ b/src/pylms/__main__.py @@ -1,10 +1,17 @@ #!/bin/env python3 +import logging from sys import argv +import pylms.pylms from pylms.pylms import list_persons, store_person, update_person, delete_person, link_persons, ExitPyLMS +from pylms.cli import CLI def main() -> None: + _cli = CLI() + pylms.pylms.ios = _cli + pylms.pylms.events = _cli + logging.basicConfig(level=logging.INFO) try: _read_and_execute_commands() except ExitPyLMS: diff --git a/src/pylms/cli.py b/src/pylms/cli.py new file mode 100644 index 0000000..5d6ccf2 --- /dev/null +++ b/src/pylms/cli.py @@ -0,0 +1,111 @@ +from pylms.pylms import IOs, EventListener, ExitPyLMS +from pylms.pylms import Person, Relationship, RelationshipDefinition + + +class CLI(IOs, EventListener): + def creating_person(self, person: Person) -> None: + print(f"Create Person {person}.") + + def show_person(self, person: Person) -> None: + created = person.created + print( + f"({person.person_id})", + person, + f"({created.year}-{created.month}-{created.day} {created.hour}-{created.minute}-{created.second})", + ) + + def list_persons(self, resolved_persons: list[(Person, list[Relationship])]) -> None: + if resolved_persons: + for person, rs in sorted(resolved_persons, key=lambda t: t[0].person_id): + self.show_person(person) + for r in rs: + other = r.right if r.left == person else r.left + print(f" -> {r.repr_for(person)} de ({other.person_id}) {other}") + else: + print("No Person registered yet.") + + def _interactive_hit_enter(self): + while True: + s = self._input_or_exit_pylms() + + if len(s) == 0: + return + + print("Just hit ENTER") + + def _input_or_exit_pylms(self): + try: + return input() + except KeyboardInterrupt: + raise ExitPyLMS() + + def _interactive_person_id(self, valid_ids: list[int]) -> int: + if not valid_ids: + raise ValueError("valid_ids can not be empty.") + + while True: + n = self._input_or_exit_pylms() + + try: + res = int(n) + if res not in valid_ids: + print("Not a valid id.") + continue + + return res + except ValueError: + print("Not an integer.") + + def select_person(self, persons: list[Person]) -> Person | None: + print("Input id of person to update:") + for person in sorted(persons, key=lambda p: p.person_id): + self.show_person(person) + print("CTRL+C to exit") + + person_id = self._interactive_person_id([person.person_id for person in persons]) + for person in persons: + if person.person_id == person_id: + return person + + # should not happen + raise RuntimeError(f"id {person_id} does not exist in list of Persons") + + def update_person(self, person_to_update: Person) -> Person: + print("Input new first name and last name to update:") + self.show_person(person_to_update) + print("CTRL+C to exit") + + firstname: str + lastname: str + firstname, lastname = self._get_person_details() + + person_to_update.firstname = firstname + person_to_update.lastname = lastname + + return person_to_update + + def _get_person_details(self) -> tuple[str, str | None]: + while True: + text = self._input_or_exit_pylms() + + words = text.split(" ") + if len(words) > 2: + print("Too many words.") + continue + + if len(words) == 1: + return words[0], None + return words[0], words[1] + + def deleting_person(self, person_to_delete: Person) -> None: + print("Hit ENTER to delete:") + self.show_person(person_to_delete) + print("CTRL+C to exit") + self._interactive_hit_enter() + + def creating_link(self, rl_definition: RelationshipDefinition, person_left: Person, person_right: Person) -> None: + print(f'Hit ENTER to link as "{rl_definition.name}":') + self.show_person(person_left) + self.show_person(person_right) + print("CTRL+C to exit") + self._interactive_hit_enter() diff --git a/src/pylms/pylms.py b/src/pylms/pylms.py index 67907eb..20fd763 100644 --- a/src/pylms/pylms.py +++ b/src/pylms/pylms.py @@ -2,96 +2,72 @@ from pylms.core import Person, PersonIdGenerator from pylms.core import relationship_definitions, RelationshipDefinition, Relationship from pylms.core import resolve_persons +from abc import abstractmethod, ABC +import logging + +logger = logging.getLogger(__name__) class ExitPyLMS(BaseException): pass -def _input_or_exit_pylms(): - try: - return input() - except KeyboardInterrupt: - raise ExitPyLMS() +class IOs(ABC): + @abstractmethod + def show_person(self, person: Person) -> None: + pass + @abstractmethod + def list_persons(self, persons: list[Person]) -> None: + pass -def list_persons() -> None: - persons = storage.read_persons() - if persons: - relationships = storage.read_relationships(persons) - resolved_persons = resolve_persons(persons, relationships) + @abstractmethod + def select_person(self, persons: list[Person]) -> Person | None: + pass + + @abstractmethod + def update_person(self, person_to_update: Person) -> Person: + pass - for person, rs in sorted(resolved_persons, key=lambda t: t[0].person_id): - _print_person(person) - for r in rs: - other = r.right if r.left == person else r.left - print(f" -> {r.repr_for(person)} de ({other.person_id}) {other}") - else: - print("No Person registered yet.") - - -def _print_person(person): - created = person.created - print( - f"({person.person_id})", - person, - f"({created.year}-{created.month}-{created.day} {created.hour}-{created.minute}-{created.second})", - ) +class EventListener(ABC): + @abstractmethod + def creating_person(self, person: Person) -> None: + pass -def _interactive_person_id(valid_ids: list[int]) -> int: - if not valid_ids: - raise ValueError("valid_ids can not be empty.") + @abstractmethod + def deleting_person(self, person_to_delete: Person) -> None: + pass - while True: - n = _input_or_exit_pylms() + @abstractmethod + def creating_link(self, rl_definition: RelationshipDefinition, person_left: Person, person_right: Person) -> None: + pass - try: - res = int(n) - if res not in valid_ids: - print("Not a valid id.") - continue - return res - except ValueError: - print("Not an integer.") +ios: IOs +events: EventListener + + +def list_persons() -> None: + persons = storage.read_persons() + + resolved_persons = [] + if persons: + relationships = storage.read_relationships(persons) + resolved_persons = resolve_persons(persons, relationships) + + ios.list_persons(resolved_persons) def _interactive_select_person(pattern: str) -> Person | None: persons = _search_person(pattern) if not persons: - print("No match.") return if len(persons) == 1: return persons[0] - print("Input id of person to update:") - for person in sorted(persons, key=lambda p: p.person_id): - _print_person(person) - print("CTRL+C to exit") - - person_id = _interactive_person_id([person.person_id for person in persons]) - for person in persons: - if person.person_id == person_id: - return person - - # should not happen - raise RuntimeError(f"id {person_id} does not exist in list of Persons") - - -def _interactive_person_details() -> tuple[str, str | None]: - while True: - text = _input_or_exit_pylms() - - words = text.split(" ") - if len(words) > 2: - print("Too many words.") - continue - - if len(words) == 1: - return words[0], None - return words[0], words[1] + return ios.select_person(persons) def update_person(pattern: str) -> None: @@ -99,25 +75,19 @@ def update_person(pattern: str) -> None: if not person_to_update: return - print("Input new first name and last name to update:") - _print_person(person_to_update) - print("CTRL+C to exit") - - firstname: str - lastname: str - firstname, lastname = _interactive_person_details() + updated_person = ios.update_person(person_to_update) persons = storage.read_persons() for person in persons: if person.person_id == person_to_update.person_id: - person.firstname = firstname - person.lastname = lastname + person.firstname = updated_person.firstname + person.lastname = updated_person.lastname storage.store_persons(persons) def search_person(pattern: str) -> None: for person in _search_person(pattern): - _print_person(person) + ios.show_person(person) def _search_person(pattern: str) -> list[Person]: @@ -143,7 +113,7 @@ def store_person(firstname: str) -> None: persons = storage.read_persons() id_generator = PersonIdGenerator(persons) person = Person(person_id=id_generator.next_person_id(), firstname=firstname) - print(f"Create Person {person}.") + events.creating_person(person) storage.store_persons([person] + persons) @@ -151,31 +121,16 @@ def store_person(firstname: str, lastname: str = None) -> None: persons = storage.read_persons() id_generator = PersonIdGenerator(persons) person = Person(person_id=id_generator.next_person_id(), firstname=firstname, lastname=lastname) - print(f"Create Person {person}.") + events.creating_person(person) storage.store_persons([person] + persons) -def _interactive_hit_enter(): - while True: - s = _input_or_exit_pylms() - - if len(s) == 0: - return - - print("Just hit ENTER") - continue - - def delete_person(pattern: str) -> None: person_to_delete: Person = _interactive_select_person(pattern) if not person_to_delete: return - print("Hit ENTER to delete:") - _print_person(person_to_delete) - print("CTRL+C to exit") - - _interactive_hit_enter() + events.deleting_person(person_to_delete) persons = storage.read_persons() persons.remove(person_to_delete) @@ -215,7 +170,7 @@ def _parse_nl_link_request(natural_language_link_request: str) -> LinkRequest | person_patterns = list(filter(lambda s: len(s) > 0, map(str.strip, natural_language_link_request.split(alias)))) patterns_count = len(person_patterns) if patterns_count != 2: - print(f"Unsupported link request: wrong number of person patterns ({patterns_count})") + logger.error(f"Unsupported link request: wrong number of person patterns ({patterns_count})") return None return LinkRequest( @@ -235,18 +190,13 @@ def link_persons(natural_language_link_request: str) -> None: person_right = _interactive_select_person(link_request.right_person_pattern) if person_left is None: - print(f'No match for "{link_request.left_person_pattern}".') + logger.info(f'No match for "{link_request.left_person_pattern}".') if person_right is None: - print(f'No match for "{link_request.right_person_pattern}".') + logger.info(f'No match for "{link_request.right_person_pattern}".') if person_left is None or person_right is None: return - print(f'Hit ENTER to link as "{link_request.definition.name}":') - _print_person(person_left) - _print_person(person_right) - print("CTRL+C to exit") - - _interactive_hit_enter() + events.creating_link(link_request.definition, person_left, person_right) persons = storage.read_persons() relationships = storage.read_relationships(persons) diff --git a/tests/pylms/cli_test.py b/tests/pylms/cli_test.py new file mode 100644 index 0000000..908ed6c --- /dev/null +++ b/tests/pylms/cli_test.py @@ -0,0 +1,159 @@ +from pylms.cli import CLI +from pylms.pylms import Person +from pytest import raises, mark +from unittest.mock import patch, call + +under_test = CLI() + + +class Test_list_persons: + + @patch("builtins.print") + def test_no_persons(self, mock_print): + under_test.list_persons([]) + + mock_print.assert_called_with("No Person registered yet.") + assert mock_print.call_count == 1 + + @patch("builtins.print") + @patch.object(under_test, "show_person") + def test_one_person_no_relationship(self, mock_show_person, mock_print): + person = Person(3, "John", "Doe") + + under_test.list_persons([(person, [])]) + + assert mock_show_person.call_count == 1 + mock_show_person.assert_called_once_with(person) + assert mock_print.call_count == 0 + + @patch("builtins.print") + @patch.object(under_test, "show_person") + def test_persons_no_relationship(self, mock_show_person, mock_print): + persons = [ + Person(3, "Bob"), + Person(1, "Seb", "King"), + Person(2, "Mario", "Bros"), + ] + + under_test.list_persons([(person, []) for person in persons]) + + assert mock_show_person.call_count == 3 + mock_show_person.assert_has_calls( + [ + call(persons[1]), + call(persons[2]), + call(persons[0]), + ] + ) + assert mock_print.call_count == 0 + + +class Test_interactive_person_id: + + def test_sanity_check(self): + with raises(ValueError, match="valid_ids can not be empty."): + under_test._interactive_person_id([]) + + @patch("builtins.input") + @mark.parametrize("valid_ids", [[1], [2, 1], [23, 7, 1, 2]]) + def test_straight_correct_input(self, mock_input, valid_ids): + mock_input.return_value = "1" + + res = under_test._interactive_person_id([2, 1]) + + assert res == 1 + + +class Test_interactive_hit_enter: + + @patch("builtins.input") + def test_interactive_hit_enter_straight_correct_input(self, mock_input): + mock_input.return_value = "" + + under_test._interactive_hit_enter() + + mock_input.assert_called_once_with() + assert mock_input.call_count == 1 + + @patch("builtins.input") + def test_interactive_hit_enter_incorrect_inputs(self, mock_input): + mock_input.side_effect = ["q", "12", "ENTER", ""] + + under_test._interactive_hit_enter() + + mock_input.assert_has_calls([call(), call(), call(), call()]) + assert mock_input.call_count == 4 + + +class Test_update_person: + @patch("builtins.print") + @patch.object(under_test, "_get_person_details") + @patch.object(under_test, "show_person") + def test_top_level(self, mock_show_person, mock_get_person_details, mock_print): + person = Person(1, "John", "Doe") + mock_get_person_details.return_value = ("foo", "bar") + + res = under_test.update_person(person) + + assert res.firstname == "foo" + assert res.lastname == "bar" + + mock_show_person.assert_called_once_with(person) + mock_get_person_details.assert_called_once_with() + mock_print.assert_has_calls( + [ + call("Input new first name and last name to update:"), + call("CTRL+C to exit"), + ] + ) + + @patch("builtins.print") + @patch("builtins.input") + @patch.object(under_test, "show_person") + def test_get_person_details_one_word(self, mock_show_person, mock_input, mock_print): + person = Person(1, "John", "Doe") + + mock_input.return_value = "foo" + + res = under_test.update_person(person) + + assert res.firstname == "foo" + assert res.lastname is None + mock_input.assert_called_once_with() + assert mock_print.call_count == 2 + + @patch("builtins.print") + @patch("builtins.input") + @patch.object(under_test, "show_person") + def test_get_person_details_two_words(self, mock_show_person, mock_input, mock_print): + person = Person(1, "John", "Doe") + + mock_input.return_value = "foo bar" + + res = under_test.update_person(person) + + assert res.firstname == "foo" + assert res.lastname == "bar" + mock_input.assert_called_once_with() + assert mock_print.call_count == 2 + + @patch("builtins.print") + @patch("builtins.input") + @patch.object(under_test, "show_person") + def test_get_person_three_words(self, mock_show_person, mock_input, mock_print): + person = Person(1, "John", "Doe") + + mock_input.side_effect = ["foo bar acme", "foo bar"] + + res = under_test.update_person(person) + + assert res.firstname == "foo" + assert res.lastname == "bar" + assert mock_input.call_count == 2 + mock_print.has_calls( + [ + call("Input new first name and last name to update:"), + call("CTRL+C to exit"), + call("Too many words."), + ] + ) diff --git a/tests/pylms/pylms_protected_test.py b/tests/pylms/pylms_protected_test.py index f99f0d1..6b75362 100644 --- a/tests/pylms/pylms_protected_test.py +++ b/tests/pylms/pylms_protected_test.py @@ -1,8 +1,8 @@ from pylms.core import Person from pylms.core import RelationshipDefinition -from pylms.pylms import _search_person, _interactive_person_id, _interactive_select_person, _interactive_hit_enter +from pylms.pylms import _search_person, _interactive_select_person from pylms.pylms import _parse_nl_link_request -from pytest import raises, mark +from pytest import mark from unittest.mock import patch, call @@ -22,22 +22,6 @@ def test_search_person(mock_storage): assert _search_person("foo") == [] -class Test_interactive_person_id: - - def test_sanity_check(self): - with raises(ValueError, match="valid_ids can not be empty."): - _interactive_person_id([]) - - @patch("builtins.input") - @mark.parametrize("valid_ids", [[1], [2, 1], [23, 7, 1, 2]]) - def test_straight_correct_input(self, mock_input, valid_ids): - mock_input.return_value = "1" - - res = _interactive_person_id([2, 1]) - - assert res == 1 - - class Test_interactive_select_person: @patch("builtins.print") @@ -50,8 +34,7 @@ def test_no_match(self, mock_search_person, mock_print): assert res is None mock_search_person.assert_called_once_with("p") assert mock_search_person.call_count == 1 - mock_print.assert_called_once_with("No match.") - assert mock_print.call_count == 1 + assert mock_print.call_count == 0 @patch("builtins.print") @patch("pylms.pylms._search_person") @@ -66,66 +49,23 @@ def test_one_match(self, mock_search_person, mock_print): assert mock_search_person.call_count == 1 assert mock_print.call_count == 0 - @patch("builtins.print") - @patch("pylms.pylms._interactive_person_id") - @patch("pylms.pylms._print_person") + @patch("pylms.pylms.ios.select_person") @patch("pylms.pylms._search_person") - def test_several_matches(self, mock_search_person, mock_print_person, mock_interactive_person_id, mock_print): + def test_several_matches(self, mock_search_person, mock_select_person): persons = [ Person(3, "Bob"), Person(1, "Seb"), ] mock_search_person.return_value = persons - mock_interactive_person_id.return_value = 3 + mock_select_person.return_value = persons[0] res = _interactive_select_person("p") assert res == persons[0] mock_search_person.assert_called_once_with("p") assert mock_search_person.call_count == 1 - assert mock_print.call_count == 2 - mock_print.assert_has_calls([call("Input id of person to update:"), call("CTRL+C to exit")]) - assert mock_print_person.call_count == 2 - mock_print_person.assert_has_calls([call(persons[1]), call(persons[0])]) - assert mock_interactive_person_id.call_count == 1 - mock_interactive_person_id.assert_called_once_with([3, 1]) - - @patch("builtins.print") - @patch("pylms.pylms._interactive_person_id") - @patch("pylms.pylms._search_person") - def test_interactive_raise_runtime_error_if_interactive_person_id_returns_crap( - self, mock_search_person, mock_interactive_person_id, mock_print - ): - persons = [ - Person(3, "Bob"), - Person(1, "Seb"), - ] - mock_search_person.return_value = persons - mock_interactive_person_id.return_value = 123 - - with raises(RuntimeError, match="id 123 does not exist in list of Persons"): - _interactive_select_person("p") - - -class Test_interactive_hit_enter: - - @patch("builtins.input") - def test_interactive_hit_enter_straight_correct_input(self, mock_input): - mock_input.return_value = "" - - _interactive_hit_enter() - - mock_input.assert_called_once_with() - assert mock_input.call_count == 1 - - @patch("builtins.input") - def test_interactive_hit_enter_incorrect_inputs(self, mock_input): - mock_input.side_effect = ["q", "12", "ENTER", ""] - - _interactive_hit_enter() - - mock_input.assert_has_calls([call(), call(), call(), call()]) - assert mock_input.call_count == 4 + assert mock_select_person.call_count == 1 + mock_select_person.assert_called_once_with(persons) relationship_definition_1 = RelationshipDefinition( diff --git a/tests/pylms/pymls_test.py b/tests/pylms/pymls_test.py index 998fe75..6c7b58a 100644 --- a/tests/pylms/pymls_test.py +++ b/tests/pylms/pymls_test.py @@ -77,79 +77,69 @@ def test_only_firstname_and_add_to_existing_persons(self, mock_store_persons, mo assert mock_print.call_count == 1 -@patch("builtins.print") -@patch("pylms.pylms.storage.read_persons") -@patch("pylms.pylms.storage.store_persons") -def test_list_persons_empty_storage(mock_store_persons, mock_read_persons, mock_print): - mock_read_persons.return_value = [] - - list_persons() - - mock_read_persons.assert_called_with() - assert mock_read_persons.call_count == 1 - assert mock_store_persons.call_count == 0 - mock_print.assert_called_with("No Person registered yet.") - assert mock_print.call_count == 1 - - -@patch("builtins.print") -@patch("pylms.pylms.storage.read_relationships", return_value=[]) -@patch("pylms.pylms.storage.read_persons") -@patch("pylms.pylms.storage.store_persons") -def test_list_persons_persons_but_no_relationships( - mock_store_persons, mock_read_persons, mock_read_relationships, mock_print -): - t1 = datetime(2024, 4, 5, 12, 41, 58) - t2 = datetime(2024, 9, 18, 21, 8, 8) - persons = [ - Person(3, "Bob", created=t1), - Person(1, "Seb", "King", t1), - Person(2, "Mario", "Bros", t2), - ] - - mock_read_persons.return_value = persons - - list_persons() - - mock_read_persons.assert_called_with() - assert mock_read_persons.call_count == 1 - assert mock_store_persons.call_count == 0 - mock_print.assert_has_calls( - [ - call("(1)", persons[1], "(2024-4-5 12-41-58)"), - call("(2)", persons[2], "(2024-9-18 21-8-8)"), - call("(3)", persons[0], "(2024-4-5 12-41-58)"), +class Test_list_persons: + @patch("pylms.pylms.ios.list_persons") + @patch("pylms.pylms.storage.read_relationships") + @patch("pylms.pylms.storage.read_persons") + def test_empty_storage(self, mock_read_persons, mock_read_relationships, mock_list_persons): + mock_read_persons.return_value = [] + + list_persons() + + mock_read_persons.assert_called_once_with() + assert mock_read_relationships.call_count == 0 + mock_list_persons.assert_called_once_with([]) + + @patch("pylms.pylms.ios.list_persons") + @patch("pylms.pylms.storage.read_relationships", return_value=[]) + @patch("pylms.pylms.storage.read_persons") + def test_persons_but_no_relationships( + self, + mock_read_persons, + mock_read_relationships, + mock_list_persons, + ): + persons = [ + Person(3, "Bob"), + Person(1, "Seb", "King"), + Person(2, "Mario", "Bros"), ] - ) - assert mock_print.call_count == 3 + mock_read_persons.return_value = persons -@patch("builtins.print") -@patch("pylms.pylms._search_person") -def test_search_person_no_result(mock_search_person, mock_print): - mock_search_person.return_value = [] + list_persons() - search_person("p") + mock_read_persons.assert_called_once_with() + mock_read_relationships.assert_called_once_with(persons) + mock_list_persons.assert_called_once_with([(person, []) for person in persons]) - mock_search_person.assert_called_once_with("p") - assert mock_print.call_count == 0 +class Test_search_person: + @patch("builtins.print") + @patch("pylms.pylms._search_person") + def test_search_person_no_result(self, mock_search_person, mock_print): + mock_search_person.return_value = [] -@patch("pylms.pylms._print_person") -@patch("pylms.pylms._search_person") -def test_search_person_results(mock_search_person, mock_print_persons): - persons = [ - Person(3, "Bob"), - Person(1, "Seb"), - Person(2, "MarioEb"), - ] - mock_search_person.return_value = persons + search_person("p") - search_person("p") + mock_search_person.assert_called_once_with("p") + assert mock_print.call_count == 0 - mock_search_person.assert_called_once_with("p") - assert mock_print_persons.call_count == 3 - mock_print_persons.assert_has_calls([call(person) for person in persons]) + @patch("pylms.pylms.ios.show_person") + @patch("pylms.pylms._search_person") + def test_search_person_results(self, mock_search_person, mock_show_person): + persons = [ + Person(3, "Bob"), + Person(1, "Seb"), + Person(2, "MarioEb"), + ] + mock_search_person.return_value = persons + + search_person("p") + + mock_search_person.assert_called_once_with("p") + assert mock_show_person.call_count == 3 + mock_show_person.assert_has_calls([call(person) for person in persons]) class Test_update_person: @@ -165,48 +155,44 @@ def test_no_person_selected(self, mock_select, mock_storage): assert mock_storage.store_persons.call_count == 0 @patch("pylms.pylms.storage") - @patch("pylms.pylms._interactive_person_details") + @patch("pylms.pylms.ios.update_person") @patch("pylms.pylms._interactive_select_person") - def test_single_person(self, mock_select, mock_person_details, mock_storage): + def test_single_person(self, mock_select, mock_update_person, mock_storage): person = Person(1, "foo", "bar") mock_select.return_value = person - mock_person_details.return_value = ("donut", "acme") + mock_update_person.return_value = Person(234, "donut", "acme") mock_storage.read_persons.return_value = [person] update_person("p") mock_select.assert_called_once_with("p") - assert mock_storage.read_persons.call_count == 1 - assert mock_storage.store_persons.call_count == 1 + mock_update_person.assert_called_once_with(person) mock_storage.store_persons.assert_called_once_with([Person(1, "donut", "acme")]) @patch("pylms.pylms.storage") - @patch("pylms.pylms._interactive_person_details") + @patch("pylms.pylms.ios.update_person") @patch("pylms.pylms._interactive_select_person") - def test_out_of_several(self, mock_select, mock_person_details, mock_storage): + def test_out_of_several(self, mock_select, mock_update_person, mock_storage): person1 = Person(1, "foo", "bar") person2 = Person(2, "foo", "bar") person3 = Person(3, "foo", "bar") mock_select.return_value = person2 - mock_person_details.return_value = ("donut", "acme") + mock_update_person.return_value = Person(888, "donut", "acme") mock_storage.read_persons.return_value = [person3, person1, person2] update_person("p") mock_select.assert_called_once_with("p") - assert mock_storage.read_persons.call_count == 1 - assert mock_storage.store_persons.call_count == 1 + mock_update_person.assert_called_once_with(person2) mock_storage.store_persons.assert_called_once_with([person3, person1, Person(2, "donut", "acme")]) class Test_delete_person: - @patch("builtins.print") @patch("pylms.pylms.storage") - @patch("pylms.pylms._interactive_hit_enter") - @patch("pylms.pylms._print_person") + @patch("pylms.pylms.events.deleting_person") @patch("pylms.pylms._interactive_select_person") - def test_delete_person(self, mock_select, mock_print_person, mock_hit_enter, mock_storage, mock_print): + def test_delete_person(self, mock_select, mock_deleting_person, mock_storage): person1 = Person(1, "foo", "bar") person2 = Person(2, "foo", "bar") person3 = Person(3, "foo", "bar") @@ -216,34 +202,25 @@ def test_delete_person(self, mock_select, mock_print_person, mock_hit_enter, moc delete_person("p") mock_select.assert_called_once_with("p") - assert mock_select.call_count == 1 - mock_print.assert_has_calls([call("Hit ENTER to delete:"), call("CTRL+C to exit")]) - assert mock_print.call_count == 2 - mock_print_person.assert_called_once_with(person3) - assert mock_print_person.call_count == 1 - mock_hit_enter.assert_called_once_with() - assert mock_hit_enter.call_count == 1 + mock_deleting_person.assert_called_once_with(person3) mock_storage.read_persons.assert_called_once_with() - assert mock_storage.read_persons.call_count == 1 mock_storage.store_persons.assert_called_once_with([person2, person1]) - assert mock_storage.store_persons.call_count == 1 @patch("builtins.print") @patch("pylms.pylms.storage") - @patch("pylms.pylms._interactive_hit_enter") - @patch("pylms.pylms._print_person") + @patch("pylms.pylms.events.deleting_person") + @patch("pylms.pylms.ios.show_person") @patch("pylms.pylms._interactive_select_person") def test_no_person_selected_to_delete( - self, mock_select, mock_print_person, mock_hit_enter, mock_storage, mock_print + self, mock_select, mock_show_person, mock_creating_person, mock_storage, mock_print ): mock_select.return_value = None delete_person("p") mock_select.assert_called_once_with("p") - assert mock_select.call_count == 1 - assert mock_print_person.call_count == 0 - assert mock_hit_enter.call_count == 0 + assert mock_show_person.call_count == 0 + assert mock_creating_person.call_count == 0 assert mock_storage.call_count == 0 assert mock_print.call_count == 0 @@ -261,4 +238,3 @@ def test_no_relationship_definition_match(self, mock_parse_link_request): assert link_persons(request) is None mock_parse_link_request.assert_called_once_with(request) - assert mock_parse_link_request.call_count == 1