Example #1
0
 def run_execute_inputs_schema_variant(self, inputs_param, preload=False, expect_success=True, mock_exec=True):
     if isinstance(inputs_param, str):
         if preload:
             inputs_param = self.load_resource_file(inputs_param)
         else:
             inputs_param = self.get_resource_file(inputs_param)
     with contextlib.ExitStack() as stack_exec:
         # use pass-through function because don't care about execution result here, only the parsing of I/O
         if mock_exec:
             mock_exec_func = lambda *_, **__: None  # noqa
         else:
             mock_exec_func = None
         for mock_exec_proc in mocked_execute_process(func_execute_process=mock_exec_func):
             stack_exec.enter_context(mock_exec_proc)
         result = mocked_sub_requests(self.app, self.client.execute, self.test_process, inputs=inputs_param)
     if expect_success:
         assert result.success, result.text
         assert "jobID" in result.body
         assert "processID" in result.body
         assert "status" in result.body
         assert "location" in result.body
         assert result.body["processID"] == self.test_process
         assert result.body["status"] == STATUS_ACCEPTED
         assert result.body["location"] == result.headers["Location"]
         assert "undefined" not in result.message
     else:
         assert not result.success, result.text
     return result
Example #2
0
 def test_execute_allowed_demo(self):
     template = "service=wps&request=execute&version=1.0.0&identifier={}&datainputs=name=tux"
     params = template.format(HelloWPS.identifier)
     url = self.make_url(params)
     with contextlib.ExitStack() as stack_exec:
         for mock_exec in mocked_execute_process():
             stack_exec.enter_context(mock_exec)
         resp = self.app.get(url)
     assert resp.status_code == 200  # FIXME: replace by 202 Accepted (?) https://github.com/crim-ca/weaver/issues/14
     assert resp.content_type in CONTENT_TYPE_ANY_XML
     resp.mustcontain("<wps:ExecuteResponse")
     resp.mustcontain("<wps:ProcessAccepted")
     resp.mustcontain("PyWPS Process {}".format(HelloWPS.identifier))
Example #3
0
 def test_execute_deployed_with_visibility_allowed(self):
     headers = {"Accept": CONTENT_TYPE_APP_XML}
     params_template = "service=wps&request=execute&version=1.0.0&identifier={}&datainputs=test_input=test"
     url = self.make_url(
         params_template.format(self.process_public.identifier))
     with contextlib.ExitStack() as stack_exec:
         for mock_exec in mocked_execute_process():
             stack_exec.enter_context(mock_exec)
         resp = self.app.get(url, headers=headers)
     assert resp.status_code == 200  # FIXME: replace by 202 Accepted (?) https://github.com/crim-ca/weaver/issues/14
     assert resp.content_type in CONTENT_TYPE_ANY_XML
     resp.mustcontain("<wps:ExecuteResponse")
     resp.mustcontain("<wps:ProcessAccepted")
     resp.mustcontain("PyWPS Process {}".format(
         self.process_public.identifier))
Example #4
0
 def test_execute_process_transmission_mode_value_not_supported(self):
     execute_data = self.get_process_execute_template(
         fully_qualified_name(self))
     execute_data["outputs"][0][
         "transmissionMode"] = EXECUTE_TRANSMISSION_MODE_VALUE
     uri = "/processes/{}/jobs".format(self.process_public.identifier)
     with contextlib.ExitStack() as stack_exec:
         for mock_exec in mocked_execute_process():
             stack_exec.enter_context(mock_exec)
         resp = self.app.post_json(uri,
                                   params=execute_data,
                                   headers=self.json_headers,
                                   expect_errors=True)
     assert resp.status_code == 501
     assert resp.content_type == CONTENT_TYPE_APP_JSON
