Skip to content

Commit 4bbb8b8

Browse files
committed
test(seqera-api): add nf-test helper coverage
Add lightweight nextflow_function coverage for lib/SeqeraApi.groovy and document the lib test layout with a link to the nf-test function testing docs.
1 parent 266688c commit 4bbb8b8

3 files changed

Lines changed: 117 additions & 2 deletions

File tree

tests/AGENTS.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Layout
44

5-
Each pipeline-level scenario lives in its own directory:
5+
Pipeline-level scenarios live in their own directories:
66

77
- `pipeline_api_only/`
88
- `pipeline_mixed_no_benchmark/`
@@ -16,13 +16,21 @@ Each scenario directory contains:
1616
- `main.nf.test.snap` — the snapshot for that scenario
1717
- `AGENTS.md` — scenario-specific guidance for future edits
1818

19+
Function-level nf-test coverage for `lib/` helpers lives separately under:
20+
21+
- `lib/`
22+
23+
Reference: nf-test Function Testing docs — https://www.nf-test.com/docs/testcases/nextflow_function/
24+
1925
## Conventions
2026

2127
- One scenario per directory.
2228
- Keep assertions local and explicit rather than heavily abstracted.
2329
- Prefer stable fixture files under `workflows/nf_aggregate/assets/`.
2430
- If routing behavior changes, update the scenario-specific `AGENTS.md` along with the test.
25-
- This directory is for pipeline routing/integration scenarios only; stage-specific pytest tests now live beside the relevant module under `modules/local/*/tests/`.
31+
- Pipeline routing/integration scenarios live under `tests/pipeline_*/`.
32+
- Function-level nf-test coverage for Groovy helpers under `lib/` lives under `tests/lib/`.
33+
- Stage-specific pytest tests live beside the relevant module under `modules/local/*/tests/`.
2634

2735
## Running tests
2836

tests/lib/AGENTS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# tests/lib/
2+
3+
## Purpose
4+
5+
This directory holds nf-test `nextflow_function` tests for helper code under `lib/`.
6+
7+
Current coverage:
8+
9+
- `SeqeraApi.groovy.test` — unit-ish behavioral coverage for `SeqeraApi` helpers using nf-test function tests and inline Groovy stubbing.
10+
11+
## Conventions
12+
13+
Reference: nf-test Function Testing docs — https://www.nf-test.com/docs/testcases/nextflow_function/
14+
15+
- Prefer `nextflow_function` tests here instead of introducing a separate Spock/Gradle harness.
16+
- Keep assertions local and explicit.
17+
- Stub `SeqeraApi.metaClass.'static'.apiGet` inline when isolating pagination or workspace-resolution behavior.
18+
- Reserve pipeline routing/integration scenarios for the sibling `tests/pipeline_*/` directories.

tests/lib/SeqeraApi.groovy.test

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
nextflow_function {
2+
name "SeqeraApi helper functions"
3+
4+
test("apiGetAllTasks paginates until the final short page") {
5+
function "SeqeraApi.apiGetAllTasks"
6+
7+
when {
8+
function {
9+
"""
10+
SeqeraApi.metaClass.'static'.apiGet = { String url, Map headers ->
11+
if (url.contains('offset=0')) {
12+
return [tasks: (1..100).collect { [taskId: it] }]
13+
}
14+
if (url.contains('offset=100')) {
15+
return [tasks: [[taskId: 101], [taskId: 102]]]
16+
}
17+
throw new RuntimeException("Unexpected URL: \${url}")
18+
}
19+
20+
input[0] = 'https://example.com/tasks?workspaceId=55'
21+
input[1] = [Authorization: 'Bearer test-token']
22+
"""
23+
}
24+
}
25+
26+
then {
27+
assert function.success
28+
assert function.result*.taskId == (1..102).toList()
29+
}
30+
}
31+
32+
test("resolveWorkspaceId returns the matching workspace id") {
33+
function "SeqeraApi.resolveWorkspaceId"
34+
35+
when {
36+
function {
37+
"""
38+
SeqeraApi.metaClass.'static'.apiGet = { String url, Map headers ->
39+
if (url == 'https://api.example.com/orgs') {
40+
return [organizations: [[name: 'acme', orgId: 7], [name: 'other', orgId: 8]]]
41+
}
42+
if (url == 'https://api.example.com/orgs/7/workspaces') {
43+
return [workspaces: [[name: 'workspace-a', id: 11], [name: 'workspace-b', id: 42]]]
44+
}
45+
throw new RuntimeException("Unexpected URL: \${url}")
46+
}
47+
48+
input[0] = 'acme/workspace-b'
49+
input[1] = 'https://api.example.com'
50+
input[2] = [Authorization: 'Bearer test-token']
51+
"""
52+
}
53+
}
54+
55+
then {
56+
assert function.success
57+
assert function.result == 42L
58+
}
59+
}
60+
61+
test("resolveWorkspaceId fails when the workspace is missing") {
62+
function "SeqeraApi.resolveWorkspaceId"
63+
64+
when {
65+
function {
66+
"""
67+
SeqeraApi.metaClass.'static'.apiGet = { String url, Map headers ->
68+
if (url == 'https://api.example.com/orgs') {
69+
return [organizations: [[name: 'acme', orgId: 7]]]
70+
}
71+
if (url == 'https://api.example.com/orgs/7/workspaces') {
72+
return [workspaces: [[name: 'workspace-a', id: 11]]]
73+
}
74+
throw new RuntimeException("Unexpected URL: \${url}")
75+
}
76+
77+
input[0] = 'acme/workspace-b'
78+
input[1] = 'https://api.example.com'
79+
input[2] = [Authorization: 'Bearer test-token']
80+
"""
81+
}
82+
}
83+
84+
then {
85+
assert function.failed
86+
assert function.stdout.any { it.contains("Workspace 'workspace-b' not found in org 'acme'") }
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)