swagger-ize result-apis of testAPI 09/14809/3
authorSerenaFeng <feng.xiaowei@zte.com.cn>
Sat, 28 May 2016 17:12:54 +0000 (01:12 +0800)
committerSerenaFeng <feng.xiaowei@zte.com.cn>
Sat, 28 May 2016 17:24:21 +0000 (01:24 +0800)
JIRA: FUNCTEST-270

Change-Id: I82b1e3acee82d9b4931531c9404e13a663ff32de
Signed-off-by: SerenaFeng <feng.xiaowei@zte.com.cn>
14 files changed:
utils/test/result_collection_api/resources/handlers.py
utils/test/result_collection_api/resources/pod_handlers.py
utils/test/result_collection_api/resources/project_handlers.py
utils/test/result_collection_api/resources/result_handlers.py [new file with mode: 0644]
utils/test/result_collection_api/resources/result_models.py
utils/test/result_collection_api/resources/testcase_handlers.py
utils/test/result_collection_api/resources/testcase_models.py
utils/test/result_collection_api/result_collection_api.py
utils/test/result_collection_api/tests/unit/fake_pymongo.py
utils/test/result_collection_api/tests/unit/test_base.py
utils/test/result_collection_api/tests/unit/test_pod.py
utils/test/result_collection_api/tests/unit/test_project.py
utils/test/result_collection_api/tests/unit/test_result.py
utils/test/result_collection_api/tests/unit/test_testcase.py

index 7d2e816..b5f7145 100644 (file)
@@ -67,13 +67,14 @@ class GenericApiHandler(RequestHandler):
         self.finish()
 
     def _create_response(self, resource):
-        href = self.request.full_url() + '/' + resource
+        href = self.request.full_url() + '/' + str(resource)
         return CreateResponse(href=href).format()
 
     @asynchronous
     @gen.coroutine