Example #5
0
    def test_execute_wps_rest_resp_json(self):
        """
        Test validates that basic Docker application runs successfully, fetching the reference as needed.

        The job execution is launched using the WPS-REST endpoint for this test.
        Both the request body and response content are JSON.

        .. seealso::
            - :meth:`test_execute_wps_kvp_get_resp_xml`
            - :meth:`test_execute_wps_kvp_get_resp_json`
            - :meth:`test_execute_wps_xml_post_resp_xml`
            - :meth:`test_execute_wps_xml_post_resp_json`
        """

        test_content = "Test file in Docker - WPS-REST job endpoint"
        with contextlib.ExitStack() as stack_exec:
            # setup
            dir_name = tempfile.gettempdir()
            tmp_path = tempfile.NamedTemporaryFile(dir=dir_name, mode="w", suffix=".txt")
            tmp_file = stack_exec.enter_context(tmp_path)  # noqa
            tmp_file.write(test_content)
            tmp_file.seek(0)
            exec_body = {
                "mode": EXECUTE_MODE_ASYNC,
                "response": EXECUTE_RESPONSE_DOCUMENT,
                "inputs": [
                    {"id": "file", "href": tmp_file.name},
                ],
                "outputs": [
                    {"id": self.out_key, "transmissionMode": EXECUTE_TRANSMISSION_MODE_REFERENCE},
                ]
            }
            for mock_exec in mocked_execute_process():
                stack_exec.enter_context(mock_exec)

            # execute
            proc_url = "/processes/{}/jobs".format(self.process_id)
            resp = mocked_sub_requests(self.app, "post_json", proc_url,
                                       data=exec_body, headers=self.json_headers, only_local=True)
            assert resp.status_code in [200, 201], "Failed with: [{}]\nReason:\n{}".format(resp.status_code, resp.json)
            status_url = resp.json["location"]
            job_id = resp.json["jobID"]

            # job monitoring
            result = self.monitor_job(status_url)

        self.validate_outputs(job_id, result, test_content)
Example #6
0
 def test_execute_deployed_with_visibility_denied(self):
     headers = {"Accept": CONTENT_TYPE_APP_XML}
     params_template = "service=wps&request=execute&version=1.0.0&identifier={}&datainputs=test_input=test"
     url = self.make_url(
         params_template.format(self.process_private.identifier))
     with contextlib.ExitStack() as stack_exec:
         for mock_exec in mocked_execute_process():
             stack_exec.enter_context(mock_exec)
         resp = self.app.get(url, headers=headers, expect_errors=True)
     assert resp.status_code == 403
     assert resp.content_type in CONTENT_TYPE_ANY_XML, "Error Response: {}".format(
         resp.text)
     resp.mustcontain(
         "<Exception exceptionCode=\"AccessForbidden\" locator=\"service\">"
     )
     err_desc = "Process with ID '{}' is not accessible.".format(
         self.process_private.identifier)
     resp.mustcontain("<ExceptionText>{}</ExceptionText>".format(err_desc))
Example #7
0
    def test_execute_manual_monitor(self):
        with contextlib.ExitStack() as stack_exec:
            for mock_exec_proc in mocked_execute_process():
                stack_exec.enter_context(mock_exec_proc)

            lines = mocked_sub_requests(
                self.app, run_command,
                [
                    # "weaver",
                    "execute",
                    self.url,
                    "-p", self.test_process,
                    "-I", "message='TEST MESSAGE!'"
                ],
                trim=False,
                entrypoint=weaver_cli,
                only_local=True,
            )
            # ignore indents of fields from formatted JSON content
            assert any(f"\"processID\": \"{self.test_process}\"" in line for line in lines)
            assert any("\"jobID\": \"" in line for line in lines)
            assert any("\"location\": \"" in line for line in lines)
            job_loc = [line for line in lines if "location" in line][0]
            job_ref = [line for line in job_loc.split("\"") if line][-1]
            job_id = job_ref.split("/")[-1]

            lines = mocked_sub_requests(
                self.app, run_command,
                [
                    # "weaver",
                    "monitor",
                    "-j", job_ref,
                    "-T", 10,
                    "-W", 1,
                ],
                trim=False,
                entrypoint=weaver_cli,
                only_local=True,
            )

            assert any(f"\"jobID\": \"{job_id}\"" in line for line in lines)
            assert any(f"\"status\": \"{STATUS_SUCCEEDED}\"" in line for line in lines)
            assert any(f"\"href\": \"{job_ref}/results\"" in line for line in lines)
            assert any("\"rel\": \"http://www.opengis.net/def/rel/ogc/1.0/results\"" in line for line in lines)
