Files
ucvl-home-vision/test_network_status.py
T

67 lines
4.1 KiB
Python
Raw Normal View History

"""Network status must be scoped, bounded and honest about reachability."""
import json
import unittest
from unittest.mock import patch,Mock
import network_status as ns
from test_app import app
import test_households as fixture
class NetworkTests(unittest.TestCase):
setUp=fixture.HouseholdTests.setUp
stop_server=fixture.HouseholdTests.stop_server
request=fixture.HouseholdTests.request
def test_scoped_checks_cannot_probe_an_arbitrary_host(self):
device=app.INVENTORY.save(dict(name='网关',deviceType='路由器',managementHost='192.168.8.1'),self.owner)
endpoint='/api/devices/network?source=device&id='+device['id']
with patch.object(ns,'probe',return_value=dict(host='192.168.8.1',reachable=True,ports=[80])) as probe:
result=self.request(endpoint)[1]
self.assertTrue(result['targets'][0]['reachable'])
self.assertEqual(result['links'][0]['url'],'http://192.168.8.1/')
self.assertEqual(self.request(endpoint,self.other)[0],404)
self.assertEqual(self.request(endpoint,self.members[self.aid])[0],403)
self.assertEqual(self.request('/api/devices/network?source=device&host=example.com')[0],404)
again=self.request(endpoint)[1];self.assertTrue(again['cached']);self.assertEqual(probe.call_count,1)
def test_record_changes_invalidate_cache(self):
device=app.INVENTORY.save(dict(name='网关',deviceType='路由器',managementHost='192.168.8.1'),self.owner)
with patch.object(ns,'probe',side_effect=lambda host:dict(host=host,ports=[],reachable=False)) as probe:
query=dict(source='device',id=device['id'])
app.NETWORK.read(self.owner,query)
app.INVENTORY.save(dict(device,managementHost='192.168.8.2'),self.owner)
result=app.NETWORK.read(self.owner,query)
self.assertEqual(probe.call_count,2)
self.assertFalse(result['targets'][0]['reachable'])
self.assertFalse(result['links'][0]['verified'])
self.assertEqual(result['links'][0]['url'],'http://192.168.8.2/')
class NetworkUnitTests(unittest.TestCase):
def test_rejects_local_metadata_and_unsafe_links(self):
for host in ['127.0.0.1','169.254.169.254','::1','0.0.0.0','224.0.0.1']:
self.assertFalse(ns.safe_ip(host))
self.assertTrue(ns.safe_ip('192.168.8.1'));self.assertTrue(ns.safe_ip('100.64.1.2'))
for host in ['127.0.0.1','javascript:alert(1)','user:secret@example.com','example.com/path']:
self.assertEqual(ns.management_links([dict(host=host,kind='local',ports=[80])]),[])
with patch.object(ns.socket,'getaddrinfo',return_value=[(2,1,6,'',('127.0.0.1',0))]),patch.object(ns.socket,'create_connection') as connect:
self.assertFalse(ns.probe('malicious.example')['reachable']);connect.assert_not_called()
def test_peer_online_does_not_prove_subnet_route(self):
status={'Peer':{'abc':{'DNSName':'gateway.example.ts.net.','TailscaleIPs':['100.64.1.2'],'Online':True,'Active':False,'AllowedIPs':['100.64.1.2/32'],'PrimaryRoutes':None}}}
with patch.object(ns.subprocess,'run',return_value=Mock(stdout=json.dumps(status))):
peer=ns.tail_peer('100.64.1.2','192.168.8.0/24')
self.assertTrue(peer['online']);self.assertFalse(peer['subnetAvailable']);self.assertNotIn('Peer',peer)
status['Peer']['abc']['PrimaryRoutes']=['192.168.8.0/24']
with patch.object(ns.subprocess,'run',return_value=Mock(stdout=json.dumps(status))):
self.assertTrue(ns.tail_peer('gateway.example.ts.net','192.168.8.0/24')['subnetAvailable'])
with patch.object(ns.subprocess,'run',side_effect=FileNotFoundError):self.assertFalse(ns.tail_peer('100.64.1.2','')['available'])
def test_uses_detected_web_port_and_ipv6_brackets(self):
links=ns.management_links([dict(host='192.168.3.151',kind='local',ports=[8790]),dict(host='fd7a:115c:a1e0::1',kind='tailscale',ports=[443])])
self.assertEqual(links[0]['url'],'http://192.168.3.151:8790/')
self.assertEqual(links[1]['url'],'https://[fd7a:115c:a1e0::1]/')
if __name__=='__main__':unittest.main()