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
91 changes: 91 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Testing
.pytest_cache/
.coverage
htmlcov/
coverage.xml
.tox/
.nox/
.cache/
nosetests.xml
coverage/
*.cover
*.py,cover
.hypothesis/

# Claude
.claude/*

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

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

# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# Logs
*.log
logs/

# Temporary files
*.tmp
*.temp
.tmp/
temp/

# Database
*.db
*.sqlite
*.sqlite3

# OS generated files
Thumbs.db
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db

# Project specific
*.osm.pbf
*.osm
*.obf
*.zip
osmand_osm/osm/aa/
osmand_osm/osm/ab/
data/
output/
5 changes: 5 additions & 0 deletions osmand_osm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""
osmand-osm: OpenAddresses to OSM format converter for OSMAnd
"""

__version__ = "0.1.0"
3 changes: 3 additions & 0 deletions osmand_osm/osm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
OSM processing modules for osmand-osm
"""
354 changes: 354 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

76 changes: 76 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
[tool.poetry]
name = "osmand-osm"
version = "0.1.0"
description = "OpenAddresses to OSM format converter for OSMAnd"
authors = ["Your Name <your.email@example.com>"]
readme = "README.md"
packages = [{include = "osmand_osm"}]

[tool.poetry.dependencies]
python = "^3.8"
psycopg = {version = "^3.0", optional = true}

[tool.poetry.group.dev.dependencies]
pytest = "^7.0"
pytest-cov = "^4.0"
pytest-mock = "^3.10"

[tool.poetry.extras]
database = ["psycopg"]


[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--strict-config",
"--verbose",
"--cov=osmand_osm",
"--cov-report=term-missing",
"--cov-report=html",
"--cov-report=xml",
"--cov-fail-under=80"
]
markers = [
"unit: marks tests as unit tests (fast, isolated)",
"integration: marks tests as integration tests (slower, require external resources)",
"slow: marks tests as slow running"
]

[tool.coverage.run]
source = ["osmand_osm"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/.venv/*",
"*/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__.:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod"
]
precision = 2
show_missing = true

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

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Empty file added tests/__init__.py
Empty file.
178 changes: 178 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
"""
Shared pytest fixtures for the osmand-osm project.
"""
import os
import tempfile
import shutil
from pathlib import Path
from unittest.mock import Mock, MagicMock
import pytest

# Handle optional psycopg import
try:
import psycopg
PSYCOPG_AVAILABLE = True
except ImportError:
psycopg = None
PSYCOPG_AVAILABLE = False


@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 mock_config():
"""Mock configuration values for testing."""
return {
'DB_NAME': 'test_db',
'ID_START': 17179869184,
'test_working_dir': '/tmp/test'
}


@pytest.fixture
def mock_db_connection():
"""Mock database connection for testing."""
if PSYCOPG_AVAILABLE:
mock_conn = Mock(spec=psycopg.Connection)
else:
mock_conn = Mock()

mock_cursor = Mock()
mock_conn.cursor.return_value = mock_cursor
mock_conn.commit = Mock()
mock_conn.rollback = Mock()
mock_conn.close = Mock()
mock_cursor.execute = Mock()
mock_cursor.fetchall = Mock(return_value=[])
mock_cursor.fetchone = Mock(return_value=None)
mock_cursor.close = Mock()
return mock_conn


@pytest.fixture
def sample_geojson_data():
"""Sample GeoJSON data for testing."""
return {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"number": "123",
"street": "Main St",
"city": "Test City",
"postcode": "12345",
"hash": "abc123def456"
},
"geometry": {
"type": "Point",
"coordinates": [-71.4188401, 41.8572897]
}
}
]
}


@pytest.fixture
def sample_osm_data():
"""Sample OSM XML data for testing."""
return """<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="test">
<node id="-1" lat="41.8572897" lon="-71.4188401">
<tag k="addr:housenumber" v="123"/>
<tag k="addr:street" v="Main St"/>
<tag k="addr:city" v="Test City"/>
<tag k="addr:postcode" v="12345"/>
</node>
</osm>"""


@pytest.fixture
def mock_working_area():
"""Mock WorkingArea object for testing."""
from unittest.mock import MagicMock

mock_area = MagicMock()
mock_area.name = "test"
mock_area.name_underscore = "test"
mock_area.country = "test"
mock_area.short_name = "test"
mock_area.directory = Path("test")
mock_area.is_3166_2 = False
mock_area.master_list = []
mock_area.obf_name = "Test.obf"
mock_area.pbf = "test/test.osm.pbf"
mock_area.pbf_osm = "test/test-latest.osm.pbf"

return mock_area


@pytest.fixture
def mock_subprocess():
"""Mock subprocess.run calls."""
from unittest.mock import patch
with patch('subprocess.run') as mock_run:
mock_run.return_value.returncode = 0
yield mock_run


@pytest.fixture
def test_data_dir(temp_dir):
"""Create a test data directory with sample files."""
data_dir = temp_dir / "test_data"
data_dir.mkdir()

# Create sample address file
address_file = data_dir / "addresses.geojson"
address_file.write_text('{"type": "FeatureCollection", "features": []}')

# Create sample zip file structure
zip_dir = data_dir / "zip_content"
zip_dir.mkdir()
(zip_dir / "addresses-city.geojson").write_text('{"type": "FeatureCollection", "features": []}')

return data_dir


@pytest.fixture
def mock_ogr2osm():
"""Mock ogr2osm module for testing."""
from unittest.mock import MagicMock, patch

mock_ogr2osm = MagicMock()
mock_ogr2osm.ogr2osm = MagicMock()

with patch('ogr2osm.ogr2osm', mock_ogr2osm.ogr2osm):
yield mock_ogr2osm


@pytest.fixture(autouse=True)
def setup_test_environment(monkeypatch):
"""Set up test environment variables and cleanup."""
# Set test environment variables
monkeypatch.setenv("TESTING", "1")

# Ensure we don't accidentally use real database
monkeypatch.delenv("DATABASE_URL", raising=False)

yield

# Cleanup after test


@pytest.fixture
def mock_logger():
"""Mock logger for testing."""
from unittest.mock import Mock
logger = Mock()
logger.debug = Mock()
logger.info = Mock()
logger.warning = Mock()
logger.error = Mock()
return logger
Empty file added tests/integration/__init__.py
Empty file.
Loading