4a7f24348c
- Add instance_events and health_checks tables with Alembic migration - InstanceEventBus: typed pub/sub singleton with wildcard support - HealthMonitor: async background loop polling containers every 15s - SSE endpoint GET /events/stream with auth and connection limits - Lifecycle hooks in tool_instances.py (create/start/stop/restart/delete) - Structured JSON logging with correlation IDs - 15 new unit tests (EventBus, HealthMonitor, MonitoringModels) Quality gates: pytest 15 new passed, ruff clean
144 lines
3.9 KiB
Python
144 lines
3.9 KiB
Python
"""Unit tests for monitoring models and migration compatibility."""
|
|
|
|
import uuid
|
|
from datetime import datetime
|
|
|
|
import pytest
|
|
from sqlalchemy import select
|
|
|
|
from src.models.health_check import HealthCheck
|
|
from src.models.instance_event import InstanceEvent
|
|
from src.models.tool_instance import ToolInstance
|
|
from src.models.user import User
|
|
|
|
|
|
@pytest.mark.unit
|
|
async def test_instance_event_creation(db_session) -> None:
|
|
"""InstanceEvent model can be created and persisted."""
|
|
user = User(
|
|
id=uuid.uuid4(),
|
|
email="test@example.com",
|
|
name="Test",
|
|
authentik_id="auth-1",
|
|
)
|
|
db_session.add(user)
|
|
await db_session.commit()
|
|
|
|
instance = ToolInstance(
|
|
id=uuid.uuid4(),
|
|
name="test-instance",
|
|
display_name="Test Instance",
|
|
tool_type_id=uuid.uuid4(),
|
|
repository_id=uuid.uuid4(),
|
|
project_id=uuid.uuid4(),
|
|
owner_id=user.id,
|
|
status="pending",
|
|
)
|
|
db_session.add(instance)
|
|
await db_session.commit()
|
|
|
|
event = InstanceEvent(
|
|
instance_id=instance.id,
|
|
event_type="started",
|
|
status="starting",
|
|
message="Container starting...",
|
|
created_by=user.id,
|
|
event_metadata={"previous_status": "pending"},
|
|
)
|
|
db_session.add(event)
|
|
await db_session.commit()
|
|
await db_session.refresh(event)
|
|
|
|
assert event.id is not None
|
|
assert event.instance_id == instance.id
|
|
assert event.event_type == "started"
|
|
assert event.status == "starting"
|
|
assert event.created_by == user.id
|
|
assert event.event_metadata == {"previous_status": "pending"}
|
|
assert isinstance(event.created_at, datetime)
|
|
|
|
|
|
@pytest.mark.unit
|
|
async def test_health_check_creation(db_session) -> None:
|
|
"""HealthCheck model can be created and persisted."""
|
|
user = User(
|
|
id=uuid.uuid4(),
|
|
email="test2@example.com",
|
|
name="Test2",
|
|
authentik_id="auth-2",
|
|
)
|
|
db_session.add(user)
|
|
await db_session.commit()
|
|
|
|
instance = ToolInstance(
|
|
id=uuid.uuid4(),
|
|
name="test-instance-2",
|
|
display_name="Test Instance 2",
|
|
tool_type_id=uuid.uuid4(),
|
|
repository_id=uuid.uuid4(),
|
|
project_id=uuid.uuid4(),
|
|
owner_id=user.id,
|
|
status="running",
|
|
)
|
|
db_session.add(instance)
|
|
await db_session.commit()
|
|
|
|
check = HealthCheck(
|
|
instance_id=instance.id,
|
|
container_status="running",
|
|
container_healthy=True,
|
|
tunnel_healthy=True,
|
|
exit_code=None,
|
|
probe_status="passed",
|
|
probe_output="OK",
|
|
)
|
|
db_session.add(check)
|
|
await db_session.commit()
|
|
await db_session.refresh(check)
|
|
|
|
assert check.id is not None
|
|
assert check.instance_id == instance.id
|
|
assert check.container_status == "running"
|
|
assert check.container_healthy is True
|
|
assert check.tunnel_healthy is True
|
|
assert isinstance(check.checked_at, datetime)
|
|
|
|
|
|
@pytest.mark.unit
|
|
async def test_instance_event_query_by_instance(db_session) -> None:
|
|
"""InstanceEvent rows can be queried by instance_id."""
|
|
user = User(
|
|
id=uuid.uuid4(),
|
|
email="test3@example.com",
|
|
name="Test3",
|
|
authentik_id="auth-3",
|
|
)
|
|
db_session.add(user)
|
|
await db_session.commit()
|
|
|
|
instance = ToolInstance(
|
|
id=uuid.uuid4(),
|
|
name="test-instance-3",
|
|
display_name="Test Instance 3",
|
|
tool_type_id=uuid.uuid4(),
|
|
repository_id=uuid.uuid4(),
|
|
project_id=uuid.uuid4(),
|
|
owner_id=user.id,
|
|
status="pending",
|
|
)
|
|
db_session.add(instance)
|
|
await db_session.commit()
|
|
|
|
event = InstanceEvent(
|
|
instance_id=instance.id,
|
|
event_type="created",
|
|
status="pending",
|
|
)
|
|
db_session.add(event)
|
|
await db_session.commit()
|
|
|
|
result = await db_session.execute(
|
|
select(InstanceEvent).where(InstanceEvent.instance_id == instance.id)
|
|
)
|
|
assert result.scalar_one() is not None
|