91e75e620b
CI: cua-driver distro-compat matrix / Resolve release version (push) Waiting to run
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / Distro compat summary (push) Blocked by required conditions
CI: Nix Linux Rust source / Nix / compositor build (push) Waiting to run
CI: Nix Linux Rust source / Nix / driver package (push) Waiting to run
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Waiting to run
CI: Rust Linux unit / Rust Linux unit and compile (push) Waiting to run
CI: Rust Windows unit / Rust Windows unit and compile (push) Waiting to run
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Waiting to run
CD: Docs MCP Server / build (linux/amd64) (push) Waiting to run
CD: Docs MCP Server / build (linux/arm64) (push) Waiting to run
CD: Docs MCP Server / merge (push) Blocked by required conditions
248 lines
9.5 KiB
Python
248 lines
9.5 KiB
Python
"""Unit tests for core telemetry functionality.
|
|
|
|
This file tests ONLY telemetry logic, following SRP.
|
|
All external dependencies (PostHog, file system) are mocked.
|
|
"""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, Mock, mock_open, patch
|
|
|
|
import pytest
|
|
|
|
|
|
class TestTelemetryEnabled:
|
|
"""Test telemetry enable/disable logic (SRP: Only tests enable/disable)."""
|
|
|
|
def test_telemetry_enabled_by_default(self, monkeypatch):
|
|
"""Test that telemetry is enabled by default."""
|
|
# Remove any environment variables that might affect the test
|
|
monkeypatch.delenv("CUA_TELEMETRY", raising=False)
|
|
monkeypatch.delenv("CUA_TELEMETRY_ENABLED", raising=False)
|
|
monkeypatch.delenv("CUA_TELEMETRY_DISABLED", raising=False)
|
|
|
|
from cua_core.telemetry import is_telemetry_enabled
|
|
|
|
assert is_telemetry_enabled() is True
|
|
|
|
def test_telemetry_disabled_with_flag(self, monkeypatch):
|
|
"""Test that telemetry can be disabled with CUA_TELEMETRY_ENABLED=false."""
|
|
monkeypatch.setenv("CUA_TELEMETRY_ENABLED", "false")
|
|
|
|
from cua_core.telemetry import is_telemetry_enabled
|
|
|
|
assert is_telemetry_enabled() is False
|
|
|
|
@pytest.mark.parametrize("value", ["0", "false", "no", "off"])
|
|
def test_telemetry_disabled_with_various_values(self, monkeypatch, value):
|
|
"""Test that telemetry respects various disable values."""
|
|
monkeypatch.setenv("CUA_TELEMETRY_ENABLED", value)
|
|
|
|
from cua_core.telemetry import is_telemetry_enabled
|
|
|
|
assert is_telemetry_enabled() is False
|
|
|
|
@pytest.mark.parametrize("value", ["1", "true", "yes", "on"])
|
|
def test_telemetry_enabled_with_various_values(self, monkeypatch, value):
|
|
"""Test that telemetry respects various enable values."""
|
|
monkeypatch.delenv("CUA_TELEMETRY_DISABLED", raising=False)
|
|
monkeypatch.setenv("CUA_TELEMETRY_ENABLED", value)
|
|
|
|
from cua_core.telemetry import is_telemetry_enabled
|
|
|
|
assert is_telemetry_enabled() is True
|
|
|
|
|
|
class TestPostHogTelemetryClient:
|
|
"""Test PostHogTelemetryClient class (SRP: Only tests client logic)."""
|
|
|
|
@patch("cua_core.telemetry.posthog.posthog")
|
|
@patch("cua_core.telemetry.posthog.Path")
|
|
def test_client_initialization(self, mock_path, mock_posthog, disable_telemetry):
|
|
"""Test that client initializes correctly."""
|
|
from cua_core.telemetry.posthog import PostHogTelemetryClient
|
|
|
|
# Mock the storage directory
|
|
mock_storage_dir = MagicMock()
|
|
mock_storage_dir.exists.return_value = False
|
|
mock_path.return_value.parent.parent = MagicMock()
|
|
mock_path.return_value.parent.parent.__truediv__.return_value = mock_storage_dir
|
|
|
|
# Reset singleton
|
|
PostHogTelemetryClient.destroy_client()
|
|
|
|
client = PostHogTelemetryClient()
|
|
|
|
assert client is not None
|
|
assert hasattr(client, "installation_id")
|
|
assert hasattr(client, "initialized")
|
|
assert hasattr(client, "queued_events")
|
|
|
|
@patch("cua_core.telemetry.posthog.posthog")
|
|
@patch("cua_core.telemetry.posthog.Path")
|
|
def test_installation_id_generation(self, mock_path, mock_posthog, disable_telemetry):
|
|
"""Test that installation ID is generated if not exists."""
|
|
from cua_core.telemetry.posthog import PostHogTelemetryClient
|
|
|
|
# Mock file system: Path.home() / ".config" / "cua" / "installation_id"
|
|
mock_id_file = MagicMock()
|
|
mock_id_file.exists.return_value = False
|
|
mock_config_dir = MagicMock()
|
|
mock_config_dir.__truediv__.return_value = mock_id_file
|
|
mock_path.home.return_value.__truediv__.return_value.__truediv__.return_value = (
|
|
mock_config_dir
|
|
)
|
|
|
|
# Reset singleton
|
|
PostHogTelemetryClient.destroy_client()
|
|
|
|
client = PostHogTelemetryClient()
|
|
|
|
# Should have generated a new UUID
|
|
assert client.installation_id is not None
|
|
assert len(client.installation_id) == 36 # UUID format
|
|
|
|
@patch("cua_core.telemetry.posthog.posthog")
|
|
@patch("cua_core.telemetry.posthog.Path")
|
|
def test_installation_id_persistence(self, mock_path, mock_posthog, disable_telemetry):
|
|
"""Test that installation ID is read from file if exists."""
|
|
from cua_core.telemetry.posthog import PostHogTelemetryClient
|
|
|
|
existing_id = "test-installation-id-123"
|
|
|
|
# Mock file system: Path.home() / ".config" / "cua" / "installation_id"
|
|
mock_id_file = MagicMock()
|
|
mock_id_file.exists.return_value = True
|
|
mock_id_file.read_text.return_value.strip.return_value = existing_id
|
|
mock_config_dir = MagicMock()
|
|
mock_config_dir.__truediv__.return_value = mock_id_file
|
|
mock_path.home.return_value.__truediv__.return_value.__truediv__.return_value = (
|
|
mock_config_dir
|
|
)
|
|
|
|
# Reset singleton
|
|
PostHogTelemetryClient.destroy_client()
|
|
|
|
client = PostHogTelemetryClient()
|
|
|
|
assert client.installation_id == existing_id
|
|
|
|
@patch("cua_core.telemetry.posthog.posthog")
|
|
@patch("cua_core.telemetry.posthog.Path")
|
|
def test_record_event_when_disabled(self, mock_path, mock_posthog, monkeypatch):
|
|
"""Test that events are not recorded when telemetry is disabled."""
|
|
from cua_core.telemetry.posthog import PostHogTelemetryClient
|
|
|
|
# Disable telemetry explicitly using the correct environment variable
|
|
monkeypatch.setenv("CUA_TELEMETRY_ENABLED", "false")
|
|
|
|
# Mock file system
|
|
mock_storage_dir = MagicMock()
|
|
mock_storage_dir.exists.return_value = False
|
|
mock_path.return_value.parent.parent = MagicMock()
|
|
mock_path.return_value.parent.parent.__truediv__.return_value = mock_storage_dir
|
|
|
|
# Reset singleton
|
|
PostHogTelemetryClient.destroy_client()
|
|
|
|
client = PostHogTelemetryClient()
|
|
client.record_event("test_event", {"key": "value"})
|
|
|
|
# PostHog capture should not be called at all when telemetry is disabled
|
|
mock_posthog.capture.assert_not_called()
|
|
|
|
@patch("cua_core.telemetry.posthog.posthog")
|
|
@patch("cua_core.telemetry.posthog.Path")
|
|
def test_record_event_when_enabled(self, mock_path, mock_posthog, monkeypatch):
|
|
"""Test that events are recorded when telemetry is enabled."""
|
|
from cua_core.telemetry.posthog import PostHogTelemetryClient
|
|
|
|
# Enable telemetry
|
|
monkeypatch.delenv("CUA_TELEMETRY_DISABLED", raising=False)
|
|
monkeypatch.setenv("CUA_TELEMETRY_ENABLED", "true")
|
|
|
|
# Mock file system
|
|
mock_storage_dir = MagicMock()
|
|
mock_storage_dir.exists.return_value = False
|
|
mock_path.return_value.parent.parent = MagicMock()
|
|
mock_path.return_value.parent.parent.__truediv__.return_value = mock_storage_dir
|
|
|
|
# Reset singleton
|
|
PostHogTelemetryClient.destroy_client()
|
|
|
|
client = PostHogTelemetryClient()
|
|
client.initialized = True # Pretend it's initialized
|
|
|
|
event_name = "test_event"
|
|
event_props = {"key": "value"}
|
|
client.record_event(event_name, event_props)
|
|
|
|
# PostHog capture should be called
|
|
assert mock_posthog.capture.call_count >= 1
|
|
|
|
@patch("cua_core.telemetry.posthog.posthog")
|
|
@patch("cua_core.telemetry.posthog.Path")
|
|
def test_singleton_pattern(self, mock_path, mock_posthog, disable_telemetry):
|
|
"""Test that get_client returns the same instance."""
|
|
from cua_core.telemetry.posthog import PostHogTelemetryClient
|
|
|
|
# Mock file system
|
|
mock_storage_dir = MagicMock()
|
|
mock_storage_dir.exists.return_value = False
|
|
mock_path.return_value.parent.parent = MagicMock()
|
|
mock_path.return_value.parent.parent.__truediv__.return_value = mock_storage_dir
|
|
|
|
# Reset singleton
|
|
PostHogTelemetryClient.destroy_client()
|
|
|
|
client1 = PostHogTelemetryClient.get_client()
|
|
client2 = PostHogTelemetryClient.get_client()
|
|
|
|
assert client1 is client2
|
|
|
|
|
|
class TestRecordEvent:
|
|
"""Test the public record_event function (SRP: Only tests public API)."""
|
|
|
|
@patch("cua_core.telemetry.posthog.PostHogTelemetryClient")
|
|
def test_record_event_calls_client(self, mock_client_class, disable_telemetry):
|
|
"""Test that record_event delegates to the client."""
|
|
from cua_core.telemetry import record_event
|
|
|
|
mock_client_instance = Mock()
|
|
mock_client_class.get_client.return_value = mock_client_instance
|
|
|
|
event_name = "test_event"
|
|
event_props = {"key": "value"}
|
|
|
|
record_event(event_name, event_props)
|
|
|
|
mock_client_instance.record_event.assert_called_once_with(event_name, event_props)
|
|
|
|
@patch("cua_core.telemetry.posthog.PostHogTelemetryClient")
|
|
def test_record_event_without_properties(self, mock_client_class, disable_telemetry):
|
|
"""Test that record_event works without properties."""
|
|
from cua_core.telemetry import record_event
|
|
|
|
mock_client_instance = Mock()
|
|
mock_client_class.get_client.return_value = mock_client_instance
|
|
|
|
event_name = "test_event"
|
|
|
|
record_event(event_name)
|
|
|
|
mock_client_instance.record_event.assert_called_once_with(event_name, {})
|
|
|
|
|
|
class TestDestroyTelemetryClient:
|
|
"""Test client destruction (SRP: Only tests cleanup)."""
|
|
|
|
@patch("cua_core.telemetry.posthog.PostHogTelemetryClient")
|
|
def test_destroy_client_calls_class_method(self, mock_client_class):
|
|
"""Test that destroy_telemetry_client delegates correctly."""
|
|
from cua_core.telemetry import destroy_telemetry_client
|
|
|
|
destroy_telemetry_client()
|
|
|
|
mock_client_class.destroy_client.assert_called_once()
|