Source code for tests.logentries_tests

#!/usr/bin/env python3
"""Test logentries module."""
#
# (C) Pywikibot team, 2015-2023
#
# Distributed under the terms of the MIT license.
#
from __future__ import annotations

import datetime
import unittest
from contextlib import suppress

import pywikibot
from pywikibot.exceptions import HiddenKeyError, NoMoveTargetError
from pywikibot.family import AutoFamily
from pywikibot.logentries import (
    LogEntryFactory,
    OtherLogEntry,
    UserTargetLogEntry,
)
from tests import unittest_print
from tests.aspects import MetaTestCaseClass, TestCase
from tests.utils import skipping


[docs] class TestLogentriesBase(TestCase): """ Base class for log entry tests. It uses the German Wikipedia for a current representation of the log entries and the test Wikipedia for the future representation. It also tests on a wiki with MW <= 1.27 to check that the module works with older wikis. It currently uses infogalacticwiki which as of this commit uses 1.27.1. """ sites = { 'tewp': { 'family': 'wikipedia', 'code': 'test', 'target': 'Main Page on wheels', }, 'dewp': { 'family': 'wikipedia', 'code': 'de', 'target': 'Hauptseite', }, 'enwow': { 'family': 'wowwiki', 'code': 'en', 'target': None, }, 'old': { 'family': AutoFamily('infogalactic', 'https://infogalactic.com/info/Main_Page'), 'code': 'en', 'target': None, } } def _get_logentry(self, logtype): """Retrieve a single log entry.""" if self.site_key == 'old': # This is an assertion as the tests don't make sense with newer # MW versions and otherwise it might not be visible that the test # isn't run on an older wiki. self.assertEqual(self.site.mw_version, '1.27.1') with skipping(StopIteration, msg=f'No entry found for {logtype!r}'): le = next(self.site.logevents(logtype=logtype, total=1)) return le def _test_logevent(self, logtype): """Test a single logtype entry.""" logentry = self._get_logentry(logtype) self.assertIn(logtype, logentry.__class__.__name__.lower()) self.assertEqual(logentry._expected_type, logtype) if logtype not in LogEntryFactory._logtypes: self.assertIsInstance(logentry, OtherLogEntry) # check that we only have the new implementation self.assertNotIn(logentry.type(), logentry.data) self.assertIsInstance(logentry.action(), str) try: self.assertIsInstance(logentry.comment(), str) self.assertIsInstance(logentry.user(), str) self.assertEqual(logentry.user(), logentry['user']) except HiddenKeyError as e: # pragma: no cover self.assertRegex( str(e), r"Log entry \([^)]+\) has a hidden '\w+' key and you " r"don't have permission to view it") except KeyError as e: # pragma: no cover self.assertRegex(str(e), "Log entry ([^)]+) has no 'comment' key") else: self.assertEqual(logentry.comment(), logentry['comment']) self.assertIsInstance(logentry.logid(), int) self.assertIsInstance(logentry.timestamp(), pywikibot.Timestamp) if 'title' in logentry.data: # title may be missing self.assertIsInstance(logentry.ns(), int) self.assertIsInstance(logentry.pageid(), int) # test new UserDict style self.assertEqual(logentry.data['title'], logentry['title']) self.assertEqual(logentry.ns(), logentry['ns']) self.assertEqual(logentry.pageid(), logentry['pageid']) self.assertGreaterEqual(logentry.ns(), -2) self.assertGreaterEqual(logentry.pageid(), 0) if logtype == 'block' and logentry.isAutoblockRemoval: self.assertIsInstance(logentry.page(), int) elif isinstance(logentry, UserTargetLogEntry): self.assertIsInstance(logentry.page(), pywikibot.User) elif logtype == 'upload': self.assertIsInstance(logentry.page(), pywikibot.FilePage) else: self.assertIsInstance(logentry.page(), pywikibot.Page) else: # pragma: no cover with self.assertRaises(KeyError): logentry.page() self.assertEqual(logentry.type(), logtype) self.assertGreaterEqual(logentry.logid(), 0) # test new UserDict style self.assertEqual(logentry.type(), logentry['type']) self.assertEqual(logentry.logid(), logentry['logid'])
[docs] class LogentriesTestMeta(MetaTestCaseClass): """Test meta class for TestLogentries.""" def __new__(cls, name, bases, dct): """Create the new class.""" def test_method(logtype): def test_logevent(self, key): """Test a single logtype entry.""" site = self.sites[key]['site'] if logtype not in site.logtypes: self.skipTest( f'{key}: {logtype!r} logtype not available on {site}.') if logtype == 'upload' and key == 'old': self.skipTest(f'{key}: frequently timeouts for ' f'{logtype!r} logtype on {site} (T334729).') self._test_logevent(logtype) return test_logevent # create test methods for the support logtype classes for logtype in LogEntryFactory._logtypes: cls.add_method(dct, f'test_{logtype.title()}Entry', test_method(logtype)) return super().__new__(cls, name, bases, dct)
[docs] class TestLogentries(TestLogentriesBase, metaclass=LogentriesTestMeta): """Test general LogEntry properties."""
[docs] class TestSimpleLogentries(TestLogentriesBase): """Test logentry classes without special classes.""" def test_simple_entries(self, key): """Test those entries which don't have an extra LogEntry subclass.""" # Unfortunately it's not possible to use the metaclass to create a # bunch of test methods for this too as the site instances haven't # been initialized yet. for simple_type in (self.site.logtypes - set(LogEntryFactory._logtypes)): if not simple_type: # paraminfo also reports an empty string as a type continue # pragma: no cover try: self._test_logevent(simple_type) except StopIteration: # pragma: no cover unittest_print( f'Unable to test "{simple_type}" on "{key}" because there' ' are no log entries with that type.')
[docs] class TestLogentryParams(TestLogentriesBase): """Test LogEntry properties specific to their action.""" def test_block_entry(self, key): """Test BlockEntry methods.""" # only 'block' entries can be tested for logentry in self.site.logevents(logtype='block', total=5): if logentry.action() == 'block': self.assertIsInstance(logentry.flags(), list) # Check that there are no empty strings for flag in logentry.flags(): self.assertIsInstance(flag, str) self.assertNotEqual(flag, '') if logentry.expiry() is not None: self.assertIsInstance(logentry.expiry(), pywikibot.Timestamp) self.assertIsInstance(logentry.duration(), datetime.timedelta) self.assertEqual( logentry.timestamp() + logentry.duration(), logentry.expiry()) else: self.assertIsNone(logentry.duration()) break def test_rights_entry(self, key): """Test RightsEntry methods.""" logentry = self._get_logentry('rights') self.assertIsInstance(logentry.oldgroups, list) self.assertIsInstance(logentry.newgroups, list) def test_move_entry(self, key): """Test MoveEntry methods.""" logentry = self._get_logentry('move') if 'actionhidden' in logentry: self.skipTest( f'move action was hidden due to {logentry.comment()}') self.assertIsInstance(logentry.target_ns, pywikibot.site.Namespace) self.assertEqual(logentry.target_page.namespace(), logentry.target_ns.id) self.assertIsInstance(logentry.target_title, str) self.assertIsInstance(logentry.target_page, pywikibot.Page) self.assertIsInstance(logentry.suppressedredirect(), bool) def test_patrol_entry(self, key): """Test PatrolEntry methods.""" logentry = self._get_logentry('patrol') self.assertIsInstance(logentry.current_id, int) self.assertIsInstance(logentry.previous_id, int) self.assertIsInstance(logentry.auto, bool) def test_moved_target(self, key): """Test moved_target method.""" # main page was moved around mainpage = self.get_mainpage(self.site) if self.sites[key]['target'] is None: self.skipTest('No moved target') target = mainpage.moved_target() self.assertIsInstance(target, pywikibot.Page) self.assertEqual(target.title(), self.sites[key]['target']) # main page was moved back again, we test it. self.assertEqual(mainpage, target.moved_target())
[docs] def test_moved_target_fail_old(self): """Test moved_target method failing on older wiki.""" site = self.get_site('old') with self.assertRaises(NoMoveTargetError): self.get_mainpage(site).moved_target()
[docs] def test_moved_target_fail_de(self): """Test moved_target method failing on de-wiki.""" page = pywikibot.Page(self.get_site('dewp'), 'Main Page') with self.assertRaises(NoMoveTargetError): page.moved_target()
def test_thanks_page(self, key): """Test Thanks page method return type.""" if not self.site.has_extension('Thanks'): self.skipTest('Thanks extension not available.') logentry = self._get_logentry('thanks') self.assertIsInstance(logentry.page(), pywikibot.User)
[docs] def test_equality(self): """Test equality of LogEntry instances.""" site = self.get_site('dewp') other_site = self.get_site('tewp') gen1 = site.logevents(reverse=True, total=2) gen2 = site.logevents(reverse=True, total=2) le1 = next(gen1) le2 = next(gen2) le3 = next(other_site.logevents(reverse=True, total=1)) le4 = next(gen1) le5 = next(gen2) self.assertEqual(le1, le2) self.assertFalse(le1 != le2) # noqa: H204 self.assertNotEqual(le1, le3) self.assertNotEqual(le1, site) self.assertIsInstance(le4, OtherLogEntry) self.assertIsInstance(le5, OtherLogEntry) self.assertEqual(type(le4), type(le5))
if __name__ == '__main__': with suppress(SystemExit): unittest.main()