-    def _create(self, db_checks, **kwargs):
+    def _create(self, miss_checks, db_checks, **kwargs):
         """
+        :param miss_checks: [miss1, miss2]
         :param db_checks: [(table, exist, query, (error, message))]
         :param db_op: (insert/remove)
         :param res_op: (_create_response/None)
@@ -83,10 +84,11 @@ class GenericApiHandler(RequestHandler):
             raise HTTPError(HTTP_BAD_REQUEST, "no body")
 
         data = self.table_cls.from_dict(self.json_args)
-        name = data.name
-        if name is None or name == '':
-            raise HTTPError(HTTP_BAD_REQUEST,
-                            '{} name missing'.format(self.table[:-1]))
+        for miss in miss_checks:
+            miss_data = data.__getattribute__(miss)
+            if miss_data is None or miss_data == '':
+                raise HTTPError(HTTP_BAD_REQUEST,
+                                '{} missing'.format(miss))
 
         for k, v in kwargs.iteritems():
             data.__setattr__(k, v)
@@ -98,8 +100,12 @@ class GenericApiHandler(RequestHandler):
                 raise HTTPError(code, message)
 
         data.creation_date = datetime.now()
-        yield self._eval_db(self.table, 'insert', data.format())
-        self.finish_request(self._create_response(name))
+        _id = yield self._eval_db(self.table, 'insert', data.format())
+        if 'name' in self.json_args:
+            resource = data.name
+        else:
+            resource = _id
+        self.finish_request(self._create_response(resource))
 
     @asynchronous
     @gen.coroutine
@@ -198,172 +204,6 @@ class VersionHandler(GenericApiHandler):
         self.finish_request([{'v1': 'basics'}])
 
 
-class TestResultsHandler(GenericApiHandler):
-    """
-    TestResultsHandler Class
-    Handle the requests about the Test project's results
-    HTTP Methdods :
-        - GET : Get all test results and details about a specific one
-        - POST : Add a test results
-        - DELETE : Remove a test result
-    """
-
-    def initialize(self):
-        """ Prepares the database for the entire class """
-        super(TestResultsHandler, self).initialize()
-        self.name = "test_result"
-
-    @asynchronous
-    @gen.coroutine
-    def get(self, result_id=None):
-        """
-        Retrieve result(s) for a test project on a specific POD.
-        Available filters for this request are :
-         - project : project name
-         - case : case name
-         - pod : pod name
-         - version : platform version (Arno-R1, ...)
-         - installer (fuel, ...)
-         - build_tag : Jenkins build tag name
-         - period : x (x last days)
-         - scenario : the test scenario (previously version)
-         - criteria : the global criteria status passed or failed
-         - trust_indicator : evaluate the stability of the test case to avoid
-         running systematically long and stable test case
-
-
-        :param result_id: Get a result by ID
-        :raise HTTPError
-
-        GET /results/project=functest&case=vPing&version=Arno-R1 \
-        &pod=pod_name&period=15
-        => get results with optional filters
-        """
-
-        # prepare request
-        query = dict()
-        if result_id is not None:
-            query["_id"] = result_id
-            answer = yield self.db.results.find_one(query)
-            if answer is None:
-                raise HTTPError(HTTP_NOT_FOUND,
-                                "test result {} Not Exist".format(result_id))
-            else:
-                answer = format_data(answer, TestResult)
-        else:
-            pod_arg = self.get_query_argument("pod", None)
-            project_arg = self.get_query_argument("project", None)
-            case_arg = self.get_query_argument("case", None)
-            version_arg = self.get_query_argument("version", None)
-            installer_arg = self.get_query_argument("installer", None)
-            build_tag_arg = self.get_query_argument("build_tag", None)
-            scenario_arg = self.get_query_argument("scenario", None)
-            criteria_arg = self.get_query_argument("criteria", None)
-            period_arg = self.get_query_argument("period", None)
-            trust_indicator_arg = self.get_query_argument("trust_indicator",
-                                                          None)
-
-            if project_arg is not None:
-                query["project_name"] = project_arg
-
-            if case_arg is not None:
-                query["case_name"] = case_arg
-
-            if pod_arg is not None:
-                query["pod_name"] = pod_arg
-
-            if version_arg is not None:
-                query["version"] = version_arg
-
-            if installer_arg is not None:
-                query["installer"] = installer_arg
-
-            if build_tag_arg is not None:
-                query["build_tag"] = build_tag_arg
-
-            if scenario_arg is not None:
-                query["scenario"] = scenario_arg
-
-            if criteria_arg is not None:
-                query["criteria_tag"] = criteria_arg
-
-            if trust_indicator_arg is not None:
-                query["trust_indicator_arg"] = trust_indicator_arg
-
-            if period_arg is not None:
-                try:
-                    period_arg = int(period_arg)
-                except:
-                    raise HTTPError(HTTP_BAD_REQUEST)
-
-                if period_arg > 0:
-                    period = datetime.now() - timedelta(days=period_arg)
-                    obj = {"$gte": str(period)}
-                    query["creation_date"] = obj
-
-            res = []
-            cursor = self.db.results.find(query)
-            while (yield cursor.fetch_next):
-                res.append(format_data(cursor.next_object(), TestResult))
-            answer = {'results': res}
-
-        self.finish_request(answer)
-
-    @asynchronous
-    @gen.coroutine
-    def post(self):
-        """
-        Create a new test result
-        :return: status of the request
-        :raise HTTPError
-        """
-
-        # check for request payload
-        if self.json_args is None:
-            raise HTTPError(HTTP_BAD_REQUEST, 'no payload')
-
-        result = TestResult.from_dict(self.json_args)
-
-        # check for pod_name instead of id,
-        # keeping id for current implementations
-        if result.pod_name is None:
-            raise HTTPError(HTTP_BAD_REQUEST, 'pod is not provided')
-
-        # check for missing parameters in the request payload
-        if result.project_name is None:
-            raise HTTPError(HTTP_BAD_REQUEST, 'project is not provided')
-
-        if result.case_name is None:
-            raise HTTPError(HTTP_BAD_REQUEST, 'testcase is not provided')
-
-        # TODO : replace checks with jsonschema
-        # check for pod
-        the_pod = yield self.db.pods.find_one({"name": result.pod_name})
-        if the_pod is None:
-            raise HTTPError(HTTP_NOT_FOUND,
-                            "Could not find POD [{}] "
-                            .format(self.json_args.get("pod_name")))
-
-        # check for project
-        the_project = yield self.db.projects.find_one(
-            {"name": result.project_name})
-        if the_project is None:
-            raise HTTPError(HTTP_NOT_FOUND, "Could not find project [{}] "
-                            .format(result.project_name))
-
-        # check for testcase
-        the_testcase = yield self.db.testcases.find_one(
-            {"name": result.case_name})
-        if the_testcase is None:
-            raise HTTPError(HTTP_NOT_FOUND,
-                            "Could not find testcase [{}] "
-                            .format(result.case_name))
-
-        _id = yield self.db.results.insert(result.format(), check_keys=False)
-
-        self.finish_request(self._create_response(_id))
-
-
 class DashboardHandler(GenericApiHandler):
     """
     DashboardHandler Class
