diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..87d62a2 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,41 @@ +name: test + +on: [push,pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.12"] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: true + + - name: Clear conda cache + run: conda clean --all -y + + - name: Conda setup + uses: conda-incubator/setup-miniconda@v4 + with: + activate-environment: tdbsumstat + environment-file: base_environment.yml + auto-update-conda: true + auto-activate-base: false + python-version: ${{ matrix.python-version }} + miniforge-version: latest + + - name: Install project + shell: bash -el {0} + run: | + make dependencies + make install + + - name: Run test + shell: bash -el {0} + run: | + make test-unit \ No newline at end of file diff --git a/.gitignore b/.gitignore index f3d6dcd..b8fe6ac 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dist/* work/ results/ .nextflow* +.coverage diff --git a/Makefile b/Makefile index 65a14cc..f63d942 100755 --- a/Makefile +++ b/Makefile @@ -17,6 +17,12 @@ clean: find . -type d -name '__pycache__' -exec rm -rf {} + rm -rf dist build +test-all: + pytest --cov=tdbsumstat --cov-report=term-missing + +test-unit: + pytest tests/unit + dependencies: poetry install --no-root diff --git a/run_ingestion_pkgh.sh b/run_ingestion_pkgh.sh deleted file mode 100644 index dc88044..0000000 --- a/run_ingestion_pkgh.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -#BSUB -n 2 -#BSUB -M 4G -#BSUB -q normal -#BSUB -W 12:00 # time in HH:MM - don't put seconds! -#BSUB -G team151 -#BSUB -R "select[mem>4G] rusage[mem=4G] span[hosts=1]" -#BSUB -o tiledb_ingestion.pkgh.log # Log file for each job -#BSUB -e tiledb_ingestion.pkgh.err # Error file for each job -set -eo pipefail - -#Add the following line if you need to activate conda envs in your script -module load HGI/common/conda -source activate /software/cardinal_analysis/ht/conda_envs/tdbsumstat -module load HGI/common/nextflow/25.04.6 -nextflow run ../TileDB-sumstat/main.nf -c ingestion_config_pkgh.nf -profile sanger,conda -work-dir workdir_tiledb -resume diff --git a/tdbsumstat/utils/harmonize_ingest.py b/tdbsumstat/utils/harmonize_ingest.py index 370a2d6..1153ea7 100755 --- a/tdbsumstat/utils/harmonize_ingest.py +++ b/tdbsumstat/utils/harmonize_ingest.py @@ -34,7 +34,6 @@ def __init__(self, mapping_file: str, uri: str, type_sumstat: str, pvar_file: st self.mac = mac self.maf = maf self.permuted = permuted - print(self.permuted) def create_mapping(self): df = pd.read_csv(self.mapping_file, header=None, names=["key", "value"]) diff --git a/example_data/dummy_out_ENSG0000010000.tsv.gz b/tests/data/dummy_out_ENSG0000010000.tsv.gz similarity index 100% rename from example_data/dummy_out_ENSG0000010000.tsv.gz rename to tests/data/dummy_out_ENSG0000010000.tsv.gz diff --git a/example_data/dummy_out_ENSG0000010001.tsv.gz b/tests/data/dummy_out_ENSG0000010001.tsv.gz similarity index 100% rename from example_data/dummy_out_ENSG0000010001.tsv.gz rename to tests/data/dummy_out_ENSG0000010001.tsv.gz diff --git a/example_data/example_data_table.csv b/tests/data/example_data_table.csv similarity index 100% rename from example_data/example_data_table.csv rename to tests/data/example_data_table.csv diff --git a/example_data/locusbreaker_test_table_sc.csv b/tests/data/locusbreaker_test_table_sc.csv similarity index 100% rename from example_data/locusbreaker_test_table_sc.csv rename to tests/data/locusbreaker_test_table_sc.csv diff --git a/example_data/mapping_file_test.csv b/tests/data/mapping_file_test.csv similarity index 100% rename from example_data/mapping_file_test.csv rename to tests/data/mapping_file_test.csv diff --git a/example_data/region_list.csv b/tests/data/region_list.csv similarity index 100% rename from example_data/region_list.csv rename to tests/data/region_list.csv diff --git a/example_data/region_list_sc.csv b/tests/data/region_list_sc.csv similarity index 100% rename from example_data/region_list_sc.csv rename to tests/data/region_list_sc.csv diff --git a/example_data/snp_list_sc.csv b/tests/data/snp_list_sc.csv similarity index 100% rename from example_data/snp_list_sc.csv rename to tests/data/snp_list_sc.csv diff --git a/example_data/trait_list.csv b/tests/data/trait_list.csv similarity index 100% rename from example_data/trait_list.csv rename to tests/data/trait_list.csv diff --git a/example_data/trait_list_r.csv b/tests/data/trait_list_r.csv similarity index 100% rename from example_data/trait_list_r.csv rename to tests/data/trait_list_r.csv diff --git a/tests/unit/core/conftest.py b/tests/unit/core/conftest.py new file mode 100644 index 0000000..4a70f9a --- /dev/null +++ b/tests/unit/core/conftest.py @@ -0,0 +1,39 @@ +import pytest +import pandas as pd +from tdbsumstat.utils.harmonize_ingest import Harmonize + +@pytest.fixture +def create_df_sumstat(): + ensg00 = pd.read_csv('tests/data/dummy_out_ENSG0000010001.tsv.gz', sep = '\t') + return ensg00 + +@pytest.fixture +def create_harmonized_obj(tmp_path): + obj = Harmonize( + mapping_file= 'tests/data/mapping_file_test.csv', + uri = str(tmp_path / "test_tiledb_array"), + type_sumstat = "qtl", + pvar_file = str(tmp_path / "temp_pvar.pvar"), + type_trait = 'quant', + permuted = False, + mac = 10, + maf = 0.001, + ) + return obj + +@pytest.fixture +def create_pvar(tmp_path): + # Added list brackets to ensure pandas creates rows correctly + pvar = pd.DataFrame({ + "CHROM": ["1"], + "POS": ["1234"], + "SNPID": ["1:1234:A:G"], + "REF": ["A"], + "ALT": ["G"] + }) + + # CRITICAL: index=False prevents pandas from writing a row-number column + pvar_path = f"{tmp_path}/temp_pvar.pvar" + pvar.to_csv(pvar_path, sep='\t', index=False) + + return pvar_path \ No newline at end of file diff --git a/tests/unit/core/test_ingestion.py b/tests/unit/core/test_ingestion.py new file mode 100644 index 0000000..2fb0fec --- /dev/null +++ b/tests/unit/core/test_ingestion.py @@ -0,0 +1,40 @@ +import os +import polars as pl +from tdbsumstat.utils.harmonize_ingest import Harmonize + +def test_create_mapping(create_harmonized_obj): + # 1. Execute the method + create_harmonized_obj.create_mapping() + # 2. Check the attribute on the object + assert create_harmonized_obj.mapping_types == { + "Chr": "CHR", + "Gene": "GENE", + "cell.type": "CELL", + "pos": "POS", + "a0": "A1", + "a1": "A2", + "p": "P", + "N": "N", + "beta": "BETA", + "se": "SE", + } + +def test_create_tiledb(create_harmonized_obj, tmp_path): + create_harmonized_obj.create_tiledb() + # 2. Check the attribute on the object + assert os.path.exists(f'{tmp_path}/test_tiledb_array') + +def test_align_alleles(create_harmonized_obj, create_pvar): + create_harmonized_obj.chunk_pl = pl.DataFrame({ + "CHR": 1, + "POS": 1234, + "SNPID": "1:1234:A:G", + "BETA": -0.1, + "SE": 0.01, + "EAF": 0.3 + }) + create_harmonized_obj.align_alleles() + result_df = create_harmonized_obj.chunk_pl + assert result_df.select("BETA").item() == 0.1 + assert result_df.select("EAF").item() == 0.7 +