from http import HTTPStatus from unittest.mock import patch import pytest from requests import HTTPError from gso.services.librenms_client import LibreNMSClient from gso.utils.helpers import SNMPVersion @pytest.fixture() def mock_get_device_success(faker): with patch("gso.services.librenms_client.requests.get") as mock_get_device: mock_get_device().status_code = HTTPStatus.OK mock_get_device().json.return_value = { "status": "ok", "devices": [ { "device_id": 1, "inserted": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "hostname": "localhost", "sysName": "librenms", "display": None, "ip": faker.ipv4(), "overwrite_ip": None, "community": "librenms-community", "authlevel": None, "authname": None, "authpass": None, "authalgo": None, "cryptopass": None, "cryptoalgo": None, "snmpver": "v2c", "port": faker.port_number(is_user=True), "transport": "udp", "timeout": None, "retries": None, "snmp_disable": 0, "bgpLocalAs": None, "sysObjectID": ".1.3.6.1.4.1.8072.3.2.10", "sysDescr": "Linux librenms 5.15.0-79-generic #86-Ubuntu SMP Mon Jul 10 16:07:21 UTC 2023 x86_64", "sysContact": "Your Name <your@email.address>", "version": "5.15.0-79-generic", "hardware": "Generic x86 64-bit", "features": "Ubuntu 22.04", "location_id": 1, "os": "linux", "status": True, "status_reason": "", "ignore": 0, "disabled": 0, "uptime": faker.pyint(), "agent_uptime": 0, "last_polled": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "last_poll_attempted": None, "last_polled_timetaken": faker.pyfloat(left_digits=1, positive=True), "last_discovered_timetaken": faker.pyfloat(left_digits=1, positive=True), "last_discovered": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "last_ping": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "last_ping_timetaken": faker.pyfloat(left_digits=1, positive=True), "purpose": None, "type": "server", "serial": None, "icon": "images/os/ubuntu.svg", "poller_group": 0, "override_sysLocation": 0, "notes": None, "port_association_mode": 1, "max_depth": 0, "disable_notify": 0, "location": "Rack, Room, Building, City, Country [Lat, Lon]", "lat": None, "lng": None, }, ], "count": 1, } yield mock_get_device @pytest.fixture() def mock_get_device_not_found(): with patch("gso.services.librenms_client.requests.get") as mock_get_not_found: mock_get_not_found().status_code = HTTPStatus.NOT_FOUND mock_get_not_found().json.return_value = { "status": "error", "message": "Device non-existent-url does not exist", } mock_get_not_found().raise_for_status.side_effect = HTTPError( "404 Client Error: Not Found for url: http://librenms/devices/non-existent-url", response=mock_get_not_found(), ) yield mock_get_not_found @pytest.fixture() def mock_get_device_misconfigured(faker): with patch("gso.services.librenms_client.requests.get") as mock_get_device: mock_get_device().status_code = HTTPStatus.OK mock_get_device().json.return_value = { "status": "ok", "devices": [ { "device_id": 1, "inserted": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "hostname": "127.0.0.1", "sysName": "librenms", "display": None, "ip": faker.ipv4(), "overwrite_ip": None, "community": "librenms-community", "authlevel": None, "authname": None, "authpass": None, "authalgo": None, "cryptopass": None, "cryptoalgo": None, "snmpver": "v2c", "port": faker.port_number(is_user=True), "transport": "udp", "timeout": None, "retries": None, "snmp_disable": 0, "bgpLocalAs": None, "sysObjectID": ".1.3.6.1.4.1.8072.3.2.10", "sysDescr": "Linux librenms 5.15.0-79-generic #86-Ubuntu SMP Mon Jul 10 16:07:21 UTC 2023 x86_64", "sysContact": "Your Name <your@email.address>", "version": "5.15.0-79-generic", "hardware": "Generic x86 64-bit", "features": "Ubuntu 22.04", "location_id": 1, "os": "linux", "status": True, "status_reason": "", "ignore": 0, "disabled": 0, "uptime": faker.pyint(), "agent_uptime": 0, "last_polled": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "last_poll_attempted": None, "last_polled_timetaken": faker.pyfloat(left_digits=1, positive=True), "last_discovered_timetaken": faker.pyfloat(left_digits=1, positive=True), "last_discovered": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "last_ping": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "last_ping_timetaken": faker.pyfloat(left_digits=1, positive=True), "purpose": None, "type": "server", "serial": None, "icon": "images/os/ubuntu.svg", "poller_group": 0, "override_sysLocation": 0, "notes": None, "port_association_mode": 1, "max_depth": 0, "disable_notify": 0, "location": "Rack, Room, Building, City, Country [Lat, Lon]", "lat": None, "lng": None, }, ], "count": 1, } yield mock_get_device @pytest.fixture() def mock_get_device_unauthenticated(): with ( patch("gso.services.librenms_client.requests.get") as mock_get_unauthorized, patch( "gso.services.librenms_client.LibreNMSClient.get_device", ) as mock_get_device, ): mock_get_unauthorized().status_code = HTTPStatus.UNAUTHORIZED mock_get_unauthorized().json.return_value = {"message": "Unauthenticated."} mock_get_device.side_effect = HTTPError( "401 Client Error: Unauthorized for url: http://librenms/devices/naughty-url", response=mock_get_unauthorized(), ) yield mock_get_unauthorized @pytest.fixture() def mock_add_device_success(): with patch("gso.services.librenms_client.requests.post") as mock_post_device: mock_post_device().status_code = HTTPStatus.OK mock_post_device().json.return_value = { "status": "ok", "devices": [ { "community": "secret-community", "display": "localhost", "hostname": "localhost", "snmpver": "v2c", "port": 161, "transport": "udp", "poller_group": 0, "os": "linux", "status_reason": "", "sysName": "librenms", "port_association_mode": 1, "authlevel": None, "authname": None, "authalgo": None, "cryptopass": None, "cryptoalgo": None, "sysDescr": "Linux librenms 5.15.0-79-generic #86-Ubuntu SMP Mon Jul 10 16:07:21 UTC 2023 x86_64", "sysObjectID": ".1.3.6.1.4.1.8072.3.2.10", "device_id": 2, }, ], "message": "Device localhost has been added successfully", "count": 1, } yield mock_post_device @pytest.fixture() def mock_add_device_bad_url(): with patch("gso.services.librenms_client.requests.post") as mock_post_device: mock_post_device().status_code = HTTPStatus.INTERNAL_SERVER_ERROR mock_post_device().json.return_value = { "status": "error", "message": "Could not ping non-existent-url (Hostname did not resolve to IP)", } mock_post_device().raise_for_status.side_effect = HTTPError( "500 Server Error: Internal server error for url: http://librenms/devices", response=mock_post_device(), ) yield mock_post_device @pytest.fixture() def mock_add_device_unreachable(): with patch("gso.services.librenms_client.requests.post") as mock_post_device: mock_post_device().status_code = HTTPStatus.INTERNAL_SERVER_ERROR mock_post_device().json.return_value = { "status": "error", "message": "Could not connect to non-existent-url, please check the snmp details and snmp reachability", } mock_post_device().raise_for_status.side_effect = HTTPError( "500 Server Error: Internal server error for url: http://librenms/devices", response=mock_post_device(), ) yield mock_post_device @pytest.fixture() def mock_remove_device_success(faker): with patch("gso.services.librenms_client.requests.delete") as mock_remove_device: mock_remove_device().status_code = HTTPStatus.OK mock_remove_device().json.return_value = { "status": "ok", "devices": [ { "device_id": 2, "inserted": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "hostname": "localhost", "sysName": "librenms", "display": "localhost", "ip": faker.ipv4(), "overwrite_ip": None, "community": "snmp-community", "authlevel": None, "authname": None, "authpass": None, "authalgo": None, "cryptopass": None, "cryptoalgo": None, "snmpver": "v2c", "port": 161, "transport": "udp", "timeout": None, "retries": None, "snmp_disable": 0, "bgpLocalAs": None, "sysObjectID": ".1.3.6.1.4.1.8072.3.2.10", "sysDescr": "Linux librenms 5.15.0-79-generic #86-Ubuntu SMP Mon Jul 10 16:07:21 UTC 2023 x86_64", "sysContact": "Your Name <your@email.address>", "version": "5.15.0-79-generic", "hardware": "Generic x86 64-bit", "features": "Ubuntu 22.04", "location_id": 1, "os": "linux", "status": True, "status_reason": "", "ignore": 0, "disabled": 0, "uptime": 8057430, "agent_uptime": 0, "last_polled": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "last_poll_attempted": None, "last_polled_timetaken": faker.pyfloat(left_digits=1, positive=True), "last_discovered_timetaken": faker.pyfloat(left_digits=1, positive=True), "last_discovered": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "last_ping": faker.date("%Y-%m-%dT%H:%M:%S.%fZ"), "last_ping_timetaken": faker.pyfloat(left_digits=1, positive=True), "purpose": None, "type": "server", "serial": None, "icon": "images/os/ubuntu.svg", "poller_group": 0, "override_sysLocation": 0, "notes": None, "port_association_mode": 1, "max_depth": 0, "disable_notify": 0, "location": "Rack, Room, Building, City, Country [Lat, Lon]", "lat": None, "lng": None, }, ], "message": "Removed device localhost\n", "count": 1, } yield mock_remove_device @pytest.fixture() def mock_remove_device_non_existent(faker): with patch("gso.services.librenms_client.requests.delete") as mock_remove_device: mock_remove_device().status_code = HTTPStatus.NOT_FOUND mock_remove_device().json.return_value = {"status": "error", "message": "Device non-existent-url not found"} mock_remove_device().raise_for_status.side_effect = HTTPError( "404 Client Error: Not Found for url: http://librenms/devices/non-existent-url", response=mock_remove_device(), ) yield mock_remove_device def test_get_device_success(mock_get_device_success): client = LibreNMSClient() device = client.get_device("localhost") assert device["status"] == "ok" assert device["devices"][0]["hostname"] == "localhost" def test_get_device_not_found(mock_get_device_not_found): client = LibreNMSClient() with pytest.raises(HTTPError) as e: client.get_device("non-existent-url") assert e.value.response.status_code == HTTPStatus.NOT_FOUND assert e.value.response.json() == {"status": "error", "message": "Device non-existent-url does not exist"} assert e.value.args[0] == "404 Client Error: Not Found for url: http://librenms/devices/non-existent-url" def test_device_exists_true(mock_get_device_success): client = LibreNMSClient() assert client.device_exists("localhost") def test_device_exists_false(mock_get_device_not_found): client = LibreNMSClient() assert not client.device_exists("non-existent-url") def test_device_exists_bad_request(mock_get_device_unauthenticated): client = LibreNMSClient() with pytest.raises(HTTPError) as e: client.device_exists("naughty-url") assert e.value.response.status_code == HTTPStatus.UNAUTHORIZED assert e.value.response.json() == {"message": "Unauthenticated."} assert e.value.args[0] == "401 Client Error: Unauthorized for url: http://librenms/devices/naughty-url" def test_add_device_success(mock_add_device_success): fqdn = "localhost" client = LibreNMSClient() new_device = client.add_device(fqdn, SNMPVersion.V2C) assert new_device["status"] == "ok" assert new_device["devices"][0]["hostname"] == fqdn assert new_device["devices"][0]["snmpver"] == SNMPVersion.V2C.value def test_add_device_bad_fqdn(mock_add_device_bad_url): fqdn = "non-existent-url" client = LibreNMSClient() with pytest.raises(HTTPError) as e: client.add_device(fqdn, SNMPVersion.V2C) assert e.value.response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR assert e.value.response.json() == { "status": "error", "message": "Could not ping non-existent-url (Hostname did not resolve to IP)", } assert e.value.args[0] == "500 Server Error: Internal server error for url: http://librenms/devices" def test_add_device_no_ping(mock_add_device_unreachable): fqdn = "non-existent-url" client = LibreNMSClient() with pytest.raises(HTTPError) as e: client.add_device(fqdn, SNMPVersion.V2C) assert e.value.response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR assert e.value.response.json() == { "status": "error", "message": "Could not connect to non-existent-url, please check the snmp details and snmp reachability", } assert e.value.args[0] == "500 Server Error: Internal server error for url: http://librenms/devices" def test_remove_device_success(mock_remove_device_success): client = LibreNMSClient() device = client.remove_device("localhost") assert device["status"] == "ok" assert device["devices"][0]["hostname"] == "localhost" def test_remove_non_existent_device(mock_remove_device_non_existent): client = LibreNMSClient() with pytest.raises(HTTPError) as e: client.remove_device("non-existent-url") assert e.value.response.status_code == HTTPStatus.NOT_FOUND assert e.value.response.json() == {"status": "error", "message": "Device non-existent-url not found"} assert e.value.args[0] == "404 Client Error: Not Found for url: http://librenms/devices/non-existent-url" def test_validate_device_success(mock_get_device_success): client = LibreNMSClient() errors = client.validate_device("localhost") assert not errors def test_validate_device_non_existing(mock_get_device_not_found): client = LibreNMSClient() error = client.validate_device("localhost") assert error == "Device does not exist in LibreNMS." def test_validate_device_misconfigured(mock_get_device_misconfigured): client = LibreNMSClient() error = client.validate_device("localhost") assert error == "Device hostname in LibreNMS does not match FQDN.\nExpected 'localhost' but got '127.0.0.1'."