BE: linting fixes

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
jokob-sk
2025-11-22 13:14:06 +11:00
parent f0abd500d9
commit 5c14b34a8b
104 changed files with 2163 additions and 2199 deletions

View File

@@ -1 +1 @@
""" tests for NetAlertX """
""" tests for NetAlertX """

View File

@@ -7,9 +7,9 @@ import pytest
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from utils.datetime_utils import timeNowDB
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from utils.datetime_utils import timeNowDB # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
@@ -26,7 +26,7 @@ def client():
@pytest.fixture(scope="session")
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
return "AA:BB:CC:" + ":".join(f"{random.randint(0, 255):02X}" for _ in range(3))
def auth_headers(token):

View File

@@ -1,17 +1,17 @@
import sys
import pathlib
import sqlite3
# import pathlib
# import sqlite3
import random
import string
import uuid
# import string
# import uuid
import os
import pytest
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
@@ -28,7 +28,7 @@ def client():
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
return "AA:BB:CC:" + ":".join(f"{random.randint(0, 255):02X}" for _ in range(3))
def auth_headers(token):
@@ -38,7 +38,6 @@ def auth_headers(token):
def test_create_device(client, api_token, test_mac):
payload = {
"createNew": True,
"devType": "Test Device",
"devOwner": "Unit Test",
"devType": "Router",
"devVendor": "TestVendor",
@@ -103,7 +102,7 @@ def test_copy_device(client, api_token, test_mac):
# Step 2: Generate a target MAC
target_mac = "AA:BB:CC:" + ":".join(
f"{random.randint(0,255):02X}" for _ in range(3)
f"{random.randint(0, 255):02X}" for _ in range(3)
)
# Step 3: Copy device

View File

@@ -1,32 +1,36 @@
import sys
import pathlib
import sqlite3
# import pathlib
# import sqlite3
import base64
import random
import string
import uuid
# import string
# import uuid
import os
import pytest
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
return "AA:BB:CC:" + ":".join(f"{random.randint(0, 255):02X}" for _ in range(3))
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
@@ -40,12 +44,13 @@ def create_dummy(client, api_token, test_mac):
"devType": "Router",
"devVendor": "TestVendor",
}
resp = client.post(f"/device/{test_mac}", json=payload, headers=auth_headers(api_token))
client.post(f"/device/{test_mac}", json=payload, headers=auth_headers(api_token))
def test_get_all_devices(client, api_token, test_mac):
# Ensure there is at least one device
create_dummy(client, api_token, test_mac)
# Fetch all devices
resp = client.get("/devices", headers=auth_headers(api_token))
assert resp.status_code == 200
@@ -59,7 +64,7 @@ def test_get_all_devices(client, api_token, test_mac):
def test_delete_devices_with_macs(client, api_token, test_mac):
# First create device so it exists
create_dummy(client, api_token, test_mac)
client.post(f"/device/{test_mac}", json={"createNew": True}, headers=auth_headers(api_token))
# Delete by MAC
@@ -67,6 +72,7 @@ def test_delete_devices_with_macs(client, api_token, test_mac):
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_delete_all_empty_macs(client, api_token):
resp = client.delete("/devices/empty-macs", headers=auth_headers(api_token))
assert resp.status_code == 200
@@ -79,6 +85,7 @@ def test_delete_unknown_devices(client, api_token):
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_export_devices_csv(client, api_token, test_mac):
# Create a device first
create_dummy(client, api_token, test_mac)
@@ -92,6 +99,7 @@ def test_export_devices_csv(client, api_token, test_mac):
# CSV should contain test_mac
assert test_mac in resp.data.decode()
def test_export_devices_json(client, api_token, test_mac):
# Create a device first
create_dummy(client, api_token, test_mac)
@@ -101,7 +109,7 @@ def test_export_devices_json(client, api_token, test_mac):
assert resp.status_code == 200
assert resp.is_json
data = resp.get_json()
assert any(dev.get("devMac") == test_mac for dev in data["data"])
assert any(dev.get("devMac") == test_mac for dev in data["data"])
def test_export_devices_invalid_format(client, api_token):
@@ -143,6 +151,7 @@ def test_export_import_cycle_base64(client, api_token, test_mac):
assert resp.json.get("inserted") >= 1
assert resp.json.get("skipped_lines") == []
def test_devices_totals(client, api_token, test_mac):
# 1. Create a dummy device
create_dummy(client, api_token, test_mac)
@@ -189,9 +198,10 @@ def test_devices_by_status(client, api_token, test_mac):
assert fav_data is not None
assert "&#9733" in fav_data["title"]
def test_delete_test_devices(client, api_token, test_mac):
# Delete by MAC
resp = client.delete("/devices", json={"macs": ["AA:BB:CC:*"]}, headers=auth_headers(api_token))
assert resp.status_code == 200
assert resp.json.get("success") is True
assert resp.json.get("success") is True

View File