index c50ec51..9f583bb 100644 (file)
@@ -31,7 +31,7 @@ class PodCLHandler(GenericPodHandler):
             @rtype: L{Pod}
             @return 200: pod is created.
             @raise 403: pod already exists
-            @raise 400: post without body
+            @raise 400: body or name not provided
         """
         def query(data):
             return {'name': data.name}
@@ -40,8 +40,9 @@ class PodCLHandler(GenericPodHandler):
             message = '{} already exists as a pod'.format(data.name)
             return HTTP_FORBIDDEN, message
 
-        db_check = [(self.table, False, query, error)]
-        self._create(db_check)
+        miss_checks = ['name']
+        db_checks = [(self.table, False, query, error)]
+        self._create(miss_checks, db_checks)
 
 
 class PodGURHandler(GenericPodHandler):
index e56c01c..0bc1a61 100644 (file)
@@ -33,7 +33,7 @@ class ProjectCLHandler(GenericProjectHandler):
             @rtype: L{Project}
             @return 200: project is created.
             @raise 403: project already exists
-            @raise 400: post without body
+            @raise 400:  body or name not provided
         """
         def query(data):
             return {'name': data.name}
@@ -42,8 +42,9 @@ class ProjectCLHandler(GenericProjectHandler):
             message = '{} already exists as a project'.format(data.name)
             return HTTP_FORBIDDEN, message
 
-        db_check = [(self.table, False, query, error)]
-        self._create(db_check)
+        miss_checks = ['name']
+        db_checks = [(self.table, False, query, error)]
+        self._create(miss_checks, db_checks)
 
 
 class ProjectGURHandler(GenericProjectHandler):
