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
80 changes: 80 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
*.manifest
*.spec

# Unit test / coverage reports
.pytest_cache/
.coverage
htmlcov/
coverage.xml
.tox/
.nox/
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/

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

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

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

# Project specific
secrets.py
*.log
*.tmp

# Claude Code settings
.claude/*
282 changes: 282 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
[tool.poetry]
name = "dcf77-mutteruhr"
version = "0.1.0"
description = "DCF77 time signal receiver emulator for MutterUhr word clock"
authors = ["Veeb Projects <info@veeb.ch>"]
readme = "README.md"
license = "GPL-3.0"
packages = [{include = "dcf77_mutteruhr", from = "."}]

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

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

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

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

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

[tool.coverage.run]
source = ["."]
omit = [
"tests/*",
"*/tests/*",
"*/.venv/*",
"*/venv/*",
"*/__pycache__/*",
"*/site-packages/*",
".pytest_cache/*",
"htmlcov/*",
"setup.py",
"conftest.py",
"main.py",
"webtime.py",
"secrets_example.py",
]

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

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

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

import pytest
import tempfile
import shutil
from pathlib import Path
from unittest.mock import MagicMock, patch


@pytest.fixture
def temp_dir():
"""Create a temporary directory for testing."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path, ignore_errors=True)


@pytest.fixture
def mock_machine_pin():
"""Mock machine.Pin for testing without hardware."""
mock_instance = MagicMock()
with patch.dict('sys.modules', {'machine': MagicMock()}):
with patch('machine.Pin', return_value=mock_instance):
yield mock_instance


@pytest.fixture
def mock_machine_rtc():
"""Mock machine.RTC for testing without hardware."""
mock_instance = MagicMock()
with patch.dict('sys.modules', {'machine': MagicMock()}):
with patch('machine.RTC', return_value=mock_instance):
yield mock_instance


@pytest.fixture
def mock_network():
"""Mock network module for testing without WiFi."""
mock_instance = MagicMock()
mock_instance.isconnected.return_value = True
mock_instance.status.return_value = 3 # Connected status

mock_network_module = MagicMock()
mock_network_module.WLAN.return_value = mock_instance

with patch.dict('sys.modules', {'network': mock_network_module}):
yield mock_instance


@pytest.fixture
def mock_urequests():
"""Mock urequests for testing without network calls."""
with patch('urequests.get') as mock_get:
mock_response = MagicMock()
mock_response.json.return_value = {
"year": 2023,
"month": 12,
"day": 25,
"hour": 12,
"minute": 30,
"seconds": 45,
"timeZone": "Australia/Canberra"
}
mock_get.return_value = mock_response
yield mock_get


@pytest.fixture
def mock_secrets():
"""Mock secrets module with test credentials."""
mock_secrets_module = MagicMock()
mock_secrets_module.WIFI_SSID = "test_ssid"
mock_secrets_module.WIFI_PASSWORD = "test_password"

with patch.dict('sys.modules', {'secrets': mock_secrets_module}):
yield mock_secrets_module


@pytest.fixture
def mock_microdot():
"""Mock Microdot web framework for testing."""
with patch('microdot.Microdot') as mock_app:
mock_instance = MagicMock()
mock_app.return_value = mock_instance
yield mock_instance


@pytest.fixture
def sample_time_data():
"""Provide sample time data for testing."""
return {
"current_time": (2023, 12, 25, 12, 30, 45, 0, 0),
"formatted": "2023-12-25 12:30:45",
"epoch": 1703505045
}


@pytest.fixture
def sample_dcf_signal():
"""Provide sample DCF77 signal data for testing."""
return {
"valid_signal": True,
"time_data": "010011010101010101010101010101010101010101010101010101010101",
"parity_ok": True,
"signal_quality": 95
}
1 change: 1 addition & 0 deletions tests/integration/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Integration tests package
70 changes: 70 additions & 0 deletions tests/test_setup_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Validation tests to ensure testing infrastructure is working correctly."""

import pytest
import sys
from pathlib import Path


class TestSetupValidation:
"""Test suite to validate that the testing infrastructure is properly configured."""

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

def test_python_version(self):
"""Test that we're running a supported Python version."""
assert sys.version_info >= (3, 8)

def test_project_structure(self):
"""Test that the project has the expected structure."""
project_root = Path(__file__).parent.parent

# Check main files exist
assert (project_root / "main.py").exists()
assert (project_root / "webtime.py").exists()
assert (project_root / "pyproject.toml").exists()

# Check test structure
assert (project_root / "tests").is_dir()
assert (project_root / "tests" / "__init__.py").exists()
assert (project_root / "tests" / "unit").is_dir()
assert (project_root / "tests" / "integration").is_dir()

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

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

@pytest.mark.slow
def test_slow_marker(self):
"""Test that slow test marker is working."""
assert True

def test_fixtures_available(self, temp_dir, sample_time_data):
"""Test that shared fixtures are available and working."""
# Test temp_dir fixture
assert temp_dir.exists()
assert temp_dir.is_dir()

# Test sample_time_data fixture
assert "current_time" in sample_time_data
assert "formatted" in sample_time_data
assert "epoch" in sample_time_data

def test_mocking_capabilities(self, mock_machine_pin, mock_network):
"""Test that mocking fixtures work correctly."""
# Test that mocks are available
assert mock_machine_pin is not None
assert mock_network is not None

# Test mock functionality
mock_machine_pin.on()
mock_machine_pin.on.assert_called_once()

assert mock_network.isconnected() is True
1 change: 1 addition & 0 deletions tests/unit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Unit tests package