mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
fix(telemetry): Fix enterprise linting errors
- Fix mypy type error: collector_results should store int not str - Remove unused variables in test files (F841 errors) - All 99 tests still passing after fixes Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
be4fa60970
commit
92a86624b0
@ -15,6 +15,7 @@ from telemetry.registry import CollectorRegistry
|
|||||||
# Optional import for Replicated SDK (to be implemented in M4)
|
# Optional import for Replicated SDK (to be implemented in M4)
|
||||||
try:
|
try:
|
||||||
from replicated import InstanceStatus, ReplicatedClient
|
from replicated import InstanceStatus, ReplicatedClient
|
||||||
|
|
||||||
REPLICATED_AVAILABLE = True
|
REPLICATED_AVAILABLE = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
REPLICATED_AVAILABLE = False
|
REPLICATED_AVAILABLE = False
|
||||||
@ -233,7 +234,9 @@ class TelemetryService:
|
|||||||
# If identity was just established, it will be created during upload
|
# If identity was just established, it will be created during upload
|
||||||
# Continue with short interval for one more cycle to ensure upload succeeds
|
# Continue with short interval for one more cycle to ensure upload succeeds
|
||||||
if not was_established_before and self._is_identity_established():
|
if not was_established_before and self._is_identity_established():
|
||||||
logger.info('Identity just established - first upload completed')
|
logger.info(
|
||||||
|
'Identity just established - first upload completed'
|
||||||
|
)
|
||||||
|
|
||||||
logger.info('Metrics upload completed')
|
logger.info('Metrics upload completed')
|
||||||
|
|
||||||
@ -268,9 +271,11 @@ class TelemetryService:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with session_maker() as session:
|
with session_maker() as session:
|
||||||
identity = session.query(TelemetryIdentity).filter(
|
identity = (
|
||||||
TelemetryIdentity.id == 1
|
session.query(TelemetryIdentity)
|
||||||
).first()
|
.filter(TelemetryIdentity.id == 1)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
|
||||||
# Identity is established if we have both customer_id and instance_id
|
# Identity is established if we have both customer_id and instance_id
|
||||||
return (
|
return (
|
||||||
@ -316,9 +321,11 @@ class TelemetryService:
|
|||||||
|
|
||||||
if not last_uploaded:
|
if not last_uploaded:
|
||||||
# Check if we have any pending metrics to upload
|
# Check if we have any pending metrics to upload
|
||||||
pending_count = session.query(TelemetryMetrics).filter(
|
pending_count = (
|
||||||
TelemetryMetrics.uploaded_at.is_(None)
|
session.query(TelemetryMetrics)
|
||||||
).count()
|
.filter(TelemetryMetrics.uploaded_at.is_(None))
|
||||||
|
.count()
|
||||||
|
)
|
||||||
return pending_count > 0
|
return pending_count > 0
|
||||||
|
|
||||||
hours_since = (
|
hours_since = (
|
||||||
@ -355,7 +362,7 @@ class TelemetryService:
|
|||||||
f'Collector {collector.collector_name} failed: {e}',
|
f'Collector {collector.collector_name} failed: {e}',
|
||||||
exc_info=True,
|
exc_info=True,
|
||||||
)
|
)
|
||||||
collector_results[collector.collector_name] = f'error: {str(e)}'
|
collector_results[collector.collector_name] = 0
|
||||||
|
|
||||||
# Store metrics in database
|
# Store metrics in database
|
||||||
with session_maker() as session:
|
with session_maker() as session:
|
||||||
@ -483,13 +490,11 @@ class TelemetryService:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_or_create_identity(
|
def _get_or_create_identity(self, session, admin_email: str) -> TelemetryIdentity:
|
||||||
self, session, admin_email: str
|
|
||||||
) -> TelemetryIdentity:
|
|
||||||
"""Get or create telemetry identity with customer and instance IDs."""
|
"""Get or create telemetry identity with customer and instance IDs."""
|
||||||
identity = session.query(TelemetryIdentity).filter(
|
identity = (
|
||||||
TelemetryIdentity.id == 1
|
session.query(TelemetryIdentity).filter(TelemetryIdentity.id == 1).first()
|
||||||
).first()
|
)
|
||||||
|
|
||||||
if not identity:
|
if not identity:
|
||||||
identity = TelemetryIdentity(id=1)
|
identity = TelemetryIdentity(id=1)
|
||||||
|
|||||||
@ -5,10 +5,7 @@ from datetime import datetime, timedelta, timezone
|
|||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from server.telemetry.service import TelemetryService
|
from server.telemetry.service import TelemetryService
|
||||||
from storage.telemetry_identity import TelemetryIdentity
|
|
||||||
from storage.telemetry_metrics import TelemetryMetrics
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -40,10 +37,10 @@ class TestTelemetryServiceLifecycle:
|
|||||||
"""Test that service starts and stops without errors."""
|
"""Test that service starts and stops without errors."""
|
||||||
with patch.object(
|
with patch.object(
|
||||||
fresh_telemetry_service, '_collection_loop', new_callable=AsyncMock
|
fresh_telemetry_service, '_collection_loop', new_callable=AsyncMock
|
||||||
) as mock_collection:
|
):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
fresh_telemetry_service, '_upload_loop', new_callable=AsyncMock
|
fresh_telemetry_service, '_upload_loop', new_callable=AsyncMock
|
||||||
) as mock_upload:
|
):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
fresh_telemetry_service,
|
fresh_telemetry_service,
|
||||||
'_initial_collection_check',
|
'_initial_collection_check',
|
||||||
@ -78,7 +75,7 @@ class TestTelemetryServiceLifecycle:
|
|||||||
fresh_telemetry_service,
|
fresh_telemetry_service,
|
||||||
'_initial_collection_check',
|
'_initial_collection_check',
|
||||||
new_callable=AsyncMock,
|
new_callable=AsyncMock,
|
||||||
) as mock_initial:
|
):
|
||||||
await fresh_telemetry_service.start()
|
await fresh_telemetry_service.start()
|
||||||
|
|
||||||
# Wait for async task to be created
|
# Wait for async task to be created
|
||||||
@ -114,7 +111,8 @@ class TestTelemetryServiceLifecycle:
|
|||||||
|
|
||||||
# Verify tasks are the same (not recreated)
|
# Verify tasks are the same (not recreated)
|
||||||
assert (
|
assert (
|
||||||
fresh_telemetry_service._collection_task == first_collection_task
|
fresh_telemetry_service._collection_task
|
||||||
|
== first_collection_task
|
||||||
)
|
)
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
@ -286,16 +284,22 @@ class TestCollectionLoop:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
with patch.object(
|
with patch.object(
|
||||||
fresh_telemetry_service, '_collect_metrics', side_effect=mock_collect_with_error
|
fresh_telemetry_service,
|
||||||
|
'_collect_metrics',
|
||||||
|
side_effect=mock_collect_with_error,
|
||||||
):
|
):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
fresh_telemetry_service, '_should_collect', return_value=True
|
fresh_telemetry_service, '_should_collect', return_value=True
|
||||||
):
|
):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
fresh_telemetry_service, '_is_identity_established', return_value=True
|
fresh_telemetry_service,
|
||||||
|
'_is_identity_established',
|
||||||
|
return_value=True,
|
||||||
):
|
):
|
||||||
# Start collection loop
|
# Start collection loop
|
||||||
task = asyncio.create_task(fresh_telemetry_service._collection_loop())
|
task = asyncio.create_task(
|
||||||
|
fresh_telemetry_service._collection_loop()
|
||||||
|
)
|
||||||
|
|
||||||
# Wait for multiple iterations
|
# Wait for multiple iterations
|
||||||
await asyncio.sleep(0.3)
|
await asyncio.sleep(0.3)
|
||||||
@ -354,13 +358,17 @@ class TestUploadLoop:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
with patch.object(
|
with patch.object(
|
||||||
fresh_telemetry_service, '_upload_pending_metrics', side_effect=mock_upload_with_error
|
fresh_telemetry_service,
|
||||||
|
'_upload_pending_metrics',
|
||||||
|
side_effect=mock_upload_with_error,
|
||||||
):
|
):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
fresh_telemetry_service, '_should_upload', return_value=True
|
fresh_telemetry_service, '_should_upload', return_value=True
|
||||||
):
|
):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
fresh_telemetry_service, '_is_identity_established', return_value=True
|
fresh_telemetry_service,
|
||||||
|
'_is_identity_established',
|
||||||
|
return_value=True,
|
||||||
):
|
):
|
||||||
# Start upload loop
|
# Start upload loop
|
||||||
task = asyncio.create_task(fresh_telemetry_service._upload_loop())
|
task = asyncio.create_task(fresh_telemetry_service._upload_loop())
|
||||||
@ -392,7 +400,9 @@ class TestMetricsCollection:
|
|||||||
mock_session.__exit__ = MagicMock(return_value=None)
|
mock_session.__exit__ = MagicMock(return_value=None)
|
||||||
mock_session_maker.return_value = mock_session
|
mock_session_maker.return_value = mock_session
|
||||||
|
|
||||||
with patch('server.telemetry.service.CollectorRegistry') as mock_registry_class:
|
with patch(
|
||||||
|
'server.telemetry.service.CollectorRegistry'
|
||||||
|
) as mock_registry_class:
|
||||||
mock_registry = MagicMock()
|
mock_registry = MagicMock()
|
||||||
mock_registry_class.return_value = mock_registry
|
mock_registry_class.return_value = mock_registry
|
||||||
|
|
||||||
@ -464,14 +474,18 @@ class TestReplicatedIntegration:
|
|||||||
with patch('server.telemetry.service.REPLICATED_AVAILABLE', True):
|
with patch('server.telemetry.service.REPLICATED_AVAILABLE', True):
|
||||||
with patch('server.telemetry.service.InstanceStatus') as mock_status:
|
with patch('server.telemetry.service.InstanceStatus') as mock_status:
|
||||||
mock_status.RUNNING = 'RUNNING'
|
mock_status.RUNNING = 'RUNNING'
|
||||||
with patch('server.telemetry.service.ReplicatedClient') as mock_client_class:
|
with patch(
|
||||||
|
'server.telemetry.service.ReplicatedClient'
|
||||||
|
) as mock_client_class:
|
||||||
mock_client = MagicMock()
|
mock_client = MagicMock()
|
||||||
mock_customer = MagicMock()
|
mock_customer = MagicMock()
|
||||||
mock_customer.customer_id = 'cust-123'
|
mock_customer.customer_id = 'cust-123'
|
||||||
mock_instance = MagicMock()
|
mock_instance = MagicMock()
|
||||||
mock_instance.instance_id = 'inst-456'
|
mock_instance.instance_id = 'inst-456'
|
||||||
|
|
||||||
mock_customer.get_or_create_instance.return_value = mock_instance
|
mock_customer.get_or_create_instance.return_value = (
|
||||||
|
mock_instance
|
||||||
|
)
|
||||||
mock_client.customer.get_or_create.return_value = mock_customer
|
mock_client.customer.get_or_create.return_value = mock_customer
|
||||||
|
|
||||||
mock_client_class.return_value = mock_client
|
mock_client_class.return_value = mock_client
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
"""Unit tests for the TelemetryService."""
|
"""Unit tests for the TelemetryService."""
|
||||||
|
|
||||||
import asyncio
|
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from server.telemetry.service import TelemetryService
|
from server.telemetry.service import TelemetryService
|
||||||
|
|
||||||
|
|
||||||
@ -39,7 +37,6 @@ class TestTelemetryServiceInitialization:
|
|||||||
|
|
||||||
def test_initialization_once(self, telemetry_service):
|
def test_initialization_once(self, telemetry_service):
|
||||||
"""Test that __init__ only runs once."""
|
"""Test that __init__ only runs once."""
|
||||||
initial_collection_interval = telemetry_service.collection_interval_days
|
|
||||||
telemetry_service.collection_interval_days = 999
|
telemetry_service.collection_interval_days = 999
|
||||||
|
|
||||||
# Create another "instance" (should be same singleton)
|
# Create another "instance" (should be same singleton)
|
||||||
@ -132,9 +129,7 @@ class TestIdentityEstablishment:
|
|||||||
assert not telemetry_service._is_identity_established()
|
assert not telemetry_service._is_identity_established()
|
||||||
|
|
||||||
@patch('server.telemetry.service.session_maker')
|
@patch('server.telemetry.service.session_maker')
|
||||||
def test_identity_established_complete(
|
def test_identity_established_complete(self, mock_session_maker, telemetry_service):
|
||||||
self, mock_session_maker, telemetry_service
|
|
||||||
):
|
|
||||||
"""Test identity established when both customer_id and instance_id exist."""
|
"""Test identity established when both customer_id and instance_id exist."""
|
||||||
mock_session = MagicMock()
|
mock_session = MagicMock()
|
||||||
mock_session.__enter__ = MagicMock(return_value=mock_session)
|
mock_session.__enter__ = MagicMock(return_value=mock_session)
|
||||||
@ -239,9 +234,7 @@ class TestUploadLogic:
|
|||||||
assert telemetry_service._should_upload()
|
assert telemetry_service._should_upload()
|
||||||
|
|
||||||
@patch('server.telemetry.service.session_maker')
|
@patch('server.telemetry.service.session_maker')
|
||||||
def test_should_not_upload_no_pending(
|
def test_should_not_upload_no_pending(self, mock_session_maker, telemetry_service):
|
||||||
self, mock_session_maker, telemetry_service
|
|
||||||
):
|
|
||||||
"""Test should not upload when no pending metrics exist."""
|
"""Test should not upload when no pending metrics exist."""
|
||||||
mock_session = MagicMock()
|
mock_session = MagicMock()
|
||||||
mock_session.__enter__ = MagicMock(return_value=mock_session)
|
mock_session.__enter__ = MagicMock(return_value=mock_session)
|
||||||
@ -355,9 +348,7 @@ class TestGetAdminEmail:
|
|||||||
"""Test when no admin email is available."""
|
"""Test when no admin email is available."""
|
||||||
mock_getenv.return_value = None
|
mock_getenv.return_value = None
|
||||||
|
|
||||||
mock_session.query.return_value.filter.return_value.filter.return_value.order_by.return_value.first.return_value = (
|
mock_session.query.return_value.filter.return_value.filter.return_value.order_by.return_value.first.return_value = None
|
||||||
None
|
|
||||||
)
|
|
||||||
|
|
||||||
email = telemetry_service._get_admin_email(mock_session)
|
email = telemetry_service._get_admin_email(mock_session)
|
||||||
|
|
||||||
@ -388,7 +379,7 @@ class TestGetOrCreateIdentity:
|
|||||||
mock_customer
|
mock_customer
|
||||||
)
|
)
|
||||||
|
|
||||||
identity = telemetry_service._get_or_create_identity(
|
telemetry_service._get_or_create_identity(
|
||||||
mock_session, 'test@example.com'
|
mock_session, 'test@example.com'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -405,9 +396,7 @@ class TestGetOrCreateIdentity:
|
|||||||
mock_identity
|
mock_identity
|
||||||
)
|
)
|
||||||
|
|
||||||
identity = telemetry_service._get_or_create_identity(
|
telemetry_service._get_or_create_identity(mock_session, 'test@example.com')
|
||||||
mock_session, 'test@example.com'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Should not create new instance since both IDs exist
|
# Should not create new instance since both IDs exist
|
||||||
assert mock_session.add.call_count == 0
|
assert mock_session.add.call_count == 0
|
||||||
@ -425,9 +414,7 @@ class TestLicenseWarningStatus:
|
|||||||
mock_session.__exit__ = MagicMock(return_value=None)
|
mock_session.__exit__ = MagicMock(return_value=None)
|
||||||
mock_session_maker.return_value = mock_session
|
mock_session_maker.return_value = mock_session
|
||||||
|
|
||||||
mock_session.query.return_value.filter.return_value.order_by.return_value.first.return_value = (
|
mock_session.query.return_value.filter.return_value.order_by.return_value.first.return_value = None
|
||||||
None
|
|
||||||
)
|
|
||||||
|
|
||||||
status = telemetry_service.get_license_warning_status()
|
status = telemetry_service.get_license_warning_status()
|
||||||
|
|
||||||
@ -446,9 +433,7 @@ class TestLicenseWarningStatus:
|
|||||||
mock_metric = MagicMock()
|
mock_metric = MagicMock()
|
||||||
mock_metric.uploaded_at = datetime.now(timezone.utc) - timedelta(days=2)
|
mock_metric.uploaded_at = datetime.now(timezone.utc) - timedelta(days=2)
|
||||||
|
|
||||||
mock_session.query.return_value.filter.return_value.order_by.return_value.first.return_value = (
|
mock_session.query.return_value.filter.return_value.order_by.return_value.first.return_value = mock_metric
|
||||||
mock_metric
|
|
||||||
)
|
|
||||||
|
|
||||||
status = telemetry_service.get_license_warning_status()
|
status = telemetry_service.get_license_warning_status()
|
||||||
|
|
||||||
@ -466,9 +451,7 @@ class TestLicenseWarningStatus:
|
|||||||
mock_metric = MagicMock()
|
mock_metric = MagicMock()
|
||||||
mock_metric.uploaded_at = datetime.now(timezone.utc) - timedelta(days=5)
|
mock_metric.uploaded_at = datetime.now(timezone.utc) - timedelta(days=5)
|
||||||
|
|
||||||
mock_session.query.return_value.filter.return_value.order_by.return_value.first.return_value = (
|
mock_session.query.return_value.filter.return_value.order_by.return_value.first.return_value = mock_metric
|
||||||
mock_metric
|
|
||||||
)
|
|
||||||
|
|
||||||
status = telemetry_service.get_license_warning_status()
|
status = telemetry_service.get_license_warning_status()
|
||||||
|
|
||||||
@ -489,7 +472,9 @@ class TestLifecycleManagement:
|
|||||||
telemetry_service, '_upload_loop', new_callable=AsyncMock
|
telemetry_service, '_upload_loop', new_callable=AsyncMock
|
||||||
):
|
):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
telemetry_service, '_initial_collection_check', new_callable=AsyncMock
|
telemetry_service,
|
||||||
|
'_initial_collection_check',
|
||||||
|
new_callable=AsyncMock,
|
||||||
):
|
):
|
||||||
await telemetry_service.start()
|
await telemetry_service.start()
|
||||||
|
|
||||||
@ -509,7 +494,9 @@ class TestLifecycleManagement:
|
|||||||
telemetry_service, '_upload_loop', new_callable=AsyncMock
|
telemetry_service, '_upload_loop', new_callable=AsyncMock
|
||||||
):
|
):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
telemetry_service, '_initial_collection_check', new_callable=AsyncMock
|
telemetry_service,
|
||||||
|
'_initial_collection_check',
|
||||||
|
new_callable=AsyncMock,
|
||||||
):
|
):
|
||||||
await telemetry_service.start()
|
await telemetry_service.start()
|
||||||
first_collection_task = telemetry_service._collection_task
|
first_collection_task = telemetry_service._collection_task
|
||||||
@ -535,7 +522,9 @@ class TestLifecycleManagement:
|
|||||||
telemetry_service, '_upload_loop', new_callable=AsyncMock
|
telemetry_service, '_upload_loop', new_callable=AsyncMock
|
||||||
):
|
):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
telemetry_service, '_initial_collection_check', new_callable=AsyncMock
|
telemetry_service,
|
||||||
|
'_initial_collection_check',
|
||||||
|
new_callable=AsyncMock,
|
||||||
):
|
):
|
||||||
await telemetry_service.start()
|
await telemetry_service.start()
|
||||||
await telemetry_service.stop()
|
await telemetry_service.stop()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user