def test_list_packages_items_per_page_with_page(self): """Assert retrieving other pages works.""" project = models.Project(name="requests", homepage="https://pypi.io/project/requests", backend="PyPI") fedora_package = models.Packages(distro_name="Fedora", project=project, package_name="python-requests") debian_package = models.Packages(distro_name="Debian", project=project, package_name="python-requests") Session.add_all([project, fedora_package, debian_package]) Session.commit() output = self.app.get("/api/v2/packages/?items_per_page=1&page=2") self.assertEqual(output.status_code, 200) data = _read_json(output) exp = { "page": 2, "items_per_page": 1, "total_items": 2, "items": [{ "distribution": "Debian", "name": "python-requests", "project": "requests", "ecosystem": "pypi", }], } self.assertEqual(data, exp)
def test_filter_distribution(self): """Assert retrieving other pages works.""" project = models.Project( name='requests', homepage='https://pypi.io/project/requests', backend='PyPI', ) fedora_package = models.Packages(distro_name='Fedora', project=project, package_name='python-requests') debian_package = models.Packages(distro_name='Debian', project=project, package_name='python-requests') Session.add_all([project, fedora_package, debian_package]) Session.commit() fedora = self.app.get('/api/v2/packages/?distribution=Fedora') debian = self.app.get('/api/v2/packages/?distribution=Debian') self.assertEqual(fedora.status_code, 200) self.assertEqual(debian.status_code, 200) fedora_data = _read_json(fedora) debian_data = _read_json(debian) fedora_exp = { 'page': 1, 'items_per_page': 25, 'total_items': 1, 'items': [ { "distribution": "Fedora", "name": "python-requests", "project": "requests", "ecosystem": "pypi" }, ] } debian_exp = { 'page': 1, 'items_per_page': 25, 'total_items': 1, 'items': [ { "distribution": "Debian", "name": "python-requests", "project": "requests", "ecosystem": "pypi" }, ] } self.assertEqual(fedora_data, fedora_exp) self.assertEqual(debian_data, debian_exp)
def create_package(session): """ Create some basic packages to work with. """ package = models.Packages(project_id=1, distro_name="Fedora", package_name="geany") session.add(package) package = models.Packages( project_id=2, distro_name="Fedora", package_name="subsurface" ) session.add(package) session.commit()
def test_packages(self): """Assert packages are returned when they exist.""" project = models.Project( name='requests', homepage='https://pypi.io/project/requests', backend='PyPI', ) fedora_package = models.Packages(distro_name='Fedora', project=project, package_name='python-requests') debian_package = models.Packages(distro_name='Debian', project=project, package_name='python-requests') jcline_package = models.Packages(distro_name='jcline linux', project=project, package_name='requests') Session.add_all( [project, fedora_package, debian_package, jcline_package]) Session.commit() output = self.app.get('/api/v2/packages/') self.assertEqual(output.status_code, 200) data = _read_json(output) exp = { 'page': 1, 'items_per_page': 25, 'total_items': 3, 'items': [ { "distribution": "Fedora", "name": "python-requests", "project": "requests", "ecosystem": "pypi" }, { "distribution": "Debian", "name": "python-requests", "project": "requests", "ecosystem": "pypi" }, { "distribution": "jcline linux", "name": "requests", "project": "requests", "ecosystem": "pypi" }, ] } self.assertEqual(data, exp)
def test_packages(self): """Assert packages are returned when they exist.""" project = models.Project(name="requests", homepage="https://pypi.io/project/requests", backend="PyPI") fedora_package = models.Packages(distro_name="Fedora", project=project, package_name="python-requests") debian_package = models.Packages(distro_name="Debian", project=project, package_name="python-requests") jcline_package = models.Packages(distro_name="jcline linux", project=project, package_name="requests") Session.add_all( [project, fedora_package, debian_package, jcline_package]) Session.commit() output = self.app.get("/api/v2/packages/") self.assertEqual(output.status_code, 200) data = _read_json(output) exp = { "page": 1, "items_per_page": 25, "total_items": 3, "items": [ { "distribution": "Fedora", "name": "python-requests", "project": "requests", "ecosystem": "pypi", }, { "distribution": "Debian", "name": "python-requests", "project": "requests", "ecosystem": "pypi", }, { "distribution": "jcline linux", "name": "requests", "project": "requests", "ecosystem": "pypi", }, ], } self.assertEqual(data, exp)
def test_filter_distribution(self): """Assert retrieving other pages works.""" project = models.Project(name="requests", homepage="https://pypi.io/project/requests", backend="PyPI") fedora_package = models.Packages(distro_name="Fedora", project=project, package_name="python-requests") debian_package = models.Packages(distro_name="Debian", project=project, package_name="python-requests") Session.add_all([project, fedora_package, debian_package]) Session.commit() fedora = self.app.get("/api/v2/packages/?distribution=Fedora") debian = self.app.get("/api/v2/packages/?distribution=Debian") self.assertEqual(fedora.status_code, 200) self.assertEqual(debian.status_code, 200) fedora_data = _read_json(fedora) debian_data = _read_json(debian) fedora_exp = { "page": 1, "items_per_page": 25, "total_items": 1, "items": [{ "distribution": "Fedora", "name": "python-requests", "project": "requests", "ecosystem": "pypi", }], } debian_exp = { "page": 1, "items_per_page": 25, "total_items": 1, "items": [{ "distribution": "Debian", "name": "python-requests", "project": "requests", "ecosystem": "pypi", }], } self.assertEqual(fedora_data, fedora_exp) self.assertEqual(debian_data, debian_exp)
def setUp(self): super(EditProjectMappingTests, self).setUp() # Set up a mapping to edit session = Session() self.user = models.User( email='*****@*****.**', username='******', ) user_social_auth = social_models.UserSocialAuth(user_id=self.user.id, user=self.user) self.session.add(self.user) self.session.add(user_social_auth) self.distro1 = models.Distro(name='CentOS') self.distro2 = models.Distro(name='Fedora') self.project = models.Project( name='python_project', homepage='https://example.com/python_project', backend='PyPI', ecosystem_name='pypi', ) self.package = models.Packages(package_name='python_project', distro=self.distro1.name, project=self.project) session.add_all( [self.distro1, self.distro2, self.project, self.package]) session.commit() self.client = self.flask_app.test_client()
def test_distro_delete_cascade(self): """ Assert deletion of mapped packages when project is deleted """ project = models.Project( name="test", homepage="https://example.com", backend="custom", ecosystem_name="pypi", version_scheme="Invalid", ) self.session.add(project) distro = models.Distro(name="Fedora") self.session.add(distro) package = models.Packages(project_id=1, distro_name="Fedora", package_name="test") self.session.add(package) self.session.commit() distros = self.session.query(models.Distro).all() self.assertEqual(len(distros), 1) self.assertEqual(len(distros[0].package), 1) self.session.delete(distros[0]) self.session.commit() distros = self.session.query(models.Distro).all() packages = self.session.query(models.Packages).all() self.assertEqual(len(distros), 0) self.assertEqual(len(packages), 0)
def test_map_project_no_project_for_package(self): """ Test package duplicity when package is not associated to project """ create_distro(self.session) create_project(self.session) pkg = models.Packages(distro_name="Fedora", project_id=None, package_name="geany") self.session.add(pkg) self.session.commit() distro_objs = self.session.query(models.Distro).all() project_obj = models.Project.get(self.session, 1) self.assertEqual(project_obj.name, "geany") self.assertEqual(len(project_obj.packages), 0) self.assertEqual(distro_objs[0].name, "Fedora") utilities.map_project( self.session, project=project_obj, package_name="geany", distribution="Fedora", user_id="*****@*****.**", old_package_name=None, ) self.session.commit() project_obj = models.Project.get(self.session, 1) packages = self.session.query(models.Packages).all() self.assertEqual(project_obj.name, "geany") self.assertEqual(len(project_obj.packages), 1) self.assertEqual(len(packages), 1) self.assertEqual(project_obj.packages[0].package_name, "geany") self.assertEqual(project_obj.packages[0].distro_name, "Fedora")
def test_is_delete_candidate_mapping_no_version(self): """ Assert that project is marked as delete candidate, if it has mapping added and no version. """ project = models.Project( name="Foobar", backend="GitHub", homepage="www.fakeproject.com", next_check=arrow.utcnow().datetime, error_counter=100, ) self.session.add(project) self.session.commit() distro = models.Distro(name="Fedora") self.session.add(distro) self.session.commit() mapping = models.Packages(distro_name="Fedora", project_id=project.id) self.session.add(mapping) self.session.commit() result = self.checker.is_delete_candidate(project) self.assertTrue(result)
def setUp(self): super(DeleteProjectMappingTests, self).setUp() self.project = models.Project( name='test_project', homepage='https://example.com/test_project', backend='PyPI', ) self.distro = models.Distro(name='Fedora') self.package = models.Packages( distro=self.distro.name, project=self.project, package_name='test-project') # Add a regular user and an admin user session = Session() self.user = models.User(email='*****@*****.**', username='******') self.admin = models.User(email='*****@*****.**', username='******') session.add_all([self.user, self.admin, self.distro, self.project, self.package]) session.commit() mock_config = mock.patch.dict( models.anitya_config, {'ANITYA_WEB_ADMINS': [six.text_type(self.admin.id)]}) mock_config.start() self.addCleanup(mock_config.stop) self.client = self.flask_app.test_client()
def create_package(session): """ Create some basic packages to work with. """ package = models.Packages( project_id=1, distro='Fedora', package_name='geany', ) session.add(package) package = models.Packages( project_id=2, distro='Fedora', package_name='subsurface', ) session.add(package) session.commit()
def test_list_packages_items_per_page_with_page(self): """Assert retrieving other pages works.""" project = models.Project( name='requests', homepage='https://pypi.io/project/requests', backend='PyPI', ) fedora_package = models.Packages(distro_name='Fedora', project=project, package_name='python-requests') debian_package = models.Packages(distro_name='Debian', project=project, package_name='python-requests') Session.add_all([project, fedora_package, debian_package]) Session.commit() output = self.app.get('/api/v2/packages/?items_per_page=1&page=2') self.assertEqual(output.status_code, 200) data = _read_json(output) exp = { 'page': 2, 'items_per_page': 1, 'total_items': 2, 'items': [ { "distribution": "Debian", "name": "python-requests", "project": "requests", "ecosystem": "pypi" }, ] } self.assertEqual(data, exp)
def test_project_search_by_distro(self): """ Assert that only projects with mappings to specific distro are returned when distro is provided. """ create_project(self.session) create_package(self.session) # Create mapping for another distro to be sure that only Fedora mappings # are taken into account package = models.Packages(distro_name="Debian", project_id=3) self.session.add(package) self.session.commit() projects = models.Project.search(self.session, "*", distro="Fedora") self.assertEqual(len(projects), 2)
def test_project_delete_cascade(self): """ Assert deletion of mapped packages when project is deleted """ project = models.Project( name='test', homepage='https://example.com', backend='custom', ecosystem_name='pypi', version_scheme='Invalid', ) self.session.add(project) package = models.Packages( project_id=1, distro_name='Fedora', package_name='test', ) self.session.add(package) self.session.commit() projects = self.session.query(models.Project).all() self.assertEqual(len(projects), 1) self.assertEqual(len(projects[0].package), 1) self.session.delete(projects[0]) self.session.commit() projects = self.session.query(models.Project).all() packages = self.session.query(models.Packages).all() self.assertEqual(len(projects), 0) self.assertEqual(len(packages), 0) create_flagged_project(self.session) projects = self.session.query(models.Project).all() self.assertEqual(len(projects), 1) self.assertEqual(len(projects[0].flags), 1) self.session.delete(projects[0]) self.session.commit() projects = self.session.query(models.Project).all() packages = self.session.query(models.ProjectFlag).all() self.assertEqual(len(projects), 0) self.assertEqual(len(packages), 0)
def test_clashing_package_name(self): """Assert two projects can't map to the same package name in a distro.""" # Set up a package to clash with. session = Session() best_project = models.Project( name='best_project', homepage='https://example.com/best_project', backend='PyPI', ecosystem_name='pypi', ) best_package = models.Packages(package_name='best_project', distro=self.distro1.name, project=best_project) session.add_all([best_project, best_package]) session.commit() with login_user(self.flask_app, self.user): pre_edit_output = self.client.get('/project/1/map/1') csrf_token = pre_edit_output.data.split( b'name="csrf_token" type="hidden" value="')[1].split(b'">')[0] data = { 'package_name': self.project.name, 'distro': self.distro1.name, 'csrf_token': csrf_token, } output = self.client.post('/project/1/map/1', data=data, follow_redirects=True) self.assertEqual(output.status_code, 200) self.assertEqual(2, models.Packages.query.count()) self.assertEqual( 1, models.Packages.query.filter_by( package_name='best_project').count()) self.assertEqual( 1, models.Packages.query.filter_by( package_name='python_project').count()) self.assertTrue(b'Could not edit the mapping' in output.data)
def map_project(session, project, package_name, distribution, user_id, old_package_name=None, old_distro_name=None): """ Map a project to a distribution. Args: session (sqlalchemy.orm.session.Session): The database session. project (anitya.db.modelss.Project): The project to map to a distribution. package_name (str): The name of the mapped package. distribution (str): The name of the distribution. user_id (str): The user ID. old_package_name (str): The name of the old package mapping, if this is being used to edit a mapping. old_distro_name (str): The name of the old distro of the package mapping, if this is being used to edit a mapping. """ distribution = distribution.strip() distro_obj = models.Distro.get(session, distribution) if not distro_obj: distro_obj = models.Distro(name=distribution) log(session, distro=distro_obj, topic='distro.add', message=dict( agent=user_id, distro=distro_obj.name, )) session.add(distro_obj) try: session.flush() except SQLAlchemyError as err: # pragma: no cover # We cannot test this situation session.rollback() raise exceptions.AnityaException( 'Could not add the distribution %s to the database, ' 'please inform an admin.' % distribution, 'errors') pkgname = old_package_name or package_name distro = old_distro_name or distribution pkg = models.Packages.get(session, project.id, distro, pkgname) # See if the new mapping would clash with an existing mapping try: other_pkg = models.Packages.query.filter_by( distro=distribution, package_name=package_name).one() except NoResultFound: other_pkg = None if other_pkg: raise exceptions.AnityaInvalidMappingException(pkgname, distro, package_name, distribution, other_pkg.project.id, other_pkg.project.name) edited = None if not pkg: topic = 'project.map.new' if not other_pkg: pkg = models.Packages(distro=distro_obj.name, project_id=project.id, package_name=package_name) else: other_pkg.project = project pkg = other_pkg else: topic = 'project.map.update' edited = [] if pkg.distro != distro_obj.name: pkg.distro = distro_obj.name edited.append('distribution') if pkg.package_name != package_name: pkg.package_name = package_name edited.append('package_name') session.add(pkg) try: session.flush() except SQLAlchemyError as err: # pragma: no cover _log.exception(err) # We cannot test this situation session.rollback() raise exceptions.AnityaException( 'Could not add the mapping of %s to %s, please inform an ' 'admin.' % (package_name, distribution)) message = dict( agent=user_id, project=project.name, distro=distro_obj.name, new=package_name, ) if edited: message['prev'] = old_package_name or package_name message['edited'] = edited log( session, project=project, distro=distro_obj, topic=topic, message=message, ) return pkg
def post(self): """ Create a new package associated with an existing project and distribution. **Example request**: .. sourcecode:: http POST /api/v2/packages/ HTTP/1.1 Accept: application/json Accept-Encoding: gzip, deflate Authorization: Token gAOFi2wQPzUJFIfDkscAKjbJfXELCz0r44m57Ur2 Connection: keep-alive Content-Length: 120 Content-Type: application/json Host: localhost:5000 User-Agent: HTTPie/0.9.4 { "distribution": "Fedora", "package_name": "python-requests", "project_ecosystem": "pypi", "project_name": "requests" } .. sourcecode:: http HTTP/1.0 201 CREATED Content-Length: 69 Content-Type: application/json Date: Mon, 15 Jan 2018 21:49:01 GMT Server: Werkzeug/0.14.1 Python/2.7.14 { "distribution": "Fedora", "name": "python-requests" } :reqheader Authorization: API token to use for authentication :reqjson string distribution: The name of the distribution that contains this package. :reqjson string package_name: The name of the package in the distribution repository. :reqjson string project_name: The project name in Anitya. :reqjson string project_ecosystem: The ecosystem the project is a part of. If it's not part of an ecosystem, use the homepage used in the Anitya project. :statuscode 201: When the package was successfully created. :statuscode 400: When required arguments are missing or malformed. :statuscode 401: When your access token is missing or invalid :statuscode 409: When the package already exists. """ distribution_help = _( "The name of the distribution that contains this package.") package_name_help = _( "The name of the package in the distribution repository.") project_name_help = _("The project name in Anitya.") project_ecosystem_help = _( "The ecosystem the project is a part of. If it's not part of an ecosystem," " use the homepage used in the Anitya project.") parser = _BASE_ARG_PARSER.copy() parser.add_argument("distribution", type=str, help=distribution_help, required=True) parser.add_argument("package_name", type=str, help=package_name_help, required=True) parser.add_argument("project_name", type=str, help=project_name_help, required=True) parser.add_argument("project_ecosystem", type=str, help=project_ecosystem_help, required=True) args = parser.parse_args(strict=True) try: project = models.Project.query.filter( func.lower(models.Project.name) == func.lower( args.project_name), func.lower(models.Project.ecosystem_name) == func.lower( args.project_ecosystem), ).one() except NoResultFound: return ( { "error": 'Project "{}" in ecosystem "{}" not found'.format( args.project_name, args.project_ecosystem) }, 400, ) try: distro = models.Distro.query.filter( func.lower(models.Distro.name) == func.lower( args.distribution)).one() except NoResultFound: return ( { "error": 'Distribution "{}" not found'.format(args.distribution) }, 400, ) try: package = models.Packages(distro_name=distro.name, project=project, package_name=args.package_name) Session.add(package) Session.commit() message = dict( agent=flask_login.current_user.email, project=project.name, distro=distro.name, new=package.package_name, ) utilities.publish_message( project=project.__json__(), distro=distro.__json__(), topic="project.map.new", message=message, ) return { "distribution": distro.name, "name": package.package_name }, 201 except IntegrityError: Session.rollback() return {"error": "package already exists in distribution"}, 409
def map_project( session, project, package_name, distribution, user_id, old_package_name=None, old_distro_name=None, ): """ Map a project to a distribution. Args: session (sqlalchemy.orm.session.Session): The database session. project (anitya.db.modelss.Project): The project to map to a distribution. package_name (str): The name of the mapped package. distribution (str): The name of the distribution. user_id (str): The user ID. old_package_name (str): The name of the old package mapping, if this is being used to edit a mapping. old_distro_name (str): The name of the old distro of the package mapping, if this is being used to edit a mapping. """ distribution = distribution.strip() distro_obj = models.Distro.get(session, distribution) if not distro_obj: distro_obj = models.Distro(name=distribution) session.add(distro_obj) try: session.flush() except SQLAlchemyError: session.rollback() raise exceptions.AnityaException( "Could not add the distribution %s to the database, " "please inform an admin." % distribution, "errors", ) log( session, distro=distro_obj.__json__(), topic="distro.add", message=dict(agent=user_id, distro=distro_obj.name), ) session.add(distro_obj) try: session.flush() except SQLAlchemyError: # pragma: no cover # We cannot test this situation session.rollback() raise exceptions.AnityaException( "Could not add the distribution %s to the database, " "please inform an admin." % distribution, "errors", ) pkgname = old_package_name or package_name distro = old_distro_name or distribution pkg = models.Packages.get(session, project.id, distro, pkgname) # See if the new mapping would clash with an existing mapping try: other_pkg = models.Packages.query.filter_by( distro_name=distribution, package_name=package_name ).one() except NoResultFound: other_pkg = None # Only raise exception if the package is already associated # to project if other_pkg and other_pkg.project: raise exceptions.AnityaInvalidMappingException( pkgname, distro, package_name, distribution, other_pkg.project.id, other_pkg.project.name, ) edited = None if not pkg: topic = "project.map.new" if not other_pkg: pkg = models.Packages( distro_name=distro_obj.name, project_id=project.id, package_name=package_name, ) else: other_pkg.project = project pkg = other_pkg else: topic = "project.map.update" edited = [] if pkg.distro_name != distro_obj.name: pkg.distro_name = distro_obj.name edited.append("distribution") if pkg.package_name != package_name: pkg.package_name = package_name edited.append("package_name") session.add(pkg) try: session.flush() except SQLAlchemyError as err: _log.exception(err) session.rollback() raise exceptions.AnityaException( "Could not add the mapping of %s to %s, please inform an " "admin." % (package_name, distribution) ) message = dict( agent=user_id, project=project.name, distro=distro_obj.name, new=package_name ) if edited: message["prev"] = old_package_name or package_name message["edited"] = edited log( session, project=project.__json__(), distro=distro_obj.__json__(), topic=topic, message=message, ) return pkg