diff --git a/src/basic_memory/cli/analytics.py b/src/basic_memory/cli/analytics.py index 063ca198..5b93a7cd 100644 --- a/src/basic_memory/cli/analytics.py +++ b/src/basic_memory/cli/analytics.py @@ -6,10 +6,10 @@ no cookies. Respects the same opt-out mechanisms as promo messaging. Events are fire-and-forget — analytics never blocks or breaks the CLI. -Setup: - Set these environment variables (or leave unset to disable): - BASIC_MEMORY_UMAMI_HOST — Umami instance URL (e.g. https://analytics.basicmemory.com) - BASIC_MEMORY_UMAMI_SITE_ID — Website ID from Umami dashboard +Defaults point to the Basic Memory Umami Cloud instance. Override via: + BASIC_MEMORY_UMAMI_HOST — Custom Umami instance URL + BASIC_MEMORY_UMAMI_SITE_ID — Custom Website ID +Opt out entirely with BASIC_MEMORY_NO_PROMOS=1. """ import json @@ -22,16 +22,19 @@ import basic_memory # --------------------------------------------------------------------------- -# Configuration — read from environment so nothing is hard-coded in source +# Configuration — defaults baked in, overridable via environment # --------------------------------------------------------------------------- +_DEFAULT_UMAMI_HOST = "https://cloud.umami.is" +_DEFAULT_UMAMI_SITE_ID = "f6479898-ebaf-4e60-bce2-6dc60a3f6c5c" + def _umami_host() -> Optional[str]: - return os.getenv("BASIC_MEMORY_UMAMI_HOST", "").strip() or None + return os.getenv("BASIC_MEMORY_UMAMI_HOST", "").strip() or _DEFAULT_UMAMI_HOST def _umami_site_id() -> Optional[str]: - return os.getenv("BASIC_MEMORY_UMAMI_SITE_ID", "").strip() or None + return os.getenv("BASIC_MEMORY_UMAMI_SITE_ID", "").strip() or _DEFAULT_UMAMI_SITE_ID def _analytics_disabled() -> bool: diff --git a/tests/cli/test_analytics.py b/tests/cli/test_analytics.py index d24282b8..b43c837c 100644 --- a/tests/cli/test_analytics.py +++ b/tests/cli/test_analytics.py @@ -9,6 +9,8 @@ from basic_memory.cli.analytics import ( track, _analytics_disabled, _is_configured, + _umami_host, + _umami_site_id, EVENT_PROMO_SHOWN, EVENT_CLOUD_LOGIN_STARTED, EVENT_CLOUD_LOGIN_SUCCESS, @@ -37,20 +39,17 @@ class TestIsConfigured: monkeypatch.setenv("BASIC_MEMORY_UMAMI_SITE_ID", "abc-123") assert _is_configured() is True - def test_not_configured_when_host_missing(self, monkeypatch): + def test_configured_by_default(self, monkeypatch): + """Defaults are baked in — always configured unless explicitly emptied.""" monkeypatch.delenv("BASIC_MEMORY_UMAMI_HOST", raising=False) - monkeypatch.setenv("BASIC_MEMORY_UMAMI_SITE_ID", "abc-123") - assert _is_configured() is False - - def test_not_configured_when_site_id_missing(self, monkeypatch): - monkeypatch.setenv("BASIC_MEMORY_UMAMI_HOST", "https://analytics.example.com") monkeypatch.delenv("BASIC_MEMORY_UMAMI_SITE_ID", raising=False) - assert _is_configured() is False + assert _is_configured() is True - def test_not_configured_when_empty_strings(self, monkeypatch): - monkeypatch.setenv("BASIC_MEMORY_UMAMI_HOST", "") - monkeypatch.setenv("BASIC_MEMORY_UMAMI_SITE_ID", "") - assert _is_configured() is False + def test_env_override_takes_precedence(self, monkeypatch): + monkeypatch.setenv("BASIC_MEMORY_UMAMI_HOST", "https://custom.example.com") + monkeypatch.setenv("BASIC_MEMORY_UMAMI_SITE_ID", "custom-id") + assert _umami_host() == "https://custom.example.com" + assert _umami_site_id() == "custom-id" class TestTrack: @@ -60,13 +59,15 @@ class TestTrack: track("test-event") mock_thread.assert_not_called() - def test_no_op_when_not_configured(self, monkeypatch): + def test_sends_when_using_defaults(self, monkeypatch): + """With baked-in defaults, track() fires even without env vars.""" monkeypatch.delenv("BASIC_MEMORY_NO_PROMOS", raising=False) monkeypatch.delenv("BASIC_MEMORY_UMAMI_HOST", raising=False) monkeypatch.delenv("BASIC_MEMORY_UMAMI_SITE_ID", raising=False) with patch("basic_memory.cli.analytics.threading.Thread") as mock_thread: + mock_thread.return_value = MagicMock() track("test-event") - mock_thread.assert_not_called() + mock_thread.assert_called_once() def test_sends_event_when_configured(self, monkeypatch): monkeypatch.delenv("BASIC_MEMORY_NO_PROMOS", raising=False)