mirror of
https://github.com/mblanke/ThreatHunt.git
synced 2026-03-01 14:00:20 -05:00
feat: Add Playbook Manager, Saved Searches, and Timeline View components
- Implemented PlaybookManager for creating and managing investigation playbooks with templates. - Added SavedSearches component for managing bookmarked queries and recurring scans. - Introduced TimelineView for visualizing forensic event timelines with zoomable charts. - Enhanced backend processing with auto-queued jobs for dataset uploads and improved database concurrency. - Updated frontend components for better user experience and performance optimizations. - Documented changes in update log for future reference.
This commit is contained in:
84
backend/tests/test_network.py
Normal file
84
backend/tests/test_network.py
Normal file
@@ -0,0 +1,84 @@
|
||||
"""Tests for network inventory endpoints and cache/polling behavior."""
|
||||
|
||||
import io
|
||||
|
||||
import pytest
|
||||
|
||||
from app.services.host_inventory import inventory_cache
|
||||
from tests.conftest import SAMPLE_CSV
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_inventory_status_none_for_unknown_hunt(client):
|
||||
hunt_id = "hunt-does-not-exist"
|
||||
inventory_cache.invalidate(hunt_id)
|
||||
inventory_cache.clear_building(hunt_id)
|
||||
|
||||
res = await client.get(f"/api/network/inventory-status?hunt_id={hunt_id}")
|
||||
assert res.status_code == 200
|
||||
body = res.json()
|
||||
assert body["hunt_id"] == hunt_id
|
||||
assert body["status"] == "none"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_host_inventory_cold_cache_returns_202(client):
|
||||
# Create hunt and upload dataset linked to that hunt
|
||||
hunt = await client.post("/api/hunts", json={"name": "Net Hunt"})
|
||||
hunt_id = hunt.json()["id"]
|
||||
|
||||
files = {"file": ("network.csv", io.BytesIO(SAMPLE_CSV), "text/csv")}
|
||||
up = await client.post("/api/datasets/upload", files=files, params={"hunt_id": hunt_id})
|
||||
assert up.status_code == 200
|
||||
|
||||
# Ensure cache is cold for this hunt
|
||||
inventory_cache.invalidate(hunt_id)
|
||||
inventory_cache.clear_building(hunt_id)
|
||||
|
||||
res = await client.get(f"/api/network/host-inventory?hunt_id={hunt_id}")
|
||||
assert res.status_code == 202
|
||||
body = res.json()
|
||||
assert body["status"] == "building"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_host_inventory_ready_cache_returns_200(client):
|
||||
hunt = await client.post("/api/hunts", json={"name": "Ready Hunt"})
|
||||
hunt_id = hunt.json()["id"]
|
||||
|
||||
mock_inventory = {
|
||||
"hosts": [
|
||||
{
|
||||
"id": "host-1",
|
||||
"hostname": "HOST-1",
|
||||
"fqdn": "HOST-1.local",
|
||||
"client_id": "C.1234abcd",
|
||||
"ips": ["10.0.0.10"],
|
||||
"os": "Windows 10",
|
||||
"users": ["alice"],
|
||||
"datasets": ["test"],
|
||||
"row_count": 5,
|
||||
}
|
||||
],
|
||||
"connections": [],
|
||||
"stats": {
|
||||
"total_hosts": 1,
|
||||
"hosts_with_ips": 1,
|
||||
"hosts_with_users": 1,
|
||||
"total_datasets_scanned": 1,
|
||||
"total_rows_scanned": 5,
|
||||
},
|
||||
}
|
||||
|
||||
inventory_cache.put(hunt_id, mock_inventory)
|
||||
|
||||
res = await client.get(f"/api/network/host-inventory?hunt_id={hunt_id}")
|
||||
assert res.status_code == 200
|
||||
body = res.json()
|
||||
assert body["stats"]["total_hosts"] == 1
|
||||
assert len(body["hosts"]) == 1
|
||||
assert body["hosts"][0]["hostname"] == "HOST-1"
|
||||
|
||||
status_res = await client.get(f"/api/network/inventory-status?hunt_id={hunt_id}")
|
||||
assert status_res.status_code == 200
|
||||
assert status_res.json()["status"] == "ready"
|
||||
Reference in New Issue
Block a user