add endpoints plugin with fleet list view and monitoring drafts
Server-side RMM telemetry plugin: current-snapshot endpointstates plus append-only curated endpointevents, resolved to existing Computer assets by hostname. Adds ADR-010 asset panels (status, services, recent events), dashboard quiet and stability cards, and a minimal Fleet list view that links into existing PC detail pages. Registers the activity sidebar icon and a dev-only sample seeder. Ingest arrives on the generic collector route; the future C# agent design record lives in docs/DESIGN-DECISIONS-agent.md.
This commit is contained in:
92
plugins/endpoints/tests/test_plugin.py
Normal file
92
plugins/endpoints/tests/test_plugin.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""Endpoints plugin smoke + collector-contract tests.
|
||||
|
||||
Asserts the plugin loads cleanly, satisfies the framework contract, and that
|
||||
its ADR-006 collector schema is well formed. Extend with DB-backed ingest tests
|
||||
as the plugin grows.
|
||||
"""
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
from plugins.endpoints.plugin import EndpointsPlugin, CURATED_EVENT_IDS
|
||||
from plugins.endpoints.eventcatalog import EVENT_CATALOG, CRITICAL_EVENT_IDS
|
||||
|
||||
|
||||
def test_endpoints_plugin_meta_is_valid():
|
||||
"""meta returns a PluginMeta with the expected name and prefix."""
|
||||
plugin = EndpointsPlugin()
|
||||
assert plugin.meta.name == 'endpoints'
|
||||
assert plugin.meta.api_prefix == '/api/endpoints'
|
||||
assert 'computers' in plugin.meta.dependencies
|
||||
|
||||
|
||||
def test_endpoints_plugin_get_blueprint_returns_blueprint():
|
||||
"""get_blueprint returns a Flask Blueprint, not None."""
|
||||
plugin = EndpointsPlugin()
|
||||
assert isinstance(plugin.get_blueprint(), Blueprint)
|
||||
|
||||
|
||||
def test_endpoints_plugin_get_models_returns_models():
|
||||
"""get_models returns the two telemetry tables."""
|
||||
plugin = EndpointsPlugin()
|
||||
models = plugin.get_models()
|
||||
assert len(models) >= 2
|
||||
for model in models:
|
||||
assert hasattr(model, '__tablename__')
|
||||
|
||||
|
||||
def test_endpoints_collector_schema_identity_is_hostname():
|
||||
"""ADR-006 schema declares hostname as the identity field."""
|
||||
plugin = EndpointsPlugin()
|
||||
schema = plugin.get_collector_schema()
|
||||
assert schema is not None
|
||||
assert schema['identityfield'] == 'hostname'
|
||||
assert 'hostname' in schema['fields']['required']
|
||||
|
||||
|
||||
def test_endpoints_curated_event_ids_include_core_security_ids():
|
||||
"""The curated catalog covers the agreed logon/stability IDs."""
|
||||
for eventid in (4624, 4625, 4634, 4672, 4697, 4698, 41, 4618, 55, 7011,
|
||||
10016, 1001):
|
||||
assert eventid in CURATED_EVENT_IDS
|
||||
|
||||
|
||||
def test_curated_ids_match_the_event_catalog():
|
||||
"""Ingest filter and display catalog cannot drift apart."""
|
||||
assert CURATED_EVENT_IDS == frozenset(EVENT_CATALOG)
|
||||
|
||||
|
||||
def test_critical_event_ids_are_a_subset_of_the_catalog():
|
||||
"""Dashboard stability card only queries known critical IDs."""
|
||||
assert set(CRITICAL_EVENT_IDS)
|
||||
assert set(CRITICAL_EVENT_IDS).issubset(set(EVENT_CATALOG))
|
||||
|
||||
|
||||
def test_endpoints_asset_panels_are_computer_scoped():
|
||||
"""Every asset panel targets computer assets only, with an endpoint."""
|
||||
plugin = EndpointsPlugin()
|
||||
panels = plugin.get_asset_panels()
|
||||
assert len(panels) == 3
|
||||
for panel in panels:
|
||||
assert panel['assettypes'] == ['computer']
|
||||
assert panel['endpoint'].startswith('/api/endpoints/asset/{assetid}')
|
||||
assert panel['render'] in ('keyvalue', 'list')
|
||||
|
||||
|
||||
def test_endpoints_dashboard_widgets_declared():
|
||||
"""Quiet + stability exception cards are declared and hide when empty."""
|
||||
plugin = EndpointsPlugin()
|
||||
widgets = plugin.get_dashboard_widgets()
|
||||
ids = {w['id'] for w in widgets}
|
||||
assert {'endpoints-quiet', 'endpoints-stability'} <= ids
|
||||
for widget in widgets:
|
||||
assert widget['render'] == 'exceptions'
|
||||
assert widget['empty'] == 'hide'
|
||||
assert widget['permission'] == 'endpoints.view'
|
||||
|
||||
|
||||
def test_endpoints_navigation_item_points_at_fleet_view():
|
||||
"""The fleet list view is exposed as a single /endpoints nav item."""
|
||||
plugin = EndpointsPlugin()
|
||||
items = plugin.get_navigation_items()
|
||||
routes = {item['route'] for item in items}
|
||||
assert '/endpoints' in routes
|
||||
Reference in New Issue
Block a user