feat: add telemetry collection functionality

This commit is contained in:
dido committed 2026-09-24 14:54:17 +03:00
1 parent 5141bcdb0f
commit 5a285ab1a6
10 files changed
+412 -5

No files matched your search

+100
View File
@@ -0,0 +1,100 @@
import hashlib
import hmac
import unittest
import urllib.error
from unittest.mock import patch
import webssh_backend.telemetry as telemetry
from webssh_backend.telemetry import TelemetryClient, canonical_payload, sign_request
class TelemetrySigningTests(unittest.TestCase):
def test_canonical_payload_has_exact_newlines_and_body_hash(self):
body = b'{"version":"1.0.0"}'
expected = "POST\n/api/v1/telemetry/heartbeat\n1700000000\nnonce\nrequest\n" + hashlib.sha256(body).hexdigest()
self.assertEqual(canonical_payload("post", "/api/v1/telemetry/heartbeat", 1700000000, "nonce", "request", body), expected)
self.assertFalse(expected.endswith("\n"))
def test_signature_is_lowercase_hmac_sha256(self):
body = b"{}"
canonical = canonical_payload("POST", "/path", 1, "n", "r", body)
expected = hmac.new(b"secret", canonical.encode(), hashlib.sha256).hexdigest()
self.assertEqual(sign_request("secret", "POST", "/path", 1, "n", "r", body), expected)
self.assertEqual(len(expected), 64)
self.assertEqual(expected, expected.lower())
class FakeResponse:
status = 201
headers = {}
def __init__(self, payload):
self.payload = payload
def __enter__(self):
return self
def __exit__(self, *args):
return False
def read(self):
import json
return json.dumps(self.payload).encode()
class FakeState:
def __init__(self, state=None):
self.state = state or {}
def get_telemetry_state(self):
return dict(self.state) if self.state else None
def save_telemetry_state(self, state):
self.state = dict(state)
return True
class TelemetryClientTests(unittest.TestCase):
def test_disabled_telemetry_does_not_call_opener(self):
called = []
def opener(request, timeout):
called.append(request)
return FakeResponse({"data": {}})
with patch.object(telemetry, "creds_manager", FakeState({"credential_secret": "secret"})), patch.object(telemetry, "STRIX_TELEMETRY_ENABLED", False), patch.object(telemetry, "STRIX_TELEMETRY_URL", "https://telemetry.test"):
TelemetryClient(opener).uninstall()
self.assertEqual(called, [])
def test_registration_persists_installation_and_credential(self):
state = FakeState()
opener = lambda request, timeout: FakeResponse({"data": {
"installation_uuid": state.state["installation_uuid"],
"credential": "credential-id.secret-value",
"expires_at": "2099-01-01T00:00:00Z",
}})
with patch.object(telemetry, "creds_manager", state), patch.object(telemetry, "STRIX_TELEMETRY_URL", "https://telemetry.test"):
TelemetryClient(opener).register()
self.assertTrue(state.state["installation_uuid"])
self.assertEqual(state.state["credential_id"], "credential-id")
self.assertEqual(state.state["credential_secret"], "secret-value")
def test_retry_reuses_request_id_and_regenerates_nonce(self):
state = FakeState({
"installation_uuid": "installation",
"credential_id": "credential-id",
"credential_secret": "secret-value",
})
requests = []
def opener(request, timeout):
requests.append(request)
if len(requests) == 1:
raise urllib.error.URLError("temporary failure")
return FakeResponse({"data": {"accepted": True}})
with patch.object(telemetry, "creds_manager", state), patch.object(telemetry, "STRIX_TELEMETRY_URL", "https://telemetry.test"), patch.object(telemetry.time, "sleep"):
TelemetryClient(opener).heartbeat()
self.assertEqual(len(requests), 2)
self.assertEqual(requests[0].headers["X-strix-request-id"], requests[1].headers["X-strix-request-id"])
self.assertNotEqual(requests[0].headers["X-strix-nonce"], requests[1].headers["X-strix-nonce"])