Example #8
0
    def test_execute_docker_embedded_python_script(self):
        test_proc = "test-docker-python-script"
        cwl = load_file(os.path.join(WEAVER_ROOT_DIR, "docs/examples/docker-python-script-report.cwl"))
        body = {
            "processDescription": {
                "process": {
                    "id": test_proc
                }
            },
            "executionUnit": [{"unit": cwl}],
            "deploymentProfileName": "http://www.opengis.net/profiles/eoc/dockerizedApplication"
        }
        self.deploy_process(body)

        with contextlib.ExitStack() as stack:
            for mock_exec in mocked_execute_process():
                stack.enter_context(mock_exec)

            path = f"/processes/{test_proc}/execution"
            cost = 2.45
            amount = 3
            body = {
                "mode": EXECUTE_MODE_ASYNC,
                "response": EXECUTE_RESPONSE_DOCUMENT,
                "inputs": [
                    {"id": "amount", "value": amount},
                    {"id": "cost", "value": cost}
                ],
                "outputs": [
                    {"id": "quote", "transmissionMode": EXECUTE_TRANSMISSION_MODE_REFERENCE},
                ]
            }
            resp = mocked_sub_requests(self.app, "POST", path, json=body, headers=self.json_headers, only_local=True)
            status_url = resp.headers["Location"]
            results = self.monitor_job(status_url)

            assert results["quote"]["href"].startswith("http")
            stack.enter_context(mocked_wps_output(self.settings))
            tmpdir = stack.enter_context(tempfile.TemporaryDirectory())
            report_file = fetch_file(results["quote"]["href"], tmpdir, self.settings)
            report_data = load_file(report_file, text=True)
            assert report_data == f"Order Total: {amount * cost:0.2f}$\n"
Example #9
0
    def test_execute_auto_monitor(self):
        with contextlib.ExitStack() as stack_exec:
            for mock_exec_proc in mocked_execute_process():
                stack_exec.enter_context(mock_exec_proc)

            lines = mocked_sub_requests(
                self.app, run_command,
                [
                    # "weaver",
                    "execute",
                    self.url,
                    "-p", self.test_process,
                    "-I", "message='TEST MESSAGE!'",
                    "-M",
                    "-T", 10,
                    "-W", 1
                ],
                trim=False,
                entrypoint=weaver_cli,
                only_local=True,
            )
            assert any("\"jobID\": \"" in line for line in lines)  # don't care value, self-handled
            assert any(f"\"status\": \"{STATUS_SUCCEEDED}\"" in line for line in lines)
            assert any("\"rel\": \"http://www.opengis.net/def/rel/ogc/1.0/results\"" in line for line in lines)
Example #10
0
 def test_execute_inputs_capture(self):
     """
     Verify that specified inputs are captured for a limited number of 1 item per ``-I`` option.
     """
     with contextlib.ExitStack() as stack_exec:
         for mock_exec_proc in mocked_execute_process():
             stack_exec.enter_context(mock_exec_proc)
         lines = mocked_sub_requests(
             self.app, run_command,
             [
                 # "weaver",
                 "execute",
                 "-p", self.test_process,
                 "-I", "message='TEST MESSAGE!'",  # if -I not capture as indented, URL after would be combined in it
                 self.url,
                 "-M",
                 "-T", 10,
                 "-W", 1,
             ],
             trim=False,
             entrypoint=weaver_cli,
             only_local=True,
         )
         assert any(f"\"status\": \"{STATUS_SUCCEEDED}\"" in line for line in lines)
