Misc Changes

This commit is contained in:
2025-10-28 00:38:38 -06:00
parent b760ac3083
commit ec56966827
8 changed files with 1147 additions and 8 deletions

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import json
import sqlite3
from dataclasses import dataclass
from pathlib import Path
@@ -11,12 +12,30 @@ from flask import Flask
from Data.Engine.server import create_app
_SCHEMA_DEFINITION = """
CREATE TABLE IF NOT EXISTS devices (
_SCHEMA_DEFINITION = """CREATE TABLE IF NOT EXISTS devices (
guid TEXT PRIMARY KEY,
hostname TEXT,
description TEXT,
created_at INTEGER,
agent_hash TEXT,
memory TEXT,
network TEXT,
software TEXT,
storage TEXT,
cpu TEXT,
device_type TEXT,
domain TEXT,
external_ip TEXT,
internal_ip TEXT,
last_reboot TEXT,
last_seen INTEGER,
last_user TEXT,
operating_system TEXT,
uptime INTEGER,
agent_id TEXT,
ansible_ee_ver TEXT,
connection_type TEXT,
connection_endpoint TEXT,
ssl_key_fingerprint TEXT,
token_version INTEGER,
status TEXT,
@@ -64,6 +83,28 @@ CREATE TABLE IF NOT EXISTS device_keys (
added_at TEXT,
retired_at TEXT
);
CREATE TABLE IF NOT EXISTS device_list_views (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE,
columns_json TEXT,
filters_json TEXT,
created_at INTEGER,
updated_at INTEGER
);
CREATE TABLE IF NOT EXISTS sites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
description TEXT
);
CREATE TABLE IF NOT EXISTS device_sites (
device_hostname TEXT,
site_id INTEGER,
PRIMARY KEY (device_hostname, site_id)
);
CREATE TABLE IF NOT EXISTS github_token (
id INTEGER PRIMARY KEY,
token TEXT
);
"""
@@ -102,6 +143,83 @@ def engine_harness(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Iterator[
db_path = tmp_path / "database" / "engine.sqlite3"
_initialise_legacy_schema(db_path)
conn = sqlite3.connect(str(db_path))
try:
cur = conn.cursor()
cur.execute(
"""
INSERT INTO devices (
guid,
hostname,
description,
created_at,
agent_hash,
memory,
network,
software,
storage,
cpu,
device_type,
domain,
external_ip,
internal_ip,
last_reboot,
last_seen,
last_user,
operating_system,
uptime,
agent_id,
ansible_ee_ver,
connection_type,
connection_endpoint,
ssl_key_fingerprint,
token_version,
status,
key_added_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
"GUID-TEST-0001",
"test-device",
"Test device for Engine API",
1_700_000_000,
"hash-123",
json.dumps([{"slot": "DIMM1", "size_gb": 16}]),
json.dumps([{"iface": "eth0", "mac": "00:11:22:33:44:55"}]),
json.dumps(["sample-app"]),
json.dumps([{"drive": "C", "size_gb": 256}]),
json.dumps({"name": "Intel", "cores": 8}),
"Workstation",
"example.local",
"203.0.113.5",
"10.0.0.10",
"2025-10-01T00:00:00Z",
1_700_000_500,
"Alice",
"Windows 11 Pro",
7200,
"test-device-agent",
"1.0.0",
"",
"",
"FF:FF:FF",
1,
"active",
"2025-10-01T00:00:00Z",
),
)
cur.execute(
"INSERT INTO sites (id, name, description) VALUES (?, ?, ?)",
(1, "Main Lab", "Primary integration site"),
)
cur.execute(
"INSERT INTO device_sites (device_hostname, site_id) VALUES (?, ?)",
("test-device", 1),
)
conn.commit()
finally:
conn.close()
tls_dir = tmp_path / "tls"
tls_dir.mkdir()
@@ -133,7 +251,7 @@ def engine_harness(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Iterator[
"LOG_FILE": str(log_path),
"ERROR_LOG_FILE": str(error_log_path),
"STATIC_FOLDER": str(static_dir),
"API_GROUPS": ("core", "auth", "tokens", "enrollment"),
"API_GROUPS": ("core", "auth", "tokens", "enrollment", "devices"),
}
app, _socketio, _context = create_app(config)

View File

@@ -0,0 +1,122 @@
from __future__ import annotations
from typing import Any
import pytest
from Data.Engine.services.API.devices import management as device_management
from .conftest import EngineTestHarness
def _client_with_admin_session(harness: EngineTestHarness):
client = harness.app.test_client()
with client.session_transaction() as sess:
sess["username"] = "admin"
sess["role"] = "Admin"
return client
def test_list_devices(engine_harness: EngineTestHarness) -> None:
client = engine_harness.app.test_client()
response = client.get("/api/devices")
assert response.status_code == 200
payload = response.get_json()
assert isinstance(payload, dict)
devices = payload.get("devices")
assert isinstance(devices, list) and devices
device = devices[0]
assert device["hostname"] == "test-device"
assert "summary" in device and isinstance(device["summary"], dict)
def test_device_details(engine_harness: EngineTestHarness) -> None:
client = engine_harness.app.test_client()
response = client.get("/api/device/details/test-device")
assert response.status_code == 200
payload = response.get_json()
assert payload["summary"]["hostname"] == "test-device"
def test_device_description_requires_login(engine_harness: EngineTestHarness) -> None:
client = engine_harness.app.test_client()
response = client.post(
"/api/device/description/test-device",
json={"description": "Updated"},
)
assert response.status_code == 401
def test_device_description_update(engine_harness: EngineTestHarness) -> None:
client = _client_with_admin_session(engine_harness)
response = client.post(
"/api/device/description/test-device",
json={"description": "Updated"},
)
assert response.status_code == 200
detail = client.get("/api/device/details/test-device").get_json()
assert detail["description"] == "Updated"
def test_device_list_views_lifecycle(engine_harness: EngineTestHarness) -> None:
client = _client_with_admin_session(engine_harness)
create_resp = client.post(
"/api/device_list_views",
json={"name": "Custom", "columns": ["hostname"], "filters": {"site": "Main"}},
)
assert create_resp.status_code == 201
view_id = create_resp.get_json()["id"]
fetch_resp = client.get("/api/device_list_views")
assert any(view["id"] == view_id for view in fetch_resp.get_json()["views"])
update_resp = client.put(
f"/api/device_list_views/{view_id}",
json={"name": "Custom-2"},
)
assert update_resp.status_code == 200
assert update_resp.get_json()["name"] == "Custom-2"
delete_resp = client.delete(f"/api/device_list_views/{view_id}")
assert delete_resp.status_code == 200
def test_repo_current_hash_uses_cache(engine_harness: EngineTestHarness, monkeypatch: pytest.MonkeyPatch) -> None:
calls = {"count": 0}
class DummyResponse:
def __init__(self, status_code: int, payload: Any):
self.status_code = status_code
self._payload = payload
def json(self) -> Any:
return self._payload
def fake_get(url: str, headers: Any, timeout: int) -> DummyResponse:
calls["count"] += 1
if calls["count"] == 1:
return DummyResponse(200, {"commit": {"sha": "abc123"}})
raise device_management.requests.RequestException("network error")
monkeypatch.setattr(device_management.requests, "get", fake_get)
client = engine_harness.app.test_client()
first = client.get("/api/repo/current_hash?repo=test/test&branch=main")
assert first.status_code == 200
assert first.get_json()["sha"] == "abc123"
second = client.get("/api/repo/current_hash?repo=test/test&branch=main")
assert second.status_code == 200
second_payload = second.get_json()
assert second_payload["sha"] == "abc123"
assert second_payload["cached"] is True or second_payload["source"].startswith("cache")
assert calls["count"] == 1
def test_agent_hash_list_permissions(engine_harness: EngineTestHarness) -> None:
client = engine_harness.app.test_client()
forbidden = client.get("/api/agent/hash_list", environ_base={"REMOTE_ADDR": "192.0.2.10"})
assert forbidden.status_code == 403
allowed = client.get("/api/agent/hash_list", environ_base={"REMOTE_ADDR": "127.0.0.1"})
assert allowed.status_code == 200
agents = allowed.get_json()["agents"]
assert agents and agents[0]["hostname"] == "test-device"