From 4ebe8fe29a1bd23f3cd7d04f40ea18b373c57ece Mon Sep 17 00:00:00 2001 From: Joakim Soderlund Date: Sun, 29 Jan 2017 00:06:15 +0100 Subject: [PATCH] Add merge method to Story --- fimfarchive/stories.py | 15 +++++++++++++++ tests/test_mappers.py | 20 ++++++++++---------- tests/test_selectors.py | 16 +++------------- tests/test_stories.py | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 23 deletions(-) diff --git a/fimfarchive/stories.py b/fimfarchive/stories.py index 7bd4f02..85749c1 100644 --- a/fimfarchive/stories.py +++ b/fimfarchive/stories.py @@ -94,3 +94,18 @@ class Story: self._data = self.fetcher.fetch_data(self.key) return self._data + + def merge(self, **params): + """ + Returns a shallow copy, optionally replacing attributes. + + Args: + **params: Overrides parameters from the current instance. + + Raises: + TypeError: If passed an unexpected parameter. + """ + kwargs = {k.lstrip('_'): v for k, v in vars(self).items()} + kwargs.update(params) + + return type(self)(**kwargs) diff --git a/tests/test_mappers.py b/tests/test_mappers.py index 5c77c5d..eb63dd3 100644 --- a/tests/test_mappers.py +++ b/tests/test_mappers.py @@ -121,7 +121,7 @@ class TestStoryDateMapper: """ Tests `None` is returned when meta is empty. """ - story = self.merge(story, dict()) + story = story.merge(meta=dict()) assert mapper(story) is None @@ -137,7 +137,7 @@ class TestStoryDateMapper: ], } - story = self.merge(story, meta) + story = story.merge(meta=meta) assert mapper(story) is None @@ -149,7 +149,7 @@ class TestStoryDateMapper: MODIFIED: 5, } - story = self.merge(story, meta) + story = story.merge(meta=meta) assert mapper(story) == 5 @@ -162,7 +162,7 @@ class TestStoryDateMapper: CHAPTERS: None, } - story = self.merge(story, meta) + story = story.merge(meta=meta) assert mapper(story) == 5 @@ -175,7 +175,7 @@ class TestStoryDateMapper: CHAPTERS: [], } - story = self.merge(story, meta) + story = story.merge(meta=meta) assert mapper(story) == 5 @@ -191,7 +191,7 @@ class TestStoryDateMapper: ], } - story = self.merge(story, meta) + story = story.merge(meta=meta) assert mapper(story) == 5 @@ -208,7 +208,7 @@ class TestStoryDateMapper: ], } - story = self.merge(story, meta) + story = story.merge(meta=meta) assert mapper(story) == 5 @@ -225,7 +225,7 @@ class TestStoryDateMapper: ], } - story = self.merge(story, meta) + story = story.merge(meta=meta) assert mapper(story) == 5 @@ -242,7 +242,7 @@ class TestStoryDateMapper: ], } - story = self.merge(story, meta) + story = story.merge(meta=meta) assert mapper(story) == 5 @@ -259,7 +259,7 @@ class TestStoryDateMapper: ], } - story = self.merge(story, meta) + story = story.merge(meta=meta) assert mapper(story) == 5 diff --git a/tests/test_selectors.py b/tests/test_selectors.py index 8244e86..26b926d 100644 --- a/tests/test_selectors.py +++ b/tests/test_selectors.py @@ -46,16 +46,6 @@ class TestUpdateSelector: """ return UpdateSelector() - def merge(self, story, **params): - """ - Returns a cloned story, optionally overriding parameters. - """ - data = vars(story) - data = {k.lstrip('_'): v for k, v in data.items()} - data.update(params) - - return Story(**data) - def populate(self, story, date=0): """ Returns a cloned story populated with chapter meta. @@ -70,7 +60,7 @@ class TestUpdateSelector: ], } - return self.merge(story, meta=meta) + return story.merge(meta=meta) def test_filter_empty_with_chapters(self, selector, story): """ @@ -98,7 +88,7 @@ class TestUpdateSelector: 'chapters': [] } - story = self.merge(story, meta=meta) + story = story.merge(meta=meta) selected = selector.filter_empty(story) assert selected is None @@ -268,7 +258,7 @@ class TestUpdateSelector: fetcher = Mock(spec=Fetcher) fetcher.fetch_data.side_effect = InvalidStoryError - new = self.merge(story, fetcher=fetcher, data=None) + new = story.merge(fetcher=fetcher, data=None) selected = selector(old, new) diff --git a/tests/test_stories.py b/tests/test_stories.py index 865a120..78af850 100644 --- a/tests/test_stories.py +++ b/tests/test_stories.py @@ -211,3 +211,40 @@ class TestStory: assert story.flavors is not flavors assert story.flavors == {flavor.A} assert type(story.flavors) == set + + def test_merge_without_parameters(self, story): + """ + Tests `merge` returns new story containing current attributes. + """ + merge = story.merge() + + assert merge is not story + assert merge.data is story.data + assert merge.meta is story.meta + + for k, v in vars(story).items(): + assert getattr(merge, k) == v + + def test_merge_with_parameters(self, story): + """ + Tests `merge` can override attributes. + """ + meta = dict(story.meta) + merge = story.merge(meta=meta) + + assert meta is not story.meta + assert meta is merge.meta + + def test_merge_with_invalid_state(self, story): + """ + Tests `merge` is affected by validation in `__init__`. + """ + with pytest.raises(ValueError): + story.merge(fetcher=None, meta=None) + + def test_merge_with_invalid_arguments(self, story): + """ + Tests `merge` cannot create story using invalid arguments. + """ + with pytest.raises(TypeError): + story.merge(alpaca=True)