Example #11
0
    def wps_execute(self, version, accept):
        wps_url = get_wps_url(self.settings)
        if version == "1.0.0":
            test_content = "Test file in Docker - WPS KVP"
            wps_method = "GET"
        elif version == "2.0.0":
            test_content = "Test file in Docker - WPS XML"
            wps_method = "POST"
        else:
            raise ValueError("Invalid WPS version: {}".format(version))
        test_content += " {} request - Accept {}".format(wps_method, accept.split("/")[-1].upper())

        with contextlib.ExitStack() as stack_exec:
            # setup
            dir_name = tempfile.gettempdir()
            tmp_path = tempfile.NamedTemporaryFile(dir=dir_name, mode="w", suffix=".txt")
            tmp_file = stack_exec.enter_context(tmp_path)  # noqa
            tmp_file.write(test_content)
            tmp_file.seek(0)
            for mock_exec in mocked_execute_process():
                stack_exec.enter_context(mock_exec)

            # execute
            if version == "1.0.0":
                wps_inputs = ["file={}@mimeType={}".format(tmp_file.name, CONTENT_TYPE_TEXT_PLAIN)]
                wps_params = {
                    "service": "WPS",
                    "request": "Execute",
                    "version": version,
                    "identifier": self.process_id,
                    "DataInputs": wps_inputs,
                }
                wps_headers = {"Accept": accept}
                wps_data = None
            else:
                wps_inputs = [("file", ComplexDataInput(tmp_file.name, mimeType=CONTENT_TYPE_TEXT_PLAIN))]
                wps_outputs = [(self.out_key, True)]  # as reference
                wps_exec = WPSExecution(version=version, url=wps_url)
                wps_req = wps_exec.buildRequest(self.process_id, wps_inputs, wps_outputs)
                wps_data = lxml.etree.tostring(wps_req)
                wps_headers = {"Accept": accept, "Content-Type": CONTENT_TYPE_APP_XML}
                wps_params = None
            resp = mocked_sub_requests(self.app, wps_method, wps_url,
                                       params=wps_params, data=wps_data, headers=wps_headers, only_local=True)
            assert resp.status_code in [200, 201], \
                "Failed with: [{}]\nTest: [{}]\nReason:\n{}".format(resp.status_code, test_content, resp.text)

            # parse response status
            if accept == CONTENT_TYPE_APP_XML:
                assert resp.content_type in CONTENT_TYPE_ANY_XML, test_content
                xml = lxml.etree.fromstring(str2bytes(resp.text))
                status_url = xml.get("statusLocation")
                job_id = status_url.split("/")[-1]
            elif accept == CONTENT_TYPE_APP_JSON:
                assert resp.content_type == CONTENT_TYPE_APP_JSON, test_content
                status_url = resp.json["location"]
                job_id = resp.json["jobID"]
            assert status_url
            assert job_id

            # job monitoring
            result = self.monitor_job(status_url)

        self.validate_outputs(job_id, result, test_content)
Example #12
0
    def test_jsonarray2netcdf_execute(self):
        dirname = tempfile.gettempdir()
        nc_data = "Hello NetCDF!"
        with contextlib.ExitStack() as stack_exec:
            tmp_ncdf = tempfile.NamedTemporaryFile(dir=dirname, mode="w", suffix=".nc")
            tmp_json = tempfile.NamedTemporaryFile(dir=dirname, mode="w", suffix=".json")
            tmp_ncdf = stack_exec.enter_context(tmp_ncdf)  # noqa
            tmp_json = stack_exec.enter_context(tmp_json)  # noqa
            tmp_ncdf.write(nc_data)
            tmp_ncdf.seek(0)
            tmp_json.write(json.dumps(["file://{}".format(os.path.join(dirname, tmp_ncdf.name))]))
            tmp_json.seek(0)
            data = {
                "mode": EXECUTE_MODE_ASYNC,
                "response": EXECUTE_RESPONSE_DOCUMENT,
                "inputs": [{"id": "input", "href": os.path.join(dirname, tmp_json.name)}],
                "outputs": [{"id": "output", "transmissionMode": EXECUTE_TRANSMISSION_MODE_REFERENCE}],
            }

            for mock_exec in mocked_execute_process():
                stack_exec.enter_context(mock_exec)
            path = "/processes/jsonarray2netcdf/jobs"
            resp = mocked_sub_requests(self.app, "post_json", path,
                                       data=data, headers=self.json_headers, only_local=True)

        assert resp.status_code == 201, "Error: {}".format(resp.json)
        assert resp.content_type in CONTENT_TYPE_APP_JSON
        job_url = resp.json["location"]
        results = self.monitor_job(job_url)

        # first validate format of OGC-API results
        assert "output" in results, "Expected result ID 'output' in response body"
        assert isinstance(results["output"], dict), "Container of result ID 'output' should be a dict"
        assert "href" in results["output"]
        assert "format" in results["output"]
        fmt = results["output"]["format"]  # type: JSON
        assert isinstance(fmt, dict), "Result format should be provided with content details"
        assert "mediaType" in fmt
        assert isinstance(fmt["mediaType"], str), "Result format Content-Type should be a single string definition"
        assert fmt["mediaType"] == CONTENT_TYPE_APP_NETCDF, "Result 'output' format expected to be NetCDF file"
        nc_path = results["output"]["href"]
        assert isinstance(nc_path, str) and len(nc_path)
        settings = get_settings_from_testapp(self.app)
        wps_out = "{}{}".format(settings.get("weaver.url"), settings.get("weaver.wps_output_path"))
        nc_real_path = nc_path.replace(wps_out, settings.get("weaver.wps_output_dir"))
        assert nc_path.startswith(wps_out)
        assert os.path.split(nc_real_path)[-1] == os.path.split(nc_path)[-1]
        assert os.path.isfile(nc_real_path)
        with open(nc_real_path, "r") as f:
            assert f.read() == nc_data

        # if everything was valid for results, validate equivalent but differently formatted outputs response
        output_url = job_url + "/outputs"
        resp = self.app.get(output_url, headers=self.json_headers)
        assert resp.status_code == 200, "Error job outputs:\n{}".format(resp.json)
        outputs = resp.json
        assert outputs["outputs"][0]["id"] == "output"
        nc_path = outputs["outputs"][0]["href"]
        assert isinstance(nc_path, str) and len(nc_path)
        assert nc_path.startswith(wps_out)
        assert os.path.split(nc_real_path)[-1] == os.path.split(nc_path)[-1]
