Skip to content

Commit

Permalink
[wip] define test combinations for some prefer/return combinations
Browse files Browse the repository at this point in the history
  • Loading branch information
fmigneault committed Sep 17, 2024
1 parent 3c1e4ca commit 2b57583
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,34 @@ baseCommand: echo
requirements:
DockerRequirement:
dockerPull: "debian:stretch-slim"
InlineJavascriptRequirement: {}
InitialWorkDirRequirement:
listing:
- entryname: result.json
entry: |
{"data":"$(inputs.message)"}
- entryname: result.txt
entry: |
$(inputs.message)
inputs:
message:
type: string
inputBinding:
position: 1
outputs:
output_reference:
type: File
outputBinding:
glob: "stdout.log"
output_data:
type: string
outputBinding:
outputEval: $(inputs.message)
stdout: stdout.log
output_text:
type: File
outputBinding:
glob: result.txt
format: "iana:text/plain"
output_json:
type: File
outputBinding:
glob: result.json
format: "iana:application/json"
$namespaces:
iana: "https://www.iana.org/assignments/media-types/"
121 changes: 115 additions & 6 deletions tests/functional/test_wps_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
.. seealso::
- :mod:`tests.processes.wps_package`.
"""
import inspect

import contextlib
import copy
import json
import logging
import os
import re
import shutil
import tempfile
from inspect import cleandoc
Expand Down Expand Up @@ -71,7 +74,7 @@
)
from weaver.processes.types import ProcessType
from weaver.status import Status
from weaver.utils import fetch_file, get_any_value, get_path_kvp, load_file
from weaver.utils import fetch_file, get_any_value, get_path_kvp, load_file, parse_kvp
from weaver.wps.utils import get_wps_output_dir, get_wps_output_url, map_wps_output_location
from weaver.wps_restapi import swagger_definitions as sd

Expand Down Expand Up @@ -3519,16 +3522,49 @@ def test_execute_cwl_enum_schema_combined_type_single_array_from_wps(self, mock_
assert results

def test_execute_single_output_prefer_header_return_representation(self):
body = self.retrieve_payload("EchoResultsTester", "deploy", local=True)
desc = self.deploy_process(body)
proc = "EchoResultsTester"
p_id = self.fully_qualified_test_process_name(proc)
body = self.retrieve_payload(proc, "deploy", local=True)
self.deploy_process(body, process_id=p_id)

exec_headers = {
"Prefer": f"return={ExecuteReturnPreference.REPRESENTATION}"
"Prefer": f"return={ExecuteReturnPreference.REPRESENTATION}, respond-async"
}
exec_headers.update(self.json_headers)
exec_content = {
"inputs": {
"message": "test"
},
"outputs": {
"output_json": {} # no 'transmissionMode' to auto-resolve 'value' from 'return=representation'
}
}
with contextlib.ExitStack() as stack:
for mock_exec in mocked_execute_celery():
stack.enter_context(mock_exec)
raise NotImplementedError # FIXME: implement
path = f"/processes/{p_id}/execution"
resp = mocked_sub_requests(self.app, "post_json", path, timeout=5,
data=exec_content, headers=exec_headers, only_local=True)
assert resp.status_code == 201, f"Failed with: [{resp.status_code}]\nReason:\n{resp.json}"

# request status instead of results since not expecting 'document' JSON in this case
status_url = resp.json["location"]
status = self.monitor_job(status_url, return_status=True)
assert status["status"] == Status.SUCCEEDED

job_id = status["jobID"]
out_url = get_wps_output_url(self.settings)
results = self.app.get(f"/jobs/{job_id}/results")
assert results.content_type.startswith(ContentType.APP_JSON)
outputs = self.app.get(f"/jobs/{job_id}/outputs")
output_json = json.dumps({"data": "test"}, separators=(",", ":"))
assert results.text == output_json
assert outputs.json == {
"output_json": {
"href": f"{out_url}/{job_id}/output_json/output.json",
"type": ContentType.APP_JSON,
},
}

def test_execute_single_output_prefer_header_return_minimal(self):
raise NotImplementedError # FIXME: implement
Expand All @@ -3540,7 +3576,68 @@ def test_execute_single_output_response_raw_reference(self):
raise NotImplementedError # FIXME: implement

def test_execute_multi_output_prefer_header_return_representation(self):
raise NotImplementedError # FIXME: implement
proc = "EchoResultsTester"
p_id = self.fully_qualified_test_process_name(proc)
body = self.retrieve_payload(proc, "deploy", local=True)
self.deploy_process(body, process_id=p_id)

exec_headers = {
"Prefer": f"return={ExecuteReturnPreference.REPRESENTATION}, respond-async"
}
exec_headers.update(self.json_headers)
exec_content = {
"inputs": {
"message": "test"
},
"outputs": {
# no 'transmissionMode' to auto-resolve 'value' from 'return=representation'
# request multiple outputs, but not 'all', to test filter behavior at the same time
# use 1 expected as 'File' and 1 'string' literal to test conversion to raw 'value'
"output_json": {},
"output_data": {}
}
}
with contextlib.ExitStack() as stack:
for mock_exec in mocked_execute_celery():
stack.enter_context(mock_exec)
path = f"/processes/{p_id}/execution"
resp = mocked_sub_requests(self.app, "post_json", path, timeout=5,
data=exec_content, headers=exec_headers, only_local=True)
assert resp.status_code == 201, f"Failed with: [{resp.status_code}]\nReason:\n{resp.json}"

# request status instead of results since not expecting 'document' JSON in this case
status_url = resp.json["location"]
status = self.monitor_job(status_url, return_status=True)
assert status["status"] == Status.SUCCEEDED

job_id = status["jobID"]
out_url = get_wps_output_url(self.settings)
results = self.app.get(f"/jobs/{job_id}/results")
assert results.content_type.startswith(ContentType.MULTIPART_RELATED)
boundary = parse_kvp(results.content_type)["boundary"][0]
outputs = self.app.get(f"/jobs/{job_id}/outputs")
output_json = json.dumps({"data": "test"}, separators=(",", ":"))
results_body = inspect.cleandoc(f"""
--{boundary}
Content-Type: {ContentType.TEXT_PLAIN}
Content-ID: output_data
test
--{boundary}
Content-Type: {ContentType.APP_JSON}
Content-ID: output_json
{output_json}
--{boundary}--
""")
assert results.text == results_body
assert outputs.json["outputs"] == {
"output_data": "test",
"output_json": {
"href": f"{out_url}/{job_id}/output_json/output.json",
"type": ContentType.APP_JSON,
},
}

def test_execute_multi_output_prefer_header_return_minimal(self):
raise NotImplementedError # FIXME: implement
Expand All @@ -3554,6 +3651,18 @@ def test_execute_multi_output_response_raw_reference(self):
def test_execute_multi_output_response_raw_mixed(self):
raise NotImplementedError # FIXME: implement

def test_execute_multi_output_response_document_defaults(self):
"""
Test ``response: document`` with default ``transmissionMode`` resolutions for literal/complex outputs.
"""
raise NotImplementedError # FIXME: implement

def test_execute_multi_output_response_document_mixed(self):
"""
Test ``response: document`` with ``transmissionMode`` specified to force convertion of literal/complex outputs.
"""
raise NotImplementedError # FIXME: implement

# FIXME: implement other variations as well... see doc 'Execution Results' combinations


Expand Down
4 changes: 2 additions & 2 deletions tests/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def test_get_format(test_content_type, expected_content_type, expected_content_e
[
f.ContentType.APP_OCTET_STREAM,
f.ContentType.APP_FORM,
f.ContentType.MULTI_PART_FORM,
f.ContentType.MULTIPART_FORM,
]
)
def test_get_format_media_type_no_extension(test_extension):
Expand Down Expand Up @@ -289,7 +289,7 @@ def test_get_format_media_type_from_schema(test_format, expect_media_type):
[
f.ContentType.APP_OCTET_STREAM,
f.ContentType.APP_FORM,
f.ContentType.MULTI_PART_FORM,
f.ContentType.MULTIPART_FORM,
]
)
)
Expand Down
7 changes: 4 additions & 3 deletions weaver/formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ class ContentType(Constants):
IMAGE_GIF = "image/gif"
IMAGE_PNG = "image/png"
IMAGE_TIFF = "image/tiff"
MULTI_PART_FORM = "multipart/form-data"
MULTIPART_FORM = "multipart/form-data"
MULTIPART_RELATED = "multipart/related"
TEXT_ENRICHED = "text/enriched"
TEXT_HTML = "text/html"
TEXT_PLAIN = "text/plain"
Expand Down Expand Up @@ -447,12 +448,12 @@ class SchemaRole(Constants):
ContentType.APP_DIR: "/", # force href to finish with explicit '/' to mark directory
ContentType.APP_OCTET_STREAM: "",
ContentType.APP_FORM: "",
ContentType.MULTI_PART_FORM: "",
ContentType.MULTIPART_FORM: "",
}
_CONTENT_TYPE_EXCLUDE = [
ContentType.APP_OCTET_STREAM,
ContentType.APP_FORM,
ContentType.MULTI_PART_FORM,
ContentType.MULTIPART_FORM,
]
_EXTENSION_CONTENT_TYPES_OVERRIDES = {
".text": ContentType.TEXT_PLAIN, # common alias to .txt, especially when using format query
Expand Down
6 changes: 3 additions & 3 deletions weaver/wps_restapi/swagger_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,8 +766,8 @@ class NoContent(ExtendedMappingSchema):
class FileUploadHeaders(RequestHeaders):
# MUST be multipart for upload
content_type = ContentTypeHeader(
example=f"{ContentType.MULTI_PART_FORM}; boundary=43003e2f205a180ace9cd34d98f911ff",
default=ContentType.MULTI_PART_FORM,
example=f"{ContentType.MULTIPART_FORM}; boundary=43003e2f205a180ace9cd34d98f911ff",
default=ContentType.MULTIPART_FORM,
description="Desired Content-Type of the file being uploaded.", missing=required)
content_length = ContentLengthHeader(description="Uploaded file contents size in bytes.")
content_disposition = ContentDispositionHeader(example="form-data; name=\"file\"; filename=\"desired-name.ext\"",
Expand Down Expand Up @@ -7359,7 +7359,7 @@ class VaultUploadBody(ExtendedSchemaNode):
schema_type = String
description = "Multipart file contents for upload to the vault."
examples = {
ContentType.MULTI_PART_FORM: {
ContentType.MULTIPART_FORM: {
"summary": "Upload JSON file to vault as multipart content.",
"value": EXAMPLES["vault_file_upload.txt"],
}
Expand Down

0 comments on commit 2b57583

Please sign in to comment.