Add sample_sheet workflow runtime framework tests

7 new framework workflow tests for sample_sheet collection semantics:
- SAMPLE_SHEET_MAPPING (cat workflow, map over data)
- SAMPLE_SHEET_MATCHES_LIST (cat_collection, canMatch reduction)
- SAMPLE_SHEET_PAIRED_MAPPING_OVER_PAIRED (paired_or_unpaired workflow)
- SAMPLE_SHEET_PAIRED_MATCHES_LIST_PAIRED (new list_paired workflow)
- SAMPLE_SHEET_PAIRED_MAPPING_OVER_PAIRED_OR_UNPAIRED
- SAMPLE_SHEET_PAIRED_OR_UNPAIRED_MATCHES_LIST_PAIRED_OR_UNPAIRED
- SAMPLE_SHEET_MATCHES_SAMPLE_SHEET (new cat_sample_sheet workflow
  using __SAMPLE_SHEET_TO_TABULAR__ tool)

Make rows optional in galactic_job_json() replacement_collection()
(value.get("rows") instead of value["rows"]). Test data still provides
rows: {} per element since server-side SampleSheetDatasetCollectionType
requires it.

Add 3 unit tests for sample_sheet collection creation in test_cwl_util.py.
Add framework_test references to collection_semantics.yml for 7 examples.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John Chilton
2026-03-17 15:15:28 -04:00
co-authored by Claude Opus 4.6
parent e0c1a00678
commit 494722a792
11 changed files with 330 additions and 1 deletions
@@ -1012,6 +1012,8 @@
invocation: {inputs: {i: {type: dataset, ref: d_n}}}
output: o
tests:
workflow_runtime:
framework_test: "collection_semantics_cat_4"
workflow_editor: "accepts sample_sheet data -> data connection (maps like list)"
- example:
@@ -1031,6 +1033,8 @@
produces:
o: {type: dataset}
tests:
workflow_runtime:
framework_test: "collection_semantics_cat_collection_1"
workflow_editor: "accepts sample_sheet -> list connection (canMatch)"
- doc: |
@@ -1063,6 +1067,8 @@
invocation: {inputs: {i: {type: collection, ref: "C\\_PAIRED"}}}
output: o
tests:
workflow_runtime:
framework_test: "collection_semantics_paired_or_unpaired_5"
workflow_editor: "accepts sample_sheet:paired -> paired connection (maps over like list:paired)"
- example:
@@ -1082,6 +1088,8 @@
produces:
o: {type: dataset}
tests:
workflow_runtime:
framework_test: "collection_semantics_list_paired_0"
workflow_editor: "accepts sample_sheet:paired -> list:paired connection (canMatch)"
- doc: |
@@ -1142,6 +1150,8 @@
inputs:
i: {type: map_over, collection: C_AS_MIXED}
tests:
workflow_runtime:
framework_test: "collection_semantics_paired_or_unpaired_6"
workflow_editor: "accepts sample_sheet:paired -> paired_or_unpaired connection"
- example:
@@ -1161,6 +1171,8 @@
produces:
o: {type: dataset}
tests:
workflow_runtime:
framework_test: "collection_semantics_list_paired_or_unpaired_1"
workflow_editor: "accepts sample_sheet:paired_or_unpaired -> list:paired_or_unpaired connection (canMatch)"
- doc: |
@@ -1223,4 +1235,6 @@
produces:
o: {type: dataset}
tests:
workflow_runtime:
framework_test: "collection_semantics_cat_sample_sheet_0"
workflow_editor: "accepts sample_sheet -> sample_sheet connection"
+1 -1
View File
@@ -365,7 +365,7 @@ def galactic_job_json(
elements = to_elements(value, collection_type)
kwds = {}
if collection_type.startswith("sample_sheet"):
kwds["rows"] = value["rows"]
kwds["rows"] = value.get("rows")
if "name" in value:
kwds["name"] = value["name"]
collection = collection_create_func(elements, collection_type, **kwds)
@@ -108,3 +108,34 @@
asserts:
- that: has_text
text: "reverse content"
- doc: |
SAMPLE_SHEET_MAPPING: A sample_sheet mapped over a data input produces
a sample_sheet output collection.
job:
input1:
class: Collection
collection_type: sample_sheet
rows:
el1: []
el2: []
elements:
- identifier: el1
class: File
contents: "element 1"
- identifier: el2
class: File
contents: "element 2"
outputs:
wf_output:
class: Collection
collection_type: sample_sheet
elements:
el1:
asserts:
- that: has_text
text: "element 1"
el2:
asserts:
- that: has_text
text: "element 2"
@@ -19,3 +19,28 @@
text: "element 1"
- that: has_text
text: "element 2"
- doc: |
SAMPLE_SHEET_MATCHES_LIST: A sample_sheet collection consumed by a list
collection input via canMatch (reduction).
job:
input1:
class: Collection
collection_type: sample_sheet
rows:
el1: []
el2: []
elements:
- identifier: el1
class: File
contents: "element 1"
- identifier: el2
class: File
contents: "element 2"
outputs:
wf_output:
asserts:
- that: has_text
text: "element 1"
- that: has_text
text: "element 2"
@@ -0,0 +1,24 @@
- doc: |
SAMPLE_SHEET_MATCHES_SAMPLE_SHEET: A sample_sheet consumed by a sample_sheet
collection input (identity match, reduction).
job:
input1:
class: Collection
collection_type: sample_sheet
rows:
el1: []
el2: []
elements:
- identifier: el1
class: File
contents: "element 1"
- identifier: el2
class: File
contents: "element 2"
outputs:
wf_output:
asserts:
- that: has_text
text: "el1"
- that: has_text
text: "el2"
@@ -0,0 +1,13 @@
class: GalaxyWorkflow
inputs:
input1:
type: collection
collection_type: sample_sheet
outputs:
wf_output:
outputSource: tool_step/output
steps:
tool_step:
tool_id: __SAMPLE_SHEET_TO_TABULAR__
in:
input: input1
@@ -0,0 +1,27 @@
- doc: |
SAMPLE_SHEET_PAIRED_MATCHES_LIST_PAIRED: A sample_sheet:paired consumed by a
list:paired collection input via canMatch (reduction).
job:
input1:
class: Collection
collection_type: sample_sheet:paired
rows:
el1: []
elements:
- identifier: el1
class: Collection
type: paired
elements:
- identifier: forward
class: File
contents: "forward content"
- identifier: reverse
class: File
contents: "reverse content"
outputs:
wf_output:
asserts:
- that: has_text
text: "identifier is el1"
- that: has_text
text: "collection_type<paired>"
@@ -0,0 +1,13 @@
class: GalaxyWorkflow
inputs:
input1:
type: collection
collection_type: list:paired
outputs:
wf_output:
outputSource: tool_step/out1
steps:
tool_step:
tool_id: collection_paired_or_list_paired_input
in:
f1: input1
@@ -23,3 +23,31 @@
text: "forward content"
- that: has_text
text: "reverse content"
- doc: |
SAMPLE_SHEET_PAIRED_OR_UNPAIRED_MATCHES_LIST_PAIRED_OR_UNPAIRED: A
sample_sheet:paired_or_unpaired consumed by list:paired_or_unpaired via canMatch.
job:
input1:
class: Collection
collection_type: sample_sheet:paired_or_unpaired
rows:
el1: []
elements:
- identifier: el1
class: Collection
type: paired_or_unpaired
elements:
- identifier: forward
class: File
contents: "forward content"
- identifier: reverse
class: File
contents: "reverse content"
outputs:
wf_output:
asserts:
- that: has_text
text: "forward content"
- that: has_text
text: "reverse content"
@@ -114,3 +114,53 @@
wf_output:
class: Collection
collection_type: list
- doc: |
SAMPLE_SHEET_PAIRED_MAPPING_OVER_PAIRED: A sample_sheet:paired mapped over
a paired_or_unpaired tool input produces a sample_sheet output.
job:
input1:
class: Collection
collection_type: sample_sheet:paired
rows:
el1: []
elements:
- identifier: el1
class: Collection
type: paired
elements:
- identifier: forward
class: File
contents: "forward content"
- identifier: reverse
class: File
contents: "reverse content"
outputs:
wf_output:
class: Collection
collection_type: sample_sheet
- doc: |
SAMPLE_SHEET_PAIRED_MAPPING_OVER_PAIRED_OR_UNPAIRED: A sample_sheet:paired
mapped over paired_or_unpaired (equiv to sample_sheet:paired_or_unpaired mapping).
job:
input1:
class: Collection
collection_type: sample_sheet:paired
rows:
el1: []
elements:
- identifier: el1
class: Collection
type: paired
elements:
- identifier: forward
class: File
contents: "forward content"
- identifier: reverse
class: File
contents: "reverse content"
outputs:
wf_output:
class: Collection
collection_type: sample_sheet
+104
View File
@@ -151,3 +151,107 @@ def test_galactic_job_json_collection_element_filetype():
for target in captured_targets:
assert isinstance(target, FileLiteralTarget)
assert target.properties.get("filetype") == "fastqsanger"
def test_galactic_job_json_sample_sheet_collection_without_rows():
"""sample_sheet collection works without rows metadata."""
created_collections = []
def collection_create_func(element_identifiers, collection_type, rows=None, name=None):
created_collections.append(
{"element_identifiers": element_identifiers, "collection_type": collection_type, "rows": rows, "name": name}
)
return {"id": f"collection_{collection_type}"}
job = {
"input1": {
"class": "Collection",
"collection_type": "sample_sheet",
"elements": [
{"identifier": "el1", "class": "File", "contents": "element 1"},
{"identifier": "el2", "class": "File", "contents": "element 2"},
],
}
}
result_job, datasets = galactic_job_json(
job, ".", _mock_upload_func, collection_create_func, tool_or_workflow="workflow"
)
assert len(created_collections) == 1
coll = created_collections[0]
assert coll["collection_type"] == "sample_sheet"
assert coll["rows"] is None
assert len(coll["element_identifiers"]) == 2
assert coll["element_identifiers"][0]["name"] == "el1"
assert coll["element_identifiers"][1]["name"] == "el2"
def test_galactic_job_json_sample_sheet_collection_with_rows():
"""sample_sheet collection passes rows metadata through."""
created_collections = []
def collection_create_func(element_identifiers, collection_type, rows=None, name=None):
created_collections.append(
{"element_identifiers": element_identifiers, "collection_type": collection_type, "rows": rows, "name": name}
)
return {"id": f"collection_{collection_type}"}
job = {
"input1": {
"class": "Collection",
"collection_type": "sample_sheet",
"elements": [
{"identifier": "el1", "class": "File", "contents": "element 1"},
{"identifier": "el2", "class": "File", "contents": "element 2"},
],
"rows": {"el1": {"condition": "treatment"}, "el2": {"condition": "control"}},
}
}
result_job, datasets = galactic_job_json(
job, ".", _mock_upload_func, collection_create_func, tool_or_workflow="workflow"
)
assert len(created_collections) == 1
coll = created_collections[0]
assert coll["collection_type"] == "sample_sheet"
assert coll["rows"] == {"el1": {"condition": "treatment"}, "el2": {"condition": "control"}}
def test_galactic_job_json_sample_sheet_paired_collection():
"""sample_sheet:paired creates nested collection with paired sub-elements."""
created_collections = []
def collection_create_func(element_identifiers, collection_type, rows=None, name=None):
created_collections.append(
{"element_identifiers": element_identifiers, "collection_type": collection_type, "rows": rows, "name": name}
)
return {"id": f"collection_{collection_type}"}
job = {
"input1": {
"class": "Collection",
"collection_type": "sample_sheet:paired",
"elements": [
{
"identifier": "sample1",
"class": "Collection",
"type": "paired",
"elements": [
{"identifier": "forward", "class": "File", "contents": "fwd reads"},
{"identifier": "reverse", "class": "File", "contents": "rev reads"},
],
}
],
}
}
result_job, datasets = galactic_job_json(
job, ".", _mock_upload_func, collection_create_func, tool_or_workflow="workflow"
)
assert len(created_collections) == 1
coll = created_collections[0]
assert coll["collection_type"] == "sample_sheet:paired"
assert coll["rows"] is None
assert len(coll["element_identifiers"]) == 1
el = coll["element_identifiers"][0]
assert el["name"] == "sample1"
assert el["src"] == "new_collection"
assert el["collection_type"] == "paired"
assert len(el["element_identifiers"]) == 2