32a1885f0d
支持随时暂停/恢复实时监测并保留槽位;TTL 到期自动暂停不占删槽;修复到期竞态导致空槽与唯一约束冲突;探针 parse_error 与中文错误;移除失败 snackbar。
146 lines
4.2 KiB
Python
146 lines
4.2 KiB
Python
"""Watch metrics unit tests — rates, redis IO skip, failure samples."""
|
|
|
|
import pytest
|
|
|
|
from server.utils.watch_metrics import (
|
|
WatchProbeSample,
|
|
apply_rates_to_sample,
|
|
compute_rates,
|
|
failure_sample,
|
|
merge_redis_and_ssh,
|
|
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
|