Skip to content

Commit 2773ca2

Browse files
authored
Merge pull request #74 from UCLOrengoGroup/nf_test
Added nf-test
2 parents 33264be + 7842929 commit 2773ca2

11 files changed

Lines changed: 411 additions & 16 deletions

File tree

.github/workflows/build-test-push.yml

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# build docker images from docker compose and push to docker hub
2-
32
name: Build, Test, and Push Docker Compose Images
43
on:
54
push:
@@ -36,6 +35,12 @@ jobs:
3635
sudo mv nextflow /usr/local/bin/nextflow
3736
nextflow -version
3837
38+
- name: Install nf-test
39+
run: |
40+
curl -fsSL https://get.nf-test.com | bash
41+
sudo mv nf-test /usr/local/bin/nf-test
42+
nf-test version
43+
3944
- name: Build docker images for tests (amd64 only)
4045
run: |
4146
docker compose version
@@ -50,18 +55,28 @@ jobs:
5055
docker tag domain-annotation-pipeline-ted-tools ghcr.io/uclorengogroup/domain-annotation-pipeline-ted-tools:${TEST_TAG}
5156
docker tag domain-annotation-pipeline-foldseek ghcr.io/uclorengogroup/domain-annotation-pipeline-foldseek:${TEST_TAG}
5257
53-
- name: Run pipeline (docker,git_actions_test)
58+
- name: Run test
5459
env:
5560
NXF_ANSI_LOG: "false"
61+
CONTAINER_TAG_NAME: ci-local
62+
NFT_DIFF_ARGS: "-u -U 5"
5663
run: |
5764
set -euxo pipefail
58-
rm -rf work .nextflow* results/git_actions_test || true
59-
nextflow run workflows/annotate.nf -profile docker,git_actions_test --container_tag_name ci-local
65+
nf-test test --profile +docker --ci
6066
61-
- name: Compare final_results.tsv to expected
62-
run: |
63-
set -euxo pipefail
64-
diff -U 0 <(sort assets/test_ids/final_results.tsv) <(sort results/git_actions_test/final_results.tsv)
67+
- name: Upload nf-test artifacts on failure
68+
if: failure()
69+
uses: actions/upload-artifact@v4
70+
with:
71+
name: nf-test-failure-${{ github.run_id }}-${{ github.run_attempt }}
72+
path: |
73+
.nf-test/tests/*/output
74+
.nf-test/tests/*/reports
75+
.nf-test/tests/*/meta
76+
.nextflow.log
77+
include-hidden-files: true
78+
if-no-files-found: warn
79+
retention-days: 7
6580

6681
push-images:
6782
needs: test

.github/workflows/test-pipeline.yml

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,40 @@ jobs:
2222
run: |
2323
curl -s https://get.nextflow.io | bash
2424
sudo mv nextflow /usr/local/bin/nextflow
25-
nextflow -version
25+
nextflow version
26+
27+
- name: Install nf-test
28+
run: |
29+
curl -fsSL https://get.nf-test.com | bash
30+
sudo mv nf-test /usr/local/bin/nf-test
31+
nf-test -version
2632
2733
- name: Build docker images for tests (temporary until GHCR default)
2834
run: |
2935
docker compose version
3036
docker compose build
3137
32-
- name: Run pipeline (docker,git_actions_test)
38+
- name: Run test
3339
env:
3440
NXF_ANSI_LOG: "false"
41+
NFT_DIFF_ARGS: "-u -U 5"
3542
run: |
3643
set -euxo pipefail
37-
rm -rf work .nextflow* results/git_actions_test || true
38-
nextflow run workflows/annotate.nf -profile docker,git_actions_test
44+
nf-test test --profile +docker --ci
45+
46+
- name: Upload nf-test artifacts on failure
47+
if: failure()
48+
uses: actions/upload-artifact@v4
49+
with:
50+
name: nf-test-failure-${{ github.run_id }}-${{ github.run_attempt }}
51+
path: |
52+
.nf-test/tests/*/output
53+
.nf-test/tests/*/reports
54+
.nf-test/tests/*/meta
55+
.nextflow.log
56+
include-hidden-files: true
57+
if-no-files-found: warn
58+
retention-days: 7
3959

40-
- name: Compare final_results.tsv to expected
41-
run: |
42-
set -euxo pipefail
43-
diff -U 0 <(sort assets/test_ids/final_results.tsv) <(sort results/git_actions_test/final_results.tsv)
4460

4561

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ repo-key.pub
118118
.nextflow/
119119
work/
120120
.nextflow.log*
121+
.nf-test/
122+
nf-test
121123

122124
# local build dir
123125
domain_assignments*.tsv
@@ -127,3 +129,5 @@ assets/uniprot_ids_100.csv
127129
assets/uniprot_1000.txt
128130
assets/bfvd.zip
129131
foldseek/assets/
132+
133+

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,34 @@ ted_stub_chain_ids.zip
194194

195195
The default files are currently set up to run a test set of 50 chain ids, producing a final results output of 100 domains.
196196

