Skip to content
Merged
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
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = 'blitzortung'
version = '1.12.2'
version = '1.12.3'
description = 'blitzortung.org python modules'
authors = [
{name = "Andreas Würl",email = "andi@tryb.de"}
Expand Down
181 changes: 181 additions & 0 deletions tests/cli/test_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
"""Tests for blitzortung.cli.db module."""

Comment on lines +1 to +2
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description mentions adding coverage for cache.py and base.py, but this change set also introduces new test modules for blitzortung.cli.db and blitzortung.cli.imprt. Please update the PR description (or scope) so reviewers understand these additional areas are being covered/changed.

Copilot uses AI. Check for mistakes.
from unittest.mock import Mock, patch
from zoneinfo import ZoneInfo

import pytest

from blitzortung.cli import db

Comment thread
wuan marked this conversation as resolved.

class TestParseTime:
"""Tests for parse_time function."""

def test_parse_time_basic(self):
"""Test basic time parsing."""
tz = ZoneInfo("UTC")
result = db.parse_time("20250101", "1200", tz, "starttime")
assert result.year == 2025
assert result.month == 1
assert result.day == 1
assert result.hour == 12
assert result.minute == 0

def test_parse_time_with_seconds(self):
"""Test time parsing with seconds."""
tz = ZoneInfo("UTC")
result = db.parse_time("20250101", "123045", tz, "starttime")
assert result.second == 45

def test_parse_time_end_time_adds_minute(self):
"""Test that end time adds a minute."""
tz = ZoneInfo("UTC")
result = db.parse_time("20250101", "1200", tz, "endtime", is_end_time=True)
assert result.hour == 12
assert result.minute == 1

def test_parse_time_end_time_with_seconds_adds_second(self):
"""Test that end time with seconds adds a second."""
tz = ZoneInfo("UTC")
result = db.parse_time("20250101", "120030", tz, "endtime", is_end_time=True)
assert result.second == 31


class TestPrepareGridIfApplicable:
"""Tests for prepare_grid_if_applicable function."""

def test_prepare_grid_returns_none_when_no_grid_options(self):
"""Test that None is returned when no grid options specified."""
options = Mock()
options.grid = None
options.xgrid = None
options.ygrid = None

area = Mock()
area.envelope.bounds = (1.0, 2.0, 3.0, 4.0)

result = db.prepare_grid_if_applicable(options, area)
assert result is None

def test_prepare_grid_uses_single_grid_option(self):
"""Test that single grid option sets both x and y."""
options = Mock()
options.grid = 0.5
options.xgrid = None
options.ygrid = None
options.srid = 4326
options.area = Mock()

area = Mock()
area.envelope.bounds = (1.0, 3.0, 2.0, 4.0)

result = db.prepare_grid_if_applicable(options, area)
assert result is not None

def test_prepare_grid_uses_xgrid_option(self):
"""Test that xgrid option is used."""
options = Mock()
options.grid = None
options.xgrid = 0.3
options.ygrid = None
options.srid = 4326
options.area = Mock()

area = Mock()
area.envelope.bounds = (1.0, 3.0, 2.0, 4.0)

result = db.prepare_grid_if_applicable(options, area)
assert result is not None

def test_prepare_grid_uses_ygrid_option(self):
"""Test that ygrid option is used."""
options = Mock()
options.grid = None
options.xgrid = None
options.ygrid = 0.4
options.srid = 4326
options.area = Mock()

area = Mock()
area.envelope.bounds = (1.0, 3.0, 2.0, 4.0)

result = db.prepare_grid_if_applicable(options, area)
assert result is not None

def test_prepare_grid_requires_area_for_grid_options(self):
"""Test that grid options require area to be defined."""
options = Mock()
options.grid = 0.5
options.xgrid = None
options.ygrid = None
options.area = None

area = Mock()

with pytest.raises(SystemExit) as exc_info:
db.prepare_grid_if_applicable(options, area)

assert exc_info.value.code == 1


class TestParseOptions:
"""Tests for parse_options function."""

def test_parse_options_defaults(self):
"""Test default option values."""
with patch('sys.argv', ['db.py']):
options = db.parse_options()

assert options.startdate == "default"
assert options.starttime == "default"
assert options.enddate == "default"
assert options.endtime == "default"
assert options.area is None
assert options.tz == "UTC"
assert options.useenv is False
assert options.srid == 4326
assert options.precision == 4

def test_parse_options_with_custom_args(self):
"""Test parsing with custom arguments."""
with patch('sys.argv', [
'db.py',
'--startdate', '20250101',
'--starttime', '1200',
'--area', 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))',
'--tz', 'Europe/Berlin',
'--precision', '2'
]):
options = db.parse_options()

assert options.startdate == "20250101"
assert options.starttime == "1200"
assert options.area == "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))"
assert options.tz == "Europe/Berlin"
assert options.precision == 2

def test_parse_options_with_grid(self):
"""Test parsing with grid options."""
with patch('sys.argv', [
'db.py',
'--grid', '0.5',
'--x-grid', '0.3',
'--y-grid', '0.4'
]):
options = db.parse_options()

