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
47 changes: 45 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,48 @@ dmypy.json
src/database
src/config.yaml
docker-compose.yaml
tests/
src/services/app/panel
src/services/app/panel

# Claude settings
.claude/

# Poetry
# Do not ignore poetry.lock - it should be committed
# poetry.lock

# IDE files
.vscode/
*.swp
*.swo
*~

# OS files
.DS_Store
Thumbs.db

# Testing artifacts
.pytest_cache/
.coverage
.coverage.*
htmlcov/
coverage.xml
*.cover
*.py,cover
.hypothesis/
pytest_cache/
.tox/
.nox/

# Build artifacts
build/
dist/
*.egg-info/
.eggs/
*.egg

# Virtual environments
.venv/
venv/
ENV/
env/
.env
2,879 changes: 2,879 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

107 changes: 107 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
[tool.poetry]
name = "python-project"
version = "0.1.0"
description = "A Python project with testing infrastructure"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
packages = [{include = "src"}]

[tool.poetry.dependencies]
python = "^3.8"
fire = ">=0.4.0"
requests = ">=2.26.0"
telethon = ">=1.24.0"
sanic = ">=21.12.0"
bs4 = ">=0.0.1"
beautifulsoup4 = ">=4.10.0"
cloudscraper = ">=1.2.58"
selenium = ">=4.1.0"
pillow = ">=8.4.0"
pydub = ">=0.25.1"
colorama = ">=0.4.4"
pytz = ">=2021.3"
pyyaml = ">=6.0"
loguru = ">=0.5.3"
gevent = ">=21.8.0"
tqdm = ">=4.62.3"
apscheduler = ">=3.8.1"
playwright = ">=1.17.2"
redis = ">=4.0.1"
SpeechRecognition = "==3.8.1"
webdriver_manager = "==3.5.2"
undetected_chromedriver = "==3.1.5.post4"

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

[tool.poetry.scripts]
test = "pytest:main"
tests = "pytest:main"

[tool.pytest.ini_options]
minversion = "7.0"
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"--cov=src",
"--cov-branch",
"--cov-report=term-missing:skip-covered",
"--cov-report=html",
"--cov-report=xml",
"--cov-fail-under=0",
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Tests that take a long time to run",
]

[tool.coverage.run]
source = ["src"]
branch = true
omit = [
"*/tests/*",
"*/__init__.py",
"*/setup.py",
"*/conf.py",
"*/.venv/*",
"*/venv/*",
"*/site-packages/*",
]

[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__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
ignore_errors = true
precision = 2
show_missing = true
skip_covered = false
skip_empty = true

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

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
Empty file added tests/__init__.py
Empty file.
180 changes: 180 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import os
import tempfile
import shutil
from pathlib import Path
from typing import Generator, Dict, Any
import pytest
import yaml


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
temp_path = Path(tempfile.mkdtemp())
try:
yield temp_path
finally:
shutil.rmtree(temp_path, ignore_errors=True)


@pytest.fixture
def temp_file(temp_dir: Path) -> Generator[Path, None, None]:
"""Create a temporary file in the temp directory."""
temp_file_path = temp_dir / "test_file.txt"
temp_file_path.write_text("test content")
yield temp_file_path


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide a mock configuration dictionary."""
return {
"debug": True,
"database": {
"host": "localhost",
"port": 5432,
"name": "test_db",
"user": "test_user",
"password": "test_password"
},
"api": {
"base_url": "http://localhost:8000",
"timeout": 30,
"retry_count": 3
},
"features": {
"feature_a": True,
"feature_b": False
}
}


@pytest.fixture
def mock_yaml_config(temp_dir: Path, mock_config: Dict[str, Any]) -> Path:
"""Create a temporary YAML configuration file."""
config_path = temp_dir / "config.yaml"
with open(config_path, 'w') as f:
yaml.dump(mock_config, f)
return config_path


@pytest.fixture
def mock_env_vars(monkeypatch) -> Dict[str, str]:
"""Set up mock environment variables."""
env_vars = {
"TEST_ENV": "testing",
"DEBUG": "true",
"API_KEY": "test_api_key_123",
"DATABASE_URL": "postgresql://user:pass@localhost/testdb"
}

for key, value in env_vars.items():
monkeypatch.setenv(key, value)

return env_vars


@pytest.fixture
def clean_env(monkeypatch):
"""Remove specific environment variables for testing."""
env_vars_to_remove = ["API_KEY", "DATABASE_URL", "SECRET_KEY"]
for var in env_vars_to_remove:
monkeypatch.delenv(var, raising=False)


@pytest.fixture
def mock_request_data() -> Dict[str, Any]:
"""Provide mock HTTP request data."""
return {
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer test_token",
"User-Agent": "TestClient/1.0"
},
"body": {
"id": 123,
"name": "Test Item",
"data": {"key": "value"}
},
"params": {
"page": 1,
"limit": 10,
"sort": "created_at"
}
}


@pytest.fixture
def sample_data_list() -> list:
"""Provide a sample list of test data."""
return [
{"id": 1, "name": "Item 1", "value": 100},
{"id": 2, "name": "Item 2", "value": 200},
{"id": 3, "name": "Item 3", "value": 300},
]


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset singleton instances between tests to ensure test isolation."""
# This is a placeholder for resetting any singleton patterns
# that might exist in the codebase
yield


@pytest.fixture
def capture_logs(caplog):
"""Fixture to help capture and assert on log messages."""
with caplog.at_level("DEBUG"):
yield caplog


@pytest.fixture
def mock_datetime(monkeypatch):
"""Mock datetime for consistent testing."""
from datetime import datetime

class MockDatetime(datetime):
@classmethod
def now(cls, tz=None):
return cls(2024, 1, 1, 12, 0, 0)

@classmethod
def utcnow(cls):
return cls(2024, 1, 1, 12, 0, 0)

monkeypatch.setattr("datetime.datetime", MockDatetime)
return MockDatetime


# Markers for test organization
def pytest_configure(config):
"""Register custom markers."""
config.addinivalue_line(
"markers", "unit: mark test as a unit test"
)
config.addinivalue_line(
"markers", "integration: mark test as an integration test"
)
config.addinivalue_line(
"markers", "slow: mark test as slow running"
)


# Skip slow tests unless explicitly requested
def pytest_collection_modifyitems(config, items):
"""Modify test collection to handle markers."""
if config.getoption("--run-slow"):
# --run-slow given in cli: do not skip slow tests
return
skip_slow = pytest.mark.skip(reason="need --run-slow option to run")
for item in items:
if "slow" in item.keywords:
item.add_marker(skip_slow)


def pytest_addoption(parser):
"""Add custom command line options."""
parser.addoption(
"--run-slow", action="store_true", default=False, help="run slow tests"
)
Empty file added tests/integration/__init__.py
Empty file.
Loading