Example #13
0
    def test_jsonarray2netcdf_execute(self):
        dirname = tempfile.gettempdir()
        nc_data = "Hello NetCDF!"
        with contextlib.ExitStack() as stack_exec:
            tmp_ncdf = tempfile.NamedTemporaryFile(dir=dirname,
                                                   mode="w",
                                                   suffix=".nc")
            tmp_json = tempfile.NamedTemporaryFile(dir=dirname,
                                                   mode="w",
                                                   suffix=".json")
            tmp_ncdf = stack_exec.enter_context(tmp_ncdf)  # noqa
            tmp_json = stack_exec.enter_context(tmp_json)  # noqa
            tmp_ncdf.write(nc_data)
            tmp_ncdf.seek(0)
            tmp_json.write(
                json.dumps(
                    ["file://{}".format(os.path.join(dirname,
                                                     tmp_ncdf.name))]))
            tmp_json.seek(0)
            data = {
                "mode":
                "async",
                "response":
                "document",
                "inputs": [{
                    "id": "input",
                    "href": os.path.join(dirname, tmp_json.name)
                }],
                "outputs": [{
                    "id":
                    "output",
                    "transmissionMode":
                    EXECUTE_TRANSMISSION_MODE_REFERENCE
                }],
            }

            for mock_exec in mocked_execute_process():
                stack_exec.enter_context(mock_exec)
            path = "/processes/jsonarray2netcdf/jobs"
            resp = mocked_sub_requests(self.app,
                                       "post_json",
                                       path,
                                       data=data,
                                       headers=self.json_headers,
                                       only_local=True)

        assert resp.status_code == 201, "Error: {}".format(resp.json)
        assert resp.content_type in CONTENT_TYPE_APP_JSON
        job_url = resp.json["location"]
        results = self.monitor_job(job_url)
        assert results["outputs"][0]["id"] == "output"
        nc_path = results["outputs"][0]["href"]
        assert isinstance(nc_path, str) and len(nc_path)
        settings = get_settings_from_testapp(self.app)
        wps_out = "{}{}".format(settings.get("weaver.url"),
                                settings.get("weaver.wps_output_path"))
        nc_real_path = nc_path.replace(wps_out,
                                       settings.get("weaver.wps_output_dir"))
        assert nc_path.startswith(wps_out)
        assert os.path.split(nc_real_path)[-1] == os.path.split(nc_path)[-1]
        assert os.path.isfile(nc_real_path)
        with open(nc_real_path, "r") as f:
            assert f.read() == nc_data