diff --git a/utils/test/result_collection_api/resources/result_handlers.py b/utils/test/result_collection_api/resources/result_handlers.py
new file mode 100644 (file)
index 0000000..d3fea1d
--- /dev/null
@@ -0,0 +1,134 @@
+from datetime import datetime, timedelta
+
+from bson.objectid import ObjectId
+from tornado.web import HTTPError
+
+from common.constants import HTTP_BAD_REQUEST, HTTP_NOT_FOUND
+from resources.handlers import GenericApiHandler
+from resources.result_models import TestResult
+from tornado_swagger_ui.tornado_swagger import swagger
+
+
+class GenericResultHandler(GenericApiHandler):
+    def __init__(self, application, request, **kwargs):
+        super(GenericResultHandler, self).__init__(application,
+                                                   request,
+                                                   **kwargs)
+        self.table = self.db_results
+        self.table_cls = TestResult
+
+
+class ResultsCLHandler(GenericResultHandler):
+    @swagger.operation(nickname="list-all")
+    def get(self):
+        """
+            @description: list all test results consist with query
+            @return 200: all test results consist with query,
+                         empty list if no result is found
+            @rtype: L{TestResults}
+        """
+        query = dict()
+        pod_arg = self.get_query_argument("pod", None)
+        project_arg = self.get_query_argument("project", None)
+        case_arg = self.get_query_argument("case", None)
+        version_arg = self.get_query_argument("version", None)
+        installer_arg = self.get_query_argument("installer", None)
+        build_tag_arg = self.get_query_argument("build_tag", None)
+        scenario_arg = self.get_query_argument("scenario", None)
+        criteria_arg = self.get_query_argument("criteria", None)
+        period_arg = self.get_query_argument("period", None)
+        trust_indicator_arg = self.get_query_argument("trust_indicator", None)
+
+        if project_arg is not None:
+            query["project_name"] = project_arg
+
+        if case_arg is not None:
+            query["case_name"] = case_arg
+
+        if pod_arg is not None:
+            query["pod_name"] = pod_arg
+
+        if version_arg is not None:
+            query["version"] = version_arg
+
+        if installer_arg is not None:
+            query["installer"] = installer_arg
+
+        if build_tag_arg is not None:
+            query["build_tag"] = build_tag_arg
+
+        if scenario_arg is not None:
+            query["scenario"] = scenario_arg
+
+        if criteria_arg is not None:
+            query["criteria_tag"] = criteria_arg
+
+        if trust_indicator_arg is not None:
+            query["trust_indicator_arg"] = trust_indicator_arg
+
+        if period_arg is not None:
+            try:
+                period_arg = int(period_arg)
+            except:
+                raise HTTPError(HTTP_BAD_REQUEST)
+
+            if period_arg > 0:
+                period = datetime.now() - timedelta(days=period_arg)
+                obj = {"$gte": str(period)}
+                query["creation_date"] = obj
+
+        self._list(query)
+
+    @swagger.operation(nickname="create")
+    def post(self):
+        """
+            @description: create a test result
+            @param body: result to be created
+            @type body: L{ResultCreateRequest}
+            @in body: body
+            @rtype: L{TestResult}
+            @return 200: result is created.
+            @raise 404: pod/project/testcase not exist
+            @raise 400: body/pod_name/project_name/case_name not provided
+        """
+        def pod_query(data):
+            return {'name': data.pod_name}
+
+        def pod_error(data):
+            message = 'Could not find pod [{}]'.format(data.pod_name)
+            return HTTP_NOT_FOUND, message
+
+        def project_query(data):
+            return {'name': data.project_name}
+
+        def project_error(data):
+            message = 'Could not find project [{}]'.format(data.project_name)
+            return HTTP_NOT_FOUND, message
+
+        def testcase_query(data):
+            return {'project_name': data.project_name, 'name': data.case_name}
+
+        def testcase_error(data):
+            message = 'Could not find testcase [{}] in project [{}]'\
+                .format(data.case_name, data.project_name)
+            return HTTP_NOT_FOUND, message
+
+        miss_checks = ['pod_name', 'project_name', 'case_name']
+        db_checks = [('pods', True, pod_query, pod_error),
+                     ('projects', True, project_query, project_error),
+                     ('testcases', True, testcase_query, testcase_error)]
+        self._create(miss_checks, db_checks)
+
+
+class ResultsGURHandler(GenericResultHandler):
+    @swagger.operation(nickname='get-one')
+    def get(self, result_id):
+        """
+            @description: get a single result by result_id
+            @rtype: L{TestResult}
+            @return 200: test result exist
+            @raise 404: test result not exist
+        """
+        query = dict()
+        query["_id"] = ObjectId(result_id)
+        self._get_one(query)
index 15684e2..7faac16 100644 (file)
@@ -1,4 +1,7 @@
+from tornado_swagger_ui.tornado_swagger import swagger
 
+
+@swagger.model()
 class ResultCreateRequest(object):
     def __init__(self,
                  pod_name=None,
@@ -43,9 +46,8 @@ class ResultCreateRequest(object):
         }
 
 
-class TestResult:
-    """ Describes a test result"""
-
+@swagger.model()
+class TestResult(object):
     def __init__(self):
         self._id = None
         self.case_name = None
@@ -132,7 +134,11 @@ class TestResult:
         }
 
 
+@swagger.model()
 class TestResults(object):
+    """
+        @ptype testcases: C{list} of L{TestResult}
+    """
     def __init__(self, results=list()):
         self.results = results
 
