mirror of
https://github.com/smittix/intercept.git
synced 2026-07-05 16:18:12 -07:00
96172ca593
First-time run of ruff-format via pre-commit hook normalises quote style, trailing commas, and whitespace across 188 Python files. No logic changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
381 lines
14 KiB
Python
381 lines
14 KiB
Python
"""Tests for DSC database operations."""
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def temp_db():
|
|
"""Use a temporary database for each test."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
test_db_path = Path(tmpdir) / "test_intercept.db"
|
|
test_db_dir = Path(tmpdir)
|
|
|
|
with patch("utils.database.DB_PATH", test_db_path), patch("utils.database.DB_DIR", test_db_dir):
|
|
from utils.database import close_db, init_db
|
|
|
|
init_db()
|
|
yield test_db_path
|
|
close_db()
|
|
|
|
|
|
class TestDSCAlertsCRUD:
|
|
"""Tests for DSC alerts CRUD operations."""
|
|
|
|
def test_store_and_get_dsc_alert(self, temp_db):
|
|
"""Test storing and retrieving a DSC alert."""
|
|
from utils.database import get_dsc_alert, store_dsc_alert
|
|
|
|
alert_id = store_dsc_alert(
|
|
source_mmsi="232123456",
|
|
format_code="100",
|
|
category="DISTRESS",
|
|
source_name="MV Test Ship",
|
|
nature_of_distress="FIRE",
|
|
latitude=51.5,
|
|
longitude=-0.1,
|
|
)
|
|
|
|
assert alert_id is not None
|
|
assert alert_id > 0
|
|
|
|
alert = get_dsc_alert(alert_id)
|
|
|
|
assert alert is not None
|
|
assert alert["source_mmsi"] == "232123456"
|
|
assert alert["format_code"] == "100"
|
|
assert alert["category"] == "DISTRESS"
|
|
assert alert["source_name"] == "MV Test Ship"
|
|
assert alert["nature_of_distress"] == "FIRE"
|
|
assert alert["latitude"] == 51.5
|
|
assert alert["longitude"] == -0.1
|
|
assert alert["acknowledged"] is False
|
|
|
|
def test_store_minimal_alert(self, temp_db):
|
|
"""Test storing alert with only required fields."""
|
|
from utils.database import get_dsc_alert, store_dsc_alert
|
|
|
|
alert_id = store_dsc_alert(source_mmsi="366000001", format_code="116", category="ROUTINE")
|
|
|
|
alert = get_dsc_alert(alert_id)
|
|
|
|
assert alert is not None
|
|
assert alert["source_mmsi"] == "366000001"
|
|
assert alert["category"] == "ROUTINE"
|
|
assert alert["latitude"] is None
|
|
assert alert["longitude"] is None
|
|
|
|
def test_get_nonexistent_alert(self, temp_db):
|
|
"""Test getting an alert that doesn't exist."""
|
|
from utils.database import get_dsc_alert
|
|
|
|
alert = get_dsc_alert(99999)
|
|
assert alert is None
|
|
|
|
def test_get_dsc_alerts_all(self, temp_db):
|
|
"""Test getting all alerts."""
|
|
from utils.database import get_dsc_alerts, store_dsc_alert
|
|
|
|
store_dsc_alert("232123456", "100", "DISTRESS")
|
|
store_dsc_alert("366000001", "120", "URGENCY")
|
|
store_dsc_alert("351234567", "116", "ROUTINE")
|
|
|
|
alerts = get_dsc_alerts()
|
|
|
|
assert len(alerts) == 3
|
|
|
|
def test_get_dsc_alerts_by_category(self, temp_db):
|
|
"""Test filtering alerts by category."""
|
|
from utils.database import get_dsc_alerts, store_dsc_alert
|
|
|
|
store_dsc_alert("232123456", "100", "DISTRESS")
|
|
store_dsc_alert("232123457", "100", "DISTRESS")
|
|
store_dsc_alert("366000001", "120", "URGENCY")
|
|
store_dsc_alert("351234567", "116", "ROUTINE")
|
|
|
|
distress_alerts = get_dsc_alerts(category="DISTRESS")
|
|
urgency_alerts = get_dsc_alerts(category="URGENCY")
|
|
|
|
assert len(distress_alerts) == 2
|
|
assert len(urgency_alerts) == 1
|
|
|
|
def test_get_dsc_alerts_by_acknowledged(self, temp_db):
|
|
"""Test filtering alerts by acknowledgement status."""
|
|
from utils.database import acknowledge_dsc_alert, get_dsc_alerts, store_dsc_alert
|
|
|
|
id1 = store_dsc_alert("232123456", "100", "DISTRESS")
|
|
id2 = store_dsc_alert("366000001", "100", "DISTRESS")
|
|
store_dsc_alert("351234567", "100", "DISTRESS")
|
|
|
|
acknowledge_dsc_alert(id1)
|
|
acknowledge_dsc_alert(id2)
|
|
|
|
unacked = get_dsc_alerts(acknowledged=False)
|
|
acked = get_dsc_alerts(acknowledged=True)
|
|
|
|
assert len(unacked) == 1
|
|
assert len(acked) == 2
|
|
|
|
def test_get_dsc_alerts_by_mmsi(self, temp_db):
|
|
"""Test filtering alerts by source MMSI."""
|
|
from utils.database import get_dsc_alerts, store_dsc_alert
|
|
|
|
store_dsc_alert("232123456", "100", "DISTRESS")
|
|
store_dsc_alert("232123456", "120", "URGENCY")
|
|
store_dsc_alert("366000001", "100", "DISTRESS")
|
|
|
|
alerts = get_dsc_alerts(source_mmsi="232123456")
|
|
|
|
assert len(alerts) == 2
|
|
for alert in alerts:
|
|
assert alert["source_mmsi"] == "232123456"
|
|
|
|
def test_get_dsc_alerts_pagination(self, temp_db):
|
|
"""Test alert pagination."""
|
|
from utils.database import get_dsc_alerts, store_dsc_alert
|
|
|
|
# Create 10 alerts
|
|
for i in range(10):
|
|
store_dsc_alert(f"23212345{i}", "100", "DISTRESS")
|
|
|
|
# Get first page
|
|
page1 = get_dsc_alerts(limit=5, offset=0)
|
|
assert len(page1) == 5
|
|
|
|
# Get second page
|
|
page2 = get_dsc_alerts(limit=5, offset=5)
|
|
assert len(page2) == 5
|
|
|
|
# Ensure no overlap
|
|
page1_ids = {a["id"] for a in page1}
|
|
page2_ids = {a["id"] for a in page2}
|
|
assert page1_ids.isdisjoint(page2_ids)
|
|
|
|
def test_get_dsc_alerts_order(self, temp_db):
|
|
"""Test alerts are returned in reverse chronological order."""
|
|
from utils.database import get_dsc_alerts, store_dsc_alert
|
|
|
|
id1 = store_dsc_alert("232123456", "100", "DISTRESS")
|
|
id2 = store_dsc_alert("366000001", "100", "DISTRESS")
|
|
id3 = store_dsc_alert("351234567", "100", "DISTRESS")
|
|
|
|
alerts = get_dsc_alerts()
|
|
|
|
# ORDER BY received_at DESC, so most recent first
|
|
# When timestamps are identical, higher IDs are more recent
|
|
# The actual order depends on the DB implementation
|
|
# We just verify all 3 are present and it's a list
|
|
assert len(alerts) == 3
|
|
alert_ids = {a["id"] for a in alerts}
|
|
assert alert_ids == {id1, id2, id3}
|
|
|
|
def test_acknowledge_dsc_alert(self, temp_db):
|
|
"""Test acknowledging a DSC alert."""
|
|
from utils.database import acknowledge_dsc_alert, get_dsc_alert, store_dsc_alert
|
|
|
|
alert_id = store_dsc_alert("232123456", "100", "DISTRESS")
|
|
|
|
# Initially not acknowledged
|
|
alert = get_dsc_alert(alert_id)
|
|
assert alert["acknowledged"] is False
|
|
|
|
# Acknowledge it
|
|
result = acknowledge_dsc_alert(alert_id)
|
|
assert result is True
|
|
|
|
# Now acknowledged
|
|
alert = get_dsc_alert(alert_id)
|
|
assert alert["acknowledged"] is True
|
|
|
|
def test_acknowledge_dsc_alert_with_notes(self, temp_db):
|
|
"""Test acknowledging with notes."""
|
|
from utils.database import acknowledge_dsc_alert, get_dsc_alert, store_dsc_alert
|
|
|
|
alert_id = store_dsc_alert("232123456", "100", "DISTRESS")
|
|
|
|
acknowledge_dsc_alert(alert_id, notes="Vessel located, rescue underway")
|
|
|
|
alert = get_dsc_alert(alert_id)
|
|
assert alert["acknowledged"] is True
|
|
assert alert["notes"] == "Vessel located, rescue underway"
|
|
|
|
def test_acknowledge_nonexistent_alert(self, temp_db):
|
|
"""Test acknowledging an alert that doesn't exist."""
|
|
from utils.database import acknowledge_dsc_alert
|
|
|
|
result = acknowledge_dsc_alert(99999)
|
|
assert result is False
|
|
|
|
def test_get_dsc_alert_summary(self, temp_db):
|
|
"""Test getting alert summary counts."""
|
|
from utils.database import acknowledge_dsc_alert, get_dsc_alert_summary, store_dsc_alert
|
|
|
|
# Create various alerts
|
|
store_dsc_alert("232123456", "100", "DISTRESS")
|
|
store_dsc_alert("232123457", "100", "DISTRESS")
|
|
store_dsc_alert("366000001", "120", "URGENCY")
|
|
store_dsc_alert("351234567", "118", "SAFETY")
|
|
acked_id = store_dsc_alert("257000001", "100", "DISTRESS")
|
|
|
|
# Acknowledge one distress
|
|
acknowledge_dsc_alert(acked_id)
|
|
|
|
summary = get_dsc_alert_summary()
|
|
|
|
assert summary["distress"] == 2 # 3 - 1 acknowledged
|
|
assert summary["urgency"] == 1
|
|
assert summary["safety"] == 1
|
|
assert summary["total"] == 4
|
|
|
|
def test_get_dsc_alert_summary_empty(self, temp_db):
|
|
"""Test alert summary with no alerts."""
|
|
from utils.database import get_dsc_alert_summary
|
|
|
|
summary = get_dsc_alert_summary()
|
|
|
|
assert summary["distress"] == 0
|
|
assert summary["urgency"] == 0
|
|
assert summary["safety"] == 0
|
|
assert summary["routine"] == 0
|
|
assert summary["total"] == 0
|
|
|
|
def test_cleanup_old_dsc_alerts(self, temp_db):
|
|
"""Test cleanup function behavior."""
|
|
from utils.database import acknowledge_dsc_alert, cleanup_old_dsc_alerts, get_dsc_alerts, store_dsc_alert
|
|
|
|
# Create and acknowledge some alerts
|
|
id1 = store_dsc_alert("232123456", "100", "DISTRESS")
|
|
id2 = store_dsc_alert("366000001", "100", "DISTRESS")
|
|
id3 = store_dsc_alert("351234567", "100", "DISTRESS") # Unacknowledged
|
|
|
|
acknowledge_dsc_alert(id1)
|
|
acknowledge_dsc_alert(id2)
|
|
|
|
# Cleanup with large max_age shouldn't delete recent records
|
|
deleted = cleanup_old_dsc_alerts(max_age_days=30)
|
|
assert deleted == 0 # Nothing old enough to delete
|
|
|
|
# All 3 should still be present
|
|
alerts = get_dsc_alerts()
|
|
assert len(alerts) == 3
|
|
|
|
# Verify unacknowledged one is still unacknowledged
|
|
unacked = get_dsc_alerts(acknowledged=False)
|
|
assert len(unacked) == 1
|
|
assert unacked[0]["id"] == id3
|
|
|
|
def test_cleanup_preserves_unacknowledged(self, temp_db):
|
|
"""Test cleanup preserves unacknowledged alerts regardless of age."""
|
|
from utils.database import cleanup_old_dsc_alerts, get_dsc_alerts, store_dsc_alert
|
|
|
|
# Create unacknowledged alerts
|
|
store_dsc_alert("232123456", "100", "DISTRESS")
|
|
store_dsc_alert("366000001", "100", "DISTRESS")
|
|
|
|
# Cleanup with 0 days
|
|
deleted = cleanup_old_dsc_alerts(max_age_days=0)
|
|
|
|
# All should remain (none were acknowledged)
|
|
alerts = get_dsc_alerts()
|
|
assert len(alerts) == 2
|
|
assert deleted == 0
|
|
|
|
def test_store_alert_with_raw_message(self, temp_db):
|
|
"""Test storing alert with raw message data."""
|
|
from utils.database import get_dsc_alert, store_dsc_alert
|
|
|
|
raw = "100023212345603660000110010010000000000127"
|
|
|
|
alert_id = store_dsc_alert(source_mmsi="232123456", format_code="100", category="DISTRESS", raw_message=raw)
|
|
|
|
alert = get_dsc_alert(alert_id)
|
|
assert alert["raw_message"] == raw
|
|
|
|
def test_store_alert_with_destination(self, temp_db):
|
|
"""Test storing alert with destination MMSI."""
|
|
from utils.database import get_dsc_alert, store_dsc_alert
|
|
|
|
alert_id = store_dsc_alert(
|
|
source_mmsi="232123456", format_code="112", category="INDIVIDUAL", dest_mmsi="366000001"
|
|
)
|
|
|
|
alert = get_dsc_alert(alert_id)
|
|
assert alert["dest_mmsi"] == "366000001"
|
|
|
|
|
|
class TestDSCDatabaseIntegration:
|
|
"""Integration tests for DSC database operations."""
|
|
|
|
def test_full_alert_lifecycle(self, temp_db):
|
|
"""Test complete lifecycle of a DSC alert."""
|
|
from utils.database import (
|
|
acknowledge_dsc_alert,
|
|
get_dsc_alert,
|
|
get_dsc_alert_summary,
|
|
get_dsc_alerts,
|
|
store_dsc_alert,
|
|
)
|
|
|
|
# 1. Store a distress alert
|
|
alert_id = store_dsc_alert(
|
|
source_mmsi="232123456",
|
|
format_code="100",
|
|
category="DISTRESS",
|
|
source_name="MV Mayday",
|
|
nature_of_distress="SINKING",
|
|
latitude=50.0,
|
|
longitude=-5.0,
|
|
)
|
|
|
|
# 2. Verify it appears in summary
|
|
summary = get_dsc_alert_summary()
|
|
assert summary["distress"] == 1
|
|
assert summary["total"] == 1
|
|
|
|
# 3. Verify it appears in unacknowledged list
|
|
unacked = get_dsc_alerts(acknowledged=False)
|
|
assert len(unacked) == 1
|
|
assert unacked[0]["source_mmsi"] == "232123456"
|
|
|
|
# 4. Acknowledge with notes
|
|
acknowledge_dsc_alert(alert_id, "Rescue helicopter dispatched")
|
|
|
|
# 5. Verify it's now acknowledged
|
|
alert = get_dsc_alert(alert_id)
|
|
assert alert["acknowledged"] is True
|
|
assert alert["notes"] == "Rescue helicopter dispatched"
|
|
|
|
# 6. Verify summary updated
|
|
summary = get_dsc_alert_summary()
|
|
assert summary["distress"] == 0
|
|
assert summary["total"] == 0
|
|
|
|
# 7. Verify it appears in acknowledged list
|
|
acked = get_dsc_alerts(acknowledged=True)
|
|
assert len(acked) == 1
|
|
|
|
def test_multiple_vessel_alerts(self, temp_db):
|
|
"""Test handling alerts from multiple vessels."""
|
|
from utils.database import get_dsc_alerts, store_dsc_alert
|
|
|
|
# Simulate multiple vessels in distress
|
|
vessels = [
|
|
("232123456", "United Kingdom", "FIRE"),
|
|
("366000001", "USA", "FLOODING"),
|
|
("351234567", "Panama", "COLLISION"),
|
|
]
|
|
|
|
for mmsi, _country, nature in vessels:
|
|
store_dsc_alert(source_mmsi=mmsi, format_code="100", category="DISTRESS", nature_of_distress=nature)
|
|
|
|
# Verify all alerts stored
|
|
alerts = get_dsc_alerts(category="DISTRESS")
|
|
assert len(alerts) == 3
|
|
|
|
# Verify each has correct nature
|
|
natures = {a["nature_of_distress"] for a in alerts}
|
|
assert natures == {"FIRE", "FLOODING", "COLLISION"}
|