Example #14
0
    def wps_execute(self, version, accept):
        wps_url = get_wps_url(self.settings)
        if version == "1.0.0":
            test_content = "Test file in Docker - WPS KVP"
            wps_method = "GET"
        elif version == "2.0.0":
            test_content = "Test file in Docker - WPS XML"
            wps_method = "POST"
        else:
            raise ValueError("Invalid WPS version: {}".format(version))
        test_content += " {} request - Accept {}".format(wps_method, accept.split("/")[-1].upper())

        with contextlib.ExitStack() as stack_exec:
            # setup
            dir_name = tempfile.gettempdir()
            tmp_file = stack_exec.enter_context(tempfile.NamedTemporaryFile(dir=dir_name, mode="w", suffix=".txt"))
            tmp_file.write(test_content)
            tmp_file.seek(0)
            for mock_exec in mocked_execute_process():
                stack_exec.enter_context(mock_exec)

            # execute
            if version == "1.0.0":
                wps_inputs = ["file={}@mimeType={}".format(tmp_file.name, CONTENT_TYPE_TEXT_PLAIN)]
                wps_params = {
                    "service": "WPS",
                    "request": "Execute",
                    "version": version,
                    "identifier": self.process_id,
                    "DataInputs": wps_inputs,
                }
                wps_headers = {"Accept": accept}
                wps_data = None
            else:
                wps_inputs = [("file", ComplexDataInput(tmp_file.name, mimeType=CONTENT_TYPE_TEXT_PLAIN))]
                wps_outputs = [(self.out_key, True)]  # as reference
                wps_exec = WPSExecution(version=version, url=wps_url)
                wps_req = wps_exec.buildRequest(self.process_id, wps_inputs, wps_outputs)
                wps_data = xml_util.tostring(wps_req)
                wps_headers = {"Accept": accept, "Content-Type": CONTENT_TYPE_APP_XML}
                wps_params = None
            resp = mocked_sub_requests(self.app, wps_method, wps_url,
                                       params=wps_params, data=wps_data, headers=wps_headers, only_local=True)
            assert resp.status_code in [200, 201], (
                "Failed with: [{}]\nTest: [{}]\nReason:\n{}".format(resp.status_code, test_content, resp.text)
            )

            # parse response status
            if accept == CONTENT_TYPE_APP_XML:
                assert resp.content_type in CONTENT_TYPE_ANY_XML, test_content
                xml_body = xml_util.fromstring(str2bytes(resp.text))
                status_url = xml_body.get("statusLocation")
                job_id = status_url.split("/")[-1].split(".")[0]
            elif accept == CONTENT_TYPE_APP_JSON:
                assert resp.content_type == CONTENT_TYPE_APP_JSON, test_content
                status_url = resp.json["location"]
                job_id = resp.json["jobID"]
            assert status_url
            assert job_id

            if accept == CONTENT_TYPE_APP_XML:
                wps_out_url = self.settings["weaver.wps_output_url"]
                weaver_url = self.settings["weaver.url"]
                assert status_url == f"{wps_out_url}/{job_id}.xml", "Status URL should be XML file for WPS-1 request"
                # remap to employ JSON monitor method (could be done with XML parsing otherwise)
                status_url = f"{weaver_url}/jobs/{job_id}"

            # job monitoring
            results = self.monitor_job(status_url)
            outputs = self.get_outputs(status_url)

            # validate XML status is updated accordingly
            wps_xml_status = os.path.join(self.settings["weaver.wps_output_dir"], job_id + ".xml")
            assert os.path.isfile(wps_xml_status)
            with open(wps_xml_status, "r") as status_file:
                assert "ProcessSucceeded" in status_file.read()

        self.validate_outputs(job_id, results, outputs, test_content)
