From eaedd89c251b5ed10c2fce8b61ec8aea24d8808a Mon Sep 17 00:00:00 2001 From: danfengliu Date: Mon, 18 Feb 2019 13:47:16 +0800 Subject: [PATCH] add api test case for robot user, and modify swagger.yaml for wrong type of return value. (#6900) Signed-off-by: danfengliu --- docs/swagger.yaml | 4 +- tests/apitests/python/library/docker_api.py | 43 ++++++- tests/apitests/python/library/project.py | 52 +++++++- tests/apitests/python/library/repository.py | 4 +- tests/apitests/python/test_robot_account.py | 135 ++++++++++++++++++++ 5 files changed, 226 insertions(+), 12 deletions(-) create mode 100644 tests/apitests/python/test_robot_account.py diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 37983fd86..7ee410ac8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -3266,7 +3266,9 @@ paths: description: The ID of robot account. responses: '200': - description: '#/definitions/RobotAccount' + description: Robot account information. + schema: + $ref: '#/definitions/RobotAccount' '401': description: User need to log in first. '403': diff --git a/tests/apitests/python/library/docker_api.py b/tests/apitests/python/library/docker_api.py index b9384d136..213c93ace 100644 --- a/tests/apitests/python/library/docker_api.py +++ b/tests/apitests/python/library/docker_api.py @@ -24,15 +24,27 @@ class DockerAPI(object): _tag = tag else: _tag = "latest" + if expected_error_message is "": + expected_error_message = None + caught_err = False + ret = "" try: - base._get_string_from_unicode(self.DCLIENT.pull(r'{}:{}'.format(image, _tag))) + ret = base._get_string_from_unicode(self.DCLIENT.pull(r'{}:{}'.format(image, _tag))) except Exception, err: + caught_err = True if expected_error_message is not None: print "docker image pull error:", str(err) if str(err).lower().find(expected_error_message.lower()) < 0: - raise Exception(r"Pull image: Return message {} is not as expected {}".format(return_message, expected_error_message)) + raise Exception(r"Pull image: Return message {} is not as expected {}".format(str(err), expected_error_message)) else: - raise Exception(r" Docker pull image {} failed, error is [{}]".format (image, e.message)) + raise Exception(r" Docker pull image {} failed, error is [{}]".format (image, err.message)) + if caught_err == False: + if expected_error_message is not None: + if str(ret).lower().find(expected_error_message.lower()) < 0: + raise Exception(r" Failed to catch error [{}] when pull image {}".format (expected_error_message, image)) + else: + if str(ret).lower().find("error".lower()) >= 0: + raise Exception(r" It's was not suppose to catch error when pull image {}, return message is [{}]".format (image, ret)) def docker_image_tag(self, image, harbor_registry, tag = None): _tag = base._random_name("tag") @@ -44,8 +56,25 @@ class DockerAPI(object): except docker.errors.APIError, e: raise Exception(r" Docker tag image {} failed, error is [{}]".format (image, e.message)) - def docker_image_push(self, harbor_registry, tag): + def docker_image_push(self, harbor_registry, tag, expected_error_message = None): + caught_err = False + ret = "" + if expected_error_message is "": + expected_error_message = None try: - base._get_string_from_unicode(self.DCLIENT.push(harbor_registry, tag, stream=True)) - except docker.errors.APIError, e: - raise Exception(r" Docker tag image {} failed, error is [{}]".format (image, e.message)) \ No newline at end of file + ret = base._get_string_from_unicode(self.DCLIENT.push(harbor_registry, tag, stream=True)) + except Exception, err: + caught_err = True + if expected_error_message is not None: + print "docker image push error:", str(err) + if str(err).lower().find(expected_error_message.lower()) < 0: + raise Exception(r"Push image: Return message {} is not as expected {}".format(str(err), expected_error_message)) + else: + raise Exception(r" Docker push image {} failed, error is [{}]".format (harbor_registry, err.message)) + if caught_err == False: + if expected_error_message is not None: + if str(ret).lower().find(expected_error_message.lower()) < 0: + raise Exception(r" Failed to catch error [{}] when push image {}".format (expected_error_message, harbor_registry)) + else: + if str(ret).lower().find("errorDetail".lower()) >= 0: + raise Exception(r" It's was not suppose to catch error when push image {}, return message is [{}]".format (harbor_registry, ret)) \ No newline at end of file diff --git a/tests/apitests/python/library/project.py b/tests/apitests/python/library/project.py index c4a8953eb..970cb9e7a 100644 --- a/tests/apitests/python/library/project.py +++ b/tests/apitests/python/library/project.py @@ -34,7 +34,6 @@ class Project(base.Base): base._assert_status_code(201, status_code) return base._get_id_from_header(header), name - def get_projects(self, params, **kwargs): client = self._get_client(**kwargs) data = [] @@ -150,12 +149,14 @@ class Project(base.Base): data = [] data, status_code, _ = client.projects_project_id_members_mid_put_with_http_info(project_id, member_id, role = role) base._assert_status_code(expect_status_code, status_code) + base._assert_status_code(200, status_code) return data def delete_project_member(self, project_id, member_id, expect_status_code = 200, **kwargs): client = self._get_client(**kwargs) _, status_code, _ = client.projects_project_id_members_mid_delete_with_http_info(project_id, member_id) base._assert_status_code(expect_status_code, status_code) + base._assert_status_code(200, status_code) def add_project_members(self, project_id, user_id, member_role_id = None, expect_status_code = 201, **kwargs): if member_role_id is None: @@ -165,6 +166,53 @@ class Project(base.Base): client = self._get_client(**kwargs) data = [] data, status_code, header = client.projects_project_id_members_post_with_http_info(project_id, project_member = projectMember) - base._assert_status_code(201, status_code) + base._assert_status_code(expect_status_code, status_code) return base._get_id_from_header(header) + def add_project_robot_account(self, project_id, project_name, robot_name = None, robot_desc = None, has_pull_right = True, has_push_right = True, expect_status_code = 201, **kwargs): + if robot_name is None: + robot_name = base._random_name("robot") + if robot_desc is None: + robot_desc = base._random_name("robot_desc") + if has_pull_right is False and has_push_right is False: + has_pull_right = True + access_list = [] + resource_by_project_id = "/project/"+str(project_id)+"/repository" + resource_by_project_name = "/project/"+project_name+"/repository" + action_pull = "pull" + action_push = "push" + if has_pull_right is True: + robotAccountAccess = swagger_client.RobotAccountAccess(resource = resource_by_project_id, action = action_pull) + access_list.append(robotAccountAccess) + robotAccountAccess = swagger_client.RobotAccountAccess(resource = resource_by_project_name, action = action_pull) + access_list.append(robotAccountAccess) + if has_push_right is True: + robotAccountAccess = swagger_client.RobotAccountAccess(resource = resource_by_project_id, action = action_push) + access_list.append(robotAccountAccess) + robotAccountAccess = swagger_client.RobotAccountAccess(resource = resource_by_project_name, action = action_push) + access_list.append(robotAccountAccess) + robotAccountCreate = swagger_client.RobotAccountCreate(robot_name, robot_desc, access_list) + client = self._get_client(**kwargs) + data = [] + data, status_code, header = client.projects_project_id_robots_post_with_http_info(project_id, robotAccountCreate) + base._assert_status_code(expect_status_code, status_code) + base._assert_status_code(201, status_code) + return base._get_id_from_header(header), data + + def get_project_robot_account_by_id(self, project_id, robot_id, **kwargs): + client = self._get_client(**kwargs) + data, status_code, _ = client.projects_project_id_robots_robot_id_get_with_http_info(project_id, robot_id) + return data + + def disable_project_robot_account(self, project_id, robot_id, disable, expect_status_code = 200, **kwargs): + client = self._get_client(**kwargs) + robotAccountUpdate = swagger_client.RobotAccountUpdate(disable) + _, status_code, _ = client.projects_project_id_robots_robot_id_put_with_http_info(project_id, robot_id, robotAccountUpdate) + base._assert_status_code(expect_status_code, status_code) + base._assert_status_code(200, status_code) + + def delete_project_robot_account(self, project_id, robot_id, expect_status_code = 200, **kwargs): + client = self._get_client(**kwargs) + _, status_code, _ = client.projects_project_id_robots_robot_id_delete_with_http_info(project_id, robot_id) + base._assert_status_code(expect_status_code, status_code) + base._assert_status_code(200, status_code) \ No newline at end of file diff --git a/tests/apitests/python/library/repository.py b/tests/apitests/python/library/repository.py index 69a891b20..ffcb0388c 100644 --- a/tests/apitests/python/library/repository.py +++ b/tests/apitests/python/library/repository.py @@ -11,7 +11,7 @@ def pull_harbor_image(registry, username, password, image, tag, expected_error_m time.sleep(2) _docker_api.docker_image_pull(r'{}/{}'.format(registry, image), tag = tag, expected_error_message = expected_error_message) -def push_image_to_project(project_name, registry, username, password, image, tag): +def push_image_to_project(project_name, registry, username, password, image, tag, expected_error_message = None): _docker_api = DockerAPI() _docker_api.docker_login(registry, username, password) time.sleep(2) @@ -22,7 +22,7 @@ def push_image_to_project(project_name, registry, username, password, image, tag new_harbor_registry, new_tag = _docker_api.docker_image_tag(r'{}:{}'.format(image, tag), r'{}/{}/{}'.format(registry, project_name, image)) time.sleep(2) - _docker_api.docker_image_push(new_harbor_registry, new_tag) + _docker_api.docker_image_push(new_harbor_registry, new_tag, expected_error_message = expected_error_message) return r'{}/{}'.format(project_name, image), new_tag diff --git a/tests/apitests/python/test_robot_account.py b/tests/apitests/python/test_robot_account.py new file mode 100644 index 000000000..1d0551a38 --- /dev/null +++ b/tests/apitests/python/test_robot_account.py @@ -0,0 +1,135 @@ +from __future__ import absolute_import + +import unittest + +from testutils import ADMIN_CLIENT +from testutils import TEARDOWN +from library.user import User +from library.project import Project +from library.repository import Repository +from library.repository import pull_harbor_image +from library.repository import push_image_to_project +from testutils import harbor_server +from library.base import _assert_status_code + +class TestProjects(unittest.TestCase): + @classmethod + def setUp(self): + project = Project() + self.project= project + + user = User() + self.user= user + + repo = Repository() + self.repo= repo + + @classmethod + def tearDown(self): + print "Case completed" + + @unittest.skipIf(TEARDOWN == False, "Test data won't be erased.") + def test_ClearData(self): + #1. Delete repository(RA) by user(UA); + self.repo.delete_repoitory(TestProjects.repo_name_in_project_a, **TestProjects.USER_RA_CLIENT) + self.repo.delete_repoitory(TestProjects.repo_name_in_project_b, **TestProjects.USER_RA_CLIENT) + self.repo.delete_repoitory(TestProjects.repo_name_in_project_c, **TestProjects.USER_RA_CLIENT) + self.repo.delete_repoitory(TestProjects.repo_name_pa, **TestProjects.USER_RA_CLIENT) + + #2. Delete project(PA); + self.project.delete_project(TestProjects.project_ra_id_a, **TestProjects.USER_RA_CLIENT) + self.project.delete_project(TestProjects.project_ra_id_b, **TestProjects.USER_RA_CLIENT) + self.project.delete_project(TestProjects.project_ra_id_c, **TestProjects.USER_RA_CLIENT) + + #3. Delete user(UA); + self.user.delete_user(TestProjects.user_ra_id, **ADMIN_CLIENT) + + def testRobotAccount(self): + """ + Test case: + Robot Account + Test step and expected result: + 1. Create user(UA); + 2. Create private project(PA), private project(PB) and public project(PC) by user(UA); + 3. Push image(ImagePA) to project(PA), image(ImagePB) to project(PB) and image(ImagePC) to project(PC) by user(UA); + 4. Create a new robot account(RA) with pull and push privilige in project(PA) by user(UA); + 5. Check robot account info, it should has both pull and push priviliges; + 6. Pull image(ImagePA) from project(PA) by robot account(RA), it must be successful; + 7. Push image(ImageRA) to project(PA) by robot account(RA), it must be successful; + 8. Push image(ImageRA) to project(PB) by robot account(RA), it must be not successful; + 9. Pull image(ImagePB) from project(PB) by robot account(RA), it must be not successful; + 10. Pull image from project(PC), it must be successful; + 11. Push image(ImageRA) to project(PC) by robot account(RA), it must be not successful; + 12. Update action property of robot account(RA); + 13. Pull image(ImagePA) from project(PA) by robot account(RA), it must be not successful; + 14. Push image(ImageRA) to project(PA) by robot account(RA), it must be not successful; + 15. Push image(ImageRA) to project(PA) by robot account(RA), it must be not successful; + Tear down: + 1. Delete project(PA) (PB) (PC); + 2. Delete user(UA). + """ + url = ADMIN_CLIENT["endpoint"] + admin_name = ADMIN_CLIENT["username"] + admin_password = ADMIN_CLIENT["password"] + user_ra_password = "Aa123456" + image_project_a = "tomcat" + image_project_b = "hello-world" + image_project_c = "mysql" + image_robot_account = "mariadb" + tag = "latest" + + print "#1. Create user(UA);" + TestProjects.user_ra_id, user_ra_name = self.user.create_user(user_password = user_ra_password, **ADMIN_CLIENT) + TestProjects.USER_RA_CLIENT=dict(endpoint = url, username = user_ra_name, password = user_ra_password) + + print "#2. Create private project(PA), private project(PB) and public project(PC) by user(UA);" + TestProjects.project_ra_id_a, project_ra_name_a = self.project.create_project(metadata = {"public": "false"}, **TestProjects.USER_RA_CLIENT) + TestProjects.project_ra_id_b, project_ra_name_b = self.project.create_project(metadata = {"public": "false"}, **TestProjects.USER_RA_CLIENT) + TestProjects.project_ra_id_c, project_ra_name_c = self.project.create_project(metadata = {"public": "true"}, **TestProjects.USER_RA_CLIENT) + + print "#3. Push image(ImagePA) to project(PA), image(ImagePB) to project(PB) and image(ImagePC) to project(PC) by user(UA);" + TestProjects.repo_name_in_project_a, tag_a = push_image_to_project(project_ra_name_a, harbor_server, user_ra_name, user_ra_password, image_project_a, tag) + TestProjects.repo_name_in_project_b, tag_b = push_image_to_project(project_ra_name_b, harbor_server, user_ra_name, user_ra_password, image_project_b, tag) + TestProjects.repo_name_in_project_c, tag_c = push_image_to_project(project_ra_name_c, harbor_server, user_ra_name, user_ra_password, image_project_c, tag) + + print "#4. Create a new robot account(RA) with pull and push privilige in project(PA) by user(UA);" + robot_id, robot_account = self.project.add_project_robot_account(TestProjects.project_ra_id_a, project_ra_name_a, **TestProjects.USER_RA_CLIENT) + print robot_account.name + print robot_account.token + + print "#5. Check robot account info, it should has both pull and push priviliges;" + data = self.project.get_project_robot_account_by_id(TestProjects.project_ra_id_a, robot_id, **TestProjects.USER_RA_CLIENT) + _assert_status_code(robot_account.name, data.name) + + print "#6. Pull image(ImagePA) from project(PA) by robot account(RA), it must be successful;" + pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_a, tag_a) + + print "#7. Push image(ImageRA) to project(PA) by robot account(RA), it must be successful;" + TestProjects.repo_name_pa, _ = push_image_to_project(project_ra_name_a, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag) + + print "#8. Push image(ImageRA) to project(PB) by robot account(RA), it must be not successful;" + push_image_to_project(project_ra_name_b, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_error_message = "denied: requested access to the resource is denied") + + print "#9. Pull image(ImagePB) from project(PB) by robot account(RA), it must be not successful;" + pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_b, tag_b, expected_error_message = r"pull access denied for " + harbor_server + "/" + TestProjects.repo_name_in_project_b) + + print "#10. Pull image from project(PC), it must be successful;" + pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_c, tag_c) + + print "#11. Push image(ImageRA) to project(PC) by robot account(RA), it must be not successful;" + push_image_to_project(project_ra_name_c, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_error_message = "denied: requested access to the resource is denied") + + print "#12. Update action property of robot account(RA);" + #self.project.disable_project_robot_account(TestProjects.project_ra_id_a, robot_id, True, **TestProjects.USER_RA_CLIENT) + + print "#13. Pull image(ImagePA) from project(PA) by robot account(RA), it must be not successful;" + #pull_harbor_image(harbor_server, robot_account.name, robot_account.token, TestProjects.repo_name_in_project_a, tag_a, expected_error_message = "") + + print "#14. Push image(ImageRA) to project(PA) by robot account(RA), it must be not successful;" + #push_image_to_project(project_ra_name_a, harbor_server, robot_account.name, robot_account.token, image_robot_account, tag, expected_error_message = "") + + print "#15. Push image(ImageRA) to project(PA) by robot account(RA), it must be not successful;" + self.project.delete_project_robot_account(TestProjects.project_ra_id_a, robot_id, **TestProjects.USER_RA_CLIENT) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file