From c985b8793dfe4b1a1a9f2c309bd3ce28bce40aec Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Wed, 10 Jan 2024 11:58:36 +0000 Subject: [PATCH] #2151 and #2166: added tests for application being unused + even more tests --- .../system/applications/application.py | 5 +- .../system/services/ftp/ftp_service.py | 6 ++- .../simulator/system/services/service.py | 2 + src/primaite/simulator/system/software.py | 2 +- tests/conftest.py | 5 +- .../system/test_application_on_node.py | 23 +++++---- .../system/test_service_on_node.py | 7 +-- .../_applications/test_application_actions.py | 0 .../_applications/test_applications.py | 50 +++++++++++++++++++ 9 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 tests/unit_tests/_primaite/_simulator/_system/_applications/test_application_actions.py create mode 100644 tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py diff --git a/src/primaite/simulator/system/applications/application.py b/src/primaite/simulator/system/applications/application.py index 09828b89..322ac808 100644 --- a/src/primaite/simulator/system/applications/application.py +++ b/src/primaite/simulator/system/applications/application.py @@ -3,7 +3,7 @@ from enum import Enum from typing import Any, Dict, Set from primaite import getLogger -from primaite.simulator.system.software import IOSoftware +from primaite.simulator.system.software import IOSoftware, SoftwareHealthState _LOGGER = getLogger(__name__) @@ -92,6 +92,9 @@ class Application(IOSoftware): if self.operating_state == ApplicationOperatingState.CLOSED: self.sys_log.info(f"Running Application {self.name}") self.operating_state = ApplicationOperatingState.RUNNING + # set software health state to GOOD if initially set to UNUSED + if self.health_state_actual == SoftwareHealthState.UNUSED: + self.set_health_state(SoftwareHealthState.GOOD) def _application_loop(self): """The main application loop.""" diff --git a/src/primaite/simulator/system/services/ftp/ftp_service.py b/src/primaite/simulator/system/services/ftp/ftp_service.py index f2c01544..276a9d5f 100644 --- a/src/primaite/simulator/system/services/ftp/ftp_service.py +++ b/src/primaite/simulator/system/services/ftp/ftp_service.py @@ -1,7 +1,7 @@ import shutil from abc import ABC from ipaddress import IPv4Address -from typing import Optional +from typing import Dict, Optional from primaite.simulator.file_system.file_system import File from primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode @@ -16,6 +16,10 @@ class FTPServiceABC(Service, ABC): Contains shared methods between both classes. """ + def describe_state(self) -> Dict: + """Returns a Dict of the FTPService state.""" + return super().describe_state() + def _process_ftp_command(self, payload: FTPPacket, session_id: Optional[str] = None, **kwargs) -> FTPPacket: """ Process the command in the FTP Packet. diff --git a/src/primaite/simulator/system/services/service.py b/src/primaite/simulator/system/services/service.py index 43c85471..162678a0 100644 --- a/src/primaite/simulator/system/services/service.py +++ b/src/primaite/simulator/system/services/service.py @@ -1,3 +1,4 @@ +from abc import abstractmethod from enum import Enum from typing import Any, Dict, Optional @@ -95,6 +96,7 @@ class Service(IOSoftware): rm.add_request("enable", RequestType(func=lambda request, context: self.enable())) return rm + @abstractmethod def describe_state(self) -> Dict: """ Produce a dictionary describing the current state of this object. diff --git a/src/primaite/simulator/system/software.py b/src/primaite/simulator/system/software.py index 4072fab1..8656154c 100644 --- a/src/primaite/simulator/system/software.py +++ b/src/primaite/simulator/system/software.py @@ -282,7 +282,7 @@ class IOSoftware(Software): Returns true if the software can perform actions. """ - if self.software_manager and self.software_manager.node.operating_state == NodeOperatingState.OFF: + if self.software_manager and self.software_manager.node.operating_state != NodeOperatingState.ON: _LOGGER.debug(f"{self.name} Error: {self.software_manager.node.hostname} is not online.") return False return True diff --git a/tests/conftest.py b/tests/conftest.py index 37289674..c37226a5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -40,6 +40,9 @@ from primaite.simulator.network.hardware.base import Link, Node class TestService(Service): """Test Service class""" + def describe_state(self) -> Dict: + return super().describe_state() + def __init__(self, **kwargs): kwargs["name"] = "TestService" kwargs["port"] = Port.HTTP @@ -60,7 +63,7 @@ class TestApplication(Application): super().__init__(**kwargs) def describe_state(self) -> Dict: - pass + return super().describe_state() @pytest.fixture(scope="function") diff --git a/tests/integration_tests/system/test_application_on_node.py b/tests/integration_tests/system/test_application_on_node.py index 46be5e55..60497f22 100644 --- a/tests/integration_tests/system/test_application_on_node.py +++ b/tests/integration_tests/system/test_application_on_node.py @@ -24,8 +24,8 @@ def populated_node(application_class) -> Tuple[Application, Computer]: return app, computer -def test_service_on_offline_node(application_class): - """Test to check that the service cannot be interacted with when node it is on is off.""" +def test_application_on_offline_node(application_class): + """Test to check that the application cannot be interacted with when node it is on is off.""" computer: Computer = Computer( hostname="test_computer", ip_address="192.168.1.2", @@ -49,8 +49,8 @@ def test_service_on_offline_node(application_class): assert app.operating_state is ApplicationOperatingState.CLOSED -def test_server_turns_off_service(populated_node): - """Check that the service is turned off when the server is turned off""" +def test_server_turns_off_application(populated_node): + """Check that the application is turned off when the server is turned off""" app, computer = populated_node assert computer.operating_state is NodeOperatingState.ON @@ -65,8 +65,8 @@ def test_server_turns_off_service(populated_node): assert app.operating_state is ApplicationOperatingState.CLOSED -def test_service_cannot_be_turned_on_when_server_is_off(populated_node): - """Check that the service cannot be started when the server is off.""" +def test_application_cannot_be_turned_on_when_computer_is_off(populated_node): + """Check that the application cannot be started when the computer is off.""" app, computer = populated_node assert computer.operating_state is NodeOperatingState.ON @@ -86,8 +86,8 @@ def test_service_cannot_be_turned_on_when_server_is_off(populated_node): assert app.operating_state is ApplicationOperatingState.CLOSED -def test_server_turns_on_service(populated_node): - """Check that turning on the server turns on service.""" +def test_computer_runs_applications(populated_node): + """Check that turning on the computer will turn on applications.""" app, computer = populated_node assert computer.operating_state is NodeOperatingState.ON @@ -109,13 +109,14 @@ def test_server_turns_on_service(populated_node): assert computer.operating_state is NodeOperatingState.ON assert app.operating_state is ApplicationOperatingState.RUNNING - computer.start_up_duration = 0 - computer.shut_down_duration = 0 - computer.power_off() + for i in range(computer.start_up_duration + 1): + computer.apply_timestep(timestep=i) assert computer.operating_state is NodeOperatingState.OFF assert app.operating_state is ApplicationOperatingState.CLOSED computer.power_on() + for i in range(computer.start_up_duration + 1): + computer.apply_timestep(timestep=i) assert computer.operating_state is NodeOperatingState.ON assert app.operating_state is ApplicationOperatingState.RUNNING diff --git a/tests/integration_tests/system/test_service_on_node.py b/tests/integration_tests/system/test_service_on_node.py index aab1e4da..9b0084bd 100644 --- a/tests/integration_tests/system/test_service_on_node.py +++ b/tests/integration_tests/system/test_service_on_node.py @@ -117,13 +117,14 @@ def test_server_turns_on_service(populated_node): assert server.operating_state is NodeOperatingState.ON assert service.operating_state is ServiceOperatingState.RUNNING - server.start_up_duration = 0 - server.shut_down_duration = 0 - server.power_off() + for i in range(server.start_up_duration + 1): + server.apply_timestep(timestep=i) assert server.operating_state is NodeOperatingState.OFF assert service.operating_state is ServiceOperatingState.STOPPED server.power_on() + for i in range(server.start_up_duration + 1): + server.apply_timestep(timestep=i) assert server.operating_state is NodeOperatingState.ON assert service.operating_state is ServiceOperatingState.RUNNING diff --git a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_application_actions.py b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_application_actions.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py new file mode 100644 index 00000000..6247a100 --- /dev/null +++ b/tests/unit_tests/_primaite/_simulator/_system/_applications/test_applications.py @@ -0,0 +1,50 @@ +from primaite.simulator.system.applications.application import ApplicationOperatingState +from primaite.simulator.system.software import SoftwareHealthState + + +def test_scan(application): + assert application.operating_state == ApplicationOperatingState.CLOSED + assert application.health_state_visible == SoftwareHealthState.UNUSED + + application.run() + assert application.operating_state == ApplicationOperatingState.RUNNING + assert application.health_state_visible == SoftwareHealthState.UNUSED + + application.scan() + assert application.operating_state == ApplicationOperatingState.RUNNING + assert application.health_state_visible == SoftwareHealthState.GOOD + + +def test_run_application(application): + assert application.operating_state == ApplicationOperatingState.CLOSED + assert application.health_state_actual == SoftwareHealthState.UNUSED + + application.run() + assert application.operating_state == ApplicationOperatingState.RUNNING + assert application.health_state_actual == SoftwareHealthState.GOOD + + +def test_close_application(application): + application.run() + assert application.operating_state == ApplicationOperatingState.RUNNING + assert application.health_state_actual == SoftwareHealthState.GOOD + + application.close() + assert application.operating_state == ApplicationOperatingState.CLOSED + assert application.health_state_actual == SoftwareHealthState.GOOD + + +def test_application_describe_states(application): + assert application.operating_state == ApplicationOperatingState.CLOSED + assert application.health_state_actual == SoftwareHealthState.UNUSED + + assert SoftwareHealthState.UNUSED.value == application.describe_state().get("health_state_actual") + + application.run() + assert SoftwareHealthState.GOOD.value == application.describe_state().get("health_state_actual") + + application.set_health_state(SoftwareHealthState.COMPROMISED) + assert SoftwareHealthState.COMPROMISED.value == application.describe_state().get("health_state_actual") + + application.patch() + assert SoftwareHealthState.PATCHING.value == application.describe_state().get("health_state_actual")