@@ -1,37 +1,38 @@
import sys
import pathlib
import sqlite3
import random
import string
import uuid
import os
import pytest
from datetime import datetime, timedelta
import random
from datetime import timedelta
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from utils.datetime_utils import timeNowTZ
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from utils.datetime_utils import timeNowTZ # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
return "AA:BB:CC:" + ":".join(f"{random.randint(0, 255):02X}" for _ in range(3))
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
def create_event(client, api_token, mac, event="UnitTest Event", days_old=None):
payload = {"ip": "0.0.0.0", "event_type": event}
@@ -43,10 +44,12 @@ def create_event(client, api_token, mac, event="UnitTest Event", days_old=None):
return client.post(f"/events/create/{mac}", json=payload, headers=auth_headers(api_token))
def list_events(client, api_token, mac=None):
url = "/events" if mac is None else f"/events?mac={mac}"
return client.get(url, headers=auth_headers(api_token))
def test_create_event(client, api_token, test_mac):
# create event
resp = create_event(client, api_token, test_mac)
@@ -82,6 +85,7 @@ def test_delete_events_for_mac(client, api_token, test_mac):
assert resp.status_code == 200
assert len(resp.json.get("events", [])) == 0
def test_get_events_totals(client, api_token):
# 1. Request totals with default period
resp = client.get(
@@ -108,7 +112,6 @@ def test_get_events_totals(client, api_token):
assert len(data_month) == 6
def test_delete_all_events(client, api_token, test_mac):
# create two events
create_event(client, api_token, test_mac)
@@ -146,5 +149,3 @@ def test_delete_events_dynamic_days(client, api_token, test_mac):
events = resp.get_json().get("events", [])
mac_events = [ev for ev in events if ev.get("eve_MAC") == test_mac]
assert len(mac_events) == 1

View File

@@ -1,31 +1,30 @@
import sys
import pathlib
import sqlite3
import random
import string
import uuid
import pytest
from datetime import datetime, timedelta
INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
return "AA:BB:CC:" + ":".join(f"{random.randint(0, 255):02X}" for _ in range(3))
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
@@ -37,6 +36,7 @@ def test_graphql_debug_get(client):
assert resp.status_code == 200
assert resp.data.decode() == "NetAlertX GraphQL server running."
def test_graphql_post_unauthorized(client):
"""POST /graphql without token should return 401"""
query = {"query": "{ devices { devName devMac } }"}
@@ -47,13 +47,14 @@ def test_graphql_post_unauthorized(client):
# --- DEVICES TESTS ---
def test_graphql_post_devices(client, api_token):
"""POST /graphql with a valid token should return device data"""
query = {
"query": """
{
devices {
devices {
devices {
devGUID
devGroup
devIsRandomMac
@@ -77,8 +78,8 @@ def test_graphql_post_devices(client, api_token):
assert isinstance(data["devices"]["devices"], list)
assert isinstance(data["devices"]["count"], int)
# --- SETTINGS TESTS ---
# --- SETTINGS TESTS ---
def test_graphql_post_settings(client, api_token):
"""POST /graphql should return settings data"""
query = {
@@ -97,8 +98,8 @@ def test_graphql_post_settings(client, api_token):
assert "settings" in data
assert isinstance(data["settings"]["settings"], list)
# --- LANGSTRINGS TESTS ---
# --- LANGSTRINGS TESTS ---
def test_graphql_post_langstrings_specific(client, api_token):
"""Retrieve a specific langString in a given language"""
query = {
@@ -167,4 +168,4 @@ def test_graphql_post_langstrings_all_languages(client, api_token):
assert data["enStrings"]["count"] >= 1
assert data["deStrings"]["count"] >= 1
# Ensure langCode matches
assert all(e["langCode"] == "en_us" for e in data["enStrings"]["langStrings"])
assert all(e["langCode"] == "en_us" for e in data["enStrings"]["langStrings"])

View File

@@ -1,17 +1,13 @@
import sys
import pathlib
import sqlite3
import random
import string
import uuid
import os
import pytest
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
@@ -28,7 +24,7 @@ def client():
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
return "AA:BB:CC:" + ":".join(f"{random.randint(0, 255):02X}" for _ in range(3))
def auth_headers(token):
@@ -36,6 +32,6 @@ def auth_headers(token):
def test_delete_history(client, api_token):
resp = client.delete(f"/history", headers=auth_headers(api_token))
resp = client.delete("/history", headers=auth_headers(api_token))
assert resp.status_code == 200
assert resp.json.get("success") is True

View File

@@ -5,8 +5,9 @@ import pytest
INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
# ----------------------------
# Fixtures
@@ -15,14 +16,17 @@ from api_server.api_server_start import app
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
# ----------------------------
# Logs Endpoint Tests
# ----------------------------
@@ -31,16 +35,18 @@ def test_clean_log(client, api_token):
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_clean_log_not_allowed(client, api_token):
resp = client.delete("/logs?file=not_allowed.log", headers=auth_headers(api_token))
assert resp.status_code == 400
assert resp.json.get("success") is False
# ----------------------------
# Execution Queue Endpoint Tests
# ----------------------------
def test_add_to_execution_queue(client, api_token):
action_name = f"test_action_{random.randint(0,9999)}"
action_name = f"test_action_{random.randint(0, 9999)}"
resp = client.post(
"/logs/add-to-execution-queue",
json={"action": action_name},
@@ -50,6 +56,7 @@ def test_add_to_execution_queue(client, api_token):
assert resp.json.get("success") is True
assert action_name in resp.json.get("message", "")
def test_add_to_execution_queue_missing_action(client, api_token):
resp = client.post(
"/logs/add-to-execution-queue",

View File

@@ -1,11 +1,8 @@
# -----------------------------
# In-app notifications tests with cleanup
# -----------------------------
import json
import random
import string
import uuid
import pytest
import os
import sys
@@ -14,26 +11,31 @@ import sys
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from api_server.api_server_start import app
from messaging.in_app import NOTIFICATION_API_FILE # Import the path to notifications file
from helper import get_setting_value
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
from messaging.in_app import NOTIFICATION_API_FILE # noqa: E402 [flake8 lint suppression]
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
@pytest.fixture
def random_content():
return "Test Notification " + "".join(random.choices(string.ascii_letters + string.digits, k=6))
@pytest.fixture
def notification_guid(client, api_token, random_content):
# Write a notification and return its GUID
@@ -50,6 +52,7 @@ def notification_guid(client, api_token, random_content):
assert guid is not None
return guid
@pytest.fixture(autouse=True)
def cleanup_notifications():
# Runs before and after each test
@@ -70,6 +73,7 @@ def cleanup_notifications():
with open(NOTIFICATION_API_FILE, "w") as f:
f.write(backup)
# -----------------------------
def test_write_notification(client, api_token, random_content):
resp = client.post(
@@ -80,6 +84,7 @@ def test_write_notification(client, api_token, random_content):
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_get_unread_notifications(client, api_token, random_content):
client.post("/messaging/in-app/write", json={"content": random_content}, headers=auth_headers(api_token))
resp = client.get("/messaging/in-app/unread", headers=auth_headers(api_token))
@@ -87,22 +92,26 @@ def test_get_unread_notifications(client, api_token, random_content):
notifications = resp.json
assert any(n["content"] == random_content for n in notifications)
def test_mark_all_notifications_read(client, api_token, random_content):
client.post("/messaging/in-app/write", json={"content": random_content}, headers=auth_headers(api_token))
resp = client.post("/messaging/in-app/read/all", headers=auth_headers(api_token))
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_mark_single_notification_read(client, api_token, notification_guid):
resp = client.post(f"/messaging/in-app/read/{notification_guid}", headers=auth_headers(api_token))
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_delete_single_notification(client, api_token, notification_guid):
resp = client.delete(f"/messaging/in-app/delete/{notification_guid}", headers=auth_headers(api_token))
assert resp.status_code == 200
assert resp.json.get("success") is True
def test_delete_all_notifications(client, api_token, random_content):
# Add a notification first
client.post("/messaging/in-app/write", json={"content": random_content}, headers=auth_headers(api_token))

View File

@@ -1,32 +1,31 @@
import sys
import pathlib
import sqlite3
import base64
import random
import string
import uuid
import os
import pytest
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
return "AA:BB:CC:" + ":".join(f"{random.randint(0, 255):02X}" for _ in range(3))
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
@@ -40,7 +39,8 @@ def create_dummy(client, api_token, test_mac):
"devType": "Router",
"devVendor": "TestVendor",
}
resp = client.post(f"/device/{test_mac}", json=payload, headers=auth_headers(api_token))
client.post(f"/device/{test_mac}", json=payload, headers=auth_headers(api_token))
def test_wakeonlan_device(client, api_token, test_mac):
# 1. Ensure at least one device exists
@@ -73,6 +73,7 @@ def test_wakeonlan_device(client, api_token, test_mac):
assert data.get("success") is True
assert "WOL packet sent" in data.get("message", "")
def test_speedtest_endpoint(client, api_token):
# 1. Call the speedtest endpoint
resp = client.get("/nettools/speedtest", headers=auth_headers(api_token))
@@ -92,7 +93,8 @@ def test_speedtest_endpoint(client, api_token):
assert isinstance(data["output"], list)
# Optionally check that output lines are strings
assert all(isinstance(line, str) for line in data["output"])
def test_traceroute_device(client, api_token, test_mac):
# 1. Ensure at least one device exists
create_dummy(client, api_token, test_mac)
@@ -127,6 +129,7 @@ def test_traceroute_device(client, api_token, test_mac):
assert "output" in data
assert isinstance(data["output"], str)
@pytest.mark.parametrize("ip,expected_status", [
("8.8.8.8", 200),
("256.256.256.256", 400), # Invalid IP
@@ -147,6 +150,7 @@ def test_nslookup_endpoint(client, api_token, ip, expected_status):
assert data.get("success") is False
assert "error" in data
@pytest.mark.parametrize("ip,mode,expected_status", [
("127.0.0.1", "fast", 200),
pytest.param("127.0.0.1", "normal", 200, marks=pytest.mark.feature_complete),
@@ -172,6 +176,7 @@ def test_nmap_endpoint(client, api_token, ip, mode, expected_status):
assert data.get("success") is False
assert "error" in data
def test_nslookup_unauthorized(client):
# No auth headers
resp = client.post("/nettools/nslookup", json={"devLastIP": "8.8.8.8"})
@@ -180,6 +185,7 @@ def test_nslookup_unauthorized(client):
assert data.get("success") is False
assert data.get("error") == "Forbidden"
def test_nmap_unauthorized(client):
# No auth headers
resp = client.post("/nettools/nmap", json={"scan": "127.0.0.1", "mode": "fast"})
@@ -201,4 +207,4 @@ def test_internet_info_endpoint(client, api_token):
# Handle errors, e.g., curl failure
assert data.get("success") is False
assert "error" in data
assert "details" in data
assert "details" in data

View File

@@ -1,9 +1,5 @@
import sys
import pathlib
import sqlite3
import random
import string
import uuid
import os
import pytest
from datetime import datetime, timedelta
@@ -11,31 +7,35 @@ from datetime import datetime, timedelta
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from utils.datetime_utils import timeNowTZ, timeNowDB
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from utils.datetime_utils import timeNowTZ, timeNowDB # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
return "AA:BB:CC:" + ":".join(f"{random.randint(0, 255):02X}" for _ in range(3))
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
def test_create_device(client, api_token, test_mac):
payload = {
"createNew": True,
"devType": "Test Device",
"devOwner": "Unit Test",
"devType": "Router",
"devVendor": "TestVendor",
@@ -129,7 +129,7 @@ def test_device_session_events(client, api_token, test_mac):
# 2. Fetch session events with default type ('all') and period ('7 days')
resp = client.get(
f"/sessions/session-events?type=all&period=7 days",
"/sessions/session-events?type=all&period=7 days",
headers=auth_headers(api_token)
)
assert resp.status_code == 200
@@ -159,6 +159,7 @@ def test_device_session_events(client, api_token, test_mac):
sessions = resp_sessions.json["data"]
assert isinstance(sessions, list)
# -----------------------------
def test_delete_session(client, api_token, test_mac):
# First create session
@@ -180,15 +181,12 @@ def test_delete_session(client, api_token, test_mac):
assert not any(ses["ses_MAC"] == test_mac for ses in sessions)
def test_get_sessions_calendar(client, api_token, test_mac):
"""
Test the /sessions/calendar endpoint.
Creates session and ensures the calendar output is correct.
Cleans up test sessions after test.
"""
# --- Setup: create two sessions for the test MAC ---
now = timeNowTZ()
start1 = (now - timedelta(days=2)).isoformat(timespec="seconds")
@@ -256,4 +254,4 @@ def test_get_sessions_calendar(client, api_token, test_mac):
assert "<still connected>" in ses["tooltip"], f"End is None but session not marked as still connected: {ses}"
# --- Cleanup: delete all test sessions for this MAC ---
client.delete(f"/sessions/delete?mac={test_mac}", headers=auth_headers(api_token))
client.delete(f"/sessions/delete?mac={test_mac}", headers=auth_headers(api_token))

View File

@@ -1,36 +1,36 @@
import sys
import pathlib
import sqlite3
import random
import string
import uuid
import os
import pytest
from datetime import datetime, timedelta
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
return "AA:BB:CC:" + ":".join(f"{random.randint(0, 255):02X}" for _ in range(3))
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
def test_get_setting_unauthorized(client):
resp = client.get("/settings/API_TOKEN") # no auth header
assert resp.status_code == 403

View File

@@ -6,16 +6,17 @@ Tests the fix for Issue #1210 - compound conditions with multiple AND/OR clauses
import sys
import pytest
import os
from unittest.mock import MagicMock
# Mock the logger module before importing SafeConditionBuilder
sys.modules['logger'] = MagicMock()
# Add parent directory to path for imports
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from server.db.sql_safe_builder import SafeConditionBuilder
from server.db.sql_safe_builder import SafeConditionBuilder # noqa: E402 [flake8 lint suppression]
@pytest.fixture
@@ -100,6 +101,7 @@ def test_multiple_or_clauses(builder):
assert 'Device2' in param_values
assert 'Device3' in param_values
def test_mixed_and_or_clauses(builder):
"""Test mixed AND/OR logical operators."""
condition = "AND devName = 'Device1' OR devName = 'Device2' AND devFavorite = '1'"

View File

@@ -137,7 +137,7 @@ def test_unicode_support(builder, unicode_str):
@pytest.mark.parametrize("case", [
"", " ", "AND devName = ''", "AND devName = 'a'", "AND devName = '" + "x"*500 + "'"
"", " ", "AND devName = ''", "AND devName = 'a'", "AND devName = '" + "x" * 500 + "'"
])
def test_edge_cases(builder, case):
try:

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3
# !/usr/bin/env python3
"""
Comprehensive SQL Injection Prevention Tests for NetAlertX
@@ -15,7 +15,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'server'))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'server', 'db'))
# Now import our module
from sql_safe_builder import SafeConditionBuilder
from sql_safe_builder import SafeConditionBuilder # noqa: E402 [flake8 lint suppression]
@pytest.fixture
@@ -28,7 +28,7 @@ def test_sql_injection_attempt_single_quote(builder):
"""Test that single quote injection attempts are blocked."""
malicious_input = "'; DROP TABLE users; --"
condition, params = builder.get_safe_condition_legacy(malicious_input)
# Should return empty condition when invalid
assert condition == ""
assert params == {}
@@ -38,7 +38,7 @@ def test_sql_injection_attempt_union(builder):
"""Test that UNION injection attempts are blocked."""
malicious_input = "1' UNION SELECT * FROM passwords --"
condition, params = builder.get_safe_condition_legacy(malicious_input)
# Should return empty condition when invalid
assert condition == ""
assert params == {}
@@ -48,7 +48,7 @@ def test_sql_injection_attempt_or_true(builder):
"""Test that OR 1=1 injection attempts are blocked."""
malicious_input = "' OR '1'='1"
condition, params = builder.get_safe_condition_legacy(malicious_input)
# Should return empty condition when invalid
assert condition == ""
assert params == {}
@@ -58,7 +58,7 @@ def test_valid_simple_condition(builder):
"""Test that valid simple conditions are handled correctly."""
valid_input = "AND devName = 'Test Device'"
condition, params = builder.get_safe_condition_legacy(valid_input)
# Should create parameterized query
assert "AND devName = :" in condition
assert len(params) == 1
@@ -69,7 +69,7 @@ def test_empty_condition(builder):
"""Test that empty conditions are handled safely."""
empty_input = ""
condition, params = builder.get_safe_condition_legacy(empty_input)
# Should return empty condition
assert condition == ""
assert params == {}
@@ -79,7 +79,7 @@ def test_whitespace_only_condition(builder):
"""Test that whitespace-only conditions are handled safely."""
whitespace_input = " \n\t "
condition, params = builder.get_safe_condition_legacy(whitespace_input)
# Should return empty condition
assert condition == ""
assert params == {}
@@ -90,7 +90,7 @@ def test_multiple_conditions_valid(builder):
# Test with a single condition first (our current parser handles single conditions well)
valid_input = "AND devName = 'Device1'"
condition, params = builder.get_safe_condition_legacy(valid_input)
# Should create parameterized query
assert "devName = :" in condition
assert len(params) == 1
@@ -101,7 +101,7 @@ def test_disallowed_column_name(builder):
"""Test that non-whitelisted column names are rejected."""
invalid_input = "AND malicious_column = 'value'"
condition, params = builder.get_safe_condition_legacy(invalid_input)
# Should return empty condition when column not in whitelist
assert condition == ""
assert params == {}
@@ -111,7 +111,7 @@ def test_disallowed_operator(builder):
"""Test that non-whitelisted operators are rejected."""
invalid_input = "AND devName SOUNDS LIKE 'test'"
condition, params = builder.get_safe_condition_legacy(invalid_input)
# Should return empty condition when operator not allowed
assert condition == ""
assert params == {}
@@ -121,7 +121,7 @@ def test_nested_select_attempt(builder):
"""Test that nested SELECT attempts are blocked."""
malicious_input = "AND devName IN (SELECT password FROM users)"
condition, params = builder.get_safe_condition_legacy(malicious_input)
# Should return empty condition when nested SELECT detected
assert condition == ""
assert params == {}
@@ -131,7 +131,7 @@ def test_hex_encoding_attempt(builder):
"""Test that hex-encoded injection attempts are blocked."""
malicious_input = "AND 0x44524f50205441424c45"
condition, params = builder.get_safe_condition_legacy(malicious_input)
# Should return empty condition when hex encoding detected
assert condition == ""
assert params == {}
@@ -141,7 +141,7 @@ def test_comment_injection_attempt(builder):
"""Test that comment injection attempts are handled."""
malicious_input = "AND devName = 'test' /* comment */ --"
condition, params = builder.get_safe_condition_legacy(malicious_input)
# Comments should be stripped and condition validated
if condition:
assert "/*" not in condition
@@ -152,7 +152,7 @@ def test_special_placeholder_replacement(builder):
"""Test that {s-quote} placeholder is safely replaced."""
input_with_placeholder = "AND devName = {s-quote}Test{s-quote}"
condition, params = builder.get_safe_condition_legacy(input_with_placeholder)
# Should handle placeholder safely
if condition:
assert "{s-quote}" not in condition
@@ -163,7 +163,7 @@ def test_null_byte_injection(builder):
"""Test that null byte injection attempts are blocked."""
malicious_input = "AND devName = 'test\x00' DROP TABLE --"
condition, params = builder.get_safe_condition_legacy(malicious_input)
# Null bytes should be sanitized
if condition:
assert "\x00" not in condition
@@ -178,7 +178,7 @@ def test_build_condition_with_allowed_values(builder):
{"column": "devName", "operator": "LIKE", "value": "%test%"}
]
condition, params = builder.build_condition(conditions, "AND")
# Should create valid parameterized condition
assert "eve_EventType = :" in condition
assert "devName LIKE :" in condition
@@ -191,7 +191,7 @@ def test_build_condition_with_invalid_column(builder):
{"column": "invalid_column", "operator": "=", "value": "test"}
]
condition, params = builder.build_condition(conditions)
# Should return empty when invalid column
assert condition == ""
assert params == {}
@@ -204,7 +204,7 @@ def test_case_variations_injection(builder):
"oR 1=1",
"UnIoN SeLeCt * FrOm users"
]
for malicious_input in malicious_inputs:
condition, params = builder.get_safe_condition_legacy(malicious_input)
# Should handle case variations safely
@@ -217,7 +217,7 @@ def test_time_based_injection_attempt(builder):
"""Test that time-based injection attempts are blocked."""
malicious_input = "AND IF(1=1, SLEEP(5), 0)"
condition, params = builder.get_safe_condition_legacy(malicious_input)
# Should return empty condition when SQL functions detected
assert condition == ""
assert params == {}
@@ -227,7 +227,7 @@ def test_stacked_queries_attempt(builder):
"""Test that stacked query attempts are blocked."""
malicious_input = "'; INSERT INTO admin VALUES ('hacker', 'password'); --"
condition, params = builder.get_safe_condition_legacy(malicious_input)
# Should return empty condition when semicolon detected
assert condition == ""
assert params == {}

