def test_set_first_last_issues_nonempty(): with mock.patch('apps.gcd.models.series.Series.save'), \ mock.patch('apps.gcd.models.series.models.QuerySet.order_by') \ as order_mock, \ mock.patch('apps.gcd.models.issue.Issue.series'): s = Series(issue_count=0) # Create some issues attached to the series (but not first/last). i1 = Issue(number='1', sort_code=0, series=s) i2 = Issue(number='2', sort_code=1, series=s) issue_list = [i1, i2] # Ensure that the active issues list is empty (i1 & i2 not attached). def index_faker(index): return issue_list[index] qs = mock.MagicMock(spec=QuerySet) qs.count.return_value = 2 qs.__getitem__.side_effect = index_faker qs.__iter__.return_value = iter(issue_list) order_mock.return_value = qs s.set_first_last_issues() assert s.first_issue is i1 assert s.last_issue is i2 s.save.assert_called_once_with()
def test_set_first_last_issues_empty(): with mock.patch('apps.gcd.models.series.Series.save'), \ mock.patch('apps.gcd.models.series.models.QuerySet.order_by') \ as order_mock, \ mock.patch('apps.gcd.models.issue.Issue.series'): # Create some issues that are set as first/last even though no longer # attached to the series. i1 = Issue(number='1', sort_code=0) i2 = Issue(number='2', sort_code=1) issue_list = [i1, i2] s = Series(issue_count=0, first_issue=i1, last_issue=i2) # Ensure that the active issues list is empty (i1 & i2 not attached). def index_faker(index): return issue_list[index] qs = mock.MagicMock(spec=QuerySet) qs.count.return_value = 0 qs.__getitem__.side_effect = index_faker order_mock.return_value = qs s.set_first_last_issues() assert s.first_issue is None assert s.last_issue is None s.save.assert_called_once_with()
def test_series_relative_bonds(): FILTER_ARGS = {'any_filter': 'any_value'} FIRST_RESULTS = [1, 2, 3] SECOND_RESULTS = [4, 5] ITER_RESULTS = itertools.chain(FIRST_RESULTS, SECOND_RESULTS) srb_path = 'apps.gcd.models.seriesbond.SeriesRelativeBond.__new__' to_path = '%s.to_series_bond' % SERIES_PATH from_path = '%s.from_series_bond' % SERIES_PATH with mock.patch(srb_path) as srb_mock, \ mock.patch(to_path) as to_mock, mock.patch(from_path) as from_mock: to_mock.filter.return_value = FIRST_RESULTS from_mock.filter.return_value = SECOND_RESULTS # Make the SRB constructor return a different value each time so # that we can verify that the calls went through in the right order. srb_mock.side_effect = lambda self, series, x: -x s = Series() srb = s.series_relative_bonds(**FILTER_ARGS) to_mock.filter.assert_called_once_with(**FILTER_ARGS) from_mock.filter.assert_called_once_with(**FILTER_ARGS) assert srb == [-x for x in ITER_RESULTS]
def test_delete(): with mock.patch('%s.save' % SERIES_PATH): s = Series() s.delete() assert s.deleted is True s.save.assert_called_once_with()
def test_has_dependents(issues, issue_revisions): with mock.patch('%s.active_issues' % SERIES_PATH) as is_mock, \ mock.patch('%s.active_set' % REVMGR_PATH) as rev_mock: rev_mock.return_value.exists.return_value = issue_revisions s = Series() is_mock.return_value.exists.return_value = issues assert s.has_dependents() is any((issue_revisions, issues))
def test_has_keywords(): with mock.patch('%s.keywords' % SERIES_PATH) as kw_mock: s = Series() kw_mock.exists.return_value = False assert s.has_keywords() is False kw_mock.exists.return_value = True assert s.has_keywords() is True
def test_active_base_issues_variant_count(): with mock.patch('%s.active_base_issues' % SERIES_PATH) as ab_issues, \ mock.patch('apps.gcd.models.series.Count') as count_class_mock: count_mock = mock.MagicMock(spec=Count) count_class_mock.return_value = count_mock ab_issues.return_value.annotate.return_value = 100 s = Series() assert s.active_base_issues_variant_count() == 100 s.active_base_issues.return_value.annotate.assert_called_once_with( variant_count=count_mock)
def test_series_changed(deleted, has_prev, changed): s1 = Series(name='One') s2 = Series(name='Two') if changed else s1 with mock.patch('%s.previous_revision' % IREV, new_callable=mock.PropertyMock) as prev_mock: rev = IssueRevision(series=s2) rev.deleted = deleted if has_prev: prev_mock.return_value = IssueRevision(series=s1) else: prev_mock.return_value = None sc = rev.series_changed assert sc is changed
def test_handle_dependents_to_singleton(year_began, key_date): with mock.patch('%s.save' % IREV) as save_mock, \ mock.patch('%s.commit_to_display' % IREV) as commit_mock: # Make the IssueRevision that would be returned by the patched # constructor call. Only patch the methods for this. s = Series() c = Changeset() ir_params = { 'changeset': c, 'series': s, 'after': None, 'number': '[nn]', 'publication_date': year_began, } ir = IssueRevision(**ir_params) with mock.patch(IREV) as ir_class_mock: # Now patch the IssueRevision constructor itself. ir_class_mock.return_value = ir sr = SeriesRevision(changeset=c, series=s, is_singleton=True, year_began=year_began) sr._handle_dependents({'to is_singleton': True}) ir_class_mock.assert_called_once_with(**ir_params) assert ir.key_date == key_date save_mock.assert_called_once_with() assert not commit_mock.called
def multiple_issue_revs(): with mock.patch('apps.oi.models.Issue.objects') as obj_mock, \ mock.patch('%s._same_series_revisions' % IREV) as same_mock, \ mock.patch('%s._same_series_open_with_after' % IREV) as after_mock: same_mock.return_value.filter.return_value.exists.return_value = False s = Series(name='Some Series') # Issues already created, so they have sort codes. i1 = Issue(number='1', series=s, sort_code=0) i4 = Issue(number='4', series=s, sort_code=1) i5 = Issue(number='5', series=s, sort_code=2) i1.save = mock.MagicMock() i4.save = mock.MagicMock() i5.save = mock.MagicMock() # Issues being created, no sort codes yet. i2 = Issue(number='2', series=s) i3 = Issue(number='3', series=s) c = Changeset() rev2 = IssueRevision(changeset=c, issue=i2, series=s, revision_sort_code=1) rev3 = IssueRevision(changeset=c, issue=i3, series=s, revision_sort_code=2) yield ((i1, i2, i3, i4, i5), (rev2, rev3), after_mock, obj_mock, same_mock)
def test_counts_non_comics(): with mock.patch('apps.gcd.models.story.Story.objects'), \ mock.patch('apps.gcd.models.series.Series.scan_count', new_callable=mock.PropertyMock) as cc_mock: cc_mock.return_value = 15 Story.objects.filter.return_value \ .exclude.return_value \ .count.return_value = 100 s = Series(is_comics_publication=False) assert s.stat_counts() == { 'covers': 15, 'stories': 100, 'publisher series': 1, }
def test_handle_prerequisites_deleted_no_issue_revision(is_singleton, deleted): with mock.patch('apps.oi.models.IssueRevision.clone') \ as cr_mock: SeriesRevision(series=Series(), previous_revision=SeriesRevision(), is_singleton=is_singleton, deleted=deleted) assert not cr_mock.called
def test_same_series_revisions(): with mock.patch('%s.issuerevisions' % CSET) as irevs_mock: series = Series() ss_revs = mock.MagicMock() irevs_mock.filter.return_value = ss_revs rev = IssueRevision(changeset=Changeset(), series=series) assert rev._same_series_revisions() == ss_revs irevs_mock.filter.assert_called_once_with(series=series)
def test_has_series_bonds(): to_path = '%s.to_series_bond' % SERIES_PATH from_path = '%s.from_series_bond' % SERIES_PATH with mock.patch(to_path) as to_mock, mock.patch(from_path) as from_mock: s = Series() to_mock.exists.return_value = False from_mock.exists.return_value = False assert s.has_series_bonds() is False to_mock.exists.return_value = True assert s.has_series_bonds() is True from_mock.exists.return_value = True assert s.has_series_bonds() is True to_mock.exists.return_value = False assert s.has_series_bonds() is True
def test_has_tracking(): with mock.patch('%s.has_series_bonds' % SERIES_PATH) as sb_mock: s = Series() s.tracking_notes = '' sb_mock.return_value = False assert s.has_tracking() is False s.tracking_notes = 'any text' assert s.has_tracking() == 'any text' sb_mock.return_value = True assert s.has_tracking() == 'any text' s.tracking_notes = '' assert s.has_tracking() is True
def pre_commit_rev(): with mock.patch('%s._same_series_revisions' % IREV), \ mock.patch('%s._same_series_open_with_after' % IREV): s = Series(name='Some Series') i = Issue(number='1', series=s) rev = IssueRevision( changeset=Changeset(), issue=i, series=s, previous_revision=IssueRevision(changeset=Changeset(), issue=i)) yield rev
def test_post_save_no_series_changed(patch_for_optional_move): patch_for_optional_move.return_value = False s = Series(name="Test Series") i = Issue(series=s) rev = IssueRevision(changeset=Changeset(), issue=i, series=s, previous_revision=IssueRevision(issue=i, series=s)) rev._post_save_object({}) # If we fail the check, there will be two calls here instead of one. s.set_first_last_issues.assert_called_once_with()
def test_counts_comics(): series_path = 'apps.gcd.models.series.Series' issues_path = '%s.active_base_issues' % series_path variants_path = '%s.active_non_base_variants' % series_path indexes_path = '%s.active_indexed_issues' % series_path cover_count_path = '%s.scan_count' % series_path with mock.patch('apps.gcd.models.story.Story.objects'), \ mock.patch(issues_path) as is_mock, \ mock.patch(variants_path) as v_mock, \ mock.patch(indexes_path) as ix_mock, \ mock.patch(cover_count_path, new_callable=mock.PropertyMock) as cc_mock: is_mock.return_value.filter.return_value.count.return_value = 30 v_mock.return_value.count.return_value = 20 ix_mock.return_value.count.return_value = 10 cc_mock.return_value = 15 Story.objects.filter.return_value \ .exclude.return_value \ .count.return_value = 100 s = Series(is_comics_publication=True) assert s.stat_counts() == { 'series': 1, 'issues': 30, 'variant issues': 20, 'issue indexes': 10, 'covers': 15, 'stories': 100, } Story.objects.filter.assert_called_once_with(issue__series=s) Story.objects.filter.return_value.exclude.assert_called_once_with( deleted=True) is_mock.return_value.filter.assert_called_once_with(variant_of=None)
def patched_for_save(): def story_uni(self): return self.title def issue_uni(self): return self.number # with mock.patch('apps.oi.models.LinkRevision.save') as save_mock, \ with mock.patch('django.db.models.base.Model.save') as save_mock, \ mock.patch('apps.gcd.models.story.Story.__str__', story_uni), \ mock.patch('apps.gcd.models.issue.Issue.__str__', issue_uni): s = Series(name='Test Series') yield (save_mock, Story(title='origin', issue=Issue(number='1', title='o issue', series=s)), Story(title='target', issue=Issue(number='9', title='t issue', series=s)))
def patch_for_move(patch_for_optional_move): patch_for_optional_move.return_value = True old = Series(name="Old Test Series") new = Series(name="New Test Series") i = Issue(series=old) rev = IssueRevision(changeset=Changeset(), issue=i, series=new, previous_revision=IssueRevision(issue=i, series=old)) # Need to track these calls independently, so replace class-level mock # with instance-level mocks. old.save = mock.MagicMock() new.save = mock.MagicMock() old.set_first_last_issues = mock.MagicMock() new.set_first_last_issues = mock.MagicMock() yield rev, i, old, new
def _build_reprint(origin_story, target_story, origin_base, notes): s = Series(name='Test Series') origin_issue = Issue(id=1, number='1', series=s) target_issue = Issue(id=2, number='2', series=s) origin = Story(id=1, title='origin', issue=origin_issue) target = Story(id=2, title='target', issue=target_issue) assert '%s' % origin == 'origin' r = Reprint(origin_issue=origin_issue, target_issue=target_issue, origin=(origin if origin_story else None), target=(target if target_story else None), notes=notes) base_issue = origin_issue if origin_base else target_issue # Pull stories from the reprint because they may be None. far_story = r.target if origin_base else r.origin far_issue = target_issue if origin_base else origin_issue return r, base_issue, far_issue, far_story
def test_get_major_changes_deleted(patched_series_class): old = Series(country=COUNTRY_ONE, language=LANGUAGE_ONE, publisher=PUBLISHER_ONE, is_comics_publication=True, is_current=True, is_singleton=False) new = SeriesRevision(changeset=NO_DB_CHANGESET, series=old, country=COUNTRY_ONE, language=LANGUAGE_ONE, publisher=PUBLISHER_ONE, is_comics_publication=True, is_current=True, is_singleton=False, previous_revision=SeriesRevision(), deleted=True) c = new._get_major_changes() assert c == { 'publisher changed': True, 'country changed': True, 'language changed': True, 'is_comics_publication changed': True, 'is_singleton changed': True, 'is_current changed': True, 'to is_comics_publication': False, 'from is_comics_publication': True, 'to is_singleton': False, 'from is_singleton': False, 'to is_current': False, 'from is_current': True, 'old publisher': PUBLISHER_ONE, 'new publisher': None, 'old country': COUNTRY_ONE, 'new country': None, 'old language': LANGUAGE_ONE, 'new language': None, }
def series_and_revision(): """ Tuple of series, series revision, and a mock of update_all_counts. For use with _adjust_stats() testing. The series and series revision are connected but not saved, and database access is patched. """ # Don't use the "constant" PUBLISHER_ONE/TWO as we expect # the mock call state to be changed. old_pub = mock.MagicMock() new_pub = mock.MagicMock() with mock.patch('%s.update_cached_counts' % SERIES), \ mock.patch('%s.publisher' % SERIES), \ mock.patch('%s.save' % SERIES), \ mock.patch('apps.gcd.models.publisher.Publisher.save'), \ mock.patch('%s.changeset' % SREV), \ mock.patch('%s.publisher' % SREV), \ mock.patch('%s.objects.update_all_counts' % CSTATS) as uac_mock: s = Series(publisher=old_pub) rev = SeriesRevision(changeset=mock.MagicMock(), series=s, publisher=new_pub) yield s, rev, uac_mock
def test_update_cached_counts_none(f_mock): s = Series(issue_count=0) s.update_cached_counts({}) assert s.issue_count == 0
def test_update_cached_counts_add(f_mock): s = Series(issue_count=0) s.update_cached_counts(DELTAS) assert s.issue_count == ISSUE_COUNT + DELTAS['series issues']
def test_update_cached_counts_subtract(f_mock): s = Series(issue_count=0) s.update_cached_counts(DELTAS, negate=True) assert s.issue_count == ISSUE_COUNT - DELTAS['series issues']
def pre_save_mocks(): with mock.patch('%s.get_ongoing_reservation' % SERIES) as get_ongoing, \ mock.patch('apps.gcd.models.series.Series.scan_count', new_callable=mock.PropertyMock) as scan_count: yield SeriesRevision(series=Series()), get_ongoing, scan_count
def test_post_assign_fields_leading_article(leading_article, name, sort_name): s = Series(name=name) sr = SeriesRevision(leading_article=leading_article, name=name, series=s) sr._post_assign_fields({}) assert s.sort_name == sort_name
def test_active_indexed_issues(issues_qs): s = Series() assert s.active_indexed_issues() == issues_qs s.active_issues.return_value.exclude.assert_called_once_with( is_indexed=INDEXED['skeleton'])
def test_counts_deleted(): s = Series(is_comics_publication=True) s.deleted = True assert s.stat_counts() == {}
def test_stat_counts(is_comics, deleted): story = Story(issue=Issue(series=Series(is_comics_publication=True))) story.deleted = deleted assert story.stat_counts() == {} if deleted else {'stories': 1}
def test_active_non_base_variants(issues_qs): s = Series() assert s.active_non_base_variants() == issues_qs s.active_issues.return_value.exclude.assert_called_once_with( variant_of=None)