assert options.grid == 0.5
assert options.xgrid == 0.3
assert options.ygrid == 0.4

def test_parse_options_flag_options(self):
"""Test boolean flag options."""
with patch('sys.argv', [
'db.py',
'--useenv',
'--map'
]):
options = db.parse_options()

assert options.useenv is True
assert options.map is True
95 changes: 95 additions & 0 deletions tests/cli/test_imprt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""Tests for blitzortung.cli.imprt module."""

import datetime
import sys
from unittest.mock import MagicMock, patch
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

patch is imported but never used in this test module. Please remove it to keep the test file minimal and avoid confusion about what is being mocked.

Suggested change
from unittest.mock import MagicMock, patch
from unittest.mock import MagicMock

Copilot uses AI. Check for mistakes.

import pytest


class TestTimestampIsNewerThan:
"""Tests for timestamp_is_newer_than function."""

@pytest.fixture(autouse=True)
def mock_dependencies(self, monkeypatch):
"""Mock optional dependencies to allow importing the module."""
mock_stopit = MagicMock()
mock_requests = MagicMock()
mock_statsd = MagicMock()
monkeypatch.setitem(sys.modules, 'stopit', mock_stopit)
monkeypatch.setitem(sys.modules, 'requests', mock_requests)
monkeypatch.setitem(sys.modules, 'statsd', mock_statsd)

def test_returns_true_when_latest_time_is_none(self):
"""Test that timestamp is newer when latest_time is None."""
from blitzortung.cli import imprt
timestamp = datetime.datetime.now(datetime.timezone.utc)
result = imprt.timestamp_is_newer_than(timestamp, None)
assert result is True

def test_returns_true_when_timestamp_is_newer(self):
"""Test that timestamp is newer when it's greater than latest_time."""
from blitzortung.cli import imprt
latest_time = datetime.datetime(2025, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)
timestamp = datetime.datetime(2025, 1, 1, 12, 1, 0, tzinfo=datetime.timezone.utc)
result = imprt.timestamp_is_newer_than(timestamp, latest_time)
assert result is True

def test_returns_false_when_timestamp_is_older(self):
"""Test that timestamp is not newer when it's less than latest_time."""
from blitzortung.cli import imprt
latest_time = datetime.datetime(2025, 1, 1, 12, 1, 0, tzinfo=datetime.timezone.utc)
timestamp = datetime.datetime(2025, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)
result = imprt.timestamp_is_newer_than(timestamp, latest_time)
assert result is False

def test_returns_false_when_timestamps_are_equal(self):
"""Test that timestamp is not newer when it's equal to latest_time."""
from blitzortung.cli import imprt
timestamp = datetime.datetime(2025, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)
result = imprt.timestamp_is_newer_than(timestamp, timestamp)
assert result is False

def test_returns_false_when_timestamp_is_one_day_older(self):
"""Test that timestamp is not newer when it's one day older."""
from blitzortung.cli import imprt
latest_time = datetime.datetime(2025, 1, 2, 12, 0, 0, tzinfo=datetime.timezone.utc)
timestamp = datetime.datetime(2025, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)
result = imprt.timestamp_is_newer_than(timestamp, latest_time)
assert result is False


class TestUpdateStartTime:
"""Tests for update_start_time function."""

@pytest.fixture(autouse=True)
def mock_dependencies(self, monkeypatch):
"""Mock optional dependencies to allow importing the module."""
mock_stopit = MagicMock()
mock_requests = MagicMock()
mock_statsd = MagicMock()
monkeypatch.setitem(sys.modules, 'stopit', mock_stopit)
monkeypatch.setitem(sys.modules, 'requests', mock_requests)
monkeypatch.setitem(sys.modules, 'statsd', mock_statsd)
Comment on lines +65 to +73
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same sys.modules mocking issue here: after the first import, blitzortung.cli.imprt will stay in sys.modules with mocked dependencies even after monkeypatch restores entries, which can leak into other tests. Consider removing the duplicate fixture and using a single session/function-scoped fixture that also unloads/reloads blitzortung.cli.imprt, or avoid mocking these modules entirely.

Copilot uses AI. Check for mistakes.

def test_update_start_time_returns_datetime(self):
"""Test that update_start_time returns a datetime object."""
from blitzortung.cli import imprt
result = imprt.update_start_time()
assert isinstance(result, datetime.datetime)

def test_update_start_time_is_30_minutes_ago(self):
"""Test that update_start_time returns time 30 minutes in the past."""
from blitzortung.cli import imprt
result = imprt.update_start_time()
expected = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(minutes=30)

# Allow 1 second tolerance for test execution time
diff = abs((result - expected).total_seconds())
assert diff < 1

def test_update_start_time_has_timezone(self):
"""Test that update_start_time returns timezone-aware datetime."""
from blitzortung.cli import imprt
result = imprt.update_start_time()
assert result.tzinfo is not None
Loading
Loading