FE+BE: performance improvements

This commit is contained in:
jokob-sk committed 2026-09-14 08:29:02 +10:00
1 parent cff3ddfc38
commit f52de6cbdc
23 files changed
+428 -31

No files matched your search

@@ -208,6 +208,53 @@ def test_devices_by_status(client, api_token, test_mac):
delete_dummy(client, api_token, test_mac)
def test_devices_by_status_pagination(client, api_token):
"""limit/offset must page through the same set ORDER BY devMac gives
unpaginated, with no gaps or duplicates, and must reject invalid values.
Doesn't assume an otherwise-empty DB: reconstructs the full 'my' list from
pages and compares it to the unpaginated response instead of asserting
exact positions for the 3 dummies.
"""
macs = [f"aa:bb:cc:dd:ee:0{i}" for i in (1, 2, 3)]
for mac in macs:
create_dummy(client, api_token, mac)
try:
full_resp = client.get("/devices/by-status?status=my", headers=auth_headers(api_token))
assert full_resp.status_code == 200
full_macs = [d["id"] for d in full_resp.json]
assert set(macs).issubset(set(full_macs))
# Page through the full set in halves and confirm the reassembled
# list matches the unpaginated one exactly (no gaps/duplicates).
total = len(full_macs)
half = (total + 1) // 2
page1 = client.get(
f"/devices/by-status?status=my&limit={half}&offset=0",
headers=auth_headers(api_token),
).json
page2 = client.get(
f"/devices/by-status?status=my&limit={total - half}&offset={half}",
headers=auth_headers(api_token),
).json
paged_macs = [d["id"] for d in page1] + [d["id"] for d in page2]
assert paged_macs == full_macs
# Invalid limit/offset are rejected, not silently clamped.
resp_bad_limit = client.get(
"/devices/by-status?status=my&limit=0", headers=auth_headers(api_token)
)
assert resp_bad_limit.status_code == 422
resp_bad_offset = client.get(
"/devices/by-status?status=my&offset=-1", headers=auth_headers(api_token)
)
assert resp_bad_offset.status_code == 422
finally:
for mac in macs:
delete_dummy(client, api_token, mac)
def test_delete_test_devices(client, api_token):
# Delete by MAC
+42 -1
View File
@@ -5,7 +5,7 @@ import pytest
INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/server/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from helper import get_setting_value, count_children_by_parent_mac # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@@ -79,6 +79,47 @@ def test_graphql_post_devices(client, api_token):
assert isinstance(data["devices"]["count"], int)
def test_graphql_devices_parent_children_count_matches_recount(client, api_token):
"""devParentChildrenCount for every returned device must match an independent
recount of the same response (regression guard for the O(n^2)->O(n) rewrite of
resolve_devices()/count_children_by_parent_mac() in graphql_endpoint.py/helper.py).
Not seeded against a freshly-created fixture pair: table_devices.json (what
resolve_devices() reads) is only refreshed by the periodic update_api() loop,
not synchronously on a POST /device/<mac> create - a create-then-query test
would be flaky against snapshot staleness. Recomputing from the response
itself avoids that while still exercising the real resolver wiring.
"""
query = {
"query": """
{
devices {
devices {
devMac
devParentMAC
devParentChildrenCount
}
count
}
}
"""
}
resp = client.post("/graphql", json=query, headers=auth_headers(api_token))
assert resp.status_code == 200
devices = resp.get_json()["data"]["devices"]["devices"]
expected_counts = count_children_by_parent_mac(
[{"devParentMAC": d["devParentMAC"]} for d in devices]
)
for device in devices:
expected = expected_counts.get((device["devMac"] or "").strip(), 0)
assert device["devParentChildrenCount"] == expected, (
f"devMac={device['devMac']}: expected {expected}, "
f"got {device['devParentChildrenCount']}"
)
# --- SETTINGS TESTS ---
def test_graphql_post_settings(client, api_token):
"""POST /graphql should return settings data"""
+68
View File
@@ -0,0 +1,68 @@
"""
Unit tests for helper.py's count_children_by_parent_mac().
Tests verify the O(n) bucket-count replacement for the old per-device
O(n) scan (get_number_of_children) produces identical results.
"""
import sys
import os
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/server/plugins", f"{INSTALL_PATH}/server"])
from helper import count_children_by_parent_mac # noqa: E402
class TestCountChildrenByParentMac:
"""Test suite for count_children_by_parent_mac()"""
def test_empty_list_returns_empty_dict(self):
assert count_children_by_parent_mac([]) == {}
def test_parent_with_two_children_and_a_grandchild(self):
devices = [
{"devMac": "aa:aa:aa:aa:aa:aa", "devParentMAC": ""},
{"devMac": "bb:bb:bb:bb:bb:bb", "devParentMAC": "aa:aa:aa:aa:aa:aa"},
{"devMac": "cc:cc:cc:cc:cc:cc", "devParentMAC": "aa:aa:aa:aa:aa:aa"},
{"devMac": "dd:dd:dd:dd:dd:dd", "devParentMAC": "bb:bb:bb:bb:bb:bb"},
]
counts = count_children_by_parent_mac(devices)
assert counts["aa:aa:aa:aa:aa:aa"] == 2
assert counts["bb:bb:bb:bb:bb:bb"] == 1
# leaf devices are absent from the dict, not present with a 0 value
assert "cc:cc:cc:cc:cc:cc" not in counts
assert "dd:dd:dd:dd:dd:dd" not in counts
def test_missing_or_empty_parent_mac_excluded(self):
devices = [
{"devMac": "aa:aa:aa:aa:aa:aa", "devParentMAC": ""},
{"devMac": "bb:bb:bb:bb:bb:bb"}, # devParentMAC key absent entirely
]
assert count_children_by_parent_mac(devices) == {}
def test_result_independent_of_input_order(self):
devices = [
{"devMac": "bb:bb:bb:bb:bb:bb", "devParentMAC": "aa:aa:aa:aa:aa:aa"},
{"devMac": "aa:aa:aa:aa:aa:aa", "devParentMAC": ""},
{"devMac": "cc:cc:cc:cc:cc:cc", "devParentMAC": "aa:aa:aa:aa:aa:aa"},
]
assert count_children_by_parent_mac(devices) == count_children_by_parent_mac(
list(reversed(devices))
)
def test_lookup_uses_stripped_devmac_like_original(self):
# Caller strips devMac before lookup (graphql_endpoint.py); the count
# dict's keys must match that stripped form for the lookup to hit.
devices = [
{"devMac": "aa:aa:aa:aa:aa:aa", "devParentMAC": ""},
{"devMac": "bb:bb:bb:bb:bb:bb", "devParentMAC": " aa:aa:aa:aa:aa:aa "},
]
counts = count_children_by_parent_mac(devices)
assert counts["aa:aa:aa:aa:aa:aa".strip()] == 1