197+
## Automated tests (nf-test)
198+
199+
The pipeline has an [nf-test](https://www.nf-test.com/) (`tests/`) that runs the full `annotate.nf` workflow against the test data located in `./assets/test_ids` and checks the results against a pre-generated snapshot.
200+
201+
Install nf-test:
202+
203+
```bash
204+
curl -fsSL https://get.nf-test.com | bash
205+
```
206+
207+
Move `nf-test` to the `bin` folder in you `$PATH`.
208+
209+
Run the test locally:
210+
211+
```bash
212+
nf-test test --profile +docker
213+
```
214+
215+
If the outputs are consistent with the pre-existing snapshot, the test will succeed.
216+
217+
If a change intentionally alters pipeline outputs, generate a new snapshot with:
218+
219+
```bash
220+
nf-test test --update-snapshot
221+
```
222+
223+
Note: nf-test isn't wired into CI yet — `test-pipeline.yml` and `build-test-push.yml` run the pipeline directly with `-profile docker,git_actions_test` rather than through nf-test. The idea is to replace it the nf-test at some point.
224+
197225
## Running on HPC
198226

199227
## Install (with singularity)

conf/test.config

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
========================================================================================
3+
Nextflow config file for running minimal tests
4+
========================================================================================
5+
Defines input files and everything required to run a fast and simple pipeline test.
6+
7+
Use as follows:
8+
nextflow run workflows/annotate.nf -profile test,singularity,myriad_cluster
9+
----------------------------------------------------------------------------------------
10+
*/
11+
12+
process {
13+
resourceLimits = [
14+
cpus: 1,
15+
memory: '8.GB',
16+
time: '10m'
17+
]
18+
}
19+
20+
params {
21+
config_profile_name = 'Test profile'
22+
config_profile_description = 'Minimal test dataset to check pipeline function'
23+
24+
// Input data
25+
input_zip_dir = "${baseDir}/../assets/test_ids"
26+
27+
// Chunking
28+
chunk_size = 25
29+
light_chunk_size = 50
30+
heavy_chunk_size = 10
31+
32+
// Compute
33+
no_gpu = true
34+
35+
// GH Actions runners lack the CPU features the default foldseek build needs
36+
foldseek_exec = 'foldseek_avx2'
37+
ci_mode = true
38+
}

nextflow.config

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ profiles {
217217
}
218218
}
219219

220+
test {
221+
includeConfig "${baseDir}/../conf/test.config"
222+
}
223+
220224
container {
221225
includeConfig "${baseDir}/../conf/container.config"
222226
}

nf-test.config

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
config {
2+
3+
testsDir "tests"
4+
workDir ".nf-test"
5+
configFile "tests/nextflow.config"
6+
profile "test"
7+
8+
plugins {
9+
load "nft-utils@0.0.3"
10+
}
11+
12+
}

tests/.nftignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
final_results.tsv
2+
domain_assignments.merizo.tsv
3+
all_domain_quality.csv
4+
*.html
5+
**/*.html
6+
WORKFLOW_COMPLETED
7+
foldseek_parsed_results.tsv

tests/nextflow.config

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
========================================================================================
3+
Nextflow config file for running tests
4+
========================================================================================
5+
*/

tests/workflows/annotate.nf.test

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
nextflow_pipeline {
2+
3+
name "Test Workflow annotate.nf"
4+
script "workflows/annotate.nf"
5+
tag "workflow"
6+
7+
test("-profile test") {
8+
9+
when {
10+
params {
11+
project_name = "nf_test_annotate"
12+
results_dir = "$outputDir"
13+
if (System.getenv('CONTAINER_TAG_NAME')) {
14+
container_tag_name = System.getenv('CONTAINER_TAG_NAME')
15+
} else if (System.getenv('CONTAINER_TAG_BASE')) {
16+
container_tag_name = "${System.getenv('CONTAINER_TAG_BASE')}${params.container_arch == 'arm64' ? '-arm64-latest' : '-latest'}"
17+
}
18+
}
19+
}
20+
21+
then {
22+
assert workflow.success
23+
24+
// Rounds floats to `decimals` places. Little groovy magic to avoid floating-point differences in test comparisons
25+
// e.g. with decimals=3, "1.2345" -> "1.235" (HALF_UP rounds the .5 tie up)
26+
// Only whole fields matching the float pattern are rounded (via ==~, a full-string match) -
27+
// multi-dot values like a cath_label "3.40.50.720" are left untouched instead of being
28+
// mangled by matching "3.40" as a substring.
29+
def roundFloats = { String path, String delimiter = '\t', int decimals = 3 ->
30+
file(path).readLines().collect { line ->
31+
line.split(delimiter, -1).collect { field ->
32+
field ==~ /-?\d+\.\d+/ ? new BigDecimal(field).setScale(decimals, java.math.RoundingMode.HALF_UP).toString() : field
33+
}.join(delimiter)
34+
}
35+
}
36+
37+
// stable_path: all files/folders under the results dir with a stable path (incl. file name)
38+
def stable_path = getAllFilesFromDir(params.results_dir, relative: true, includeDir: true, ignore: ['*.html', 'WORKFLOW_COMPLETED'])
39+
// stable_content: all files under the results dir with stable content (volatile files excluded via .nftignore)
40+
def stable_content = getAllFilesFromDir(params.results_dir, ignoreFile: 'tests/.nftignore')
41+
42+
// Files with known floating-point jitter: compare rounded content instead of raw bytes
43+
def rounded_final_results = roundFloats("${params.results_dir}/final_results.tsv")
44+
def rounded_merizo = roundFloats("${params.results_dir}/domain_assignments.merizo.tsv")
45+
def rounded_domain_quality = roundFloats("${params.results_dir}/all_domain_quality.csv", ',')
46+
47+
assertAll(
48+
{ assert snapshot(
49+
stable_path, stable_content,
50+
rounded_final_results, rounded_merizo, rounded_domain_quality
51+
).match() }
52+
)
53+
}
54+
55+
}
56+
57+
}

0 commit comments

Comments
 (0)