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
42 changes: 41 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,44 @@
build
dist
MANIFEST
*.egg-info
*.egg-info

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

# Poetry
poetry.lock
dist/
build/

# Claude
.claude/*

# Virtual environments
venv/
env/
ENV/
.venv/
virtualenv/

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

# OS
.DS_Store
Thumbs.db

# Temporary files
*.tmp
*.bak
.cache/
77 changes: 77 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
[tool.poetry]
name = "validate-email"
version = "1.3"
description = "validate_email verifies if an email address is valid and really exists."
authors = ["Syrus Akbary <[email protected]>"]
license = "LGPL"
readme = "README.rst"
repository = "https://github.com/syrusakbary/validate_email"
keywords = ["email", "validation", "verification", "mx", "verify"]
packages = [{include = "validate_email.py"}]

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

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

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

[tool.pytest.ini_options]
minversion = "8.0"
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"-ra",
"--strict-markers",
"--cov=validate_email",
"--cov-report=term-missing:skip-covered",
"--cov-report=html",
"--cov-report=xml",
"--cov-fail-under=80",
"-vv",
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]

[tool.coverage.run]
source = ["validate_email"]
omit = [
"*/tests/*",
"*/__pycache__/*",
"*/venv/*",
"*/virtualenv/*",
"*/.tox/*",
"*/setup.py",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]
precision = 2
skip_covered = true

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

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

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Empty file added tests/__init__.py
Empty file.
79 changes: 79 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Shared pytest fixtures and configuration for all tests."""

import tempfile
from pathlib import Path

import pytest


@pytest.fixture
def temp_dir():
"""Create a temporary directory for test files."""
with tempfile.TemporaryDirectory() as tmpdir:
yield Path(tmpdir)


@pytest.fixture
def mock_email_config():
"""Provide mock email configuration for testing."""
return {
"valid_emails": [
"[email protected]",
"[email protected]",
"[email protected]",
],
"invalid_emails": [
"notanemail",
"@domain.com",
"user@",
"user@domain",
"",
],
"test_mx_domain": "example.com",
"test_smtp_host": "mail.example.com",
"test_smtp_port": 25,
}


@pytest.fixture
def mock_dns_resolver(mocker):
"""Mock DNS resolver for testing MX record lookups."""
mock_resolver = mocker.MagicMock()
return mock_resolver


@pytest.fixture
def mock_smtp_connection(mocker):
"""Mock SMTP connection for testing email verification."""
mock_smtp = mocker.MagicMock()
mock_smtp.__enter__ = mocker.MagicMock(return_value=mock_smtp)
mock_smtp.__exit__ = mocker.MagicMock(return_value=None)
return mock_smtp


@pytest.fixture(autouse=True)
def reset_test_environment():
"""Reset test environment before each test."""
yield


@pytest.fixture
def sample_mx_records():
"""Provide sample MX records for testing."""
return [
(10, "mx1.example.com"),
(20, "mx2.example.com"),
(30, "mx3.example.com"),
]


@pytest.fixture
def network_timeout():
"""Provide standard network timeout for tests."""
return 5.0


@pytest.fixture(scope="session")
def test_data_dir():
"""Provide path to test data directory."""
return Path(__file__).parent / "data"
Empty file added tests/integration/__init__.py
Empty file.
98 changes: 98 additions & 0 deletions tests/test_infrastructure_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""Validation tests to ensure the testing infrastructure is set up correctly."""

import sys
from pathlib import Path

import pytest


class TestInfrastructureValidation:
"""Validate that the testing infrastructure is properly configured."""

def test_pytest_installed(self):
"""Verify pytest is installed and importable."""
assert "pytest" in sys.modules

def test_pytest_cov_installed(self):
"""Verify pytest-cov is installed."""
try:
import pytest_cov
assert pytest_cov is not None
except ImportError:
pytest.fail("pytest-cov is not installed")

def test_pytest_mock_installed(self):
"""Verify pytest-mock is installed."""
try:
import pytest_mock
assert pytest_mock is not None
except ImportError:
pytest.fail("pytest-mock is not installed")

def test_project_structure_exists(self):
"""Verify the project structure is set up correctly."""
project_root = Path(__file__).parent.parent

# Check main directories exist
assert project_root.exists()
assert (project_root / "tests").exists()
assert (project_root / "tests" / "unit").exists()
assert (project_root / "tests" / "integration").exists()

# Check __init__.py files exist
assert (project_root / "tests" / "__init__.py").exists()
assert (project_root / "tests" / "unit" / "__init__.py").exists()
assert (project_root / "tests" / "integration" / "__init__.py").exists()

# Check configuration files exist
assert (project_root / "pyproject.toml").exists()

def test_conftest_exists(self):
"""Verify conftest.py exists and is loadable."""
conftest_path = Path(__file__).parent / "conftest.py"
assert conftest_path.exists()

@pytest.mark.unit
def test_unit_marker_works(self):
"""Verify the unit test marker is configured."""
assert True

@pytest.mark.integration
def test_integration_marker_works(self):
"""Verify the integration test marker is configured."""
assert True

@pytest.mark.slow
def test_slow_marker_works(self):
"""Verify the slow test marker is configured."""
assert True

def test_fixtures_available(self, temp_dir, mock_email_config):
"""Verify custom fixtures are available and working."""
assert temp_dir.exists()
assert temp_dir.is_dir()

assert isinstance(mock_email_config, dict)
assert "valid_emails" in mock_email_config
assert "invalid_emails" in mock_email_config

def test_mock_fixtures_available(self, mock_dns_resolver, mock_smtp_connection):
"""Verify mock fixtures are available."""
assert mock_dns_resolver is not None
assert mock_smtp_connection is not None

def test_validate_email_importable(self):
"""Verify the main module can be imported."""
try:
import validate_email
assert validate_email is not None
except ImportError:
pytest.fail("validate_email module cannot be imported")


@pytest.mark.parametrize("command", ["test", "tests"])
def test_poetry_scripts_defined(command):
"""Verify Poetry scripts are defined in pyproject.toml."""
pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
content = pyproject_path.read_text()
assert f'{command} = "pytest:main"' in content
Empty file added tests/unit/__init__.py
Empty file.