View File

@@ -13,16 +13,15 @@ import unittest
import sqlite3
import tempfile
import os
from unittest.mock import Mock, patch, MagicMock
from unittest.mock import Mock, patch
# Add the server directory to the path for imports
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/server"])
sys.path.append('/home/dell/coding/bash/10x-agentic-setup/netalertx-sql-fix/server')
from db.sql_safe_builder import SafeConditionBuilder, create_safe_condition_builder
from database import DB
from messaging.reporting import get_notifications
from db.sql_safe_builder import SafeConditionBuilder # noqa: E402 [flake8 lint suppression]
from messaging.reporting import get_notifications # noqa: E402 [flake8 lint suppression]
class TestSafeConditionBuilder(unittest.TestCase):
@@ -83,7 +82,7 @@ class TestSafeConditionBuilder(unittest.TestCase):
def test_build_simple_condition_valid(self):
"""Test building valid simple conditions."""
sql, params = self.builder._build_simple_condition('AND', 'devName', '=', 'TestDevice')
self.assertIn('AND devName = :param_', sql)
self.assertEqual(len(params), 1)
self.assertIn('TestDevice', params.values())
@@ -92,20 +91,20 @@ class TestSafeConditionBuilder(unittest.TestCase):
"""Test that invalid column names are rejected."""
with self.assertRaises(ValueError) as context:
self.builder._build_simple_condition('AND', 'invalid_column', '=', 'value')
self.assertIn('Invalid column name', str(context.exception))
def test_build_simple_condition_invalid_operator(self):
"""Test that invalid operators are rejected."""
with self.assertRaises(ValueError) as context:
self.builder._build_simple_condition('AND', 'devName', 'UNION', 'value')
self.assertIn('Invalid operator', str(context.exception))
def test_build_in_condition_valid(self):
"""Test building valid IN conditions."""
sql, params = self.builder._build_in_condition('AND', 'eve_EventType', 'IN', "'Connected', 'Disconnected'")
self.assertIn('AND eve_EventType IN', sql)
self.assertEqual(len(params), 2)
self.assertIn('Connected', params.values())
@@ -114,7 +113,7 @@ class TestSafeConditionBuilder(unittest.TestCase):
def test_build_null_condition(self):
"""Test building NULL check conditions."""
sql, params = self.builder._build_null_condition('AND', 'devComments', 'IS NULL')
self.assertEqual(sql, 'AND devComments IS NULL')
self.assertEqual(len(params), 0)
@@ -154,7 +153,7 @@ class TestSafeConditionBuilder(unittest.TestCase):
def test_device_name_filter(self):
"""Test the device name filter helper method."""
sql, params = self.builder.build_device_name_filter("TestDevice")
self.assertIn('AND devName = :device_name_', sql)
self.assertIn('TestDevice', params.values())
@@ -162,14 +161,13 @@ class TestSafeConditionBuilder(unittest.TestCase):
"""Test the event type filter helper method."""
event_types = ['Connected', 'Disconnected']
sql, params = self.builder.build_event_type_filter(event_types)
self.assertIn('AND eve_EventType IN', sql)
self.assertEqual(len(params), 2)
self.assertIn('Connected', params.values())
self.assertIn('Disconnected', params.values())
class TestDatabaseParameterSupport(unittest.TestCase):
"""Test that database layer supports parameterized queries."""
@@ -177,7 +175,7 @@ class TestDatabaseParameterSupport(unittest.TestCase):
"""Set up test database."""
self.temp_db = tempfile.NamedTemporaryFile(delete=False, suffix='.db')
self.temp_db.close()
# Create test database
self.conn = sqlite3.connect(self.temp_db.name)
self.conn.execute('''CREATE TABLE test_table (
@@ -197,23 +195,23 @@ class TestDatabaseParameterSupport(unittest.TestCase):
def test_parameterized_query_execution(self):
"""Test that parameterized queries work correctly."""
cursor = self.conn.cursor()
# Test named parameters
cursor.execute("SELECT * FROM test_table WHERE name = :name", {'name': 'test1'})
results = cursor.fetchall()
self.assertEqual(len(results), 1)
self.assertEqual(results[0][1], 'test1')
def test_parameterized_query_prevents_injection(self):
"""Test that parameterized queries prevent SQL injection."""
cursor = self.conn.cursor()
# This should not cause SQL injection
malicious_input = "'; DROP TABLE test_table; --"
cursor.execute("SELECT * FROM test_table WHERE name = :name", {'name': malicious_input})
results = cursor.fetchall()
# results = cursor.fetchall()
# The table should still exist and be queryable
cursor.execute("SELECT COUNT(*) FROM test_table")
count = cursor.fetchone()[0]
@@ -228,7 +226,7 @@ class TestReportingSecurityIntegration(unittest.TestCase):
self.mock_db = Mock()
self.mock_db.sql = Mock()
self.mock_db.get_table_as_json = Mock()
# Mock successful JSON response
mock_json_obj = Mock()
mock_json_obj.columnNames = ['MAC', 'Datetime', 'IP', 'Event Type', 'Device name', 'Comments']
@@ -245,7 +243,7 @@ class TestReportingSecurityIntegration(unittest.TestCase):
}.get(key, '')
# Call the function
result = get_notifications(self.mock_db)
get_notifications(self.mock_db)
# Verify that get_table_as_json was called with parameters
self.mock_db.get_table_as_json.assert_called()
@@ -265,7 +263,6 @@ class TestReportingSecurityIntegration(unittest.TestCase):
# Ensure the parameter dict has the correct value (using actual param name)
self.assertEqual(list(params.values())[0], "TestDevice")
@patch('messaging.reporting.get_setting_value')
def test_events_section_security(self, mock_get_setting):
"""Test that events section uses safe SQL building."""
@@ -276,7 +273,7 @@ class TestReportingSecurityIntegration(unittest.TestCase):
}.get(key, '')
# Call the function
result = get_notifications(self.mock_db)
get_notifications(self.mock_db)
# Verify that get_table_as_json was called with parameters
self.mock_db.get_table_as_json.assert_called()
@@ -291,7 +288,7 @@ class TestReportingSecurityIntegration(unittest.TestCase):
}.get(key, '')
# Call the function - should not raise an exception
result = get_notifications(self.mock_db)
get_notifications(self.mock_db)
# Should still call get_table_as_json (with safe fallback query)
self.mock_db.get_table_as_json.assert_called()
@@ -306,7 +303,7 @@ class TestReportingSecurityIntegration(unittest.TestCase):
}.get(key, '')
# Call the function
result = get_notifications(self.mock_db)
get_notifications(self.mock_db)
# Should call get_table_as_json
self.mock_db.get_table_as_json.assert_called()
@@ -322,12 +319,12 @@ class TestSecurityBenchmarks(unittest.TestCase):
def test_performance_simple_condition(self):
"""Test performance of simple condition building."""
import time
start_time = time.time()
for _ in range(1000):
sql, params = self.builder.build_safe_condition("AND devName = 'TestDevice'")
end_time = time.time()
execution_time = end_time - start_time
self.assertLess(execution_time, 1.0, "Simple condition building should be fast")
@@ -339,7 +336,7 @@ class TestSecurityBenchmarks(unittest.TestCase):
self.skipTest("psutil not available")
return
import os
process = psutil.Process(os.getpid())
initial_memory = process.memory_info().rss
@@ -350,7 +347,7 @@ class TestSecurityBenchmarks(unittest.TestCase):
final_memory = process.memory_info().rss
memory_increase = final_memory - initial_memory
# Memory increase should be reasonable (less than 10MB)
self.assertLess(memory_increase, 10 * 1024 * 1024, "Memory usage should be reasonable")
@@ -376,4 +373,4 @@ class TestSecurityBenchmarks(unittest.TestCase):
if __name__ == '__main__':
# Run the test suite
unittest.main(verbosity=2)
unittest.main(verbosity=2)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3
# !/usr/bin/env python3
"""
Pytest-based Mount Diagnostic Tests for NetAlertX

View File

@@ -38,7 +38,7 @@ def dummy_container(tmp_path):
f.write(" network_mode: host\n")
f.write(" userns_mode: host\n")
f.write(" command: sh -c \"while true; do nc -l -p 20211 < /dev/null > /dev/null; done & while true; do nc -l -p 20212 < /dev/null > /dev/null; done & sleep 30\"\n")
# Start the dummy container
import subprocess
result = subprocess.run(
@@ -47,12 +47,12 @@ def dummy_container(tmp_path):
)
if result.returncode != 0:
pytest.fail(f"Failed to start dummy container: {result.stderr}")
# Wait a bit for the container to start listening
time.sleep(3)
yield "dummy"
# Cleanup
subprocess.run(["docker-compose", "-f", str(compose_file), "down"], capture_output=True)
@@ -139,10 +139,10 @@ def _run_container(
# Copy the script content and run it
script_path = pathlib.Path("install/production-filesystem/entrypoint.d/99-ports-available.sh")
with script_path.open('r', encoding='utf-8') as f:
script_content = f.read()
script_cont = f.read()
# Use printf to avoid shell interpretation issues
script = f"printf '%s\\n' '{script_content.replace(chr(39), chr(39)+chr(92)+chr(39)+chr(39))}' > /tmp/ports-check.sh && chmod +x /tmp/ports-check.sh && sh /tmp/ports-check.sh"
script = f"printf '%s\\n' '{script_cont.replace(chr(39), chr(39) + chr(92) + chr(39) + chr(39))}' > /tmp/ports-check.sh && chmod +x /tmp/ports-check.sh && sh /tmp/ports-check.sh" # noqa: E501 - inline script
cmd.extend(["--entrypoint", "/bin/sh", IMAGE, "-c", script])
print(f"\n--- DOCKER CMD ---\n{' '.join(cmd)}\n--- END CMD ---\n")
@@ -157,8 +157,7 @@ def _run_container(
# Combine and clean stdout and stderr
stdouterr = (
re.sub(r'\x1b\[[0-9;]*m', '', result.stdout or '') +
re.sub(r'\x1b\[[0-9;]*m', '', result.stderr or '')
re.sub(r'\x1b\[[0-9;]*m', '', result.stdout or '') + re.sub(r'\x1b\[[0-9;]*m', '', result.stderr or '')
)
result.output = stdouterr
print(f"\n--- CONTAINER stdout ---\n{result.stdout}")
@@ -255,4 +254,4 @@ def test_ports_in_use_warning(dummy_container, tmp_path: pathlib.Path) -> None:
_assert_contains(result, "Port Warning: Application port 20211 is already in use")
_assert_contains(result, "Port Warning: GraphQL API port 20212 is already in use")
assert result.returncode == 0
assert result.returncode == 0

View File

@@ -9,12 +9,14 @@ import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'server'))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'server', 'db'))
from db.sql_safe_builder import SafeConditionBuilder, create_safe_condition_builder
from messaging.reporting import get_notifications
from db.sql_safe_builder import create_safe_condition_builder # noqa: E402 [flake8 lint suppression]
from messaging.reporting import get_notifications # noqa: E402 [flake8 lint suppression]
# -----------------------------
# Fixtures
# -----------------------------
@pytest.fixture
def test_db_path():
path = tempfile.mktemp(suffix=".db")
@@ -22,10 +24,12 @@ def test_db_path():
if os.path.exists(path):
os.remove(path)
@pytest.fixture
def builder():
return create_safe_condition_builder()
@pytest.fixture
def test_db(test_db_path):
conn = sqlite3.connect(test_db_path)
@@ -96,6 +100,7 @@ def test_db(test_db_path):
# Tests
# -----------------------------
def test_fresh_install_compatibility(builder):
condition, params = builder.get_safe_condition_legacy("")
assert condition == ""
@@ -105,6 +110,7 @@ def test_fresh_install_compatibility(builder):
assert "devName = :" in condition
assert 'TestDevice' in params.values()
def test_existing_db_compatibility():
mock_db = Mock()
mock_result = Mock()
@@ -129,6 +135,7 @@ def test_existing_db_compatibility():
assert 'events_meta' in result
assert mock_db.get_table_as_json.called
def test_notification_system_integration(builder):
email_condition = "AND devName = 'EmailTestDevice'"
condition, params = builder.get_safe_condition_legacy(email_condition)
@@ -150,6 +157,7 @@ def test_notification_system_integration(builder):
assert "eve_MAC = :" in condition
assert 'aa:bb:cc:dd:ee:ff' in params.values()
def test_settings_persistence(builder):
test_settings = [
"AND devName = 'Persistent Device'",
@@ -163,6 +171,7 @@ def test_settings_persistence(builder):
assert isinstance(condition, str)
assert isinstance(params, dict)
def test_device_operations(builder):
device_conditions = [
"AND devName = 'Updated Device'",
@@ -175,6 +184,7 @@ def test_device_operations(builder):
assert len(params) > 0 or safe_condition == ""
assert "'" not in safe_condition
def test_plugin_functionality(builder):
plugin_conditions = [
"AND Plugin = 'TestPlugin'",
@@ -187,6 +197,7 @@ def test_plugin_functionality(builder):
assert ":" in safe_condition
assert len(params) > 0
def test_sql_injection_prevention(builder):
malicious_inputs = [
"'; DROP TABLE Events_Devices; --",
@@ -200,6 +211,7 @@ def test_sql_injection_prevention(builder):
assert condition == ""
assert params == {}
def test_error_handling(builder):
invalid_condition = "INVALID SQL SYNTAX HERE"
condition, params = builder.get_safe_condition_legacy(invalid_condition)
@@ -213,6 +225,7 @@ def test_error_handling(builder):
assert isinstance(condition, str)
assert isinstance(params, dict)
def test_backward_compatibility(builder):
legacy_conditions = [
"AND devName = {s-quote}Legacy Device{s-quote}",
@@ -226,6 +239,7 @@ def test_backward_compatibility(builder):
assert ":" in condition
assert len(params) > 0
def test_performance_impact(builder):
import time
test_condition = "AND devName = 'Performance Test Device'"

View File

@@ -1,32 +1,31 @@
import sys
import pathlib
import sqlite3
import random
import string
import uuid
import os
import pytest
from datetime import datetime, timedelta
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from helper import get_setting_value
from api_server.api_server_start import app
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from api_server.api_server_start import app # noqa: E402 [flake8 lint suppression]
@pytest.fixture(scope="session")
def api_token():
return get_setting_value("API_TOKEN")
@pytest.fixture
def client():
with app.test_client() as client:
yield client
@pytest.fixture
def test_mac():
# Generate a unique MAC for each test run
return "AA:BB:CC:" + ":".join(f"{random.randint(0,255):02X}" for _ in range(3))
return "AA:BB:CC:" + ":".join(f"{random.randint(0, 255):02X}" for _ in range(3))
def auth_headers(token):
return {"Authorization": f"Bearer {token}"}
@@ -38,6 +37,7 @@ def test_graphql_debug_get(client):
assert resp.status_code == 200
assert resp.data.decode() == "NetAlertX GraphQL server running."
def test_graphql_post_unauthorized(client):
"""POST /graphql without token should return 401"""
query = {"query": "{ devices { devName devMac } }"}
@@ -47,13 +47,14 @@ def test_graphql_post_unauthorized(client):
error_text = resp.json.get("error", "") or resp.json.get("message", "")
assert "Unauthorized" in error_text or "Forbidden" in error_text
def test_graphql_post_devices(client, api_token):
"""POST /graphql with a valid token should return device data"""
query = {
"query": """
{
devices {
devices {
devices {
devGUID
devGroup
devIsRandomMac
@@ -77,6 +78,7 @@ def test_graphql_post_devices(client, api_token):
assert isinstance(data["devices"]["devices"], list)
assert isinstance(data["devices"]["count"], int)
def test_graphql_post_settings(client, api_token):
"""POST /graphql should return settings data"""
query = {