index 9c0eb63..8f3bea6 100644 (file)
@@ -37,7 +37,7 @@ class TestcaseCLHandler(GenericTestcaseHandler):
             @return 200: testcase is created in this project.
             @raise 403: project not exist
                         or testcase already exists in this project
-            @raise 400: post without body
+            @raise 400: body or name not provided
         """
         def p_query(data):
             return {'name': data.project_name}
@@ -57,9 +57,10 @@ class TestcaseCLHandler(GenericTestcaseHandler):
                 .format(data.name, data.project_name)
             return HTTP_FORBIDDEN, message
 
+        miss_checks = ['name']
         db_checks = [(self.db_projects, True, p_query, p_error),
                      (self.db_testcases, False, tc_query, tc_error)]
-        self._create(db_checks, project_name=project_name)
+        self._create(miss_checks, db_checks, project_name=project_name)
 
 
 class TestcaseGURHandler(GenericTestcaseHandler):
index f386764..90b3f75 100644 (file)
@@ -35,8 +35,6 @@ class TestcaseUpdateRequest(object):
 
 @swagger.model()
 class Testcase(object):
-    """ Describes a test case"""
-
     def __init__(self):
         self._id = None
         self.name = None
index 25a670c..652aa58 100644 (file)
@@ -34,11 +34,11 @@ import argparse
 import tornado.ioloop
 import motor
 
-from resources.handlers import VersionHandler, \
-    TestResultsHandler, DashboardHandler
+from resources.handlers import VersionHandler, DashboardHandler
 from resources.testcase_handlers import TestcaseCLHandler, TestcaseGURHandler
 from resources.pod_handlers import PodCLHandler, PodGURHandler
 from resources.project_handlers import ProjectCLHandler, ProjectGURHandler
+from resources.result_handlers import ResultsCLHandler, ResultsGURHandler
 from common.config import APIConfig
 from tornado_swagger_ui.tornado_swagger import swagger
 
@@ -83,9 +83,8 @@ def make_app():
             # POST /results =>
             # Push results with mandatory request payload parameters
             # (project, case, and pod)
-            (r"/api/v1/results", TestResultsHandler),
-            (r"/api/v1/results([^/]*)", TestResultsHandler),
-            (r"/api/v1/results/([^/]*)", TestResultsHandler),
+            (r"/api/v1/results", ResultsCLHandler),
+            (r"/api/v1/results/([^/]+)", ResultsGURHandler),
 
             # Method to manage Dashboard ready results
             # GET /dashboard?project=functest&case=vPing&pod=opnfv-jump2
index 40eb164..3494280 100644 (file)
@@ -33,6 +33,8 @@ class MemDb(object):
     def _find_one(self, spec_or_id=None, *args):
         if spec_or_id is not None and not isinstance(spec_or_id, dict):
             spec_or_id = {"_id": spec_or_id}
+        if '_id' in spec_or_id:
+            spec_or_id['_id'] = str(spec_or_id['_id'])
         cursor = self._find(spec_or_id, *args)
         for result in cursor:
             return result
index dfb2070..036c6cf 100644 (file)
@@ -4,9 +4,9 @@ from tornado.testing import AsyncHTTPTestCase
 
 from resources.pod_handlers import PodCLHandler, PodGURHandler
 from resources.project_handlers import ProjectCLHandler, ProjectGURHandler
-from resources.handlers import VersionHandler, \
-    TestResultsHandler, DashboardHandler
+from resources.handlers import VersionHandler, DashboardHandler
 from resources.testcase_handlers import TestcaseCLHandler, TestcaseGURHandler
+from resources.result_handlers import ResultsCLHandler, ResultsGURHandler
 from resources.models import CreateResponse
 import fake_pymongo
 
@@ -36,9 +36,8 @@ class TestBase(AsyncHTTPTestCase):
                 (r"/api/v1/projects/([^/]+)/cases", TestcaseCLHandler),
                 (r"/api/v1/projects/([^/]+)/cases/([^/]+)",
                  TestcaseGURHandler),
-                (r"/api/v1/results", TestResultsHandler),
-                (r"/api/v1/results([^/]*)", TestResultsHandler),
-                (r"/api/v1/results/([^/]*)", TestResultsHandler),
+                (r"/api/v1/results", ResultsCLHandler),
+                (r"/api/v1/results/([^/]+)", ResultsGURHandler),
                 (r"/dashboard/v1/results", DashboardHandler),
                 (r"/dashboard/v1/results([^/]*)", DashboardHandler),
             ],
index 8a93027..1a43c05 100644 (file)
@@ -36,13 +36,13 @@ class TestPodCreate(TestPodBase):
         req_empty = PodCreateRequest('')
         (code, body) = self.create(req_empty)
         self.assertEqual(code, HTTP_BAD_REQUEST)
-        self.assertIn('pod name missing', body)
+        self.assertIn('name missing', body)
 
     def test_noneName(self):
         req_none = PodCreateRequest(None)
         (code, body) = self.create(req_none)
         self.assertEqual(code, HTTP_BAD_REQUEST)
-        self.assertIn('pod name missing', body)
+        self.assertIn('name missing', body)
 
     def test_success(self):
         code, body = self.create_d()
index b07cb7a..4f5bd9d 100644 (file)
@@ -34,13 +34,13 @@ class TestProjectCreate(TestProjectBase):
         req_empty = ProjectCreateRequest('')
         (code, body) = self.create(req_empty)
         self.assertEqual(code, HTTP_BAD_REQUEST)
-        self.assertIn('project name missing', body)
+        self.assertIn('name missing', body)
 
     def test_noneName(self):
         req_none = ProjectCreateRequest(None)
         (code, body) = self.create(req_none)
         self.assertEqual(code, HTTP_BAD_REQUEST)
-        self.assertIn('project name missing', body)
+        self.assertIn('name missing', body)
 
     def test_success(self):
         (code, body) = self.create_d()
index 7dd07ef..5757df0 100644 (file)
@@ -105,35 +105,35 @@ class TestResultCreate(TestResultBase):
     def test_nobody(self):
         (code, body) = self.create(None)
         self.assertEqual(code, HTTP_BAD_REQUEST)
-        self.assertIn('no payload', body)
+        self.assertIn('no body', body)
 
     def test_podNotProvided(self):
         req = self.req_d
         req.pod_name = None
         (code, body) = self.create(req)
         self.assertEqual(code, HTTP_BAD_REQUEST)
-        self.assertIn('pod is not provided', body)
+        self.assertIn('pod_name missing', body)
 
     def test_projectNotProvided(self):
         req = self.req_d
         req.project_name = None
         (code, body) = self.create(req)
         self.assertEqual(code, HTTP_BAD_REQUEST)
-        self.assertIn('project is not provided', body)
+        self.assertIn('project_name missing', body)
 
     def test_testcaseNotProvided(self):
         req = self.req_d
         req.case_name = None
         (code, body) = self.create(req)
         self.assertEqual(code, HTTP_BAD_REQUEST)
-        self.assertIn('testcase is not provided', body)
+        self.assertIn('case_name missing', body)
 
     def test_noPod(self):
         req = self.req_d
         req.pod_name = 'notExistPod'
         (code, body) = self.create(req)
         self.assertEqual(code, HTTP_NOT_FOUND)
-        self.assertIn('Could not find POD', body)
+        self.assertIn('Could not find pod', body)
 
     def test_noProject(self):
         req = self.req_d
index c6c0608..2371472 100644 (file)
@@ -85,13 +85,13 @@ class TestCaseCreate(TestCaseBase):
         req_empty = TestcaseCreateRequest('')
         (code, body) = self.create(req_empty, self.project)
         self.assertEqual(code, HTTP_BAD_REQUEST)
-        self.assertIn('testcase name missing', body)
+        self.assertIn('name missing', body)
 
     def test_noneName(self):
         req_none = TestcaseCreateRequest(None)
         (code, body) = self.create(req_none, self.project)
         self.assertEqual(code, HTTP_BAD_REQUEST)
-        self.assertIn('testcase name missing', body)
+        self.assertIn('name missing', body)
 
     def test_success(self):
         code, body = self.create_d()