Esempio n. 1
0
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
Esempio n. 2
0
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