Files
Nexus/tests/test_watch_metrics.py
T
Nexus Agent 647ebcb673 feat(watch): 监测槽分钟时长档、指标进度条与负载显示
默认 30 分钟,支持 30分/1时/2时/8时/24时;槽卡展示 CPU/内存/硬盘进度条与 1 分钟负载;API 改用 ttl_minutes。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-12 04:34:52 +08:00

156 lines
4.6 KiB
Python

"""Watch metrics unit tests — rates, redis IO skip, failure samples."""
import pytest
from server.utils.watch_metrics import (
WatchProbeSample,
WATCH_PIN_TTL_DEFAULT_MINUTES,
apply_rates_to_sample,
compute_rates,
failure_sample,
merge_redis_and_ssh,
normalize_watch_ttl_minutes,
parse_ssh_watch_payload,
redis_sample_has_io,
redis_sample_has_basic_metrics,
sanitize_processes,
PROBE_OK,
)
def test_compute_rates_basic():
from server.utils.watch_metrics import _RateState
prev = _RateState(
net_bytes_sent=1000,
net_bytes_recv=2000,
disk_read_bytes=500,
disk_write_bytes=800,
recorded_at_ts=100.0,
)
up, down, dr, dw = compute_rates(
prev,
net_bytes_sent=1500,
net_bytes_recv=2600,
disk_read_bytes=700,
disk_write_bytes=900,
now_ts=105.0,
)
assert up == 100
assert down == 120
assert dr == 40
assert dw == 20
def test_compute_rates_no_prev():
up, down, dr, dw = compute_rates(None, net_bytes_sent=1, net_bytes_recv=1, disk_read_bytes=1, disk_write_bytes=1, now_ts=10)
assert up is None and down is None and dr is None and dw is None
def test_redis_sample_has_basic_metrics():
basic = WatchProbeSample(probe_status=PROBE_OK, source="redis", cpu_pct=10)
assert redis_sample_has_basic_metrics(basic) is True
assert redis_sample_has_io(basic) is False
assert redis_sample_has_basic_metrics(None) is False
def test_parse_json_stdout_with_prefix_noise():
from server.infrastructure.ssh.remote_probe import _parse_json_stdout
stdout = "Warning: something\nOK {\"status\": \"ok\", \"cpu_usage\": 1.0}\n"
parsed = _parse_json_stdout(stdout)
assert parsed is not None
assert parsed.get("status") == "ok"
def test_redis_sample_has_io():
complete = WatchProbeSample(
probe_status=PROBE_OK,
source="redis",
cpu_pct=10,
mem_pct=20,
net_bytes_sent=1,
net_bytes_recv=2,
disk_read_bytes=3,
disk_write_bytes=4,
)
incomplete = WatchProbeSample(probe_status=PROBE_OK, source="redis", cpu_pct=10, mem_pct=20)
assert redis_sample_has_io(complete) is True
assert redis_sample_has_io(incomplete) is False
def test_failure_sample_null_metrics():
s = failure_sample(status="ssh_timeout", source="ssh", error="timeout")
assert s.cpu_pct is None
assert s.probe_status == "ssh_timeout"
def test_parse_ssh_watch_payload():
data = {
"cpu_usage": 12.4,
"mem_usage": 45.2,
"disk_usage": 61.0,
"load_avg": [0.8, 0.9, 1.0],
"net_io": {"bytes_sent": 100, "bytes_recv": 200},
"disk_io": {"read_bytes": 300, "write_bytes": 400},
"top_processes": [{"pid": 1, "name": "nginx", "cpu_pct": 2.0, "mem_pct": 1.0}],
}
sample = parse_ssh_watch_payload(data)
assert sample.cpu_pct == 12
assert sample.net_bytes_sent == 100
assert sample.processes_json and sample.processes_json[0]["name"] == "nginx"
def test_sanitize_processes_truncates_name():
raw = [{"pid": 1, "name": "x" * 100, "cpu_pct": 1}]
out = sanitize_processes(raw)
assert out and len(out[0]["name"]) == 80
def test_apply_rates_to_sample():
from server.utils.watch_metrics import _RateState
sample = WatchProbeSample(
probe_status=PROBE_OK,
source="ssh",
net_bytes_sent=2000,
net_bytes_recv=3000,
disk_read_bytes=4000,
disk_write_bytes=5000,
)
prev = _RateState(
net_bytes_sent=1000,
net_bytes_recv=2000,
disk_read_bytes=3000,
disk_write_bytes=4000,
recorded_at_ts=0.0,
)
apply_rates_to_sample(sample, prev, 10.0)
assert sample.net_up_bps == 100
assert sample.net_down_bps == 100
def test_merge_redis_and_ssh_prefers_ssh_io():
redis_s = WatchProbeSample(probe_status=PROBE_OK, source="redis", cpu_pct=10, mem_pct=20)
ssh_s = WatchProbeSample(
probe_status=PROBE_OK,
source="ssh",
cpu_pct=11,
net_bytes_sent=99,
net_bytes_recv=88,
disk_read_bytes=77,
disk_write_bytes=66,
)
merged = merge_redis_and_ssh(redis_s, ssh_s)
assert merged.source == "mixed"
assert merged.cpu_pct == 10
assert merged.net_bytes_sent == 99
def test_normalize_watch_ttl_minutes():
assert normalize_watch_ttl_minutes(30) == 30
assert normalize_watch_ttl_minutes(60) == 60
assert normalize_watch_ttl_minutes(1440) == 1440
assert normalize_watch_ttl_minutes(8) == 480
assert normalize_watch_ttl_minutes(99) == WATCH_PIN_TTL_DEFAULT_MINUTES