Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,13 @@ dmypy.json
# Ignore directories
samples/
.snakemake/

# Claude Code settings
.claude/*

# Additional testing artifacts
htmlcov/
.coverage
.coverage.*
coverage.xml
.pytest_cache/
282 changes: 282 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
[tool.poetry]
name = "princess"
version = "0.1.0"
description = "A bioinformatics tool for variant analysis and processing"
authors = ["Princess Team"]
readme = "README.md"
packages = [{include = "scripts"}, {include = "cluster"}]

[tool.poetry.dependencies]
python = "^3.8"

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.0"
pytest-cov = "^4.1.0"
pytest-mock = "^3.11.0"

# Poetry scripts are not needed for pytest - use 'poetry run pytest' directly

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--strict-config",
"--cov=scripts",
"--cov=cluster",
"--cov-report=term-missing",
"--cov-report=html:htmlcov",
"--cov-report=xml:coverage.xml",
"--cov-fail-under=80",
"-v"
]
markers = [
"unit: Unit tests that test individual functions/classes",
"integration: Integration tests that test component interactions",
"slow: Tests that take a long time to run"
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning"
]

[tool.coverage.run]
source = ["scripts", "cluster"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/.venv/*",
"*/envs/*"
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:"
]
show_missing = true
skip_covered = false

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"
Empty file added tests/__init__.py
Empty file.
87 changes: 87 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""Shared pytest fixtures for the test suite."""

import os
import tempfile
from pathlib import Path
from typing import Generator

import pytest


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for testing."""
with tempfile.TemporaryDirectory() as tmp_dir:
yield Path(tmp_dir)


@pytest.fixture
def temp_file(temp_dir: Path) -> Path:
"""Create a temporary file for testing."""
temp_file_path = temp_dir / "test_file.txt"
temp_file_path.write_text("test content")
return temp_file_path


@pytest.fixture
def sample_vcf_content() -> str:
"""Sample VCF file content for testing."""
return """##fileformat=VCFv4.2
##source=test
##INFO=<ID=DP,Number=1,Type=Integer,Description="Total Depth">
##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT SAMPLE1
chr1 100 . A G 250 . DP=30 GT 0/1
chr1 200 . T C 150 . DP=25 GT 1/1
chr1 300 . G <DEL> 300 . DP=35 GT 0/1
"""


@pytest.fixture
def sample_vcf_file(temp_dir: Path, sample_vcf_content: str) -> Path:
"""Create a sample VCF file for testing."""
vcf_file = temp_dir / "sample.vcf"
vcf_file.write_text(sample_vcf_content)
return vcf_file


@pytest.fixture
def mock_args():
"""Mock argument parser arguments."""
class MockArgs:
def __init__(self, input_file=None, output_file=None, filter_threshold=200):
self.input = input_file
self.output = output_file
self.filter = filter_threshold

return MockArgs


@pytest.fixture
def capture_stdout():
"""Fixture to capture stdout for testing."""
import io
import sys

old_stdout = sys.stdout
sys.stdout = captured_output = io.StringIO()
yield captured_output
sys.stdout = old_stdout


@pytest.fixture
def env_backup():
"""Backup and restore environment variables."""
original_env = os.environ.copy()
yield
os.environ.clear()
os.environ.update(original_env)


@pytest.fixture
def working_directory(temp_dir: Path):
"""Change to a temporary working directory for testing."""
original_cwd = os.getcwd()
os.chdir(temp_dir)
yield temp_dir
os.chdir(original_cwd)
Empty file added tests/integration/__init__.py
Empty file.
66 changes: 66 additions & 0 deletions tests/test_infrastructure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Test file to validate the testing infrastructure setup."""

import pytest
import sys
from pathlib import Path


class TestInfrastructure:
"""Test the testing infrastructure is working correctly."""

def test_pytest_is_working(self):
"""Test that pytest is functioning."""
assert True

def test_fixtures_are_available(self, temp_dir):
"""Test that fixtures are available and working."""
assert temp_dir.exists()
assert temp_dir.is_dir()

def test_temp_file_fixture(self, temp_file):
"""Test that temp file fixture works."""
assert temp_file.exists()
assert temp_file.read_text() == "test content"

def test_sample_vcf_content(self, sample_vcf_content):
"""Test that sample VCF content fixture works."""
assert "##fileformat=VCFv4.2" in sample_vcf_content
assert "#CHROM" in sample_vcf_content

def test_sample_vcf_file(self, sample_vcf_file):
"""Test that sample VCF file fixture works."""
assert sample_vcf_file.exists()
content = sample_vcf_file.read_text()
assert "##fileformat=VCFv4.2" in content

def test_mock_args_fixture(self, mock_args):
"""Test that mock args fixture works."""
args = mock_args()
assert hasattr(args, 'filter')
assert args.filter == 200

@pytest.mark.unit
def test_unit_marker(self):
"""Test that unit marker is available."""
assert True

@pytest.mark.integration
def test_integration_marker(self):
"""Test that integration marker is available."""
assert True

def test_python_path_includes_project_root(self):
"""Test that the project root is in Python path."""
project_root = Path(__file__).parent.parent
assert str(project_root) in sys.path or any(
str(project_root) in path for path in sys.path
)

def test_coverage_will_run(self):
"""Test that coverage reporting will work."""
# This is just a placeholder to ensure coverage can track this file
def dummy_function():
return "covered"

result = dummy_function()
assert result == "covered"
Empty file added tests/unit/__init__.py
Empty file.