Example #15
0
    def test_register_describe_execute_ncdump(self, mock_responses):
        # type: (RequestsMock) -> None
        """
        Test the full workflow from remote WPS-1 provider registration, process description, execution and fetch result.

        The complete execution and definitions (XML responses) of the "remote" WPS are mocked.
        Requests and response negotiation between Weaver and that "remote" WPS are effectively executed and validated.

        Validation is accomplished against the same process and mocked server from corresponding test deployment
        server in order to detect early any breaking feature. Responses XML bodies employed to simulate the mocked
        server are pre-generated from real request calls to the actual service that was running on a live platform.

        .. seealso::
            - Reference notebook testing the same process on a live server:
              https://github.com/Ouranosinc/pavics-sdi/blob/master/docs/source/notebook-components/weaver_example.ipynb
            - Evaluate format of submitted Execute body (see `#340 <https://github.com/crim-ca/weaver/issues/340>`_).
        """
        self.service_store.clear_services()

        # register the provider
        remote_provider_name = "test-wps-remote-provider-hummingbird"
        path = "/providers"
        data = {
            "id": remote_provider_name,
            "url": resources.TEST_REMOTE_SERVER_URL
        }
        resp = self.app.post_json(path, params=data, headers=self.json_headers)
        assert resp.status_code == 201

        # validate service capabilities
        path = "/providers/{}".format(remote_provider_name)
        resp = self.app.get(path, headers=self.json_headers)
        assert resp.status_code == 200
        body = resp.json
        assert "id" in body and body["id"] == remote_provider_name
        assert "hummingbird" in body["title"].lower()
        assert body["type"] == PROCESS_WPS_REMOTE

        # validate processes capabilities
        path = "/providers/{}/processes".format(remote_provider_name)
        resp = self.app.get(path, headers=self.json_headers)
        body = resp.json
        assert resp.status_code == 200
        assert "processes" in body and len(
            body["processes"]) == 14  # in TEST_HUMMINGBIRD_GETCAP_WPS1_XML
        processes = {process["id"]: process for process in body["processes"]}
        assert "ncdump" in processes
        assert processes["ncdump"]["version"] == "4.4.1.1"
        assert processes["ncdump"]["metadata"][0]["rel"] == "birdhouse"
        assert processes["ncdump"]["metadata"][1]["rel"] == "user-guide"
        # keyword 'Hummingbird' in this case is from GetCapabilities ProviderName
        # keyword of the service name within Weaver is also provided, which can be different than provider
        expect_keywords = [
            PROCESS_WPS_REMOTE, "Hummingbird", remote_provider_name
        ]
        assert all(key in processes["ncdump"]["keywords"]
                   for key in expect_keywords)
        proc_desc_url = processes["ncdump"]["processDescriptionURL"]
        proc_wps1_url = processes["ncdump"]["processEndpointWPS1"]
        proc_exec_url = processes["ncdump"]["executeEndpoint"]
        assert proc_wps1_url.startswith(resources.TEST_REMOTE_SERVER_URL)
        assert proc_desc_url == self.app_url + path + "/ncdump"
        assert proc_exec_url == self.app_url + path + "/ncdump/jobs"

        # validate process description
        resp = self.app.get(proc_desc_url, headers=self.json_headers)
        assert resp.status_code == 200
        body = resp.json

        assert "inputs" in body and len(body["inputs"]) == 2
        assert all(iid in body["inputs"]
                   for iid in ["dataset", "dataset_opendap"])
        assert body["inputs"]["dataset"]["minOccurs"] == 0
        assert body["inputs"]["dataset"]["maxOccurs"] == 100
        assert "formats" in body["inputs"]["dataset"]
        assert len(body["inputs"]["dataset"]["formats"]) == 1
        assert body["inputs"]["dataset"]["formats"][0]["default"] is True
        assert "literalDataDomains" not in body["inputs"]["dataset"]
        assert body["inputs"]["dataset"]["formats"][0][
            "mediaType"] == CONTENT_TYPE_APP_NETCDF
        assert body["inputs"]["dataset_opendap"]["minOccurs"] == 0
        assert body["inputs"]["dataset_opendap"]["maxOccurs"] == 100
        assert "formats" not in body["inputs"]["dataset_opendap"]
        assert "literalDataDomains" in body["inputs"]["dataset_opendap"]
        assert len(
            body["inputs"]["dataset_opendap"]["literalDataDomains"]) == 1
        assert body["inputs"]["dataset_opendap"]["literalDataDomains"][0][
            "dataType"]["name"] == "string"
        assert body["inputs"]["dataset_opendap"]["literalDataDomains"][0][
            "valueDefinition"]["anyValue"] is True
        assert body["inputs"]["dataset_opendap"]["literalDataDomains"][0][
            "default"] is True

        assert "outputs" in body and len(body["outputs"]) == 1
        assert "output" in body["outputs"]
        assert "formats" in body["outputs"]["output"]
        assert len(body["outputs"]["output"]["formats"]) == 1
        assert body["outputs"]["output"]["formats"][0]["default"] is True
        assert body["outputs"]["output"]["formats"][0][
            "mediaType"] == CONTENT_TYPE_TEXT_PLAIN
        assert "literalDataDomains" not in body["outputs"]["output"]

        assert body["processDescriptionURL"] == proc_desc_url
        assert body["processEndpointWPS1"] == proc_wps1_url
        assert body["executeEndpoint"] == proc_exec_url
        job_exec_url = proc_exec_url.replace(
            "/execution", "/jobs")  # both are aliases, any could be returned
        ogc_exec_url = proc_exec_url.replace("/jobs", "/execution")
        links = {
            link["rel"].rsplit("/")[-1]: link["href"]
            for link in body["links"]
        }
        assert links["execute"] in [job_exec_url, ogc_exec_url]
        assert links["process-meta"] == proc_desc_url
        # WPS-1 URL also includes relevant query parameters to obtain a valid response directly from remote service
        assert links["process-desc"] == proc_wps1_url
        assert links["service-desc"].startswith(
            resources.TEST_REMOTE_SERVER_URL)
        assert "DescribeProcess" in links["process-desc"]
        assert "GetCapabilities" in links["service-desc"]

        assert EXECUTE_CONTROL_OPTION_ASYNC in body["jobControlOptions"]
        assert EXECUTE_TRANSMISSION_MODE_REFERENCE in body[
            "outputTransmission"]

        # validate execution submission
        # (don't actually execute because server is mocked, only validate parsing of I/O and job creation)

        # first setup all expected contents and files
        exec_file = "http://localhost.com/dont/care.nc"
        exec_body = {
            "mode":
            EXECUTE_MODE_ASYNC,
            "response":
            EXECUTE_RESPONSE_DOCUMENT,
            "inputs": [{
                "id": "dataset",
                "href": exec_file
            }],
            "outputs": [{
                "id": "output",
                "transmissionMode": EXECUTE_TRANSMISSION_MODE_REFERENCE
            }]
        }
        status_url = resources.TEST_REMOTE_SERVER_URL + "/status.xml"
        output_url = resources.TEST_REMOTE_SERVER_URL + "/output.txt"
        with open(resources.TEST_HUMMINGBIRD_STATUS_WPS1_XML) as status_file:
            status = status_file.read().format(
                TEST_SERVER_URL=resources.TEST_REMOTE_SERVER_URL,
                LOCATION_XML=status_url,
                OUTPUT_FILE=output_url,
            )

        xml_headers = {"Content-Type": CONTENT_TYPE_TEXT_XML}
        ncdump_data = "Fake NetCDF Data"
        with contextlib.ExitStack() as stack_exec:
            # mock direct execution bypassing celery
            for mock_exec in mocked_execute_process():
                stack_exec.enter_context(mock_exec)
            # mock responses expected by "remote" WPS-1 Execute request and relevant documents
            mock_responses.add(
                "GET",
                exec_file,
                body=ncdump_data,
                headers={"Content-Type": CONTENT_TYPE_APP_NETCDF})
            mock_responses.add("POST",
                               resources.TEST_REMOTE_SERVER_URL,
                               body=status,
                               headers=xml_headers)
            mock_responses.add("GET",
                               status_url,
                               body=status,
                               headers=xml_headers)
            mock_responses.add(
                "GET",
                output_url,
                body=ncdump_data,
                headers={"Content-Type": CONTENT_TYPE_TEXT_PLAIN})

            # add reference to specific provider execute class to validate it was called
            # (whole procedure must run even though a lot of parts are mocked)
            real_wps1_process_execute = Wps1Process.execute
            handle_wps1_process_execute = stack_exec.enter_context(
                mock.patch.object(Wps1Process,
                                  "execute",
                                  side_effect=real_wps1_process_execute,
                                  autospec=True))  # type: MockPatch

            # launch job execution and validate
            resp = mocked_sub_requests(self.app,
                                       "post_json",
                                       proc_exec_url,
                                       timeout=5,
                                       data=exec_body,
                                       headers=self.json_headers,
                                       only_local=True)
            assert resp.status_code in [
                200, 201
            ], "Failed with: [{}]\nReason:\n{}".format(resp.status_code,
                                                       resp.json)
            assert handle_wps1_process_execute.called, "WPS-1 handler should have been called by CWL runner context"

            status_url = resp.json["location"]
            job_id = resp.json["jobID"]
            assert status_url == proc_exec_url + "/" + job_id
            results = self.monitor_job(status_url)
            output_url = "{}/{}/{}".format(
                self.settings["weaver.wps_output_url"], job_id, "output.txt")
            output_path = "{}/{}/{}".format(
                self.settings["weaver.wps_output_dir"], job_id, "output.txt")
            assert results["output"]["format"][
                "mediaType"] == CONTENT_TYPE_TEXT_PLAIN
            assert results["output"]["href"] == output_url
            with open(output_path) as out_file:
                data = out_file.read()
            assert data == ncdump_data