+
Telemetry plane
{agentTelemetry ? "Mosquitto + Timescale" : "SSH diagnostic"}
diff --git a/apps/control-station/test/contourHealth.test.mjs b/apps/control-station/test/contourHealth.test.mjs
index b98a61d..0154ce9 100644
--- a/apps/control-station/test/contourHealth.test.mjs
+++ b/apps/control-station/test/contourHealth.test.mjs
@@ -33,7 +33,7 @@ test('actual registries determine node count and offline boards prevent all-onli
const calls=installFetch(t);
const snapshot=await readContourSnapshot(new AbortController().signal);
assert.equal(snapshot.workers[0].contour.display_name,'Test compute');
- assert.deepEqual(contourSummary(snapshot),{tone:'warning',label:'Часть узлов не на связи',online:2,total:3});
+ assert.deepEqual(contourSummary(snapshot),{tone:'warning',label:'Часть узлов не в сети',online:2,total:3});
assert.equal(calls.length,4);
});
test('failed checks discard previous online data and distinguish unknown from empty',async t=>{
@@ -54,5 +54,24 @@ test('invalid health never becomes reachable; identity mismatch and stale worker
});
test('an empty configured contour contains only the operator, with no fabricated worker',async t=>{
installFetch(t,{data:{'/api/v1/system/contours':{schema_version:'missioncore.compute-contour-catalog/v1',contours:[]},'/api/v1/fleet':{items:[]}}});
- assert.deepEqual(contourSummary(await readContourSnapshot(new AbortController().signal)),{tone:'success',label:'Все узлы на связи',online:1,total:1});
+ assert.deepEqual(contourSummary(await readContourSnapshot(new AbortController().signal)),{tone:'success',label:'Все узлы в сети',online:1,total:1});
+});
+
+test('receiver failure leaves worker unknown instead of declaring it offline',async t=>{
+ installFetch(t,{data:{'/api/v1/system/contours/test-compute/telemetry?history=90':{...worker,node:null,connection:{reachable:false,identity_matches:false,error_code:'telemetry-receiver-unavailable'}}}});
+ const snapshot=await readContourSnapshot(new AbortController().signal);
+ assert.equal(contourSummary(snapshot).label,'Состояние части узлов не подтверждено');
+ const {workerConnectionStatus}=await server.ssrLoadModule('/src/core/system/workerConnectionStatus.ts');
+ const status=workerConnectionStatus(snapshot.workers[0].telemetry);
+ assert.equal(status.state,'unknown');
+ assert.equal(status.label,'Не в сети');
+ assert.equal(workerConnectionStatus({...worker,node:null,connection:{reachable:false,error_code:'telemetry-agent-stale'}}).label,'Не в сети');
+ assert.equal(workerConnectionStatus(worker).state,'online');
+});
+
+test('a responding Core with recording capacity pressure remains reachable',async t=>{
+ installFetch(t,{data:{'/api/health':{...core,ok:false,components:{recording_cache:{status:'capacity-pressure'}}}}});
+ const snapshot=await readContourSnapshot(new AbortController().signal);
+ assert.equal(contourSummary(snapshot).online,2);
+ assert.equal(contourSummary(snapshot).label,'Mission Core работает с ограничениями');
});
diff --git a/src/k1link/web/system_telemetry_api.py b/src/k1link/web/system_telemetry_api.py
index 4bc630c..a68cce5 100644
--- a/src/k1link/web/system_telemetry_api.py
+++ b/src/k1link/web/system_telemetry_api.py
@@ -722,7 +722,7 @@ def run_worker_agent_probe(
):
return _failed_probe(
profile,
- "telemetry-agent-unavailable",
+ "telemetry-receiver-unavailable",
started,
source="agent-mqtt",
)
diff --git a/tests/test_system_telemetry_api.py b/tests/test_system_telemetry_api.py
index fd90b20..e7f0c2b 100644
--- a/tests/test_system_telemetry_api.py
+++ b/tests/test_system_telemetry_api.py
@@ -497,3 +497,19 @@ def test_profile_apply_fails_closed_on_wrong_node_and_keeps_old_profile(
assert error.value.status_code == 409
assert WorkerProfileStore(tmp_path / "system").read().revision == 0
assert not WorkerProfileStore(tmp_path / "system").path.exists()
+
+
+def test_receiver_failure_is_not_reported_as_worker_failure(monkeypatch):
+ import urllib.error
+
+ from k1link.web import system_telemetry_api as api
+
+ def unavailable(*args, **kwargs):
+ raise urllib.error.URLError("receiver down")
+
+ monkeypatch.setattr(api.urllib.request, "urlopen", unavailable)
+ result = api.run_worker_agent_probe(
+ api._profile_from_compute_contour(default_compute_contour())
+ )
+ assert result["error_code"] == "telemetry-receiver-unavailable"
+ assert result["reachable"] is False and result["raw"] is None