"""Discovery authority: real PostgreSQL + registered HTTP routes; disposable only.""" from concurrent.futures import ThreadPoolExecutor import copy, json, os, threading, time, unittest from uuid import uuid4 from unittest.mock import patch from fastapi.testclient import TestClient from psycopg.rows import dict_row from psycopg.types.json import Jsonb from app.main import app from app.config import settings from app.identity import create_access_token from app.agent_auth.service import hash_agent_secret from app.agent_zones.repository import default_connection_factory as connect from app.discovery.repository import DiscoveryRepository class DiscoveryFixtures: @classmethod def setUpClass(cls): if ( os.getenv("MINEROPS_DISCOVERY_DISPOSABLE") != "1" or settings.postgres_db != "discovery_review" ): raise RuntimeError("Explicit disposable discovery database required") def setUp(self): self.sites = [uuid4(), uuid4()] self.zones = [uuid4(), uuid4()] self.agents = ["discovery-test-" + uuid4().hex for _ in range(2)] self.uid = uuid4() self.secrets = ["disposable-" + a for a in self.agents] self.client = TestClient(app, raise_server_exceptions=True) self.addCleanup(self.client.close) self.transport = patch( "app.main.send_whatsminer_read_command", side_effect=AssertionError("No miner I/O"), ).start() self.addCleanup(patch.stopall) with connect() as c, c.cursor() as q: for site, zone, agent in zip(self.sites, self.zones, self.agents): q.execute( "INSERT INTO sites(id,name) VALUES (%s,%s)", (site, str(site)) ) q.execute( "INSERT INTO agent_zones(id,site_id,name) VALUES (%s,%s,'Zone')", (zone, site), ) q.execute( "INSERT INTO agent_zone_network_ranges(agent_zone_id,network) VALUES (%s,'198.51.100.0/24')", (zone,), ) q.execute( """INSERT INTO minerops_site_agents(agent_id,site_id,agent_zone_id,site_name,agent_secret_hash,credential_created_at) VALUES (%s,%s,%s,'Test',%s,now())""", ( agent, site, zone, hash_agent_secret(self.secrets[self.agents.index(agent)]), ), ) q.execute( "INSERT INTO users(id,username,display_name,password_hash,role_id) SELECT %s,%s,'Reviewer','unused',id FROM roles WHERE name='Admin'", (self.uid, str(self.uid)), ) self.admin = { "Authorization": "Bearer " + create_access_token(str(self.uid), str(self.uid), "Admin") } self.repo = DiscoveryRepository() def headers(self, i=0): return { "X-MinerOps-Agent-ID": self.agents[i], "X-MinerOps-Agent-Secret": self.secrets[i], } def remap(self, value): if isinstance(value, dict): return {k: self.remap(v) for k, v in value.items()} if isinstance(value, list): return [self.remap(v) for v in value] if ( isinstance(value, str) and value.startswith("02:12:34:00:00:") and len(value) == 17 ): return ( "02:" + ":".join(f"{v:02X}" for v in self.uid.bytes[:4]) + ":" + value[-2:] ) return value def post(self, path, body, agent=0): return self.client.post( path, json=self.remap(body), headers=self.headers(agent) ) def review(self, row, action="add_to_inventory", **overrides): return self.client.post( "/api/agent/discoveries/review", json={ "discovery_id": row["id"], "version": row["version"], "action": action, **overrides, }, headers=self.admin, ) def observe(self, ip="198.51.100.10", mac="02:12:34:00:00:10", agent=0, **extra): result = self.post( "/api/agent/discoveries", { "agent_id": self.agents[agent], "discoveries": [{"ip_address": ip, "mac_address": mac, **extra}], }, agent, ) self.assertEqual(result.status_code, 200, result.text) return result.json()["discoveries"][0] def miner(self, i=0, ip="198.51.100.10", mac=None): mid = uuid4() mac = self.remap(mac) mac = mac or "02:" + ":".join(f"{v:02X}" for v in mid.bytes[:5]) with connect() as c, c.cursor() as q: q.execute( """INSERT INTO miners(id,site_id,agent_zone_id,current_ip,current_mac,status) VALUES (%s,%s,%s,%s,%s,'online')""", (mid, self.sites[i], self.zones[i], ip, mac), ) return mid, mac def scalar(self, sql, args=()): with connect() as c, c.cursor() as q: q.execute(sql, args) return q.fetchone()[0] def durable(self): data = {} with connect() as c, c.cursor() as q: for table in [ "miners", "miner_mac_ownerships", "miner_network_identities", "miner_status_snapshots", "minerops_agent_discoveries", "audit_logs", "minerops_agent_command_queue", ]: q.execute( "SELECT to_jsonb(t) FROM " + table + " t ORDER BY to_jsonb(t)::text" ) data[table] = q.fetchall() return data def known(self, i=0): r = self.client.get("/api/agent/known-miners", headers=self.headers(i)) self.assertEqual(r.status_code, 200, r.text) return r.json()["miners"] class DiscoverySecurityTests(DiscoveryFixtures, unittest.TestCase): def test_same_ip_distinct_zones_review_is_stable_and_scoped(self): a = self.observe(mac="02:12:34:00:00:11") b = self.observe(agent=1, mac="02:12:34:00:00:12") before = self.scalar( "SELECT to_jsonb(d) FROM minerops_agent_discoveries d WHERE id=%s", (b["id"],), ) r = self.review(a) self.assertEqual(r.status_code, 200, r.text) self.assertEqual( self.scalar( "SELECT to_jsonb(d) FROM minerops_agent_discoveries d WHERE id=%s", (b["id"],), ), before, ) self.assertEqual( self.scalar( "SELECT agent_zone_id FROM miners WHERE id=%s", (r.json()["miner_id"],) ), self.zones[0], ) r = self.review(b) self.assertEqual(r.status_code, 200, r.text) def test_ignore_non_miner_do_not_touch_other_zone(self): for action, ip in [("ignore", "198.51.100.11"), ("non_miner", "198.51.100.12")]: a = self.observe(ip=ip, mac=None) b = self.observe(ip=ip, mac=None, agent=1) self.assertEqual(self.review(a, action).status_code, 200) self.assertEqual( self.scalar( "SELECT discovery_status FROM minerops_agent_discoveries WHERE id=%s", (b["id"],), ), "new", ) def test_foreign_current_and_historical_mac_are_conflicts(self): mid, mac = self.miner(1) for spelling in [mac, mac.lower(), mac.replace(":", "-"), mac.replace(":", "")]: row = self.observe(ip="198.51.100.20", mac=spelling) self.assertEqual(row["discovery_status"], "zone_conflict") self.assertIsNone(row["matched_miner_id"]) self.assertEqual(self.review(row).status_code, 409) with connect() as c, c.cursor() as q: q.execute("UPDATE miners SET current_mac=NULL WHERE id=%s", (mid,)) row = self.observe(ip="198.51.100.21", mac=mac) self.assertEqual(row["discovery_status"], "zone_conflict") self.assertEqual( self.scalar( "SELECT miner_id FROM miner_mac_ownerships WHERE normalized_mac=%s", (mac.replace(":", "").lower(),), ), mid, ) def test_local_duplicate_ip_and_contradictory_ids_fail(self): self.miner() self.miner() row = self.observe(mac=None) self.assertEqual(row["discovery_status"], "zone_conflict") self.assertEqual(self.review(row).status_code, 409) def test_spoofed_identity_and_zone_do_not_authorize(self): r = self.post( "/api/agent/discoveries", {"agent_id": self.agents[1], "discoveries": []} ) self.assertEqual(r.status_code, 403) r = self.post( "/api/agent/discoveries", { "agent_zone_id": str(self.zones[1]), "site_id": str(self.sites[1]), "discoveries": [{"ip_address": "198.51.100.1"}], }, ) self.assertEqual(r.status_code, 200, r.text) self.assertEqual(r.json()["agent_zone_id"], str(self.zones[0])) for headers in [ {}, {"X-MinerOps-Agent-ID": self.agents[1], "X-MinerOps-Agent-Secret": "wrong"}, ]: self.assertIn( self.client.post( "/api/agent/discoveries", json={"discoveries": []}, headers=headers ).status_code, [401, 403], ) def test_review_permissions_and_missing_stable_id(self): row = self.observe() r = self.post( "/api/agent/discoveries/review", {"discovery_id": row["id"], "version": row["version"], "action": "ignore"}, ) self.assertEqual(r.status_code, 401) uid = uuid4() with connect() as c, c.cursor() as q: q.execute( "INSERT INTO users(id,username,display_name,password_hash,role_id) SELECT %s,%s,'No authority','unused',id FROM roles WHERE name='Read Only'", (uid, str(uid)), ) headers = { "Authorization": "Bearer " + create_access_token(str(uid), str(uid), "Read Only") } self.assertEqual( self.client.post( "/api/agent/discoveries/review", headers=headers, json={ "discovery_id": row["id"], "version": row["version"], "action": "ignore", }, ).status_code, 403, ) before = self.durable() for path, body in [ ( "/api/agent/discoveries/enrich-match", {"discovery_id": row["id"], "version": row["version"]}, ), ("/api/agent/commands/run-poll-now", {"agent_id": self.agents[0]}), ("/api/agent/commands/scan-new-miners", {"agent_id": self.agents[0]}), ]: self.assertEqual( self.client.post(path, json=body, headers=headers).status_code, 403 ) self.assertEqual(self.post(path, body).status_code, 401) self.assertEqual(before, self.durable()) self.assertEqual( self.client.post( "/api/agent/discoveries/review", headers=self.admin, json={"ip_address": "198.51.100.10", "action": "ignore"}, ).status_code, 422, ) def test_stale_disabled_unassigned_and_unproved_history_rejected(self): row = self.observe() with connect() as c, c.cursor() as q: q.execute( "UPDATE agent_zones SET is_enabled=false,updated_at=now() WHERE id=%s", (self.zones[0],), ) self.assertEqual(self.review(row).status_code, 409) self.assertEqual( self.scalar( "SELECT count(*) FROM miners WHERE agent_zone_id=%s", (self.zones[0],) ), 0, ) with connect() as c, c.cursor() as q: q.execute( "UPDATE agent_zones SET is_enabled=true,updated_at=now() WHERE id=%s", (self.zones[0],), ) q.execute( "UPDATE minerops_agent_discoveries SET agent_zone_id=NULL WHERE id=%s", (row["id"],), ) self.assertEqual(self.review(row).status_code, 409) def test_stale_version_superseded_observation_and_inventory_change(self): row = self.observe() self.assertEqual(self.review(row, version="stale").status_code, 409) newer = self.observe(hostname="newer") self.assertEqual(self.review(row).status_code, 409) self.miner(mac="02:12:34:00:00:10") self.assertEqual(self.review(newer).status_code, 409) def test_approval_retry_is_idempotent_and_audited(self): row = self.observe() one = self.review(row) two = self.review(row) self.assertEqual(one.status_code, 200, one.text) self.assertEqual(one.json(), two.json()) self.assertEqual( self.scalar( "SELECT count(*) FROM audit_logs WHERE entity_id=%s AND action='discovery.reviewed'", (row["id"],), ), 1, ) self.assertEqual( self.scalar( "SELECT count(*) FROM miners WHERE agent_zone_id=%s", (self.zones[0],) ), 1, ) def test_concurrent_approvals_create_one_miner(self): row = self.observe() def work(): return self.repo.review( { "discovery_id": row["id"], "version": row["version"], "action": "add_to_inventory", }, {"id": str(self.uid), "username": "reviewer"}, ) with ThreadPoolExecutor(2) as pool: a, b = [f.result() for f in [pool.submit(work), pool.submit(work)]] self.assertEqual(a, b) self.assertEqual( self.scalar( "SELECT count(*) FROM miners WHERE agent_zone_id=%s", (self.zones[0],) ), 1, ) def test_audit_denial_rolls_back_miner_and_observation(self): row = self.observe() role = "b1_limited_" + uuid4().hex[:10] with connect() as c, c.cursor() as q: q.execute("CREATE ROLE " + role) q.execute("GRANT USAGE ON SCHEMA public TO " + role) q.execute("GRANT SELECT ON ALL TABLES IN SCHEMA public TO " + role) q.execute( "GRANT INSERT,UPDATE ON miners,minerops_agent_discoveries,miner_mac_ownerships,minerops_site_agents,agent_zones,agent_zone_network_ranges TO " + role ) def limited(): c = connect() c.execute("SET ROLE " + role) return c before = self.durable() with self.assertRaisesRegex(Exception, "audit_logs"): DiscoveryRepository(limited).review( { "discovery_id": row["id"], "version": row["version"], "action": "add_to_inventory", }, {"id": str(self.uid)}, ) self.assertEqual(before, self.durable()) def test_queue_targets_observing_agent_and_claim_is_validated(self): row = self.observe(agent=1) r = self.client.post( "/api/agent/discoveries/enrich-match", json={"discovery_id": row["id"], "version": row["version"]}, headers=self.admin, ) self.assertEqual(r.status_code, 200, r.text) qid = r.json()["command_id"] self.assertEqual( self.scalar( "SELECT agent_id FROM minerops_agent_command_queue WHERE id=%s", (qid,) ), self.agents[1], ) claims = self.client.get("/api/agent/commands/pending", headers=self.headers(1)) self.assertEqual(claims.status_code, 200, claims.text) self.assertEqual([v["command_id"] for v in claims.json()["commands"]], [qid]) body = { "discovery_id": row["id"], "version": row["version"], "command_id": qid, "ip_address": "198.51.100.10", "mac_address": "02:12:34:00:00:10", "model": "Enriched", } result = self.post("/api/agent/miner-enrichment-v3", body, 1) self.assertEqual(result.status_code, 200, result.text) original = self.scalar( "SELECT raw_payload FROM minerops_agent_discoveries WHERE id=%s", (row["id"],), ) self.assertNotIn("command_id", original) self.assertNotEqual(result.json()["discovery"]["id"], row["id"]) def test_arbitrary_or_stale_enrichment_command_cannot_dispatch(self): self.assertEqual( self.client.post( "/api/agent/discoveries/enrich-match", json={"ip_address": "198.51.100.10"}, headers=self.admin, ).status_code, 422, ) row = self.observe() r = self.client.post( "/api/agent/discoveries/enrich-match", json={"discovery_id": row["id"], "version": row["version"]}, headers=self.admin, ) self.assertEqual(r.status_code, 200, r.text) self.assertEqual(self.review(row, "ignore").status_code, 200) claims = self.client.get("/api/agent/commands/pending", headers=self.headers()) self.assertEqual(claims.status_code, 200, claims.text) self.assertEqual(claims.json()["commands"], []) self.assertEqual( self.scalar( "SELECT command_status FROM minerops_agent_command_queue WHERE id=%s", (r.json()["command_id"],), ), "cancelled", ) def test_enrichment_versions_and_foreign_targets_preserve_history(self): a, am = self.miner() b, bm = self.miner(1, ip="198.51.100.11") known = self.known()[0] before = self.durable() for endpoint in [ "miner-enrichment", "miner-enrichment-v2", "miner-enrichment-v3", ]: for overrides in [ {"miner_id": str(b)}, {"ip_address": "198.51.100.11"}, {"mac_address": bm}, {"miner_version": "invalid"}, ]: body = {**known, "hostname": "attempt", **overrides} self.assertEqual( self.post("/api/agent/" + endpoint, body).status_code, 409 ) self.assertEqual(before, self.durable()) result = self.post( "/api/agent/miner-enrichment-v2", {**known, "hostname": "allowed", "raw_version": {"read": True}}, ) self.assertEqual(result.status_code, 200, result.text) self.assertEqual( self.scalar("SELECT current_mac FROM miners WHERE id=%s", (a,)), am ) self.assertEqual( self.scalar("SELECT count(*) FROM miners WHERE id IN (%s,%s)", (a, b)), 2 ) self.assertEqual( self.post( "/api/agent/miner-enrichment-v2", {**known, "hostname": "stale"} ).status_code, 409, ) def test_snapshot_cannot_claim_unassigned_or_foreign_miner(self): mid, m = self.miner() known = self.known()[0] with connect() as c, c.cursor() as q: q.execute("UPDATE miners SET agent_zone_id=NULL WHERE id=%s", (mid,)) before = self.durable() self.assertEqual( self.post( "/api/agent/miner-snapshots", {"miners": [{**known, "online": True}]} ).status_code, 409, ) self.assertEqual(self.durable(), before) def test_positive_poll_pool_and_api3_telemetry(self): mid, m = self.miner() known = self.known()[0] r = self.post( "/api/agent/miner-snapshots", { "miners": [ { **known, "online": True, "responses": {"summary": {"json": {"Msg": {"MHS av": 123}}}}, } ] }, ) self.assertEqual(r.status_code, 200, r.text) known = self.known()[0] r = self.post( "/api/agent/pool-snapshots", {"pool_snapshots": [{**known, "pools": [{"URL": "simulated"}]}]}, ) self.assertEqual(r.status_code, 200, r.text) r = self.post( "/api/agent/api3-enrichment", { "items": [ { **known, "ip": known["ip_address"], "mac": m, "api3_supported": True, } ] }, ) self.assertEqual(r.status_code, 200, r.text) def test_malformed_unicode_and_outside_network_payloads(self): for body in [ [], None, {"discoveries": {}}, {"discoveries": [{"ip_address": "203.0.113.1"}]}, {"discoveries": [{"ip_address": "198.51.100.1\u200b"}]}, { "discoveries": [ { "ip_address": "198.51.100.1", "mac_address": "02:12:34:00:00:11\u0000", } ] }, ]: before = self.durable() r = self.post("/api/agent/discoveries", body) self.assertIn(r.status_code, [409, 422], r.text) self.assertEqual(before, self.durable()) def test_concurrent_disable_waits_for_mutation_authority_lock(self): row = self.observe() entered = threading.Event() release = threading.Event() original = self.repo.audit def audit(cur, *args, **kwargs): if args[0] == "discovery.reviewed": entered.set() self.assertTrue(release.wait(5)) return original(cur, *args, **kwargs) self.repo.audit = audit def work(): return self.repo.review( { "discovery_id": row["id"], "version": row["version"], "action": "ignore", }, {"id": str(self.uid)}, ) with ThreadPoolExecutor(2) as pool: future = pool.submit(work) self.assertTrue(entered.wait(5)) def disable(): with connect() as c: c.execute( "UPDATE agent_zones SET is_enabled=false,updated_at=now() WHERE id=%s", (self.zones[0],), ) blocked = pool.submit(disable) time.sleep(0.1) self.assertFalse(blocked.done()) release.set() future.result() blocked.result() self.assertEqual(self.review(row).status_code, 409) def test_read_target_revalidated_before_simulated_transport(self): row = self.observe(agent=1) q = self.client.post( "/api/agent/discoveries/enrich-match", headers=self.admin, json={"discovery_id": row["id"], "version": row["version"]}, ).json() self.client.get("/api/agent/commands/pending", headers=self.headers(1)) body = { "command_id": q["command_id"], "discovery_id": row["id"], "version": row["version"], } result = self.post("/api/agent/discovery-target", body, 1) self.assertEqual(result.status_code, 200, result.text) self.assertEqual(result.json()["ip_address"], "198.51.100.10") self.assertEqual( self.post("/api/agent/discovery-target", body, 0).status_code, 409 ) self.review(row, "ignore") self.assertEqual( self.post("/api/agent/discovery-target", body, 1).status_code, 409 ) self.transport.assert_not_called() def test_initial_and_concurrent_self_assignment_is_prohibited(self): with connect() as c: c.execute( "UPDATE minerops_site_agents SET agent_zone_id=NULL WHERE agent_id=%s", (self.agents[0],), ) def check(): return self.post( "/api/agent/checkin", { "agent_id": self.agents[0], "site_id": str(self.sites[0]), "agent_zone_id": str(self.zones[0]), }, ).status_code with ThreadPoolExecutor(2) as pool: self.assertEqual(list(pool.map(lambda _: check(), range(2))), [422, 422]) self.assertIsNone( self.scalar( "SELECT agent_zone_id FROM minerops_site_agents WHERE agent_id=%s", (self.agents[0],), ) ) def test_pool_and_api3_foreign_or_nested_contradictions_rollback(self): a, am = self.miner() b, bm = self.miner(1, ip="198.51.100.11") known = self.known()[0] before = self.durable() self.assertEqual( self.post( "/api/agent/pool-snapshots", {"pool_snapshots": [{**known, "miner_id": str(b), "pools": [{}]}]}, ).status_code, 409, ) for extra in [{"agent_id": self.agents[1]}, {}]: body = { **extra, "items": [ { **known, "ip": known["ip_address"], "mac": am, "api3_supported": True, "device_info": {"msg": {"network": {"ip": "198.51.100.11"}}}, } ], } self.assertIn( self.post("/api/agent/api3-enrichment", body).status_code, [403, 409] ) self.assertEqual(before, self.durable()) def test_actual_observation_update_failure_rolls_back_insert_and_audit(self): row = self.observe() name = "b1_failure_" + uuid4().hex with connect() as c: c.execute( "CREATE FUNCTION " + name + "() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.id='" + row["id"] + "' THEN RAISE EXCEPTION 'injected observation update'; END IF; RETURN NEW; END $$" ) c.execute( "CREATE TRIGGER " + name + " BEFORE UPDATE ON minerops_agent_discoveries FOR EACH ROW EXECUTE FUNCTION " + name + "()" ) before = self.durable() try: with self.assertRaisesRegex(Exception, "injected observation update"): self.repo.review( { "discovery_id": row["id"], "version": row["version"], "action": "add_to_inventory", }, {"id": str(self.uid)}, ) self.assertEqual(before, self.durable()) finally: with connect() as c: c.execute("DROP TRIGGER " + name + " ON minerops_agent_discoveries") c.execute("DROP FUNCTION " + name + "()") def test_age_expiry_and_legacy_records_cannot_be_approved(self): row = self.observe() with connect() as c: c.execute( "UPDATE minerops_agent_discoveries SET created_at=now()-interval '1 hour' WHERE id=%s", (row["id"],), ) before = self.durable() self.assertEqual(self.review(row).status_code, 409) self.assertEqual(before, self.durable()) oid = str(uuid4()) with connect() as c: c.execute( "INSERT INTO minerops_agent_discoveries(id,agent_id,agent_zone_id,site_name,ip_address,raw_payload) VALUES (%s,%s,%s,'Legacy','198.51.100.12',%s) RETURNING updated_at", ( oid, self.agents[0], self.zones[0], Jsonb({"authority": "client-spoof"}), ), ) with connect() as c, c.cursor() as q: q.execute( "SELECT updated_at FROM minerops_agent_discoveries WHERE id=%s", (oid,) ) version = q.fetchone()[0].isoformat() self.assertEqual(self.review({"id": oid, "version": version}).status_code, 409) def test_historical_decision_and_raw_payload_not_overwritten_by_new_report(self): row = self.observe() self.review(row, "ignore") old = self.scalar( "SELECT to_jsonb(d) FROM minerops_agent_discoveries d WHERE id=%s", (row["id"],), ) self.observe(hostname="new report") self.assertEqual( old, self.scalar( "SELECT to_jsonb(d) FROM minerops_agent_discoveries d WHERE id=%s", (row["id"],), ), ) def test_changed_ownership_and_credential_revocation_invalidate_tokens(self): mid, m = self.miner() known = self.known()[0] with connect() as c: c.execute( "UPDATE miners SET site_id=%s,agent_zone_id=%s,updated_at=now() WHERE id=%s", (self.sites[1], self.zones[1], mid), ) before = self.durable() self.assertEqual( self.post( "/api/agent/miner-enrichment-v3", {**known, "model": "stale"} ).status_code, 409, ) self.assertEqual(before, self.durable()) with connect() as c: c.execute( "UPDATE minerops_site_agents SET credential_revoked_at=now() WHERE agent_id=%s", (self.agents[0],), ) self.assertEqual( self.post("/api/agent/discoveries", {"discoveries": []}).status_code, 403 ) def test_batch_bounds_stream_limit_stale_report_and_ipv6(self): cases = [ {"discoveries": [{"ip_address": "198.51.100.1"}] * 129}, { "discoveries": [ { "ip_address": "198.51.100.1", "discovered_at": "2000-01-01T00:00:00Z", } ] }, {"discoveries": [{"ip_address": "fe80::1%eth0"}]}, ] for body in cases: self.assertIn( self.post("/api/agent/discoveries", body).status_code, [409, 422] ) response = self.client.post( "/api/agent/discoveries", headers=self.headers(), content="x" * (1024 * 1024 + 1), ) self.assertEqual(response.status_code, 413) with connect() as c: c.execute( "INSERT INTO agent_zone_network_ranges(agent_zone_id,network) VALUES (%s,'2001:db8::/64')", (self.zones[0],), ) self.assertEqual( self.observe(ip="2001:db8::12", mac=None)["ip_address"], "2001:db8::12" ) def test_positive_telemetry_preserves_numeric_measurements(self): mid, m = self.miner() known = self.known()[0] r = self.post( "/api/agent/miner-snapshots", { "miners": [ { **known, "online": True, "responses": { "summary": { "raw": json.dumps( { "Msg": { "MHS av": 123, "Power": 456, "Fan Speed In": 789, } } ) } }, } ] }, ) self.assertEqual(r.status_code, 200, r.text) self.assertEqual( self.scalar( "SELECT mhs_av FROM miner_status_snapshots WHERE miner_id=%s ORDER BY observed_at DESC LIMIT 1", (mid,), ), 123, ) known = self.known()[0] r = self.post( "/api/agent/api3-enrichment", { "items": [ { **known, "ip": known["ip_address"], "mac": m, "api3_supported": True, "miner_status": { "msg": { "summary": {"hash-average": 12}, "pools": [ {"id": 0, "url": "stratum+tcp://simulated.invalid"} ], "edevs": [{"slot": 0, "hash-average": 12}], } }, } ] }, ) self.assertEqual(r.status_code, 200, r.text) self.assertEqual( self.scalar( "SELECT count(*) FROM miner_api3_hashboard_snapshots WHERE miner_id=%s", (mid,), ), 1, ) self.assertEqual( self.scalar( "SELECT count(*) FROM miner_pool_snapshots WHERE miner_id=%s", (mid,) ), 1, ) def test_metadata_and_failed_api3_preserve_latest_measurements(self): mid, m = self.miner() room = uuid4() with connect() as c, c.cursor() as q: q.execute( "INSERT INTO rooms(id,site_id,name) VALUES (%s,%s,'Observed room')", (room, self.sites[0]), ) q.execute( "UPDATE miners SET room_id=%s,miner_sn='established-serial' WHERE id=%s", (room, mid), ) q.execute( "INSERT INTO miner_status_snapshots(miner_id,mhs_av) VALUES (%s,123)", (mid,), ) known = self.known()[0] self.assertEqual(known["room_name"], "Observed room") response = self.post( "/api/agent/miner-enrichment-v3", {**known, "hostname": "read-metadata", "raw_version": {"read": True}}, ) self.assertEqual(response.status_code, 200, response.text) self.assertEqual( self.scalar( "SELECT new_value_json->'raw_version' FROM audit_logs WHERE action='discovery.miner_enriched' AND entity_id=%s ORDER BY created_at DESC LIMIT 1", (str(mid),), ), {"read": True}, ) known = self.known()[0] failed = { **known, "ip": known["ip_address"], "mac": m, "api3_supported": False, "api3_error": "simulated timeout", } response = self.post("/api/agent/api3-enrichment", {"items": [failed]}) self.assertEqual(response.status_code, 200, response.text) self.assertEqual(response.json()["data"]["api3_failed"], 1) self.assertEqual(response.json()["data"]["status_rows"], 0) known = self.known()[0] metadata = { **failed, **known, "api3_supported": True, "device_info": { "msg": { "miner": { "type": "simulated-model", "miner-sn": "established-serial", }, "system": { "api": "simulated-api", "fwversion": "simulated-firmware", "platform": "simulated-platform", "apiswitch": "on", }, } }, } response = self.post("/api/agent/api3-enrichment", {"items": [metadata]}) self.assertEqual(response.status_code, 200, response.text) self.assertEqual(response.json()["data"]["api3_ok"], 1) self.assertEqual( self.scalar("SELECT api_version FROM miners WHERE id=%s", (mid,)), "simulated-api", ) self.assertEqual( self.scalar( "SELECT count(*) FROM miner_status_snapshots WHERE miner_id=%s", (mid,) ), 1, ) self.assertEqual( self.scalar( "SELECT mhs_av FROM miner_status_snapshots WHERE miner_id=%s", (mid,) ), 123, ) known = self.known()[0] metadata.update(known) metadata["device_info"]["msg"]["miner"]["miner-sn"] = "other-device" before = self.durable() self.assertEqual( self.post("/api/agent/api3-enrichment", {"items": [metadata]}).status_code, 409, ) self.assertEqual(before, self.durable()) def test_conflicting_aliases_and_serial_cannot_poison_matches(self): mid, m = self.miner() known = self.known()[0] before = self.durable() for body in [ {**known, "ip": "198.51.100.11"}, {**known, "mac": "02:99:99:99:99:99"}, ]: self.assertEqual( self.post("/api/agent/miner-enrichment-v3", body).status_code, 409 ) self.assertEqual(before, self.durable()) other, mac = self.miner(1, ip="198.51.100.20") with connect() as c: c.execute("UPDATE miners SET miner_sn=%s WHERE id=%s", (str(other), other)) row = self.observe(ip="198.51.100.22", miner_sn=str(other)) self.assertEqual(row["discovery_status"], "zone_conflict") self.assertEqual(self.review(row).status_code, 409) for evidence in [ {"ip": "198.51.100.23"}, {"mac": "02:99:99:99:99:99"}, {"miner_id": str(other)}, ]: result = self.post( "/api/agent/discoveries", { "discoveries": [ { "ip_address": "198.51.100.24", "mac_address": "02:12:34:00:00:24", **evidence, } ] }, ) self.assertEqual(result.status_code, 200, result.text) row = result.json()["discoveries"][0] self.assertEqual(row["discovery_status"], "zone_conflict") self.assertIsNone(row["matched_miner_id"]) self.assertEqual(self.review(row).status_code, 409) def test_requeued_claimed_read_preserves_unknown_outcome_and_single_audit(self): row = self.observe() result = self.client.post( "/api/agent/discoveries/enrich-match", headers=self.admin, json={"discovery_id": row["id"], "version": row["version"]}, ) qid = result.json()["command_id"] self.client.get("/api/agent/commands/pending", headers=self.headers()) with connect() as c: c.execute( "UPDATE minerops_agent_command_queue SET command_status='pending' WHERE id=%s", (qid,), ) self.review(row, "ignore") for _ in range(2): self.assertEqual( self.client.get( "/api/agent/commands/pending", headers=self.headers() ).json()["commands"], [], ) self.assertEqual( self.scalar( "SELECT new_value_json->>'execution_outcome' FROM audit_logs WHERE action='discovery.command_rejected' AND new_value_json->>'command_id'=%s", (qid,), ), "unknown", ) self.assertEqual( self.scalar( "SELECT count(*) FROM audit_logs WHERE action='discovery.command_rejected' AND new_value_json->>'command_id'=%s", (qid,), ), 1, ) def test_read_requests_can_repeat_and_explicit_request_ids_are_idempotent(self): ids = [] for _ in range(2): response = self.client.post( "/api/agent/commands/run-poll-now", headers=self.admin, json={"agent_id": self.agents[0]}, ) self.assertEqual(response.status_code, 200, response.text) ids.append(response.json()["command_id"]) self.assertNotEqual(*ids) request_id = str(uuid4()) responses = [ self.client.post( "/api/agent/commands/scan-new-miners", headers=self.admin, json={"agent_id": self.agents[0], "request_id": request_id}, ).json() for _ in range(2) ] self.assertEqual(responses[0], responses[1]) def test_unaudited_legacy_observation_rewrite_is_rejected(self): row = self.observe() with connect() as connection: connection.execute( "UPDATE minerops_agent_discoveries SET updated_at=clock_timestamp(),discovery_status='known' WHERE id=%s", (row["id"],), ) row["version"] = self.scalar( "SELECT updated_at FROM minerops_agent_discoveries WHERE id=%s", (row["id"],), ).isoformat() before = self.durable() self.assertEqual(self.review(row).status_code, 409) self.assertEqual(before, self.durable()) def test_two_zones_concurrently_approve_one_mac_with_one_winner(self): a = self.observe() b = self.observe(agent=1) barrier = threading.Barrier(2) def approve(row): barrier.wait(5) client = TestClient(app) try: return client.post( "/api/agent/discoveries/review", headers=self.admin, json={ "discovery_id": row["id"], "version": row["version"], "action": "add_to_inventory", }, ).status_code finally: client.close() with ThreadPoolExecutor(2) as pool: results = list(pool.map(approve, [a, b])) self.assertEqual(sorted(results), [200, 409]) self.assertEqual( self.scalar( "SELECT count(*) FROM miners WHERE agent_zone_id IN (%s,%s)", tuple(self.zones), ), 1, ) def test_claim_then_concurrent_review_blocks_and_target_is_revalidated(self): row = self.observe() qid = self.client.post( "/api/agent/discoveries/enrich-match", headers=self.admin, json={"discovery_id": row["id"], "version": row["version"]}, ).json()["command_id"] entered = threading.Event() release = threading.Event() original = DiscoveryRepository.validate_command def gated(repository, cur, agent, command): if command.get("id", command.get("command_id")) == qid: entered.set() if not release.wait(5): raise AssertionError("Claim gate timed out") return original(repository, cur, agent, command) with patch.object( DiscoveryRepository, "validate_command", gated ), ThreadPoolExecutor(2) as pool: claim = pool.submit( lambda: self.client.get( "/api/agent/commands/pending", headers=self.headers() ) ) self.assertTrue(entered.wait(5)) review = pool.submit(lambda: self.review(row, "ignore")) deadline = time.monotonic() + 3 waiting = 0 while time.monotonic() < deadline: waiting = self.scalar( "SELECT count(*) FROM pg_stat_activity WHERE datname=current_database() AND wait_event='advisory'" ) if waiting: break time.sleep(0.02) self.assertGreater( waiting, 0, "Review did not reach a real PostgreSQL lock wait" ) release.set() self.assertEqual(claim.result().status_code, 200) self.assertEqual(review.result().status_code, 200) result = self.post( "/api/agent/discovery-target", {"command_id": qid, "discovery_id": row["id"], "version": row["version"]}, ) self.assertEqual(result.status_code, 409) self.transport.assert_not_called() if __name__ == "__main__": unittest.main()