class BaseTestCase(unittest.TestCase): """ The base test class for Bodhi. This class configures the global scoped session with a test database. The test database makes use of nested transactions to provide a clean slate for each test. Tests may call both ``commit`` and ``rollback`` on the database session they acquire from ``bodhi.server.Session``. """ _populate_db = True app_settings = { 'authtkt.secret': 'sssshhhhhh', 'authtkt.secure': False, 'mako.directories': 'bodhi:server/templates', 'session.type': 'memory', 'session.key': 'testing', 'session.secret': 'foo', 'dogpile.cache.backend': 'dogpile.cache.memory', 'dogpile.cache.expiration_time': 0, 'cache.type': 'memory', 'cache.regions': 'default_term, second, short_term, long_term', 'cache.second.expire': '1', 'cache.short_term.expire': '60', 'cache.default_term.expire': '300', 'cache.long_term.expire': '3600', 'acl_system': 'dummy', 'buildsystem': 'dummy', 'important_groups': 'proventesters provenpackager releng', 'admin_groups': 'bodhiadmin releng', 'admin_packager_groups': 'provenpackager', 'mandatory_packager_groups': 'packager', 'critpath_pkgs': 'kernel', 'critpath.num_admin_approvals': 0, 'bugtracker': 'dummy', 'stats_blacklist': 'bodhi autoqa', 'system_users': 'bodhi autoqa', 'max_update_length_for_ui': '70', 'openid.provider': 'https://id.stg.fedoraproject.org/openid/', 'openid.url': 'https://id.stg.fedoraproject.org', 'test_case_base_url': 'https://fedoraproject.org/wiki/', 'openid_template': '{username}.id.fedoraproject.org', 'site_requirements': u'rpmlint', 'resultsdb_api_url': 'whatever', 'base_address': 'http://0.0.0.0:6543', 'cors_connect_src': 'http://0.0.0.0:6543', 'cors_origins_ro': 'http://0.0.0.0:6543', 'cors_origins_rw': 'http://0.0.0.0:6543', 'sqlalchemy.url': DEFAULT_DB } def setUp(self): """Set up Bodhi for testing.""" # Ensure "cached" objects are cleared before each test. models.Release._all_releases = None models.Release._tag_cache = None if engine is None: self.engine = _configure_test_db() else: self.engine = engine self.connection = self.engine.connect() models.Base.metadata.create_all(bind=self.connection) self.transaction = self.connection.begin() Session.remove() Session.configure(bind=self.engine, autoflush=False, expire_on_commit=False) self.Session = Session self.db = Session() self.db.begin_nested() if self._populate_db: populate(self.db) bugs.set_bugtracker() buildsys.setup_buildsystem({'buildsystem': 'dev'}) self._request_sesh = mock.patch( 'bodhi.server.webapp._complete_database_session', webapp._rollback_or_commit) self._request_sesh.start() # Create the test WSGI app one time. We should avoid creating too many # of these since Pyramid holds global references to the objects it creates # and this results in a substantial memory leak. Long term we should figure # out how to make Pyramid forget about these. global _app if _app is None: # We don't want to call Session.remove() during the unit tests, because that will # trigger the restart_savepoint() callback defined above which will remove the data # added by populate(). with mock.patch('bodhi.server.Session.remove'): _app = BodhiTestApp( main({}, testing=u'guest', **self.app_settings)) self.app = _app def get_csrf_token(self, app=None): """ Return a CSRF token that can be used by tests as they test the REST API. Args: app (BodhiTestApp): The app to use to get the token. Defaults to None, which will use self.app. Returns: basestring: A CSRF token. """ if not app: app = self.app return app.get('/csrf', headers={ 'Accept': 'application/json' }).json_body['csrf_token'] def get_update(self, builds='bodhi-2.0-1.fc17', stable_karma=3, unstable_karma=-3): """ Return a dict describing an update. This is useful for tests that want to POST to the API to create an update. Args: builds (basestring): A comma-separated list of NVRs to include in the update. stable_karma (int): The stable karma threshold to use on the update. unstable_karma (int): The unstable karma threshold to use on the update. """ if isinstance(builds, list): builds = ','.join(builds) if not isinstance(builds, str): builds = builds.encode('utf-8') return { 'builds': builds, 'bugs': u'', 'notes': u'this is a test update', 'type': u'bugfix', 'autokarma': True, 'stable_karma': stable_karma, 'unstable_karma': unstable_karma, 'requirements': u'rpmlint', 'require_bugs': False, 'require_testcases': True, 'csrf_token': self.get_csrf_token(), } def tearDown(self): """Roll back all the changes from the test and clean up the session.""" self._request_sesh.stop() self.db.close() self.transaction.rollback() self.connection.close() Session.remove() def create_update(self, build_nvrs, release_name=u'F17'): """ Create and return an Update with the given iterable of build_nvrs. Each build_nvr should be a tuple of strings describing the name, version, and release for the build. For example, build_nvrs might look like this: ((u'bodhi', u'2.3.3', u'1.fc24'), (u'python-fedora-atomic-composer', u'2016.3', u'1.fc24')) You can optionally pass a release_name to select a different release than the default F17, but the release must already exist in the database. This is a convenience wrapper around bodhi.tests.server.create_update so that tests can just call self.create_update() and not have to pass self.db. Args: build_nvrs (iterable): An iterable of 3-tuples. Each 3-tuple is strings that express the name, version, and release of the desired build. release_name (basestring): The name of the release to associate with the new updates. Returns: bodhi.server.models.Update: The new update. """ return create_update(self.db, build_nvrs, release_name) def create_release(self, version): """ Create and return a :class:`Release` with the given version. Args: version (basestring): A string of the version of the release, such as 27. Returns: bodhi.server.models.Release: A new release. """ release = models.Release( name=u'F{}'.format(version), long_name=u'Fedora {}'.format(version), id_prefix=u'FEDORA', version=u'{}'.format(version.replace('M', '')), dist_tag=u'f{}'.format(version), stable_tag=u'f{}-updates'.format(version), testing_tag=u'f{}-updates-testing'.format(version), candidate_tag=u'f{}-updates-candidate'.format(version), pending_signing_tag=u'f{}-updates-testing-signing'.format(version), pending_testing_tag=u'f{}-updates-testing-pending'.format(version), pending_stable_tag=u'f{}-updates-pending'.format(version), override_tag=u'f{}-override'.format(version), branch=u'f{}'.format(version), state=models.ReleaseState.current) self.db.add(release) models.Release._all_releases = None models.Release._tag_cache = None self.db.flush() return release
class BaseTestCaseMixin: """ The base test class for Bodhi. This class configures the global scoped session with a test database before calling test methods. The test database makes use of nested transactions to provide a clean slate for each test. Tests may call both ``commit`` and ``rollback`` on the database session they acquire from ``bodhi.server.Session``. """ _populate_db = True def _setup_method(self): """Set up Bodhi for testing.""" self.config = testing.setUp() self.app_settings = get_appsettings(os.environ["BODHI_CONFIG"]) config.config.clear() config.config.load_config(self.app_settings) # Ensure "cached" objects are cleared before each test. models.Release.clear_all_releases_cache() models.Release._tag_cache = None if engine is None: self.engine = _configure_test_db(config.config["sqlalchemy.url"]) else: self.engine = engine self.connection = self.engine.connect() models.Base.metadata.create_all(bind=self.connection) self.transaction = self.connection.begin() Session.remove() Session.configure(bind=self.engine, autoflush=False, expire_on_commit=False) self.Session = Session self.db = Session() self.db.begin_nested() if self._populate_db: populate(self.db) bugs.set_bugtracker() buildsys.setup_buildsystem({'buildsystem': 'dev'}) self._request_sesh = mock.patch( 'bodhi.server.webapp._complete_database_session', webapp._rollback_or_commit) self._request_sesh.start() # Create the test WSGI app one time. We should avoid creating too many # of these since Pyramid holds global references to the objects it creates # and this results in a substantial memory leak. Long term we should figure # out how to make Pyramid forget about these. global _app if _app is None: # We don't want to call Session.remove() during the unit tests, because that will # trigger the restart_savepoint() callback defined above which will remove the data # added by populate(). with mock.patch('bodhi.server.Session.remove'): _app = TestApp( main({}, testing='guest', session=self.db, **self.app_settings)) self.app = _app self.registry = self.app.app.registry # ensure a clean state of the dev build system buildsys.DevBuildsys.clear() def get_csrf_token(self, app=None): """ Return a CSRF token that can be used by tests as they test the REST API. Args: app (TestApp): The app to use to get the token. Defaults to None, which will use self.app. Returns: str: A CSRF token. """ if not app: app = self.app return app.get('/csrf', headers={ 'Accept': 'application/json' }).json_body['csrf_token'] def get_update(self, builds='bodhi-2.0-1.fc17', from_tag=None, stable_karma=3, unstable_karma=-3): """ Return a dict describing an update. This is useful for tests that want to POST to the API to create an update. Args: builds (str): A comma-separated list of NVRs to include in the update. from_tag (str): A tag from which to fill the list of builds. stable_karma (int): The stable karma threshold to use on the update. unstable_karma (int): The unstable karma threshold to use on the update. """ update = { 'bugs': '', 'notes': 'this is a test update', 'type': 'bugfix', 'autokarma': True, 'stable_karma': stable_karma, 'unstable_karma': unstable_karma, 'requirements': 'rpmlint', 'require_bugs': False, 'require_testcases': True, 'csrf_token': self.get_csrf_token(), } if builds: if isinstance(builds, list): builds = ','.join(builds) if not isinstance(builds, str): builds = builds.encode('utf-8') update['builds'] = builds if from_tag: update['from_tag'] = from_tag return update def _teardown_method(self): """Roll back all the changes from the test and clean up the session.""" self._request_sesh.stop() self.db.close() self.transaction.rollback() self.connection.close() Session.remove() testing.tearDown() def create_update(self, build_nvrs, release_name='F17'): """ Create and return an Update with the given iterable of build_nvrs. Each build_nvr should be a string describing the name, version, and release for the build separated by dashes. For example, build_nvrs might look like this: ('bodhi-2.3.3-1.fc24', 'python-fedora-atomic-composer-2016.3-1.fc24') You can optionally pass a release_name to select a different release than the default F17, but the release must already exist in the database. This is a convenience wrapper around create_update so that tests can just call self.create_update() and not have to pass self.db. Args: build_nvrs (iterable): An iterable of 3-tuples. Each 3-tuple is strings that express the name, version, and release of the desired build. release_name (str): The name of the release to associate with the new updates. Returns: bodhi.server.models.Update: The new update. """ return create_update(self.db, build_nvrs, release_name) def create_release(self, version, create_automatic_updates=False): """ Create and return a :class:`Release` with the given version. Args: version (str): A string of the version of the release, such as 27. Returns: bodhi.server.models.Release: A new release. """ release = models.Release( name='F{}'.format(version), long_name='Fedora {}'.format(version), id_prefix='FEDORA', version='{}'.format(version.replace('M', '')), dist_tag='f{}'.format(version), stable_tag='f{}-updates'.format(version), testing_tag='f{}-updates-testing'.format(version), candidate_tag='f{}-updates-candidate'.format(version), pending_signing_tag='f{}-updates-testing-signing'.format(version), pending_testing_tag='f{}-updates-testing-pending'.format(version), pending_stable_tag='f{}-updates-pending'.format(version), override_tag='f{}-override'.format(version), branch='f{}'.format(version), state=models.ReleaseState.current, create_automatic_updates=create_automatic_updates, package_manager=models.PackageManager.unspecified, testing_repository=None) self.db.add(release) models.Release.clear_all_releases_cache() models.Release._tag_cache = None self.db.flush() return release