fimfarchive/tests/test_mappers.py

346 lines
8.6 KiB
Python
Raw Normal View History

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-11-10 23:38:49 +01:00
from typing import no_type_check, Dict, Any
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
2017-11-10 23:38:49 +01:00
from fimfarchive.flavors import MetaFormat
from fimfarchive.mappers import (
MetaFormatMapper, StaticMapper, StoryDateMapper, StoryPathMapper
)
2017-01-15 18:07:32 +01:00
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, story, value):
2016-12-27 20:24:29 +01:00
"""
Tests returns the supplied value.
"""
mapper = StaticMapper(value)
assert mapper(story) 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.
"""
2017-01-29 00:06:15 +01:00
story = story.merge(meta=dict())
2017-01-15 18:07:32 +01:00
assert mapper(story) is None
def test_meta_without_dates(self, mapper, story):
"""
Tests `None` is returned when meta contains no dates.
"""
2017-11-10 23:38:49 +01:00
meta: Dict[str, Any] = {
2017-01-15 18:07:32 +01:00
CHAPTERS: [
dict(),
dict(),
dict(),
],
}
2017-01-29 00:06:15 +01:00
story = story.merge(meta=meta)
2017-01-15 18:07:32 +01:00
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,
}
2017-01-29 00:06:15 +01:00
story = story.merge(meta=meta)
2017-01-15 18:07:32 +01:00
assert mapper(story).timestamp == 5
2017-01-15 18:07:32 +01:00
def test_meta_with_none_chapters(self, mapper, story):
"""
Tests timestamp is returned when chapters is `None`.
"""
meta = {
MODIFIED: 5,
CHAPTERS: None,
}
2017-01-29 00:06:15 +01:00
story = story.merge(meta=meta)
2017-01-15 18:07:32 +01:00
assert mapper(story).timestamp == 5
2017-01-15 18:07:32 +01:00
def test_meta_with_empty_chapters(self, mapper, story):
"""
Tests timestamp is returned when chapters is empty.
"""
meta = {
MODIFIED: 5,
CHAPTERS: [],
}
2017-01-29 00:06:15 +01:00
story = story.merge(meta=meta)
2017-01-15 18:07:32 +01:00
assert mapper(story).timestamp == 5
2017-01-15 18:07:32 +01:00
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},
],
}
2017-01-29 00:06:15 +01:00
story = story.merge(meta=meta)
2017-01-15 18:07:32 +01:00
assert mapper(story).timestamp == 5
2017-01-15 18:07:32 +01:00
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(),
],
}
2017-01-29 00:06:15 +01:00
story = story.merge(meta=meta)
2017-01-15 18:07:32 +01:00
assert mapper(story).timestamp == 5
2017-01-15 18:07:32 +01:00
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},
],
}
2017-01-29 00:06:15 +01:00
story = story.merge(meta=meta)
2017-01-15 18:07:32 +01:00
assert mapper(story).timestamp == 5
2017-01-15 18:07:32 +01:00
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},
],
}
2017-01-29 00:06:15 +01:00
story = story.merge(meta=meta)
2017-01-15 18:07:32 +01:00
assert mapper(story).timestamp == 5
2017-01-15 18:07:32 +01:00
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},
],
}
2017-01-29 00:06:15 +01:00
story = story.merge(meta=meta)
2017-01-15 18:07:32 +01:00
assert mapper(story).timestamp == 5
2017-01-15 18:07:32 +01:00
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
2017-11-10 23:38:49 +01:00
@no_type_check
2017-01-07 22:55:12 +01:00
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()
2017-11-10 23:38:49 +01:00
class TestMetaFormatMapper:
"""
MetaFormatMapper tests.
"""
@pytest.fixture
def mapper(self):
"""
Returns a meta format mapper instance.
"""
return MetaFormatMapper()
@pytest.fixture(params=['likes', 'dislikes', 'words'])
def alpha(self, request):
"""
Returns an alpha meta key.
"""
return request.param
@pytest.fixture(params=['num_likes', 'num_dislikes', 'num_words'])
def beta(self, request):
"""
Returns a beta meta key.
"""
return request.param
def merge(self, story, *keys):
"""
Returns a story containing the requested meta keys.
"""
meta = {key: i for i, key in enumerate(keys, 1)}
return story.merge(meta=meta)
def test_alpha_format(self, mapper, story, alpha):
"""
Tests alpha meta format is detected.
"""
story = self.merge(story, alpha, 'misc')
assert mapper(story) == MetaFormat.ALPHA
def test_beta_format(self, mapper, story, beta):
"""
Tests beta meta format is detected.
"""
story = self.merge(story, beta, 'misc')
assert mapper(story) == MetaFormat.BETA
def test_conflict(self, mapper, story, alpha, beta):
"""
Tests None is returned for conflicting meta keys.
"""
story = self.merge(story, alpha, beta)
assert mapper(story) is None
def test_existing_flavor(self, mapper, story, beta):
"""
Tests existing flavor takes precedence over meta.
"""
story = story.merge(flavors=[MetaFormat.ALPHA])
story = self.merge(story, beta, 'misc')
assert mapper(story) is MetaFormat.ALPHA