def test_post_and_get(self): """@Test Issue a POST request and GET the created system. @Assert: The created system has the correct attributes. """ # Generate some attributes and use them to create a system. gen_attrs = System().build() response = client.post( System().path(), gen_attrs, auth=get_server_credentials(), verify=False, ) path = System(uuid=response.json()['uuid']).path() self.assertIn( response.status_code, (httplib.OK, httplib.CREATED), path ) # Get the just-created system and examine its attributes. real_attrs = client.get( path, auth=get_server_credentials(), verify=False, ).json() for key, value in gen_attrs.items(): self.assertIn(key, real_attrs.keys(), path) self.assertEqual( value, real_attrs[key], '{0} {1}'.format(key, path) )
def test_positive_create_1(self, login): """ @Test Create a user providing the initial login name. @Assert: User is created and contains provided login name. @Feature: User """ path = entities.User().path() attrs = entities.User(login=login).build() response = client.post( path, attrs, auth=get_server_credentials(), verify=False, ) status_code = (httplib.OK, httplib.CREATED) self.assertIn( response.status_code, status_code, status_code_error(path, status_code, response), ) # Fetch the user real_attrs = client.get( entities.User(id=response.json()['id']).path(), auth=get_server_credentials(), verify=False, ).json() # Remove the ``password`` field from ``attrs`` since it isn't # returned by GET. attrs.pop('password') # Assert that keys and values match for key, value in attrs.items(): self.assertIn(key, real_attrs.keys()) self.assertEqual(value, real_attrs[key])
def test_put_and_get(self, entity): """@Test: Issue a PUT request and GET the updated entity. @Assert: The updated entity has the correct attributes. """ path = entity(id=entity().create()['id']).path() # Generate some attributes and use them to update an entity. gen_attrs = entity().attributes() response = client.put( path, gen_attrs, auth=get_server_credentials(), verify=False, ) self.assertEqual(response.status_code, httplib.OK, path) # Get the just-updated entity and examine its attributes. real_attrs = client.get( path, auth=get_server_credentials(), verify=False, ).json() for key, value in gen_attrs.items(): self.assertIn(key, real_attrs.keys(), path) self.assertEqual( value, real_attrs[key], '{0} {1}'.format(key, path) )
def test_update_max_content_hosts(self): """@Test: Create an activation key with ``max_content_hosts == 1``, then update that field with a string value. @Feature: ActivationKey @Assert: The update fails with an HTTP 422 return code. """ attrs = entities.ActivationKey(max_content_hosts=1).create() path = entities.ActivationKey(id=attrs['id']).path() new_attrs = attrs.copy() new_attrs['max_content_hosts'] = 'foo' response = client.put( path, new_attrs, auth=get_server_credentials(), verify=False, ) self.assertEqual( response.status_code, httplib.UNPROCESSABLE_ENTITY, status_code_error(path, httplib.UNPROCESSABLE_ENTITY, response), ) # Status code is OK. Was `max_content_hosts` changed, or is it still 1? response = client.get( path, auth=get_server_credentials(), verify=False, ).json() self.assertEqual(response['max_content_hosts'], 1)
def test_post_and_get(self, entity): """@Test Issue a POST request and GET the created entity. @Assert: The created entity has the correct attributes. """ if entity in BZ_1122267_ENTITIES and bz_bug_is_open(1122267): self.skipTest("Bugzilla bug 1122267 is open.""") # Generate some attributes and use them to create an entity. gen_attrs = entity().build() response = client.post( entity().path(), gen_attrs, auth=get_server_credentials(), verify=False, ) path = entity(id=response.json()['id']).path() self.assertIn( response.status_code, (httplib.OK, httplib.CREATED), path ) # Get the just-created entity and examine its attributes. real_attrs = client.get( path, auth=get_server_credentials(), verify=False, ).json() for key, value in gen_attrs.items(): self.assertIn(key, real_attrs.keys(), path) self.assertEqual( value, real_attrs[key], '{0} {1}'.format(key, path) )
def test_delete(self, entity): """@Test Create an entity, fetch it, DELETE it, and fetch it again. @Assert DELETE succeeds. HTTP 200, 202 or 204 is returned before deleting entity, and 404 is returned after deleting entity. """ attrs = entity().create() path = entity(id=attrs['id']).path() response = client.delete( path, auth=get_server_credentials(), verify=False, ) status_code = (httplib.NO_CONTENT, httplib.OK, httplib.ACCEPTED) self.assertIn( response.status_code, status_code, status_code_error(path, status_code, response), ) response = client.get( path, auth=get_server_credentials(), verify=False, ) status_code = httplib.NOT_FOUND self.assertEqual( status_code, response.status_code, status_code_error(path, status_code, response), )
def test_get_routes(self): """@Test: Issue an HTTP GET response to both available routes. @Assert: The same response is returned. @Feature: SyncPlan Targets BZ 1132817. """ org_id = entities.Organization().create_json()['id'] entities.SyncPlan(organization=org_id).create_json()['id'] response1 = client.get( '{0}/katello/api/v2/sync_plans'.format(get_server_url()), auth=get_server_credentials(), data={'organization_id': org_id}, verify=False, ) response2 = client.get( '{0}/katello/api/v2/organizations/{1}/sync_plans'.format( get_server_url(), org_id ), auth=get_server_credentials(), verify=False, ) for response in (response1, response2): response.raise_for_status() self.assertEqual( response1.json()['results'], response2.json()['results'], )
def test_positive_delete_filter_from_role(self): """@Test: Delete the filter to remove permissions from a role. @Feature: Role and Permissions @Assert: Filter should be deleted """ role_name = orm.StringField(str_type=('alphanumeric',)).get_value() try: role_attrs = entities.Role(name=role_name).create() except factory.FactoryError as err: self.fail(err) # fail instead of error path = entities.Role(id=role_attrs['id']).path() # GET the role and verify it's name. response = client.get( path, auth=get_server_credentials(), verify=False, ).json() self.assertEqual(response['name'], role_name) # Get permissions that have a resource_type of ConfigTemplate. permissions = client.get( entities.Permission().path(), auth=get_server_credentials(), verify=False, data={'resource_type': 'Ptable'}, ).json()['results'] # Create a filter under a selected role with all permissions # of a selected resource_type. filter_attrs = entities.Filter( role=role_attrs['id'], permission=[permission['id'] for permission in permissions] ).create() filter_path = entities.Filter(id=filter_attrs['id']).path() client.get( filter_path, auth=get_server_credentials(), verify=False, ).json() # Delete the Filter, GET it, and assert that an HTTP 404 is returned. entities.Filter(id=filter_attrs['id']).delete() response = client.get( filter_path, auth=get_server_credentials(), verify=False, ) status_code = httplib.NOT_FOUND self.assertEqual( status_code, response.status_code, status_code_error(path, status_code, response), )
def test_positive_create_role_with_permissions(self): """@Test: Create a filter to add permissions to a selected role @Feature: Role and Permissions @Assert: Filter should be created with all permissions of a selected resource_type """ role_name = orm.StringField(str_type=('alphanumeric',)).get_value() try: role_attrs = entities.Role(name=role_name).create() except factory.FactoryError as err: self.fail(err) # fail instead of error path = entities.Role(id=role_attrs['id']).path() # GET the role and verify it's name. response = client.get( path, auth=get_server_credentials(), verify=False, ).json() self.assertEqual(response['name'], role_name) # Get permissions that have a resource_type of ConfigTemplate. permissions = client.get( entities.Permission().path(), auth=get_server_credentials(), verify=False, data={'resource_type': 'ConfigTemplate'}, ).json()['results'] # Create a filter under a selected role with all permissions # of a selected resource_type. filter_attrs = entities.Filter( role=role_attrs['id'], permission=[permission['id'] for permission in permissions] ).create() # Get all permissions from created filter permission_names = client.get( entities.Filter(id=filter_attrs['id']).path(), auth=get_server_credentials(), verify=False, ).json()['permissions'] get_permissions = [permission_name['name'] for permission_name in permission_names] real_permissions = [permission['name'] for permission in permissions] self.assertListEqual(get_permissions, real_permissions)
def test_get_releases_status_code(self): """@Test: Get an activation key's releases. Check response format. @Assert: HTTP 200 is returned with an ``application/json`` content-type @Feature: ActivationKey """ try: attrs = entities.ActivationKey().create() except HTTPError as err: self.fail(err) path = entities.ActivationKey(id=attrs['id']).path(which='releases') response = client.get( path, auth=get_server_credentials(), verify=False, ) status_code = httplib.OK self.assertEqual( status_code, response.status_code, status_code_error(path, status_code, response), ) self.assertIn('application/json', response.headers['content-type'])
def test_update_gpgkey(self): """@Test: Create a repository and update its GPGKey @Assert: The updated repository points to a new GPG key. @Feature: Repository """ # Create a repo and make it point to a GPG key. key_1_id = entities.GPGKey( content=read_data_file(VALID_GPG_KEY_FILE), organization=self.org_id, ).create()['id'] repo_id = entities.Repository( gpg_key=key_1_id, product=self.prod_id, ).create()['id'] # Update the repo and make it point to a new GPG key. key_2_id = entities.GPGKey( content=read_data_file(VALID_GPG_KEY_BETA_FILE), organization=self.org_id, ).create()['id'] client.put( entities.Repository(id=repo_id).path(), { u'gpg_key_id': key_2_id }, auth=get_server_credentials(), verify=False, ).raise_for_status() # Verify the repository's attributes. attrs = entities.Repository(id=repo_id).read_json() self.assertEqual(attrs['gpg_key_id'], key_2_id)
def test_update_name(self, content_type): """@Test: Update a repository's name. @Assert: The repository's name is updated. @Feature: Repository The only data provided with the PUT request is a name. No other information about the repository (such as its URL) is provided. """ if content_type == 'docker' and bz_bug_is_open(1194476): self.skipTest(1194476) repo_id = entities.Repository( content_type=content_type).create_json()['id'] name = entities.Repository.name.gen_value() repository = entities.Repository(id=repo_id) client.put( repository.path(), { 'name': name }, auth=get_server_credentials(), verify=False, ).raise_for_status() self.assertEqual(name, repository.read_json()['name'])
def _search(self, entity, query, auth=None): """Performs a GET ``api/v2/<entity>`` and specify the ``search`` parameter. :param robottelo.orm.Entity entity: A logical representation of a Foreman entity. :param string query: A ``search`` parameter. :param tuple auth: A ``tuple`` containing the credentials to be used for authentication when accessing the API. If ``None``, credentials are automatically read from :func:`robottelo.common.helpers.get_server_credentials`. :return: A ``list`` of found entity dicts or an empty list if nothing found :rtype: list """ # Use the server credentials if None are provided if auth is None: auth = get_server_credentials() path = entity().path() response = client.get( path, auth=auth, data={u'search': query}, verify=False, ) response.raise_for_status() return response.json()['results']
def test_update_docker_repo_upstream_name(self, name): """@Test: Create a Docker-type repository and update its upstream name. @Assert: A repository is created with a Docker image and that its upstream name can be updated. @Feature: Docker @BZ: 1193669 """ upstream_name = u'busybox' new_upstream_name = u'fedora/ssh' content_type = u'docker' prod_id = entities.Product( organization=self.org_id).create_json()['id'] repo_id = _create_repository(prod_id, name, upstream_name)['id'] real_attrs = entities.Repository(id=repo_id).read_json() self.assertEqual(real_attrs['name'], name) self.assertEqual(real_attrs['docker_upstream_name'], upstream_name) self.assertEqual(real_attrs['content_type'], content_type) # Update the repository upstream name real_attrs['docker_upstream_name'] = new_upstream_name client.put( entities.Repository(id=repo_id).path(), real_attrs, auth=get_server_credentials(), verify=False, ).raise_for_status() new_attrs = entities.Repository(id=repo_id).read_json() self.assertEqual(new_attrs['docker_upstream_name'], new_upstream_name) self.assertNotEqual(new_attrs['name'], upstream_name)
def test_update_docker_repo_url(self, name): """@Test: Create a Docker-type repository and update its URL. @Assert: A repository is created with a Docker image and that its URL can be updated. @Feature: Docker """ new_url = gen_url(scheme='https') prod_id = entities.Product( organization=self.org_id).create_json()['id'] repo_id = _create_repository(prod_id, name)['id'] real_attrs = entities.Repository(id=repo_id).read_json() self.assertEqual(real_attrs['url'], DOCKER_REGISTRY_HUB) # Update the repository URL real_attrs['url'] = new_url client.put( entities.Repository(id=repo_id).path(), real_attrs, auth=get_server_credentials(), verify=False, ).raise_for_status() new_attrs = entities.Repository(id=repo_id).read_json() self.assertEqual(new_attrs['url'], new_url) self.assertNotEqual(new_attrs['url'], DOCKER_REGISTRY_HUB)
def test_update_name(self, content_type): """@Test: Update a repository's name. @Assert: The repository's name is updated. @Feature: Repository The only data provided with the PUT request is a name. No other information about the repository (such as its URL) is provided. """ if content_type == 'docker' and bz_bug_is_open(1194476): self.skipTest(1194476) repo_id = entities.Repository( content_type=content_type ).create_json()['id'] name = entities.Repository.name.gen_value() repository = entities.Repository(id=repo_id) client.put( repository.path(), {'name': name}, auth=get_server_credentials(), verify=False, ).raise_for_status() self.assertEqual(name, repository.read_json()['name'])
def test_ping(self): """@Test: Check if all services are running @Feature: Smoke Test @Assert: Overall and individual services status should be 'ok'. """ response = client.get( entities.Ping().path(), auth=get_server_credentials(), verify=False, ) response.raise_for_status() self.assertEqual(response.json()['status'], u'ok') # overall status # Check that all services are OK. ['services'] is in this format: # # {u'services': { # u'candlepin': {u'duration_ms': u'40', u'status': u'ok'}, # u'candlepin_auth': {u'duration_ms': u'41', u'status': u'ok'}, # … # }, u'status': u'ok'} services = response.json()['services'] self.assertTrue( all([service['status'] == u'ok' for service in services.values()]), u"Not all services seem to be up and running!" )
def give_user_permission(self, perm_name): """Give ``self.user`` the ``perm_name`` permission. This method creates a role and filter to accomplish the above goal. When complete, the relevant relationhips look like this: user → role ← filter → permission :param str perm_name: The name of a permission. For example: 'create_architectures'. :raises: ``AssertionError`` if more than one permission is found when searching for the permission with name ``perm_name``. :raises: ``requests.exceptions.HTTPError`` if an error occurs when updating ``self.user``'s roles. :rtype: None """ role_id = entities.Role().create()['id'] permission_ids = [ permission['id'] for permission in entities.Permission(name=perm_name).search() ] self.assertEqual(len(permission_ids), 1) entities.Filter(permission=permission_ids, role=role_id).create() # NOTE: An extra hash is used due to an API bug. client.put( self.user.path(), {u'user': {u'role_ids': [role_id]}}, auth=get_server_credentials(), verify=False, ).raise_for_status()
def test_delete_and_get(self): """@Test: Issue an HTTP DELETE request and GET the deleted system. @Assert: An HTTP 404 is returned when fetching the missing system. """ try: attrs = System().create() except factory.FactoryError as err: self.fail(err) System(uuid=attrs['uuid']).delete() # Get the now non-existent system path = System(uuid=attrs['uuid']).path() response = client.get( path, auth=get_server_credentials(), verify=False, ) status_code = httplib.NOT_FOUND self.assertEqual( status_code, response.status_code, status_code_error(path, status_code, response), )
def give_user_permission(self, perm_name): """Give ``self.user`` the ``perm_name`` permission. This method creates a role and filter to accomplish the above goal. When complete, the relevant relationhips look like this: user → role ← filter → permission :param str perm_name: The name of a permission. For example: 'create_architectures'. :raises: ``AssertionError`` if more than one permission is found when searching for the permission with name ``perm_name``. :raises: ``requests.exceptions.HTTPError`` if an error occurs when updating ``self.user``'s roles. :returns: Nothing. """ role_id = entities.Role().create()['id'] permission_ids = [ permission['id'] for permission in entities.Permission(name=perm_name).search() ] self.assertEqual(len(permission_ids), 1) entities.Filter(permission=permission_ids, role=role_id).create() # NOTE: An extra hash is used due to an API bug. client.put( self.user.path(), {u'user': {u'role_ids': [role_id]}}, auth=get_server_credentials(), verify=False, ).raise_for_status()
def test_get_status_code(self, entity): """@Test: Create an entity and GET it. @Assert: HTTP 200 is returned with an ``application/json`` content-type """ if entity is entities.ActivationKey and bz_bug_is_open(1127335): self.skipTest("Bugzilla bug 1127335 is open.""") try: attrs = entity().create() except factory.FactoryError as err: self.fail(err) path = entity(id=attrs['id']).path() response = client.get( path, auth=get_server_credentials(), verify=False, ) status_code = httplib.OK self.assertEqual( status_code, response.status_code, status_code_error(path, status_code, response), ) self.assertIn('application/json', response.headers['content-type'])
def test_positive_update_1(self, name_generator): """@Test: Update a role with and give a name of ``name_generator()``. @Feature: Role @Assert: The role is updated with the given name. """ if decorators.bz_bug_is_open(1112657) and ( name_generator is gen_cjk or name_generator is gen_latin1 or name_generator is gen_utf8): self.skipTest('Bugzilla bug 1112657 is open.') try: role_id = entities.Role().create()['id'] except HTTPError as err: self.fail(err) # fail instead of error role = entities.Role(id=role_id) name = name_generator() response = client.put( role.path(), {u'name': name}, auth=get_server_credentials(), verify=False, ) response.raise_for_status() self.assertEqual(role.read_json()['name'], name)
def test_post_status_code(self, entity): """@Test: Issue a POST request and check the returned status code. @Assert: HTTP 201 is returned with an ``application/json`` content-type """ # Some arguments are "normal" classes and others are objects produced # by functools.partial. Also, `partial(SomeClass).func == SomeClass`. if ((entity.func if isinstance(entity, partial) else entity) in BZ_1118015_ENTITIES and bz_bug_is_open(1118015)): self.skipTest('Bugzilla bug 1118015 is open.') path = entity().path() response = client.post( path, entity().build(), auth=get_server_credentials(), verify=False, ) status_code = httplib.CREATED self.assertEqual( status_code, response.status_code, status_code_error(path, status_code, response), ) self.assertIn('application/json', response.headers['content-type'])
def test_update_docker_repo_upstream_name(self, name): """@Test: Create a Docker-type repository and update its upstream name. @Assert: A repository is created with a Docker image and that its upstream name can be updated. @Feature: Docker @BZ: 1193669 """ upstream_name = u'busybox' new_upstream_name = u'fedora/ssh' content_type = u'docker' prod_id = entities.Product( organization=self.org_id ).create_json()['id'] repo_id = _create_repository(prod_id, name, upstream_name)['id'] real_attrs = entities.Repository(id=repo_id).read_json() self.assertEqual(real_attrs['name'], name) self.assertEqual(real_attrs['docker_upstream_name'], upstream_name) self.assertEqual(real_attrs['content_type'], content_type) # Update the repository upstream name real_attrs['docker_upstream_name'] = new_upstream_name client.put( entities.Repository(id=repo_id).path(), real_attrs, auth=get_server_credentials(), verify=False, ).raise_for_status() new_attrs = entities.Repository(id=repo_id).read_json() self.assertEqual(new_attrs['docker_upstream_name'], new_upstream_name) self.assertNotEqual(new_attrs['name'], upstream_name)
def test_delete_and_get(self, entity): """@Test: Issue an HTTP DELETE request and GET the deleted entity. @Assert: An HTTP 404 is returned when fetching the missing entity. """ if entity is entities.ConfigTemplate and bz_bug_is_open(1096333): self.skipTest('Cannot delete config templates.') # Create an entity, then delete it. try: entity_n = entity(id=entity().create()['id']) except factory.FactoryError as err: self.fail(err) logger.info('test_delete_and_get path: {0}'.format(entity_n.path())) entity_n.delete() # Get the now non-existent entity. response = client.get( entity_n.path(), auth=get_server_credentials(), verify=False, ) status_code = httplib.NOT_FOUND self.assertEqual( status_code, response.status_code, status_code_error(entity_n.path(), status_code, response), )
def test_update_docker_repo_url(self, name): """@Test: Create a Docker-type repository and update its URL. @Assert: A repository is created with a Docker image and that its URL can be updated. @Feature: Docker """ new_url = gen_url(scheme='https') prod_id = entities.Product( organization=self.org_id ).create_json()['id'] repo_id = _create_repository(prod_id, name)['id'] real_attrs = entities.Repository(id=repo_id).read_json() self.assertEqual(real_attrs['url'], DOCKER_REGISTRY_HUB) # Update the repository URL real_attrs['url'] = new_url client.put( entities.Repository(id=repo_id).path(), real_attrs, auth=get_server_credentials(), verify=False, ).raise_for_status() new_attrs = entities.Repository(id=repo_id).read_json() self.assertEqual(new_attrs['url'], new_url) self.assertNotEqual(new_attrs['url'], DOCKER_REGISTRY_HUB)
def test_delete_status_code(self): """@Test: Issue an HTTP DELETE request and check the returned status code. @Feature: System APIs @Assert: HTTP 200, 202 or 204 is returned with an ``application/json`` content-type. """ try: system = System(uuid=System().create_json()['uuid']) except HTTPError as err: self.fail(err) logger.debug('system uuid: {0}'.format(system.uuid)) response = client.delete( system.path(), auth=get_server_credentials(), verify=False, ) self.assertIn(response.status_code, (httplib.NO_CONTENT, httplib.OK, httplib.ACCEPTED)) # According to RFC 2616, HTTP 204 responses "MUST NOT include a # message-body". If a message does not have a body, there is no need to # set the content-type of the message. if response.status_code is not httplib.NO_CONTENT: self.assertIn('application/json', response.headers['content-type'])
def test_update_gpgkey(self): """@Test: Create a repository and update its GPGKey @Assert: The updated repository points to a new GPG key. @Feature: Repository """ # Create a repo and make it point to a GPG key. key_1_id = entities.GPGKey( content=read_data_file(VALID_GPG_KEY_FILE), organization=self.org_id, ).create_json()['id'] repo_id = entities.Repository( gpg_key=key_1_id, product=self.prod_id, ).create_json()['id'] # Update the repo and make it point to a new GPG key. key_2_id = entities.GPGKey( content=read_data_file(VALID_GPG_KEY_BETA_FILE), organization=self.org_id, ).create_json()['id'] client.put( entities.Repository(id=repo_id).path(), {u'gpg_key_id': key_2_id}, auth=get_server_credentials(), verify=False, ).raise_for_status() # Verify the repository's attributes. attrs = entities.Repository(id=repo_id).read_json() self.assertEqual(attrs['gpg_key_id'], key_2_id)
def test_put_and_get(self): """@Test: Issue a PUT request and GET the updated system. @Feature: System APIs @Assert: The updated system has the correct attributes. """ system = System(uuid=System().create_json()['uuid']) logger.debug('system uuid: {0}'.format(system.uuid)) # Generate some attributes and use them to update `system`. system.create_missing() response = client.put( system.path(), system.create_payload(), auth=get_server_credentials(), verify=False, ) self.assertEqual(response.status_code, httplib.OK) # Get the just-updated system and examine its attributes. attrs = system.read_json() for key, value in system.create_payload().items(): self.assertIn(key, attrs.keys()) self.assertEqual(value, attrs[key])
def test_update(self, attrs): """@Test: Create a repository and update its attributes. @Assert: The repository's attributes are updated. @Feature: Repository """ client.put( self.repository.path(), attrs, auth=get_server_credentials(), verify=False, ).raise_for_status() real_attrs = self.repository.read_json() for name, value in attrs.items(): self.assertIn(name, real_attrs.keys()) if name == 'content_type': # Cannot update a repository's content type. self.assertEqual( entities.Repository.content_type.default, real_attrs[name] ) else: self.assertEqual(value, real_attrs[name])
def create_raw(self, auth=None, create_missing=True): """Create an entity. Generate values for required, unset fields by calling :meth:`create_missing`. Only do this if ``create_missing`` is true. Then make an HTTP POST call to ``self.path('base')``. Return the response received from the server. :param tuple auth: A ``(username, password)`` pair to use when communicating with the API. If ``None``, the credentials returned by :func:`robottelo.common.helpers.get_server_credentials` are used. :param bool create_missing: Should :meth:`create_missing` be called? In other words, should values be generated for required, empty fields? :return: A ``requests.response`` object. """ if auth is None: auth = helpers.get_server_credentials() if create_missing: self.create_missing(auth) return client.post( self.path('base'), self.create_payload(), auth=auth, verify=False, )
def test_post_hash(self): """@Test: Do not wrap API calls in an extra hash. @Assert: It is possible to associate an activation key with an organization. @Feature: Architecture """ name = gen_utf8() os_id = entities.OperatingSystem().create()['id'] response = client.post( entities.Architecture().path(), { u'name': name, u'operatingsystem_ids': [os_id] }, auth=get_server_credentials(), verify=False, ) response.raise_for_status() attrs = response.json() # The server will accept some POSTed attributes (name) and silently # ignore others (operatingsystem_ids). self.assertIn('name', attrs) self.assertEqual(name, attrs['name']) self.assertIn('operatingsystems', attrs) self.assertEqual([os_id], attrs['operatingsystems'])
def test_delete_status_code(self, entity): """@Test Issue an HTTP DELETE request and check the returned status code. @Assert: HTTP 200, 202 or 204 is returned with an ``application/json`` content-type. """ if entity is entities.ConfigTemplate and bz_bug_is_open(1096333): self.skipTest('Cannot delete config templates.') try: attrs = entity().create() except factory.FactoryError as err: self.fail(err) path = entity(id=attrs['id']).path() response = client.delete( path, auth=get_server_credentials(), verify=False, ) status_code = (httplib.NO_CONTENT, httplib.OK, httplib.ACCEPTED) self.assertIn( response.status_code, status_code, status_code_error(path, status_code, response), ) # According to RFC 2616, HTTP 204 responses "MUST NOT include a # message-body". If a message does not have a body, there is no need to # set the content-type of the message. if response.status_code is not httplib.NO_CONTENT: self.assertIn('application/json', response.headers['content-type'])
def test_post_hash(self): """@Test: Do not wrap API calls in an extra hash. @Assert: It is possible to associate an activation key with an organization. @Feature: Architecture """ name = gen_utf8() os_id = entities.OperatingSystem().create()['id'] response = client.post( entities.Architecture().path(), {u'name': name, u'operatingsystem_ids': [os_id]}, auth=get_server_credentials(), verify=False, ) response.raise_for_status() attrs = response.json() # The server will accept some POSTed attributes (name) and silently # ignore others (operatingsystem_ids). self.assertIn('name', attrs) self.assertEqual(name, attrs['name']) self.assertIn('operatingsystems', attrs) self.assertEqual([os_id], attrs['operatingsystems'])
def test_post_and_get(self, entity): """@Test Issue a POST request and GET the created entity. @Assert: The created entity has the correct attributes. """ if entity in BZ_1122267_ENTITIES and bz_bug_is_open(1122267): self.skipTest("Bugzilla bug 1122267 is open.""") if entity is entities.AuthSourceLDAP and bz_bug_is_open(1140313): self.skipTest("Bugzilla bug 1140313 is open.""") # Generate some attributes and use them to create an entity. gen_attrs = entity().build() response = client.post( entity().path(), gen_attrs, auth=get_server_credentials(), verify=False, ) response.raise_for_status() # Get the just-created entity and examine its attributes. entity_n = entity(id=response.json()['id']) logger.info('test_post_and_get path: {0}'.format(entity_n.path())) real_attrs = entity_n.read_json() for key, value in gen_attrs.items(): self.assertIn(key, real_attrs.keys()) self.assertEqual(value, real_attrs[key], key)
def read(self, auth=None): """Return information about a foreman task. :return: Information about this foreman task. :rtype: dict :raises ReadException: If information about this foreman task could not be fetched. This could happen if, for example, the task does not exist or bad credentials are used. """ # FIXME: Need better error handling. If there's an authentication # error, the server will respond with JSON: # # {u'error': {u'text': u'Unable to authenticate user.'}} # # But what if the JSON response contains 'errors', or what if the # response cannot be converted to JSON at all? A utility function can # probably be created for this need. Perhaps # robottelo.api.utils.status_code_error() could be of use. After all, # most of that method is devoted to fetching an error message, and only # the last bit composes an error message. if auth is None: auth = get_server_credentials() response = client.get(self.path(), auth=auth, verify=False) if response.status_code is not 200: raise ReadException(response.text) if response.json() == {}: # FIXME: See bugzilla bug #1131702 raise ReadException( 'ForemanTask {0} does not exist.'.format(self.id) ) return response.json()
def test_delete_status_code(self): """@Test Issue an HTTP DELETE request and check the returned status code. @Assert: HTTP 200, 202 or 204 is returned with an ``application/json`` content-type. """ try: system = System(uuid=System().create_json()['uuid']) except HTTPError as err: self.fail(err) logger.debug('system uuid: {0}'.format(system.uuid)) response = client.delete( system.path(), auth=get_server_credentials(), verify=False, ) self.assertIn( response.status_code, (httplib.NO_CONTENT, httplib.OK, httplib.ACCEPTED) ) # According to RFC 2616, HTTP 204 responses "MUST NOT include a # message-body". If a message does not have a body, there is no need to # set the content-type of the message. if response.status_code is not httplib.NO_CONTENT: self.assertIn('application/json', response.headers['content-type'])
def test_put_status_code(self, entity_cls): """@Test Issue a PUT request and check the returned status code. @Feature: Test multiple API paths @Assert: HTTP 200 is returned with an ``application/json`` content-type """ logger.debug('test_put_status_code arg: %s', entity_cls) skip_if_sam(self, entity_cls) if entity_cls in BZ_1154156_ENTITIES and bz_bug_is_open(1154156): self.skipTest("Bugzilla bug 1154156 is open.") # Create an entity entity = entity_cls(id=entity_cls().create_json()['id']) # Update that entity. entity.create_missing() response = client.put( entity.path(), entity.create_payload(), # FIXME: use entity.update_payload() auth=get_server_credentials(), verify=False, ) self.assertEqual(httplib.OK, response.status_code) self.assertIn('application/json', response.headers['content-type'])
def _search(self, entity, query, auth=None): """Performs a GET ``api/v2/<entity>`` and specify the ``search`` parameter. :param entity: A ``nailgun.entity_mixins.Entity`` object. :param string query: A ``search`` parameter. :param tuple auth: A ``tuple`` containing the credentials to be used for authentication when accessing the API. If ``None``, credentials are automatically read from :func:`robottelo.common.helpers.get_server_credentials`. :return: A ``list`` of found entity dicts or an empty list if nothing found :rtype: list """ # Use the server credentials if None are provided if auth is None: auth = get_server_credentials() path = entity().path() response = client.get( path, auth=auth, data={u'search': query}, verify=False, ) response.raise_for_status() return response.json()['results']
def test_put_and_get(self, entity_cls): """@Test: Update an entity, then read it back. @Feature: Test multiple API paths @Assert: The entity is updated with the given attributes. """ logger.debug('test_put_and_get arg: %s', entity_cls) skip_if_sam(self, entity_cls) if entity_cls in BZ_1154156_ENTITIES and bz_bug_is_open(1154156): self.skipTest("Bugzilla bug 1154156 is open.") # Create an entity. entity_id = entity_cls().create_json()['id'] # Update that entity. FIXME: This whole procedure is a hack. entity = entity_cls() entity.create_missing() # Generate randomized instance attributes response = client.put( entity_cls(id=entity_id).path(), entity.create_payload(), auth=get_server_credentials(), verify=False, ) response.raise_for_status() # Compare `payload` against entity information returned by the server. payload = _get_readable_attributes(entity) entity_attrs = entity_cls(id=entity_id).read_json() for key, value in payload.items(): self.assertIn(key, entity_attrs.keys()) self.assertEqual(value, entity_attrs[key], key)
def _search(self, entity, query, auth=None): """ Performs a GET ``api/v2/<entity>`` and specify the ``search`` parameter. :param robottelo.orm.Entity entity: A logical representation of a Foreman entity. :param string query: A ``search`` parameter. :param tuple auth: A ``tuple`` containing the credentials to be used for authentication when accessing the API. If ``None``, credentials are automatically read from :func:`robottelo.common.helpers.get_server_credentials`. :return: A ``dict`` representing a Foreman entity. :rtype: dict """ # Use the server credentials if None are provided if auth is None: auth = get_server_credentials() path = entity().path() response = client.get( path, auth=auth, data={u'search': query}, verify=False, ) response.raise_for_status() self.assertEqual( response.json()['search'], query, u"Could not find {0}.".format(query) ) return response.json()
def test_positive_update_1(self, name_generator): """@Test: Update a role with and give a name of ``name_generator()``. @Feature: Role @Assert: The role is updated with the given name. """ if decorators.bz_bug_is_open(1112657) and ( name_generator is gen_cjk or name_generator is gen_latin1 or name_generator is gen_utf8): self.skipTest('Bugzilla bug 1112657 is open.') try: role_id = entities.Role().create_json()['id'] except HTTPError as err: self.fail(err) # fail instead of error role = entities.Role(id=role_id) name = name_generator() response = client.put( role.path(), {u'name': name}, auth=get_server_credentials(), verify=False, ) response.raise_for_status() self.assertEqual(role.read_json()['name'], name)
def test_subscribe_system_to_cv(self): """@Test: Subscribe a system to a content view. @Feature: ContentView @Assert: It is possible to create a system and set its 'content_view_id' attribute. """ # organization # ├── lifecycle environment # └── content view org = entities.Organization() org.id = org.create()['id'] lifecycle_env = entities.LifecycleEnvironment(organization=org.id) lifecycle_env.id = lifecycle_env.create()['id'] content_view = entities.ContentView(organization=org.id) content_view.id = content_view.create()['id'] # Publish the content view. response = content_view.publish() humanized_errors = response['humanized']['errors'] _check_bz_1186432(humanized_errors) self.assertEqual(response['result'], 'success', humanized_errors) # Get the content view version's ID. response = client.get( entities.ContentViewVersion().path(), auth=get_server_credentials(), data={u'content_view_id': content_view.id}, verify=False, ) response.raise_for_status() results = response.json()['results'] self.assertEqual(len(results), 1) cv_version = entities.ContentViewVersion(id=results[0]['id']) # Promote the content view version. response = cv_version.promote(environment_id=lifecycle_env.id) humanized_errors = response['humanized']['errors'] _check_bz_1186432(humanized_errors) self.assertEqual('success', response['result'], humanized_errors) # Create a system that is subscribed to the published and promoted # content view. Associating this system with the organization and # environment created above is not particularly important, but doing so # means a shorter test where fewer entities are created, as # System.organization and System.environment are required attributes. system_attrs = entities.System( content_view=content_view.id, environment=lifecycle_env.id, organization=org.id, ).create() # See BZ #1151240 self.assertEqual(system_attrs['content_view_id'], content_view.id) self.assertEqual(system_attrs['environment']['id'], lifecycle_env.id) self.assertEqual(system_attrs['organization_id'], org.id)
def _add_repo_to_content_view(repo_id, cv_id): """Adds a repository to an existing content view. :param int repo_id: The ID for an existing repository. :param int cv_id: The ID for an existing content view. """ client.put(entities.ContentView(id=cv_id).path(), auth=get_server_credentials(), verify=False, data={ u'repository_ids': [repo_id] }).raise_for_status()
def _poll_task(task_id, poll_rate=None, timeout=None, auth=None): """Implement :meth:`robottelo.entities.ForemanTask.poll`. See :meth:`robottelo.entities.ForemanTask.poll` for a full description of how this method acts. Other methods may also call this method, such as :meth:`robottelo.orm.EntityDeleteMixin.delete`. This function has been placed in this module to keep the import tree sane. This function could also be placed in :mod:`robottelo.api.utils`. However, doing so precludes :mod:`robottelo.api.utils` from importing :mod:`robottelo.entities`, which may be desirable in the future. This function is private because only entity mixins should use this. :class:`robottelo.entities.ForemanTask` is, for obvious reasons, an exception. """ if poll_rate is None: poll_rate = TASK_POLL_RATE if timeout is None: timeout = TASK_TIMEOUT if auth is None: auth = helpers.get_server_credentials() # Implement the timeout. def raise_task_timeout(): """Raise a KeyboardInterrupt exception in the main thread.""" thread.interrupt_main() timer = threading.Timer(timeout, raise_task_timeout) # Poll until the task finishes. The timeout prevents an infinite loop. try: timer.start() path = '{0}/foreman_tasks/api/tasks/{1}'.format( helpers.get_server_url(), task_id) while True: response = client.get(path, auth=auth, verify=False) response.raise_for_status() task_info = response.json() if task_info['state'] != 'running': return task_info time.sleep(poll_rate) except KeyboardInterrupt: # raise_task_timeout will raise a KeyboardInterrupt when the timeout # expires. Catch the exception and raise TaskTimeout raise TaskTimeout("Timed out polling task {0}".format(task_id)) finally: timer.cancel()
def _add_content_view_to_composite_view(cv_id, cv_version_id): """Adds a published content view to a composite content view. :param int cv_id: The ID for an existing composite content view. :param int cv_version_id: The ID for a published non-composite content view. """ client.put(entities.ContentView(id=cv_id).path(), auth=get_server_credentials(), verify=False, data={ u'component_ids': [cv_version_id] }).raise_for_status()
def test_get_status_code(self): """@Test: GET ``api/v2`` and examine the response. @Feature: API @Assert: HTTP 200 is returned with an ``application/json`` content-type """ response = client.get( self.path, auth=helpers.get_server_credentials(), verify=False, ) self.assertEqual(response.status_code, httplib.OK) self.assertIn('application/json', response.headers['content-type'])
def test_negative_update(self, attrs): """@Test: Update an organization's attributes with invalid values. @Assert: The organization's attributes are not updated. @Feature: Organization """ response = client.put( self.organization.path(), attrs, verify=False, auth=get_server_credentials(), ) with self.assertRaises(HTTPError): response.raise_for_status()
def read_raw(self, auth=None): """Get information about the current entity. Send an HTTP GET request to :meth:`Entity.path`. Return the response. Do not check the response for any errors, such as an HTTP 4XX or 5XX status code. :param tuple auth: A ``(username, password)`` tuple used when accessing the API. If ``None``, the credentials provided by :func:`robottelo.common.helpers.get_server_credentials` are used. :return: A ``requests.response`` object. """ if auth is None: auth = helpers.get_server_credentials() return client.get(self.path(), auth=auth, verify=False)
def test_get_search(self): """@Test: GET ``api/v2/hosts`` and specify the ``search`` parameter. @Feature: Host @Assert: HTTP 200 is returned, along with ``search`` term. """ query = gen_string('utf8', gen_integer(1, 100)) response = client.get( entities.Host().path(), auth=get_server_credentials(), data={u'search': query}, verify=False, ) self.assertEqual(response.status_code, httplib.OK) self.assertEqual(response.json()['search'], query)
def test_get_per_page(self): """@Test: GET ``api/v2/hosts`` and specify the ``per_page`` parameter. @Feature: Host @Assert: HTTP 200 is returned, along with per ``per_page`` value. """ per_page = gen_integer(1, 1000) response = client.get( entities.Host().path(), auth=get_server_credentials(), data={u'per_page': per_page}, verify=False, ) self.assertEqual(response.status_code, httplib.OK) self.assertEqual(response.json()['per_page'], per_page)
def test_get_status_code(self): """@Test: Create a system and GET it. @Feature: System APIs @Assert: HTTP 200 is returned with an ``application/json`` content-type """ system = System(uuid=System().create_json()['uuid']) logger.debug('system uuid: {0}'.format(system.uuid)) response = client.get( system.path(), auth=get_server_credentials(), verify=False, ) self.assertEqual(httplib.OK, response.status_code) self.assertIn('application/json', response.headers['content-type'])
def test_get_all(self): """@Test: Get ``katello/api/v2/environments`` and specify just an organization ID. @Feature: LifecycleEnvironment @Assert: HTTP 200 is returned with an ``application/json`` content-type """ org_attrs = entities.Organization().create() response = client.get( entities.LifecycleEnvironment().path(), auth=get_server_credentials(), data={u'organization_id': org_attrs['id']}, verify=False, ) self.assertEqual(response.status_code, httplib.OK) self.assertIn('application/json', response.headers['content-type'])
def single_delete(cls, uuid, thread_id): """Delete system from subscription""" start = time.time() response = requests.delete(urljoin( get_server_url(), '/katello/api/systems/{0}'.format(uuid)), auth=get_server_credentials(), verify=False) if response.status_code != 204: LOGGER.error('Fail to delete {0} on thread-{1}!'.format( uuid, thread_id)) LOGGER.error(response.content) return 0 LOGGER.info("Delete {0} on thread-{1} successful!".format( uuid, thread_id)) end = time.time() LOGGER.info('real {}s'.format(end - start)) return end - start
def test_positive_search(self): """@Test: Create an organization, then search for it by name. @Assert: Searching returns at least one result. @Feature: Organization """ name = entities.Organization().create_json()['name'] response = client.get( entities.Organization().path(), auth=get_server_credentials(), data={u'name': name}, verify=False, ) response.raise_for_status() results = response.json()['results'] self.assertGreaterEqual(len(results), 1)
def test_create_text_plain(self): """@Test Create an organization using a 'text/plain' content-type. @Assert: HTTP 415 is returned. @Feature: Organization """ organization = entities.Organization() organization.create_missing() response = client.post( organization.path(), organization.create_payload(), auth=get_server_credentials(), headers={'content-type': 'text/plain'}, verify=False, ) self.assertEqual(httplib.UNSUPPORTED_MEDIA_TYPE, response.status_code)
def test_get_with_no_args(self): """@Test: Issue an HTTP GET to the base content view filters path. @Feature: ContentViewFilter @Assert: An HTTP 400 or 422 response is received if a GET request is issued with no arguments specified. This test targets bugzilla bug #1102120. """ response = client.get( entities.ContentViewFilter().path(), auth=get_server_credentials(), verify=False, ) self.assertIn(response.status_code, (httplib.BAD_REQUEST, httplib.UNPROCESSABLE_ENTITY))
def test_positive_update_1(self, attrs): """@Test: Update a product with a new name or description. @Assert: The given attributes are used. @Feature: Product """ client.put( self.product_n.path(), attrs, auth=get_server_credentials(), verify=False, ).raise_for_status() new_attrs = self.product_n.read_json() for name, value in attrs.items(): self.assertIn(name, new_attrs.keys()) self.assertEqual(value, new_attrs[name])