feat: add agent zones

This commit is contained in:
lussierj committed 2026-08-26 15:48:59 +00:00
1 parent d8cd7ad058
commit 82309db79a
29 files changed
+2242 -255

No files matched your search

+2
View File
@@ -199,6 +199,8 @@ class AgentSourceContractTests(unittest.TestCase):
self.assertNotIn('DefaultValue "super"', service)
self.assertNotIn('DefaultValue "super"', powershell)
self.assertIn('"X-MinerOps-Agent-ID"', service)
self.assertIn('.agent_zone_id', service)
self.assertIn('"agent_zone_id"', (root / "config.example.json").read_text())
self.assertNotIn("?agent_id=", service)
self.assertIn('"get.miner.setting"', control)
self.assertIn("$attempt -le 3", control)
+283
View File
@@ -0,0 +1,283 @@
"""Focused model, service, router, and architecture tests for Agent Zones v1."""
import unittest
from pathlib import Path
from uuid import UUID, uuid4
from fastapi import FastAPI
from fastapi.testclient import TestClient
from app.agent_commands.dependencies import require_agent_identity
from app.agent_zones.dependencies import get_agent_zone_service
from app.agent_zones.exceptions import AgentZoneValidationError
from app.agent_zones.models import AgentCheckinRequest, AgentZoneCreateRequest
from app.agent_zones.router import router
from app.agent_zones.service import AgentZoneService
from app.identity import get_current_user, required_api_permission
SITE_A = UUID("00000000-0000-4000-8000-000000001601")
SITE_B = UUID("00000000-0000-4000-8000-000000001602")
ZONE_A = UUID("00000000-0000-4000-8000-000000001611")
ZONE_B = UUID("00000000-0000-4000-8000-000000001612")
MINER_A = UUID("00000000-0000-4000-8000-000000001621")
APP_ROOT = Path(__file__).parents[1] / "app"
def zone(zone_id=ZONE_A, site_id=SITE_A, *, enabled=True, name="Main Room"):
return {
"id": zone_id,
"site_id": site_id,
"name": name,
"description": None,
"is_enabled": enabled,
"assigned_agent_id": None,
"networks": [],
}
class FakeRepository:
def __init__(self):
self.zones = {ZONE_A: zone()}
self.agent = None
self.saved = []
self.assign_result = {MINER_A: "assigned"}
def get_agent(self, agent_id):
return self.agent if self.agent and self.agent["agent_id"] == agent_id else None
def get_zone(self, zone_id):
return self.zones.get(zone_id)
def list_usable_zones(self, site_id):
return [
value for value in self.zones.values()
if value["site_id"] == site_id and value["is_enabled"]
]
def list_zones(self, site_id=None):
return [
value for value in self.zones.values()
if site_id is None or value["site_id"] == site_id
]
def create_zone(self, *, site_id, name, description, networks):
created_id = uuid4()
self.zones[created_id] = {
**zone(created_id, site_id, name=name),
"description": description,
"networks": [
{"id": uuid4(), "network": network} for network in networks
],
}
return created_id
def save_checkin(self, **kwargs):
self.saved.append(kwargs)
self.agent = {
"agent_id": kwargs["agent_id"],
"site_id": kwargs["site_id"],
"agent_zone_id": kwargs["agent_zone_id"],
"site_name": kwargs["site_name"],
}
def get_agent_assignment(self, agent_id):
if not self.agent or self.agent["agent_id"] != agent_id:
return None
zone_row = self.zones.get(self.agent.get("agent_zone_id"))
return {
**self.agent,
"zone_enabled": zone_row["is_enabled"] if zone_row else None,
}
def assign_miners_to_zone(self, miner_ids, agent_zone_id):
self.assigned = (list(miner_ids), agent_zone_id)
return {miner_id: self.assign_result.get(miner_id, "miner_not_found")
for miner_id in miner_ids}
class AgentZoneModelTests(unittest.TestCase):
def test_names_and_networks_are_normalized(self):
request = AgentZoneCreateRequest(
site_id=SITE_A,
name=" Main Room ",
description=" Local VLAN ",
networks=["10.30.0.0/24", "2001:db8::/64"],
)
self.assertEqual(request.name, "Main Room")
self.assertEqual(request.description, "Local VLAN")
self.assertEqual(request.networks, ["10.30.0.0/24", "2001:db8::/64"])
def test_invalid_host_network_duplicate_and_extra_fields_are_rejected(self):
for networks in (["10.30.0.7/24"], ["10.30.0.0/24", "10.30.0.0/24"]):
with self.subTest(networks=networks), self.assertRaises(Exception):
AgentZoneCreateRequest(site_id=SITE_A, name="Zone", networks=networks)
with self.assertRaises(Exception):
AgentZoneCreateRequest(site_id=SITE_A, name="Zone", unknown=True)
class AgentZoneServiceTests(unittest.TestCase):
def setUp(self):
self.repository = FakeRepository()
self.service = AgentZoneService(self.repository)
def checkin(self, **kwargs):
return self.service.checkin(
"agent-a",
AgentCheckinRequest(agent_id="agent-a", site_name="North", **kwargs),
)
def test_explicit_first_assignment_and_same_assignment_are_allowed(self):
first = self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
second = self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
self.assertTrue(first.routing_available)
self.assertEqual(second.agent_zone_id, ZONE_A)
def test_explicit_zone_site_mismatch_and_identity_spoof_are_rejected(self):
with self.assertRaisesRegex(AgentZoneValidationError, "does not belong"):
self.checkin(site_id=SITE_B, agent_zone_id=ZONE_A)
with self.assertRaisesRegex(AgentZoneValidationError, "authenticated"):
self.service.checkin(
"agent-a", AgentCheckinRequest(agent_id="agent-b", site_id=SITE_A)
)
def test_established_agent_cannot_change_zone_within_site(self):
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
self.repository.zones[ZONE_B] = zone(ZONE_B, SITE_A, name="Room 2")
with self.assertRaisesRegex(AgentZoneValidationError, "different Agent Zone"):
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_B)
self.assertEqual(self.repository.agent["agent_zone_id"], ZONE_A)
def test_established_agent_site_conflict_precedes_zone_conflict(self):
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
self.repository.zones[ZONE_B] = zone(ZONE_B, SITE_B, name="Remote")
with self.assertRaisesRegex(AgentZoneValidationError, "cannot change Sites"):
self.checkin(site_id=SITE_B, agent_zone_id=ZONE_B)
self.assertEqual(self.repository.agent["agent_zone_id"], ZONE_A)
def test_omitted_zone_preserves_established_assignment_and_site(self):
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
result = self.service.checkin(
"agent-a", AgentCheckinRequest(agent_id="agent-a", site_name="North")
)
self.assertEqual(result.site_id, SITE_A)
self.assertEqual(result.agent_zone_id, ZONE_A)
def test_transitional_resolution_requires_exactly_one_enabled_zone(self):
single = self.checkin(site_id=SITE_A)
self.assertEqual(single.agent_zone_id, ZONE_A)
self.repository.agent = None
self.repository.zones = {}
zero = self.checkin(site_id=SITE_A)
self.assertEqual(zero.routing_reason, "agent_zone_unavailable")
self.repository.agent = None
self.repository.zones = {
ZONE_A: zone(),
ZONE_B: zone(ZONE_B, SITE_A, name="Room 2"),
}
many = self.checkin(site_id=SITE_A)
self.assertEqual(many.routing_reason, "agent_zone_ambiguous")
self.assertIsNone(many.agent_zone_id)
def test_disabled_zone_is_not_selected_and_existing_assignment_is_factual(self):
self.repository.zones[ZONE_A]["is_enabled"] = False
unassigned = self.checkin(site_id=SITE_A)
self.assertEqual(unassigned.routing_reason, "agent_zone_unavailable")
self.repository.agent = {
"agent_id": "agent-a", "site_id": SITE_A,
"agent_zone_id": ZONE_A, "site_name": "North",
}
existing = self.service.checkin(
"agent-a", AgentCheckinRequest(agent_id="agent-a")
)
self.assertEqual(existing.routing_reason, "agent_zone_disabled")
self.assertEqual(existing.agent_zone_id, ZONE_A)
def test_payload_is_sanitized_before_persistence(self):
request = AgentCheckinRequest.model_validate({
"agent_id": "agent-a", "site_id": str(SITE_A),
"api3_password": "secret", "nested": {"access_token": "secret"},
"control_available": True,
})
self.service.checkin("agent-a", request)
payload = self.repository.saved[-1]["payload"]
self.assertNotIn("password", str(payload).lower())
self.assertNotIn("token", str(payload).lower())
self.assertTrue(payload["control_available"])
def test_miner_zone_assignment_surfaces_conflicts_without_rehoming(self):
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
self.repository.assign_result[MINER_A] = "agent_zone_conflict"
result = self.service.associate_miners_from_agent("agent-a", [MINER_A])
self.assertEqual(result.outcomes[MINER_A], "agent_zone_conflict")
self.assertEqual(self.repository.assigned, ([MINER_A], ZONE_A))
def test_routing_context_rejects_stale_recorded_zone(self):
self.checkin(site_id=SITE_A, agent_zone_id=ZONE_A)
result = self.service.routing_context("agent-a", ZONE_B)
self.assertEqual(result.routing_reason, "agent_zone_conflict")
self.assertIsNone(result.site_id)
class AgentZoneRouterTests(unittest.TestCase):
def setUp(self):
self.repository = FakeRepository()
self.service = AgentZoneService(self.repository)
self.app = FastAPI()
self.app.include_router(router)
self.app.dependency_overrides[get_agent_zone_service] = lambda: self.service
self.app.dependency_overrides[require_agent_identity] = lambda: "agent-a"
self.client = TestClient(self.app)
def tearDown(self):
self.client.close()
def authenticate(self, permissions):
self.app.dependency_overrides[get_current_user] = lambda: {
"id": str(uuid4()), "username": "operator", "role": "Custom",
"permissions": list(permissions),
}
def test_list_and_create_use_existing_agent_permissions(self):
self.assertEqual(self.client.get("/api/agent-zones").status_code, 401)
self.authenticate(("agents.view",))
self.assertEqual(self.client.get("/api/agent-zones").status_code, 200)
self.assertEqual(
self.client.post(
"/api/agent-zones", json={"site_id": str(SITE_A), "name": "Room 2"}
).status_code,
403,
)
self.authenticate(("agents.manage",))
response = self.client.post(
"/api/agent-zones",
json={"site_id": str(SITE_A), "name": "Room 2", "networks": ["10.40.0.0/24"]},
)
self.assertEqual(response.status_code, 201)
self.assertEqual(response.json()["data"]["name"], "Room 2")
def test_checkin_uses_authenticated_agent_identity(self):
response = self.client.post(
"/api/agent/checkin",
json={"agent_id": "agent-a", "site_id": str(SITE_A),
"agent_zone_id": str(ZONE_A)},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["agent_id"], "agent-a")
self.assertNotIn("secret", response.text.lower())
def test_permission_and_architecture_audits(self):
self.assertEqual(required_api_permission("GET", "/api/agent-zones"), "agents.view")
self.assertEqual(required_api_permission("POST", "/api/agent-zones"), "agents.manage")
router_source = (APP_ROOT / "agent_zones" / "router.py").read_text()
service_source = (APP_ROOT / "agent_zones" / "service.py").read_text()
repository_source = (APP_ROOT / "agent_zones" / "repository.py").read_text()
self.assertNotIn("psycopg", router_source)
self.assertNotIn("SELECT ", router_source)
self.assertNotIn("fastapi", service_source.lower())
self.assertNotIn("fastapi", repository_source.lower())
self.assertNotIn('@app.post("/api/agent/checkin")', (APP_ROOT / "main.py").read_text())
if __name__ == "__main__":
unittest.main()
+244
View File
@@ -0,0 +1,244 @@
"""Real PostgreSQL integrity and routing tests for Agent Zones v1."""
import unittest
from uuid import UUID
import psycopg
from psycopg.errors import ForeignKeyViolation, UniqueViolation
from app.agent_zones.repository import AgentZoneRepository
from app.config import settings
from app.miner_control.repository import MinerControlRepository
from app.miner_control.service import MinerControlService
SITE_A = UUID("00000000-0000-4000-8000-000000001630")
SITE_B = UUID("00000000-0000-4000-8000-000000001631")
ZONE_A = UUID("00000000-0000-4000-8000-000000001632")
ZONE_B = UUID("00000000-0000-4000-8000-000000001633")
ZONE_C = UUID("00000000-0000-4000-8000-000000001636")
MINER_UNASSIGNED = UUID("00000000-0000-4000-8000-000000001634")
MINER_OTHER = UUID("00000000-0000-4000-8000-000000001635")
AGENT_A = "agent-zone-postgres-a"
def connect():
return psycopg.connect(
host=settings.postgres_host,
port=settings.postgres_port,
dbname=settings.postgres_db,
user=settings.postgres_user,
password=settings.postgres_password,
connect_timeout=5,
)
class AgentZonePostgresTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
connection = connect()
try:
with connection.cursor() as cur:
cur.execute(
"INSERT INTO sites (id, name) VALUES (%s, 'Zone Test A'), "
"(%s, 'Zone Test B') ON CONFLICT (id) DO NOTHING",
(SITE_A, SITE_B),
)
cur.execute(
"INSERT INTO agent_zones (id, site_id, name) VALUES "
"(%s, %s, 'Main Room'), (%s, %s, 'Remote Room'), "
"(%s, %s, 'Unstaffed Zone') "
"ON CONFLICT (id) DO UPDATE SET is_enabled = true",
(ZONE_A, SITE_A, ZONE_B, SITE_B, ZONE_C, SITE_A),
)
cur.execute(
"INSERT INTO miners (id, site_id, asset_tag, current_ip, status, "
"api3_supported, first_seen_at, last_seen_at) VALUES "
"(%s, %s, 'ZONE-UNASSIGNED', '192.0.2.31', 'online', true, NULL, NULL), "
"(%s, %s, 'ZONE-OTHER', '192.0.2.32', 'online', true, NULL, NULL) "
"ON CONFLICT (id) DO UPDATE SET agent_zone_id = NULL",
(MINER_UNASSIGNED, SITE_A, MINER_OTHER, SITE_B),
)
cur.execute(
"UPDATE miners SET agent_zone_id = %s WHERE id = %s",
(ZONE_B, MINER_OTHER),
)
cur.execute(
"INSERT INTO minerops_site_agents ("
"agent_id, site_id, agent_zone_id, site_name, last_payload) "
"VALUES (%s, %s, %s, 'Zone Test A', '{\"control_available\":true}') "
"ON CONFLICT (agent_id) DO UPDATE SET site_id = EXCLUDED.site_id, "
"agent_zone_id = EXCLUDED.agent_zone_id, last_payload = EXCLUDED.last_payload",
(AGENT_A, SITE_A, ZONE_A),
)
connection.commit()
finally:
connection.close()
@classmethod
def tearDownClass(cls):
connection = connect()
try:
with connection.cursor() as cur:
cur.execute("DELETE FROM minerops_site_agents WHERE agent_id LIKE 'agent-zone-postgres-%'")
cur.execute("DELETE FROM miners WHERE id IN (%s, %s)", (MINER_UNASSIGNED, MINER_OTHER))
cur.execute("DELETE FROM agent_zone_network_ranges WHERE agent_zone_id IN (%s, %s, %s)", (ZONE_A, ZONE_B, ZONE_C))
cur.execute("DELETE FROM agent_zones WHERE id IN (%s, %s, %s)", (ZONE_A, ZONE_B, ZONE_C))
cur.execute("DELETE FROM sites WHERE id IN (%s, %s)", (SITE_A, SITE_B))
connection.commit()
finally:
connection.close()
def setUp(self):
connection = connect()
try:
with connection.cursor() as cur:
cur.execute(
"UPDATE agent_zones SET is_enabled = true "
"WHERE id IN (%s, %s, %s)",
(ZONE_A, ZONE_B, ZONE_C),
)
cur.execute(
"UPDATE miners SET agent_zone_id = NULL WHERE id = %s",
(MINER_UNASSIGNED,),
)
cur.execute(
"UPDATE miners SET site_id = %s, agent_zone_id = %s WHERE id = %s",
(SITE_B, ZONE_B, MINER_OTHER),
)
cur.execute(
"UPDATE minerops_site_agents SET site_id = %s, agent_zone_id = %s "
"WHERE agent_id = %s",
(SITE_A, ZONE_A, AGENT_A),
)
connection.commit()
finally:
connection.close()
def test_repository_lists_multiple_canonical_networks(self):
connection = connect()
try:
with connection.cursor() as cur:
cur.execute(
"INSERT INTO agent_zone_network_ranges (agent_zone_id, network) "
"VALUES (%s, '10.30.0.0/24'), (%s, '10.31.0.0/24') "
"ON CONFLICT DO NOTHING",
(ZONE_A, ZONE_A),
)
connection.commit()
finally:
connection.close()
listed = AgentZoneRepository(connect).list_zones(SITE_A)
self.assertEqual([item["network"] for item in listed[0]["networks"]],
["10.30.0.0/24", "10.31.0.0/24"])
def test_active_names_are_case_insensitively_unique_per_site(self):
connection = connect()
try:
with self.assertRaises(UniqueViolation), connection.cursor() as cur:
cur.execute(
"INSERT INTO agent_zones (site_id, name) VALUES (%s, 'main room')",
(SITE_A,),
)
finally:
connection.rollback()
connection.close()
def test_one_agent_per_zone_but_multiple_zones_per_site(self):
connection = connect()
try:
with self.assertRaises(UniqueViolation), connection.cursor() as cur:
cur.execute(
"INSERT INTO minerops_site_agents "
"(agent_id, site_id, agent_zone_id, site_name) "
"VALUES ('agent-zone-postgres-duplicate', %s, %s, 'Duplicate')",
(SITE_A, ZONE_A),
)
finally:
connection.rollback()
connection.close()
def test_composite_foreign_keys_reject_cross_site_assignment(self):
connection = connect()
try:
with self.assertRaises(ForeignKeyViolation), connection.cursor() as cur:
cur.execute(
"UPDATE miners SET agent_zone_id = %s WHERE id = %s",
(ZONE_A, MINER_OTHER),
)
finally:
connection.rollback()
connection.close()
def test_unassigned_miner_inherits_zone_and_existing_other_zone_wins(self):
repository = AgentZoneRepository(connect)
outcomes = repository.assign_miners_to_zone(
[MINER_UNASSIGNED, MINER_OTHER], ZONE_A
)
self.assertEqual(outcomes[MINER_UNASSIGNED], "assigned")
self.assertEqual(outcomes[MINER_OTHER], "agent_zone_conflict")
connection = connect()
try:
with connection.cursor() as cur:
cur.execute(
"SELECT id, agent_zone_id FROM miners WHERE id IN (%s, %s)",
(MINER_UNASSIGNED, MINER_OTHER),
)
assignments = dict(cur.fetchall())
finally:
connection.close()
self.assertEqual(assignments[MINER_UNASSIGNED], ZONE_A)
self.assertEqual(assignments[MINER_OTHER], ZONE_B)
def test_disabled_zone_blocks_control_without_site_fallback(self):
connection = connect()
try:
with connection.cursor() as cur:
cur.execute(
"UPDATE miners SET agent_zone_id = %s WHERE id = %s",
(ZONE_A, MINER_UNASSIGNED),
)
cur.execute("UPDATE agent_zones SET is_enabled = false WHERE id = %s", (ZONE_A,))
connection.commit()
finally:
connection.close()
try:
projection = MinerControlService(MinerControlRepository(connect)).get_controls(
MINER_UNASSIGNED
)
self.assertEqual(projection.service_control.reason, "agent_zone_disabled")
finally:
connection = connect()
try:
with connection.cursor() as cur:
cur.execute("UPDATE agent_zones SET is_enabled = true WHERE id = %s", (ZONE_A,))
connection.commit()
finally:
connection.close()
def test_enabled_zone_without_agent_does_not_fall_back_within_site(self):
connection = connect()
try:
with connection.cursor() as cur:
cur.execute(
"UPDATE miners SET agent_zone_id = %s WHERE id = %s",
(ZONE_C, MINER_UNASSIGNED),
)
connection.commit()
finally:
connection.close()
projection = MinerControlService(MinerControlRepository(connect)).get_controls(
MINER_UNASSIGNED
)
self.assertEqual(projection.service_control.reason, "agent_unavailable")
def test_null_zone_does_not_fall_back_to_site_agent(self):
projection = MinerControlService(MinerControlRepository(connect)).get_controls(
MINER_UNASSIGNED
)
self.assertEqual(projection.service_control.reason, "agent_zone_unassigned")
if __name__ == "__main__":
unittest.main()
+7 -2
View File
@@ -26,6 +26,9 @@ def context(**overrides):
result = {
"id": MINER_ID,
"site_id": uuid4(),
"agent_zone_id": uuid4(),
"agent_zone_exists": True,
"agent_zone_enabled": True,
"current_ip": "192.0.2.10",
"runtime_state": "online",
"configured_performance_mode": "normal",
@@ -78,8 +81,10 @@ class MinerControlServiceTests(unittest.TestCase):
def test_capability_missing_facts_is_not_optimistic(self):
cases = (
({"site_id": None}, "unavailable", "site_unassigned"),
({"agent_id": None}, "unavailable", "agent_unassigned"),
({"agent_zone_id": None}, "unavailable", "agent_zone_unassigned"),
({"agent_zone_exists": False}, "unavailable", "agent_zone_missing"),
({"agent_zone_enabled": False}, "unavailable", "agent_zone_disabled"),
({"agent_id": None}, "unavailable", "agent_unavailable"),
({"current_ip": None}, "unavailable", "current_ip_missing"),
({"api3_supported": False}, "unavailable", "api3_unsupported"),
({"api3_supported": None}, "unknown", "api3_support_unknown"),
+16 -6
View File
@@ -18,6 +18,7 @@ from app.miner_control.service import MinerControlService
SITE_ID = UUID("00000000-0000-4000-8000-000000001510")
ZONE_ID = UUID("00000000-0000-4000-8000-000000001514")
MINER_ID = UUID("00000000-0000-4000-8000-000000001511")
USER_ID = UUID("00000000-0000-4000-8000-000000001512")
OTHER_MINER_ID = UUID("00000000-0000-4000-8000-000000001513")
@@ -54,6 +55,12 @@ class MinerControlPostgresTests(unittest.TestCase):
"INSERT INTO sites (id, name) VALUES (%s, 'Control Test Site') "
"ON CONFLICT (id) DO NOTHING", (SITE_ID,)
)
cur.execute(
"INSERT INTO agent_zones (id, site_id, name) "
"VALUES (%s, %s, 'Control Test Zone') "
"ON CONFLICT (id) DO UPDATE SET is_enabled = true",
(ZONE_ID, SITE_ID),
)
cur.execute(
"""
INSERT INTO users (id, username, display_name, password_hash, role_id)
@@ -64,23 +71,25 @@ class MinerControlPostgresTests(unittest.TestCase):
cur.execute(
"""
INSERT INTO miners (
id, site_id, asset_tag, current_ip, status, api3_supported,
id, site_id, agent_zone_id, asset_tag, current_ip, status, api3_supported,
configured_performance_mode, first_seen_at, last_seen_at
) VALUES (%s, %s, 'CONTROL-TEST', '192.0.2.10', 'online', true,
) VALUES (%s, %s, %s, 'CONTROL-TEST', '192.0.2.10', 'online', true,
'normal', NULL, NULL)
ON CONFLICT (id) DO UPDATE SET site_id = EXCLUDED.site_id,
agent_zone_id = EXCLUDED.agent_zone_id,
current_ip = EXCLUDED.current_ip, status = EXCLUDED.status,
api3_supported = true
""", (MINER_ID, SITE_ID)
""", (MINER_ID, SITE_ID, ZONE_ID)
)
cur.execute(
"""
INSERT INTO minerops_site_agents (
agent_id, site_id, site_name, last_payload
) VALUES (%s, %s, 'Control Test Site', %s)
agent_id, site_id, agent_zone_id, site_name, last_payload
) VALUES (%s, %s, %s, 'Control Test Site', %s)
ON CONFLICT (agent_id) DO UPDATE SET site_id = EXCLUDED.site_id,
agent_zone_id = EXCLUDED.agent_zone_id,
last_payload = EXCLUDED.last_payload
""", (AGENT_ID, SITE_ID, Jsonb({"control_available": True}))
""", (AGENT_ID, SITE_ID, ZONE_ID, Jsonb({"control_available": True}))
)
connection.commit()
finally:
@@ -100,6 +109,7 @@ class MinerControlPostgresTests(unittest.TestCase):
cur.execute("DELETE FROM minerops_site_agents WHERE agent_id = %s", (AGENT_ID,))
cur.execute("DELETE FROM miners WHERE id = %s", (MINER_ID,))
cur.execute("DELETE FROM users WHERE id = %s", (USER_ID,))
cur.execute("DELETE FROM agent_zones WHERE id = %s", (ZONE_ID,))
cur.execute("DELETE FROM sites WHERE id = %s", (SITE_ID,))
connection.commit()
finally:
+1 -1
View File
@@ -19,7 +19,7 @@ from app.system_health.service import SystemHealthService
APP_ROOT = Path(__file__).parents[1] / "app"
EXPECTED_HEAD = "0015_miner_control_foundation"
EXPECTED_HEAD = "0016_agent_zones"
def database_payload(revision=EXPECTED_HEAD):