From ebdf6cb55b1cb144fec06758f8d2f2c541715013 Mon Sep 17 00:00:00 2001 From: Pouzor Date: Sun, 10 May 2026 20:33:49 +0200 Subject: [PATCH] fix(scan): default check_method='ping' for approved devices Approving a pending device created a Node with check_method=NULL, so the status scheduler silently skipped it (scheduler.py:64 filters falsy check_method) and the bubble stayed grey forever. Default to 'ping' when the device has an IP, in both single and bulk approve paths. Also persist caller-supplied check_method/check_target in the single-approve path. Add regression tests asserting default check_method='ping' after approval for both endpoints. --- backend/app/api/routes/scan.py | 7 +++++++ backend/tests/test_scan.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/backend/app/api/routes/scan.py b/backend/app/api/routes/scan.py index 476ec23..67156ea 100644 --- a/backend/app/api/routes/scan.py +++ b/backend/app/api/routes/scan.py @@ -133,6 +133,9 @@ async def bulk_approve_devices( status="unknown", services=device.services or [], ieee_address=device.ieee_address, + # Default to ping so the status checker actually polls the new node. + # Without this the scheduler skips it (check_method NULL → no check). + check_method="ping" if device.ip else None, ) db.add(node) created_nodes.append(node) @@ -230,6 +233,10 @@ async def approve_device( status=node_data.status, services=node_data.services or [], ieee_address=device.ieee_address, + # Honour caller-supplied check_method, else default to ping when an IP exists + # so the scheduler doesn't silently skip the new node. + check_method=node_data.check_method or ("ping" if node_data.ip else None), + check_target=node_data.check_target, ) db.add(node) await db.flush() diff --git a/backend/tests/test_scan.py b/backend/tests/test_scan.py index 3fdf851..46abcdc 100644 --- a/backend/tests/test_scan.py +++ b/backend/tests/test_scan.py @@ -528,6 +528,37 @@ async def test_bulk_approve_approves_devices(client: AsyncClient, headers, two_p assert pending_res.json() == [] +@pytest.mark.asyncio +async def test_bulk_approve_sets_default_check_method(client: AsyncClient, headers, two_pending_devices, db_session): + """Approved devices with an IP must default to ping; otherwise scheduler skips them.""" + from sqlalchemy import select + + from app.db.models import Node as NodeModel + ids = [d.id for d in two_pending_devices] + res = await client.post("/api/v1/scan/pending/bulk-approve", json={"device_ids": ids}, headers=headers) + assert res.status_code == 200 + nodes = (await db_session.execute(select(NodeModel))).scalars().all() + for n in nodes: + if n.ip: + assert n.check_method == "ping", f"node {n.id} created without check_method" + + +@pytest.mark.asyncio +async def test_approve_device_sets_default_check_method(client: AsyncClient, headers, pending_device, db_session): + from sqlalchemy import select + + from app.db.models import Node as NodeModel + res = await client.post( + f"/api/v1/scan/pending/{pending_device.id}/approve", + json={"label": "h", "type": "generic", "ip": "192.168.1.10", "status": "unknown", "services": []}, + headers=headers, + ) + assert res.status_code == 200 + node = (await db_session.execute(select(NodeModel))).scalars().first() + assert node is not None + assert node.check_method == "ping" + + @pytest.mark.asyncio async def test_bulk_approve_skips_already_approved(client: AsyncClient, headers, two_pending_devices): ids = [d.id for d in two_pending_devices]