2016-12-27 20:24:29 +01:00
|
|
|
"""
|
|
|
|
Mapper tests.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# Fimfarchive, preserves stories from Fimfiction.
|
|
|
|
# Copyright (C) 2015 Joakim Soderlund
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
|
|
|
|
|
|
|
2017-01-07 22:55:12 +01:00
|
|
|
import os
|
2017-01-15 18:07:32 +01:00
|
|
|
from unittest.mock import patch, MagicMock, PropertyMock
|
2017-01-07 22:55:12 +01:00
|
|
|
|
2016-12-27 20:24:29 +01:00
|
|
|
import pytest
|
|
|
|
|
2017-01-15 18:07:32 +01:00
|
|
|
from fimfarchive.exceptions import InvalidStoryError
|
|
|
|
from fimfarchive.mappers import StaticMapper, StoryDateMapper, StoryPathMapper
|
|
|
|
from fimfarchive.stories import Story
|
|
|
|
|
|
|
|
|
|
|
|
CHAPTERS = 'chapters'
|
|
|
|
MODIFIED = 'date_modified'
|
2016-12-27 20:24:29 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestStaticMapper:
|
|
|
|
"""
|
|
|
|
StaticMapper tests.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def value(self):
|
|
|
|
"""
|
|
|
|
Returns a unique instance.
|
|
|
|
"""
|
|
|
|
return object()
|
|
|
|
|
|
|
|
def test_value(self, value):
|
|
|
|
"""
|
|
|
|
Tests returns the supplied value.
|
|
|
|
"""
|
|
|
|
mapper = StaticMapper(value)
|
|
|
|
assert mapper() is value
|
|
|
|
|
|
|
|
def test_default_value(self):
|
|
|
|
"""
|
|
|
|
Tests `None` is returned by default.
|
|
|
|
"""
|
|
|
|
mapper = StaticMapper()
|
|
|
|
assert mapper() is None
|
|
|
|
|
|
|
|
def test_args(self, value):
|
|
|
|
"""
|
|
|
|
Tests callable ignores args.
|
|
|
|
"""
|
|
|
|
mapper = StaticMapper(value)
|
|
|
|
assert mapper(1, 2, 3) is value
|
|
|
|
|
|
|
|
def test_kwargs(self, value):
|
|
|
|
"""
|
|
|
|
Tests callable ignores kwargs.
|
|
|
|
"""
|
|
|
|
mapper = StaticMapper(value)
|
|
|
|
assert mapper(a=1, b=2) is value
|
2017-01-07 22:55:12 +01:00
|
|
|
|
|
|
|
|
2017-01-15 18:07:32 +01:00
|
|
|
class TestStoryDateMapper:
|
|
|
|
"""
|
|
|
|
StoryDateMapper tests.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def mapper(self):
|
|
|
|
"""
|
|
|
|
Returns a new StoryDateMapper.
|
|
|
|
"""
|
|
|
|
return StoryDateMapper()
|
|
|
|
|
|
|
|
def merge(self, story, meta):
|
|
|
|
"""
|
|
|
|
Returns a cloned story containing the supplied meta.
|
|
|
|
"""
|
|
|
|
return Story(
|
|
|
|
key=story.key,
|
|
|
|
fetcher=None,
|
|
|
|
meta=meta,
|
|
|
|
data=story.data,
|
|
|
|
flavors=story.flavors,
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_missing_story(self, mapper):
|
|
|
|
"""
|
|
|
|
Tests `None` is returned when story is `None`.
|
|
|
|
"""
|
|
|
|
assert mapper(None) is None
|
|
|
|
|
|
|
|
def test_invalid_story(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests `None` is returned when `InvalidStoryError` is raised.
|
|
|
|
"""
|
|
|
|
with patch.object(Story, 'meta', new_callable=PropertyMock) as m:
|
|
|
|
m.side_effect = InvalidStoryError
|
|
|
|
|
|
|
|
assert mapper(story) is None
|
|
|
|
assert m.called_once_with(story)
|
|
|
|
|
|
|
|
def test_empty_meta(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests `None` is returned when meta is empty.
|
|
|
|
"""
|
|
|
|
story = self.merge(story, dict())
|
|
|
|
|
|
|
|
assert mapper(story) is None
|
|
|
|
|
|
|
|
def test_meta_without_dates(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests `None` is returned when meta contains no dates.
|
|
|
|
"""
|
|
|
|
meta = {
|
|
|
|
CHAPTERS: [
|
|
|
|
dict(),
|
|
|
|
dict(),
|
|
|
|
dict(),
|
|
|
|
],
|
|
|
|
}
|
|
|
|
|
|
|
|
story = self.merge(story, meta)
|
|
|
|
|
|
|
|
assert mapper(story) is None
|
|
|
|
|
|
|
|
def test_meta_without_chapters(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests timestamp is returned when no chapters are present.
|
|
|
|
"""
|
|
|
|
meta = {
|
|
|
|
MODIFIED: 5,
|
|
|
|
}
|
|
|
|
|
|
|
|
story = self.merge(story, meta)
|
|
|
|
|
|
|
|
assert mapper(story) == 5
|
|
|
|
|
|
|
|
def test_meta_with_none_chapters(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests timestamp is returned when chapters is `None`.
|
|
|
|
"""
|
|
|
|
meta = {
|
|
|
|
MODIFIED: 5,
|
|
|
|
CHAPTERS: None,
|
|
|
|
}
|
|
|
|
|
|
|
|
story = self.merge(story, meta)
|
|
|
|
|
|
|
|
assert mapper(story) == 5
|
|
|
|
|
|
|
|
def test_meta_with_empty_chapters(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests timestamp is returned when chapters is empty.
|
|
|
|
"""
|
|
|
|
meta = {
|
|
|
|
MODIFIED: 5,
|
|
|
|
CHAPTERS: [],
|
|
|
|
}
|
|
|
|
|
|
|
|
story = self.merge(story, meta)
|
|
|
|
|
|
|
|
assert mapper(story) == 5
|
|
|
|
|
|
|
|
def test_meta_with_only_chapter_dates(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests timestamp is returned when only chapter dates are present.
|
|
|
|
"""
|
|
|
|
meta = {
|
|
|
|
CHAPTERS: [
|
|
|
|
{MODIFIED: 3},
|
|
|
|
{MODIFIED: 5},
|
|
|
|
{MODIFIED: 4},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
|
|
|
|
story = self.merge(story, meta)
|
|
|
|
|
|
|
|
assert mapper(story) == 5
|
|
|
|
|
|
|
|
def test_meta_with_only_story_date(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests timestamp is returned when only story date is present.
|
|
|
|
"""
|
|
|
|
meta = {
|
|
|
|
MODIFIED: 5,
|
|
|
|
CHAPTERS: [
|
|
|
|
dict(),
|
|
|
|
dict(),
|
|
|
|
dict(),
|
|
|
|
],
|
|
|
|
}
|
|
|
|
|
|
|
|
story = self.merge(story, meta)
|
|
|
|
|
|
|
|
assert mapper(story) == 5
|
|
|
|
|
|
|
|
def test_meta_with_latest_chapter_date(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests latests timestamp is returned when in chapter dates.
|
|
|
|
"""
|
|
|
|
meta = {
|
|
|
|
MODIFIED: 3,
|
|
|
|
CHAPTERS: [
|
|
|
|
{MODIFIED: 1},
|
|
|
|
{MODIFIED: 5},
|
|
|
|
{MODIFIED: 3},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
|
|
|
|
story = self.merge(story, meta)
|
|
|
|
|
|
|
|
assert mapper(story) == 5
|
|
|
|
|
|
|
|
def test_meta_with_latest_story_date(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests latest timestamp is returned when in story date.
|
|
|
|
"""
|
|
|
|
meta = {
|
|
|
|
MODIFIED: 5,
|
|
|
|
CHAPTERS: [
|
|
|
|
{MODIFIED: 1},
|
|
|
|
{MODIFIED: 3},
|
|
|
|
{MODIFIED: 2},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
|
|
|
|
story = self.merge(story, meta)
|
|
|
|
|
|
|
|
assert mapper(story) == 5
|
|
|
|
|
|
|
|
def test_meta_with_both_latest(self, mapper, story):
|
|
|
|
"""
|
|
|
|
Tests latest timestamp is returned when in both.
|
|
|
|
"""
|
|
|
|
meta = {
|
|
|
|
MODIFIED: 5,
|
|
|
|
CHAPTERS: [
|
|
|
|
{MODIFIED: 1},
|
|
|
|
{MODIFIED: 5},
|
|
|
|
{MODIFIED: 3},
|
|
|
|
],
|
|
|
|
}
|
|
|
|
|
|
|
|
story = self.merge(story, meta)
|
|
|
|
|
|
|
|
assert mapper(story) == 5
|
|
|
|
|
|
|
|
|
2017-01-07 22:55:12 +01:00
|
|
|
class TestStoryPathMapper:
|
|
|
|
"""
|
|
|
|
StoryPathMapper tests.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def test_joins_paths(self, story):
|
|
|
|
"""
|
|
|
|
Tests returns directory joined with story key.
|
|
|
|
"""
|
|
|
|
directory = os.path.join('some', 'directory')
|
|
|
|
path = os.path.join(directory, str(story.key))
|
|
|
|
|
|
|
|
mapper = StoryPathMapper(directory)
|
|
|
|
|
|
|
|
assert mapper(story) == path
|
|
|
|
|
|
|
|
def test_casts_values(self, tmpdir, story):
|
|
|
|
"""
|
|
|
|
Tests casts all values to string when joining.
|
|
|
|
"""
|
|
|
|
directory = MagicMock()
|
|
|
|
directory.__str__.return_value = 'dir'
|
|
|
|
|
|
|
|
story.key = MagicMock()
|
|
|
|
story.key.__str__.return_value = 'key'
|
|
|
|
|
|
|
|
mapper = StoryPathMapper(directory)
|
|
|
|
|
|
|
|
assert mapper(story) == os.path.join('dir', 'key')
|
|
|
|
assert directory.__str__.called_once_with()
|
|
|
|
assert story.key.__str__.called_once_with()
|