Server start events (#635)
1. Refactored the core usage logic from `arcade_cli` to `arcade_core` 2. Add "MCP server started" event As always, opt out by setting `ARCADE_USAGE_TRACKING` to 0.
This commit is contained in:
parent
66a126bba5
commit
49e53d2b33
19 changed files with 236 additions and 73 deletions
|
|
@ -5,11 +5,13 @@ from typing import Any
|
|||
from urllib.parse import parse_qs
|
||||
|
||||
import yaml
|
||||
from arcade_core.constants import (
|
||||
ARCADE_CONFIG_PATH,
|
||||
CREDENTIALS_FILE_PATH,
|
||||
)
|
||||
from rich.console import Console
|
||||
|
||||
from arcade_cli.constants import (
|
||||
ARCADE_CONFIG_PATH,
|
||||
CREDENTIALS_FILE_PATH,
|
||||
LOGIN_FAILED_HTML,
|
||||
LOGIN_SUCCESS_HTML,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,8 @@
|
|||
import os
|
||||
|
||||
PROD_CLOUD_HOST = "cloud.arcade.dev"
|
||||
PROD_ENGINE_HOST = "api.arcade.dev"
|
||||
LOCALHOST = "localhost"
|
||||
LOCAL_AUTH_CALLBACK_PORT = 9905
|
||||
|
||||
# The path to the directory containing the Arcade configuration files. Typically ~/.arcade
|
||||
ARCADE_CONFIG_PATH = os.path.join(os.path.expanduser(os.getenv("ARCADE_WORK_DIR", "~")), ".arcade")
|
||||
|
||||
# The path to the file containing the user's Arcade-related credentials (e.g., ARCADE_API_KEY).
|
||||
CREDENTIALS_FILE_PATH = os.path.join(ARCADE_CONFIG_PATH, "credentials.yaml")
|
||||
|
||||
# The path to the file containing usage analytics identity data.
|
||||
USAGE_FILE_PATH = os.path.join(ARCADE_CONFIG_PATH, "usage.json")
|
||||
|
||||
_style_block = b"""
|
||||
<link rel="icon" href="https://cdn.arcade.dev/favicons/favicon.ico" sizes="any">
|
||||
<link rel="apple-touch-icon" href="https://cdn.arcade.dev/favicons/apple-touch-icon.png">
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from typing import Optional
|
|||
|
||||
import click
|
||||
import typer
|
||||
from arcade_core.constants import CREDENTIALS_FILE_PATH
|
||||
from arcadepy import Arcade
|
||||
from rich.console import Console
|
||||
from rich.text import Text
|
||||
|
|
@ -19,7 +20,6 @@ import arcade_cli.secret as secret
|
|||
import arcade_cli.worker as worker
|
||||
from arcade_cli.authn import LocalAuthCallbackServer, check_existing_login
|
||||
from arcade_cli.constants import (
|
||||
CREDENTIALS_FILE_PATH,
|
||||
PROD_CLOUD_HOST,
|
||||
PROD_ENGINE_HOST,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@ from importlib import metadata
|
|||
from typing import Any
|
||||
|
||||
import typer
|
||||
from arcade_cli.constants import ARCADE_CONFIG_PATH
|
||||
from arcade_cli.usage.constants import (
|
||||
EVENT_CLI_COMMAND_EXECUTED,
|
||||
EVENT_CLI_COMMAND_FAILED,
|
||||
PROP_CLI_VERSION,
|
||||
PROP_COMMAND_NAME,
|
||||
)
|
||||
from arcade_core.constants import ARCADE_CONFIG_PATH
|
||||
from arcade_core.usage import UsageIdentity, UsageService, is_tracking_enabled
|
||||
from arcade_core.usage.constants import (
|
||||
PROP_DEVICE_MONOTONIC_END,
|
||||
PROP_DEVICE_MONOTONIC_START,
|
||||
PROP_DURATION_MS,
|
||||
|
|
@ -22,9 +25,6 @@ from arcade_cli.usage.constants import (
|
|||
PROP_RUNTIME_LANGUAGE,
|
||||
PROP_RUNTIME_VERSION,
|
||||
)
|
||||
from arcade_cli.usage.identity import UsageIdentity
|
||||
from arcade_cli.usage.usage_service import UsageService
|
||||
from arcade_cli.usage.utils import is_tracking_enabled
|
||||
from rich.console import Console
|
||||
from typer.core import TyperCommand, TyperGroup
|
||||
from typer.models import Context
|
||||
|
|
|
|||
|
|
@ -1,41 +1,7 @@
|
|||
"""Constants for usage tracking and analytics."""
|
||||
|
||||
# Event Names
|
||||
# CLI Specific Event Names
|
||||
EVENT_CLI_COMMAND_EXECUTED = "CLI execution succeeded"
|
||||
EVENT_CLI_COMMAND_FAILED = "CLI execution failed"
|
||||
|
||||
# Property Names
|
||||
# CLI Specific Property Names
|
||||
PROP_COMMAND_NAME = "command_name"
|
||||
PROP_CLI_VERSION = "cli_version"
|
||||
PROP_RUNTIME_LANGUAGE = "runtime_language"
|
||||
PROP_RUNTIME_VERSION = "runtime_version"
|
||||
PROP_OS_TYPE = "os_type"
|
||||
PROP_OS_RELEASE = "os_release"
|
||||
PROP_DURATION_MS = "duration_ms"
|
||||
PROP_ERROR_MESSAGE = "error_message"
|
||||
PROP_DEVICE_MONOTONIC_START = "device_start_timestamp"
|
||||
PROP_DEVICE_MONOTONIC_END = "device_end_timestamp"
|
||||
# Only used for anonymous usage
|
||||
PROP_PROCESS_PERSON_PROFILE = "$process_person_profile"
|
||||
|
||||
# Identity Keys
|
||||
KEY_ANON_ID = "anon_id"
|
||||
KEY_LINKED_PRINCIPAL_ID = "linked_principal_id"
|
||||
|
||||
# File Names
|
||||
USAGE_FILE_NAME = "usage.json"
|
||||
|
||||
# Environment Variables
|
||||
# how props are passed to the usage tracking subprocess
|
||||
ARCADE_USAGE_EVENT_DATA = "ARCADE_USAGE_EVENT_DATA"
|
||||
# whether usage tracking is enabled. 1 is enabled, 0 is disabled.
|
||||
ARCADE_USAGE_TRACKING = "ARCADE_USAGE_TRACKING"
|
||||
|
||||
# Timeouts and Limits (in seconds)
|
||||
TIMEOUT_POSTHOG_ALIAS = 2
|
||||
TIMEOUT_POSTHOG_CAPTURE = 5
|
||||
TIMEOUT_ARCADE_API = 2.0
|
||||
TIMEOUT_SUBPROCESS_EXIT = 10.0
|
||||
|
||||
# Retry Configuration
|
||||
MAX_RETRIES_POSTHOG = 1
|
||||
|
|
|
|||
6
libs/arcade-core/arcade_core/constants.py
Normal file
6
libs/arcade-core/arcade_core/constants.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import os
|
||||
|
||||
# The path to the directory containing the Arcade configuration files. Typically ~/.arcade
|
||||
ARCADE_CONFIG_PATH = os.path.join(os.path.expanduser(os.getenv("ARCADE_WORK_DIR", "~")), ".arcade")
|
||||
# The path to the file containing the user's Arcade-related credentials (e.g., ARCADE_API_KEY).
|
||||
CREDENTIALS_FILE_PATH = os.path.join(ARCADE_CONFIG_PATH, "credentials.yaml")
|
||||
5
libs/arcade-core/arcade_core/usage/__init__.py
Normal file
5
libs/arcade-core/arcade_core/usage/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from arcade_core.usage.identity import UsageIdentity
|
||||
from arcade_core.usage.usage_service import UsageService
|
||||
from arcade_core.usage.utils import is_tracking_enabled
|
||||
|
||||
__all__ = ["UsageIdentity", "UsageService", "is_tracking_enabled"]
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"""Entry point for detached usage tracking subprocess.
|
||||
|
||||
This module is invoked as `python -m arcade_cli.usage` and expects
|
||||
This module is invoked as `python -m arcade_core.usage` and expects
|
||||
event data to be passed via the ARCADE_USAGE_EVENT_DATA environment variable.
|
||||
"""
|
||||
|
||||
|
|
@ -8,14 +8,15 @@ import json
|
|||
import os
|
||||
import threading
|
||||
|
||||
from arcade_cli.usage.constants import (
|
||||
from posthog import Posthog
|
||||
|
||||
from arcade_core.usage.constants import (
|
||||
ARCADE_USAGE_EVENT_DATA,
|
||||
MAX_RETRIES_POSTHOG,
|
||||
PROP_PROCESS_PERSON_PROFILE,
|
||||
TIMEOUT_POSTHOG_CAPTURE,
|
||||
TIMEOUT_SUBPROCESS_EXIT,
|
||||
)
|
||||
from posthog import Posthog
|
||||
|
||||
|
||||
def _timeout_exit() -> None:
|
||||
34
libs/arcade-core/arcade_core/usage/constants.py
Normal file
34
libs/arcade-core/arcade_core/usage/constants.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Base (common) Property Names
|
||||
PROP_RUNTIME_LANGUAGE = "runtime_language"
|
||||
PROP_RUNTIME_VERSION = "runtime_version"
|
||||
PROP_OS_TYPE = "os_type"
|
||||
PROP_OS_RELEASE = "os_release"
|
||||
PROP_DURATION_MS = "duration_ms"
|
||||
PROP_ERROR_MESSAGE = "error_message"
|
||||
PROP_DEVICE_MONOTONIC_START = "device_start_timestamp"
|
||||
PROP_DEVICE_MONOTONIC_END = "device_end_timestamp"
|
||||
PROP_DEVICE_TIMESTAMP = "device_timestamp"
|
||||
# Only used for anonymous usage
|
||||
PROP_PROCESS_PERSON_PROFILE = "$process_person_profile"
|
||||
|
||||
# Identity Keys
|
||||
KEY_ANON_ID = "anon_id"
|
||||
KEY_LINKED_PRINCIPAL_ID = "linked_principal_id"
|
||||
|
||||
# File Names
|
||||
USAGE_FILE_NAME = "usage.json"
|
||||
|
||||
# Environment Variables
|
||||
# how props are passed to the usage tracking subprocess
|
||||
ARCADE_USAGE_EVENT_DATA = "ARCADE_USAGE_EVENT_DATA"
|
||||
# whether usage tracking is enabled. 1 is enabled, 0 is disabled.
|
||||
ARCADE_USAGE_TRACKING = "ARCADE_USAGE_TRACKING"
|
||||
|
||||
# Timeouts and Limits (in seconds)
|
||||
TIMEOUT_POSTHOG_ALIAS = 2
|
||||
TIMEOUT_POSTHOG_CAPTURE = 5
|
||||
TIMEOUT_ARCADE_API = 2.0
|
||||
TIMEOUT_SUBPROCESS_EXIT = 10.0
|
||||
|
||||
# Retry Configuration
|
||||
MAX_RETRIES_POSTHOG = 1
|
||||
|
|
@ -15,8 +15,9 @@ from typing import Any
|
|||
|
||||
import httpx
|
||||
import yaml
|
||||
from arcade_cli.constants import ARCADE_CONFIG_PATH, CREDENTIALS_FILE_PATH
|
||||
from arcade_cli.usage.constants import (
|
||||
|
||||
from arcade_core.constants import ARCADE_CONFIG_PATH, CREDENTIALS_FILE_PATH
|
||||
from arcade_core.usage.constants import (
|
||||
KEY_ANON_ID,
|
||||
KEY_LINKED_PRINCIPAL_ID,
|
||||
TIMEOUT_ARCADE_API,
|
||||
|
|
@ -3,17 +3,17 @@ import os
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
from arcade_cli.usage.constants import (
|
||||
from arcade_core.usage.constants import (
|
||||
ARCADE_USAGE_EVENT_DATA,
|
||||
MAX_RETRIES_POSTHOG,
|
||||
TIMEOUT_POSTHOG_ALIAS,
|
||||
)
|
||||
from arcade_cli.usage.utils import is_tracking_enabled
|
||||
from arcade_core.usage.utils import is_tracking_enabled
|
||||
|
||||
|
||||
class UsageService:
|
||||
def __init__(self) -> None:
|
||||
self.api_key = "phc_hIqUQyJpf2TP4COePO5jEpkGeUXipa7KqTEyDeRsTmB"
|
||||
self.api_key = "phc_g7OuFqZEAVwIgRdtnZkjvBpy9weQ1f9VJW6YP1SzQRF"
|
||||
self.host = "https://us.i.posthog.com"
|
||||
|
||||
def alias(self, previous_id: str, distinct_id: str) -> None:
|
||||
|
|
@ -71,7 +71,7 @@ class UsageService:
|
|||
"is_anon": is_anon,
|
||||
})
|
||||
|
||||
cmd = [sys.executable, "-m", "arcade_cli.usage"]
|
||||
cmd = [sys.executable, "-m", "arcade_core.usage"]
|
||||
|
||||
# Pass data via environment variable (works on all platforms)
|
||||
env = os.environ.copy()
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
|
||||
from arcade_cli.usage.constants import ARCADE_USAGE_TRACKING
|
||||
from arcade_core.usage.constants import ARCADE_USAGE_TRACKING
|
||||
|
||||
|
||||
def is_tracking_enabled() -> bool:
|
||||
|
|
@ -67,6 +67,29 @@ python -m arcade_mcp_server --host 0.0.0.0 --port 8080
|
|||
- [Discord Community](https://discord.gg/arcade-mcp)
|
||||
- [Documentation](https://docs.arcade.dev)
|
||||
|
||||
## Analytics & Privacy
|
||||
|
||||
*Arcade MCP Server* collects anonymous usage data to help us improve the service and debug issues. We track "MCP server start" events to understand server usage patterns and reliability.
|
||||
|
||||
#### What We Track
|
||||
|
||||
When the server starts, we collect the following information:
|
||||
- **Server configuration**: transport type (`http` or `stdio`), host, port
|
||||
- **Server metadata**: tool count, server version
|
||||
- **Runtime environment**: Python version, OS type and release
|
||||
- **Timing**: device timestamp
|
||||
- **Errors**: error messages (if startup fails)
|
||||
|
||||
#### Privacy
|
||||
|
||||
- For **anonymous users**: Events are tracked with an anonymous ID and no user profile is created
|
||||
- For **authenticated users**: Events are linked to your account to help us provide better support
|
||||
- **No sensitive data** (credentials, tool inputs/outputs, or personal information) is ever collected
|
||||
|
||||
#### Opt Out
|
||||
|
||||
To disable usage tracking, set the environment variable ARCADE_USAGE_TRACKING to 0.
|
||||
|
||||
## License
|
||||
|
||||
Arcade MCP Server is open source software licensed under the MIT license.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ from arcade_mcp_server.exceptions import ServerError
|
|||
from arcade_mcp_server.server import MCPServer
|
||||
from arcade_mcp_server.settings import MCPSettings, ServerSettings
|
||||
from arcade_mcp_server.types import Prompt, PromptMessage, Resource
|
||||
from arcade_mcp_server.usage import ServerTracker
|
||||
from arcade_mcp_server.worker import create_arcade_mcp
|
||||
|
||||
P = ParamSpec("P")
|
||||
|
|
@ -249,6 +250,13 @@ class MCPApp:
|
|||
elif transport == "stdio":
|
||||
from arcade_mcp_server.__main__ import run_stdio_server
|
||||
|
||||
tracker = ServerTracker()
|
||||
tracker.track_server_start(
|
||||
transport="stdio",
|
||||
host=None,
|
||||
port=None,
|
||||
tool_count=len(self._catalog),
|
||||
)
|
||||
asyncio.run(
|
||||
run_stdio_server(
|
||||
catalog=self._catalog,
|
||||
|
|
@ -328,6 +336,13 @@ class MCPApp:
|
|||
**self.server_kwargs,
|
||||
)
|
||||
|
||||
tracker = ServerTracker()
|
||||
tracker.track_server_start(
|
||||
transport="http",
|
||||
host=host,
|
||||
port=port,
|
||||
tool_count=len(self._catalog),
|
||||
)
|
||||
uvicorn.run(
|
||||
app,
|
||||
host=host,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
from arcade_mcp_server.usage.server_tracker import ServerTracker
|
||||
|
||||
__all__ = ["ServerTracker"]
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# MCP Server Specific Event Names
|
||||
EVENT_MCP_SERVER_STARTED = "MCP server started"
|
||||
|
||||
# MCP Server Specific Property Names
|
||||
PROP_TRANSPORT = "transport"
|
||||
PROP_HOST = "host"
|
||||
PROP_PORT = "port"
|
||||
PROP_TOOL_COUNT = "tool_count"
|
||||
PROP_MCP_SERVER_VERSION = "arcade_mcp_server_version"
|
||||
109
libs/arcade-mcp-server/arcade_mcp_server/usage/server_tracker.py
Normal file
109
libs/arcade-mcp-server/arcade_mcp_server/usage/server_tracker.py
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import platform
|
||||
import sys
|
||||
import time
|
||||
from importlib import metadata
|
||||
|
||||
from arcade_core.usage import UsageIdentity, UsageService, is_tracking_enabled
|
||||
from arcade_core.usage.constants import (
|
||||
PROP_DEVICE_TIMESTAMP,
|
||||
PROP_OS_RELEASE,
|
||||
PROP_OS_TYPE,
|
||||
PROP_RUNTIME_LANGUAGE,
|
||||
PROP_RUNTIME_VERSION,
|
||||
)
|
||||
|
||||
from arcade_mcp_server.usage.constants import (
|
||||
EVENT_MCP_SERVER_STARTED,
|
||||
PROP_HOST,
|
||||
PROP_MCP_SERVER_VERSION,
|
||||
PROP_PORT,
|
||||
PROP_TOOL_COUNT,
|
||||
PROP_TRANSPORT,
|
||||
)
|
||||
|
||||
|
||||
class ServerTracker:
|
||||
"""Tracks MCP server events for usage analytics.
|
||||
|
||||
To opt out, set the ARCADE_USAGE_TRACKING environment variable to 0.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.usage_service = UsageService()
|
||||
self.identity = UsageIdentity()
|
||||
self._mcp_server_version: str | None = None
|
||||
self._runtime_version: str | None = None
|
||||
|
||||
@property
|
||||
def mcp_server_version(self) -> str:
|
||||
"""Get the version of arcade_mcp_server package"""
|
||||
if self._mcp_server_version is None:
|
||||
try:
|
||||
self._mcp_server_version = metadata.version("arcade-mcp-server")
|
||||
except Exception:
|
||||
self._mcp_server_version = "unknown"
|
||||
return self._mcp_server_version
|
||||
|
||||
@property
|
||||
def runtime_version(self) -> str:
|
||||
"""Get the version of the Python runtime"""
|
||||
if self._runtime_version is None:
|
||||
version_info = sys.version_info
|
||||
self._runtime_version = (
|
||||
f"{version_info.major}.{version_info.minor}.{version_info.micro}"
|
||||
)
|
||||
return self._runtime_version
|
||||
|
||||
@property
|
||||
def user_id(self) -> str:
|
||||
"""Get the distinct_id based on developer's authentication state"""
|
||||
return self.identity.get_distinct_id()
|
||||
|
||||
def track_server_start(
|
||||
self,
|
||||
transport: str,
|
||||
host: str | None,
|
||||
port: int | None,
|
||||
tool_count: int,
|
||||
) -> None:
|
||||
"""Track MCP server start event.
|
||||
|
||||
Args:
|
||||
transport: The transport type ("http" or "stdio")
|
||||
host: The host address (None for stdio)
|
||||
port: The port number (None for stdio)
|
||||
tool_count: The number of tools available at server start
|
||||
"""
|
||||
if not is_tracking_enabled():
|
||||
return
|
||||
|
||||
# Check if aliasing needed (user authenticated but not yet linked)
|
||||
if self.identity.should_alias():
|
||||
principal_id = self.identity.get_principal_id()
|
||||
if principal_id:
|
||||
self.usage_service.alias(
|
||||
previous_id=self.identity.anon_id, distinct_id=principal_id
|
||||
)
|
||||
self.identity.set_linked_principal_id(principal_id)
|
||||
|
||||
properties: dict[str, str | int | float] = {
|
||||
PROP_TRANSPORT: transport,
|
||||
PROP_TOOL_COUNT: tool_count,
|
||||
PROP_MCP_SERVER_VERSION: self.mcp_server_version,
|
||||
PROP_RUNTIME_LANGUAGE: "python",
|
||||
PROP_RUNTIME_VERSION: self.runtime_version,
|
||||
PROP_OS_TYPE: platform.system(),
|
||||
PROP_OS_RELEASE: platform.release(),
|
||||
PROP_DEVICE_TIMESTAMP: time.monotonic(),
|
||||
}
|
||||
|
||||
# HTTP Streamable specific props
|
||||
if host is not None:
|
||||
properties[PROP_HOST] = host
|
||||
if port is not None:
|
||||
properties[PROP_PORT] = port
|
||||
|
||||
is_anon = self.user_id == self.identity.anon_id
|
||||
self.usage_service.capture(
|
||||
EVENT_MCP_SERVER_STARTED, self.user_id, properties=properties, is_anon=is_anon
|
||||
)
|
||||
|
|
@ -5,7 +5,7 @@ from unittest.mock import MagicMock, patch
|
|||
|
||||
import pytest
|
||||
import yaml
|
||||
from arcade_cli.usage.identity import UsageIdentity
|
||||
from arcade_core.usage import UsageIdentity
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -15,8 +15,8 @@ def temp_config_path(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
|
|||
config_dir.mkdir()
|
||||
credentials_file = config_dir / "credentials.yaml"
|
||||
|
||||
monkeypatch.setattr("arcade_cli.usage.identity.ARCADE_CONFIG_PATH", str(config_dir))
|
||||
monkeypatch.setattr("arcade_cli.usage.identity.CREDENTIALS_FILE_PATH", str(credentials_file))
|
||||
monkeypatch.setattr("arcade_core.usage.identity.ARCADE_CONFIG_PATH", str(config_dir))
|
||||
monkeypatch.setattr("arcade_core.usage.identity.CREDENTIALS_FILE_PATH", str(credentials_file))
|
||||
|
||||
return config_dir
|
||||
|
||||
|
|
@ -154,7 +154,7 @@ class TestGetDistinctId:
|
|||
|
||||
assert distinct_id == "persisted-user-123"
|
||||
|
||||
@patch("arcade_cli.usage.identity.UsageIdentity.get_principal_id")
|
||||
@patch("arcade_core.usage.identity.UsageIdentity.get_principal_id")
|
||||
def test_returns_principal_id_from_api_when_not_persisted(
|
||||
self, mock_get_principal: MagicMock, identity: UsageIdentity
|
||||
) -> None:
|
||||
|
|
@ -166,7 +166,7 @@ class TestGetDistinctId:
|
|||
assert distinct_id == "api-user-456"
|
||||
mock_get_principal.assert_called_once()
|
||||
|
||||
@patch("arcade_cli.usage.identity.UsageIdentity.get_principal_id")
|
||||
@patch("arcade_core.usage.identity.UsageIdentity.get_principal_id")
|
||||
def test_returns_anon_id_when_not_authenticated(
|
||||
self, mock_get_principal: MagicMock, identity: UsageIdentity
|
||||
) -> None:
|
||||
|
|
@ -257,7 +257,7 @@ class TestGetPrincipalId:
|
|||
class TestShouldAlias:
|
||||
"""Tests for should_alias() method."""
|
||||
|
||||
@patch("arcade_cli.usage.identity.UsageIdentity.get_principal_id")
|
||||
@patch("arcade_core.usage.identity.UsageIdentity.get_principal_id")
|
||||
def test_returns_true_when_authenticated_but_not_linked(
|
||||
self, mock_get_principal: MagicMock, identity: UsageIdentity
|
||||
) -> None:
|
||||
|
|
@ -268,7 +268,7 @@ class TestShouldAlias:
|
|||
|
||||
assert should_alias is True
|
||||
|
||||
@patch("arcade_cli.usage.identity.UsageIdentity.get_principal_id")
|
||||
@patch("arcade_core.usage.identity.UsageIdentity.get_principal_id")
|
||||
def test_returns_false_when_already_linked(
|
||||
self, mock_get_principal: MagicMock, identity: UsageIdentity, temp_config_path: Path
|
||||
) -> None:
|
||||
|
|
@ -285,7 +285,7 @@ class TestShouldAlias:
|
|||
|
||||
assert should_alias is False
|
||||
|
||||
@patch("arcade_cli.usage.identity.UsageIdentity.get_principal_id")
|
||||
@patch("arcade_core.usage.identity.UsageIdentity.get_principal_id")
|
||||
def test_returns_false_when_not_authenticated(
|
||||
self, mock_get_principal: MagicMock, identity: UsageIdentity
|
||||
) -> None:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import pytest
|
||||
from arcade_cli.usage.utils import is_tracking_enabled
|
||||
from arcade_core.usage import is_tracking_enabled
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
Loading…
Reference in a new issue