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
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ htmlcov/
nosetests.xml
coverage.xml
*,cover
.pytest_cache/

# Claude settings
.claude/*

# Translations
*.mo
Expand All @@ -63,3 +67,25 @@ RemoteSystemsTempFiles
.metadata
.settings
*.prefs

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

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

# OS files
.DS_Store
Thumbs.db

# Poetry - DO NOT ignore lock file
# poetry.lock is intentionally not ignored
Empty file added __init__.py
Empty file.
296 changes: 296 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

94 changes: 94 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
[tool.poetry]
name = "ipgeolocation"
version = "1.0.0"
description = "IP Geolocation tool"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
license = "MIT"
packages = [{include = "core"}]

[tool.poetry.dependencies]
python = "^3.8"
termcolor = "^2.4.0"
colorama = "^0.4.6"

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

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

[tool.pytest.ini_options]
minversion = "6.0"
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--verbose",
"--cov=core",
"--cov=ipgeolocation",
"--cov-report=html",
"--cov-report=xml",
"--cov-report=term-missing",
"--cov-fail-under=80",
"--tb=short",
"--maxfail=1",
"-ra"
]
markers = [
"unit: marks tests as unit tests (fast, isolated)",
"integration: marks tests as integration tests (may require external resources)",
"slow: marks tests as slow (deselect with '-m \"not slow\"')"
]

[tool.coverage.run]
source = ["core", "ipgeolocation"]
omit = [
"*/tests/*",
"*/__pycache__/*",
"*/venv/*",
"*/virtualenv/*",
"*/.venv/*",
"*/site-packages/*",
"*/dist-packages/*",
"*/node_modules/*",
"*/.git/*",
"*/.tox/*",
"*/.eggs/*",
"*/build/*",
"*/dist/*"
]

[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"
]
show_missing = true
precision = 2
skip_covered = false
fail_under = 80

[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.
125 changes: 125 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import pytest
import tempfile
import shutil
from pathlib import Path
from unittest.mock import Mock, MagicMock
import json


@pytest.fixture
def temp_dir():
"""Create a temporary directory for test files."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path)


@pytest.fixture
def temp_file(temp_dir):
"""Create a temporary file in the temp directory."""
def _create_temp_file(filename="test_file.txt", content=""):
file_path = temp_dir / filename
file_path.write_text(content)
return file_path
return _create_temp_file


@pytest.fixture
def mock_config():
"""Mock configuration object."""
config = MagicMock()
config.api_key = "test_api_key"
config.timeout = 30
config.max_retries = 3
config.base_url = "https://api.example.com"
return config


@pytest.fixture
def mock_response():
"""Mock HTTP response object."""
response = Mock()
response.status_code = 200
response.headers = {"Content-Type": "application/json"}
response.json.return_value = {"status": "success", "data": {}}
response.text = '{"status": "success", "data": {}}'
response.raise_for_status = Mock()
return response


@pytest.fixture
def sample_ip_data():
"""Sample IP geolocation data for testing."""
return {
"ip": "8.8.8.8",
"hostname": "dns.google",
"city": "Mountain View",
"region": "California",
"country": "United States",
"loc": "37.4056,-122.0775",
"org": "AS15169 Google LLC",
"postal": "94043",
"timezone": "America/Los_Angeles"
}


@pytest.fixture
def mock_logger(mocker):
"""Mock logger object."""
logger = mocker.Mock()
logger.debug = mocker.Mock()
logger.info = mocker.Mock()
logger.warning = mocker.Mock()
logger.error = mocker.Mock()
logger.critical = mocker.Mock()
return logger


@pytest.fixture
def mock_file_operations(mocker):
"""Mock file operations."""
mock_open = mocker.mock_open(read_data="test content")
mocker.patch("builtins.open", mock_open)
return mock_open


@pytest.fixture
def capture_stdout(monkeypatch):
"""Capture stdout output."""
import io
import sys

captured_output = io.StringIO()
monkeypatch.setattr(sys, 'stdout', captured_output)
yield captured_output
monkeypatch.undo()


@pytest.fixture
def mock_requests(mocker):
"""Mock requests library."""
mock_requests = mocker.patch("requests.get")
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"status": "ok"}
mock_requests.return_value = mock_response
return mock_requests


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset singleton instances between tests."""
yield


@pytest.fixture
def mock_environment(monkeypatch):
"""Set up mock environment variables."""
test_env = {
"API_KEY": "test_key",
"DEBUG": "true",
"LOG_LEVEL": "DEBUG"
}
for key, value in test_env.items():
monkeypatch.setenv(key, value)
return test_env
Empty file added tests/integration/__init__.py
Empty file.
82 changes: 82 additions & 0 deletions tests/test_setup_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import pytest
import sys
from pathlib import Path


class TestSetupValidation:
"""Validation tests to ensure the testing infrastructure is properly configured."""

def test_pytest_installed(self):
"""Verify pytest is installed and importable."""
import pytest
assert pytest.__version__

def test_coverage_installed(self):
"""Verify pytest-cov is installed and importable."""
import pytest_cov
assert pytest_cov.__version__

def test_mock_installed(self):
"""Verify pytest-mock is installed and importable."""
import pytest_mock
assert pytest_mock

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

assert project_root.exists()
assert (project_root / "core").is_dir()
assert (project_root / "tests").is_dir()
assert (project_root / "tests" / "unit").is_dir()
assert (project_root / "tests" / "integration").is_dir()

def test_conftest_fixtures_available(self, temp_dir, mock_config, sample_ip_data):
"""Verify conftest fixtures are available and working."""
assert temp_dir.exists()
assert temp_dir.is_dir()

assert mock_config.api_key == "test_api_key"
assert mock_config.timeout == 30

assert sample_ip_data["ip"] == "8.8.8.8"
assert sample_ip_data["city"] == "Mountain View"

def test_project_importable(self):
"""Verify the project modules can be imported."""
try:
import core
assert True
except ImportError:
pytest.fail("Could not import core module")

@pytest.mark.unit
def test_unit_marker(self):
"""Verify unit test marker works."""
assert True

@pytest.mark.integration
def test_integration_marker(self):
"""Verify integration test marker works."""
assert True

@pytest.mark.slow
def test_slow_marker(self):
"""Verify slow test marker works."""
assert True

def test_temp_file_fixture(self, temp_file):
"""Verify temp_file fixture works correctly."""
test_file = temp_file("test.txt", "Hello, World!")
assert test_file.exists()
assert test_file.read_text() == "Hello, World!"

def test_mock_logger_fixture(self, mock_logger):
"""Verify mock_logger fixture works correctly."""
mock_logger.info("Test message")
mock_logger.info.assert_called_once_with("Test message")

def test_mock_response_fixture(self, mock_response):
"""Verify mock_response fixture works correctly."""
assert mock_response.status_code == 200
assert mock_response.json()["status"] == "success"
Empty file added tests/unit/__init__.py
Empty file.