Merge remote-tracking branch 'origin/dev' into feature/2623-action-masking
This commit is contained in:
@@ -9,6 +9,9 @@ io_settings:
|
||||
save_pcap_logs: true
|
||||
save_sys_logs: true
|
||||
sys_log_level: WARNING
|
||||
agent_log_level: INFO
|
||||
save_agent_logs: true
|
||||
write_agent_log_to_terminal: True
|
||||
|
||||
|
||||
game:
|
||||
|
||||
@@ -41,6 +41,12 @@ agents:
|
||||
options:
|
||||
source_node: client_1
|
||||
target_ip_address: 192.168.10.0/24
|
||||
target_port:
|
||||
- 21
|
||||
- 53
|
||||
- 80
|
||||
- 123
|
||||
- 219
|
||||
|
||||
reward_function:
|
||||
reward_components:
|
||||
|
||||
@@ -9,6 +9,8 @@ game:
|
||||
|
||||
simulation:
|
||||
network:
|
||||
airspace:
|
||||
airspace_environment_type: urban
|
||||
nodes:
|
||||
- type: computer
|
||||
hostname: pc_a
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
game:
|
||||
max_episode_length: 256
|
||||
ports:
|
||||
- ARP
|
||||
protocols:
|
||||
- ICMP
|
||||
- TCP
|
||||
- UDP
|
||||
|
||||
simulation:
|
||||
network:
|
||||
airspace:
|
||||
airspace_environment_type: blocked
|
||||
nodes:
|
||||
- type: computer
|
||||
hostname: pc_a
|
||||
ip_address: 192.168.0.2
|
||||
subnet_mask: 255.255.255.0
|
||||
default_gateway: 192.168.0.1
|
||||
start_up_duration: 0
|
||||
|
||||
- type: computer
|
||||
hostname: pc_b
|
||||
ip_address: 192.168.2.2
|
||||
subnet_mask: 255.255.255.0
|
||||
default_gateway: 192.168.2.1
|
||||
start_up_duration: 0
|
||||
|
||||
- type: wireless_router
|
||||
hostname: router_1
|
||||
start_up_duration: 0
|
||||
|
||||
router_interface:
|
||||
ip_address: 192.168.0.1
|
||||
subnet_mask: 255.255.255.0
|
||||
|
||||
wireless_access_point:
|
||||
ip_address: 192.168.1.1
|
||||
subnet_mask: 255.255.255.0
|
||||
frequency: WIFI_5
|
||||
channel_width: 80
|
||||
acl:
|
||||
1:
|
||||
action: PERMIT
|
||||
routes:
|
||||
- address: 192.168.2.0 # PC B subnet
|
||||
subnet_mask: 255.255.255.0
|
||||
next_hop_ip_address: 192.168.1.2
|
||||
metric: 0
|
||||
|
||||
- type: wireless_router
|
||||
hostname: router_2
|
||||
start_up_duration: 0
|
||||
|
||||
router_interface:
|
||||
ip_address: 192.168.2.1
|
||||
subnet_mask: 255.255.255.0
|
||||
|
||||
wireless_access_point:
|
||||
ip_address: 192.168.1.2
|
||||
subnet_mask: 255.255.255.0
|
||||
frequency: WIFI_5
|
||||
channel_width: 80
|
||||
acl:
|
||||
1:
|
||||
action: PERMIT
|
||||
routes:
|
||||
- address: 192.168.0.0 # PC A subnet
|
||||
subnet_mask: 255.255.255.0
|
||||
next_hop_ip_address: 192.168.1.1
|
||||
metric: 0
|
||||
links:
|
||||
- endpoint_a_hostname: pc_a
|
||||
endpoint_a_port: 1
|
||||
endpoint_b_hostname: router_1
|
||||
endpoint_b_port: 2
|
||||
|
||||
- endpoint_a_hostname: pc_b
|
||||
endpoint_a_port: 1
|
||||
endpoint_b_hostname: router_2
|
||||
endpoint_b_port: 2
|
||||
@@ -0,0 +1,81 @@
|
||||
game:
|
||||
max_episode_length: 256
|
||||
ports:
|
||||
- ARP
|
||||
protocols:
|
||||
- ICMP
|
||||
- TCP
|
||||
- UDP
|
||||
|
||||
simulation:
|
||||
network:
|
||||
airspace:
|
||||
airspace_environment_type: urban
|
||||
nodes:
|
||||
- type: computer
|
||||
hostname: pc_a
|
||||
ip_address: 192.168.0.2
|
||||
subnet_mask: 255.255.255.0
|
||||
default_gateway: 192.168.0.1
|
||||
start_up_duration: 0
|
||||
|
||||
- type: computer
|
||||
hostname: pc_b
|
||||
ip_address: 192.168.2.2
|
||||
subnet_mask: 255.255.255.0
|
||||
default_gateway: 192.168.2.1
|
||||
start_up_duration: 0
|
||||
|
||||
- type: wireless_router
|
||||
hostname: router_1
|
||||
start_up_duration: 0
|
||||
|
||||
router_interface:
|
||||
ip_address: 192.168.0.1
|
||||
subnet_mask: 255.255.255.0
|
||||
|
||||
wireless_access_point:
|
||||
ip_address: 192.168.1.1
|
||||
subnet_mask: 255.255.255.0
|
||||
frequency: WIFI_5
|
||||
channel_width: 80
|
||||
acl:
|
||||
1:
|
||||
action: PERMIT
|
||||
routes:
|
||||
- address: 192.168.2.0 # PC B subnet
|
||||
subnet_mask: 255.255.255.0
|
||||
next_hop_ip_address: 192.168.1.2
|
||||
metric: 0
|
||||
|
||||
- type: wireless_router
|
||||
hostname: router_2
|
||||
start_up_duration: 0
|
||||
|
||||
router_interface:
|
||||
ip_address: 192.168.2.1
|
||||
subnet_mask: 255.255.255.0
|
||||
|
||||
wireless_access_point:
|
||||
ip_address: 192.168.1.2
|
||||
subnet_mask: 255.255.255.0
|
||||
frequency: WIFI_5
|
||||
channel_width: 80
|
||||
acl:
|
||||
1:
|
||||
action: PERMIT
|
||||
routes:
|
||||
- address: 192.168.0.0 # PC A subnet
|
||||
subnet_mask: 255.255.255.0
|
||||
next_hop_ip_address: 192.168.1.1
|
||||
metric: 0
|
||||
links:
|
||||
- endpoint_a_hostname: pc_a
|
||||
endpoint_a_port: 1
|
||||
endpoint_b_hostname: router_1
|
||||
endpoint_b_port: 2
|
||||
|
||||
- endpoint_a_hostname: pc_b
|
||||
endpoint_a_port: 1
|
||||
endpoint_b_hostname: router_2
|
||||
endpoint_b_port: 2
|
||||
@@ -53,11 +53,11 @@ class TestService(Service):
|
||||
pass
|
||||
|
||||
|
||||
class TestDummyApplication(Application, identifier="TestDummyApplication"):
|
||||
class DummyApplication(Application, identifier="DummyApplication"):
|
||||
"""Test Application class"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs["name"] = "TestDummyApplication"
|
||||
kwargs["name"] = "DummyApplication"
|
||||
kwargs["port"] = Port.HTTP
|
||||
kwargs["protocol"] = IPProtocol.TCP
|
||||
super().__init__(**kwargs)
|
||||
@@ -87,9 +87,9 @@ def service_class():
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def application(file_system) -> TestDummyApplication:
|
||||
return TestDummyApplication(
|
||||
name="TestDummyApplication",
|
||||
def application(file_system) -> DummyApplication:
|
||||
return DummyApplication(
|
||||
name="DummyApplication",
|
||||
port=Port.ARP,
|
||||
file_system=file_system,
|
||||
sys_log=SysLog(hostname="dummy_application"),
|
||||
@@ -98,7 +98,7 @@ def application(file_system) -> TestDummyApplication:
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def application_class():
|
||||
return TestDummyApplication
|
||||
return DummyApplication
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
@@ -257,8 +257,7 @@ def example_network() -> Network:
|
||||
server_2.power_on()
|
||||
network.connect(endpoint_b=server_2.network_interface[1], endpoint_a=switch_1.network_interface[2])
|
||||
|
||||
router_1.acl.add_rule(action=ACLAction.PERMIT, src_port=Port.ARP, dst_port=Port.ARP, position=22)
|
||||
router_1.acl.add_rule(action=ACLAction.PERMIT, protocol=IPProtocol.ICMP, position=23)
|
||||
router_1.acl.add_rule(action=ACLAction.PERMIT, position=1)
|
||||
|
||||
assert all(link.is_up for link in network.links.values())
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ def test_dev_mode_config_sys_log_level():
|
||||
# check defaults
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["sys_log_level"] == "DEBUG" # DEBUG by default
|
||||
|
||||
result = cli(["dev-mode", "config", "-level", "WARNING"])
|
||||
result = cli(["dev-mode", "config", "--sys-log-level", "WARNING"])
|
||||
|
||||
assert "sys_log_level=WARNING" in result.output # should print correct value
|
||||
|
||||
@@ -78,10 +78,30 @@ def test_dev_mode_config_sys_log_level():
|
||||
|
||||
assert "sys_log_level=INFO" in result.output # should print correct value
|
||||
|
||||
# config should reflect that log level is WARNING
|
||||
# config should reflect that log level is INFO
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["sys_log_level"] == "INFO"
|
||||
|
||||
|
||||
def test_dev_mode_config_agent_log_level():
|
||||
"""Check that the agent log level can be changed via CLI."""
|
||||
# check defaults
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["agent_log_level"] == "DEBUG" # DEBUG by default
|
||||
|
||||
result = cli(["dev-mode", "config", "-level", "WARNING"])
|
||||
|
||||
assert "agent_log_level=WARNING" in result.output # should print correct value
|
||||
|
||||
# config should reflect that log level is WARNING
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["agent_log_level"] == "WARNING"
|
||||
|
||||
result = cli(["dev-mode", "config", "--agent-log-level", "INFO"])
|
||||
|
||||
assert "agent_log_level=INFO" in result.output # should print correct value
|
||||
|
||||
# config should reflect that log level is INFO
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["agent_log_level"] == "INFO"
|
||||
|
||||
|
||||
def test_dev_mode_config_sys_logs_enable_disable():
|
||||
"""Test that the system logs output can be enabled or disabled."""
|
||||
# check defaults
|
||||
@@ -112,6 +132,36 @@ def test_dev_mode_config_sys_logs_enable_disable():
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["output_sys_logs"] is False
|
||||
|
||||
|
||||
def test_dev_mode_config_agent_logs_enable_disable():
|
||||
"""Test that the agent logs output can be enabled or disabled."""
|
||||
# check defaults
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["output_agent_logs"] is False # False by default
|
||||
|
||||
result = cli(["dev-mode", "config", "--output-agent-logs"])
|
||||
assert "output_agent_logs=True" in result.output # should print correct value
|
||||
|
||||
# config should reflect that output_agent_logs is True
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["output_agent_logs"]
|
||||
|
||||
result = cli(["dev-mode", "config", "--no-agent-logs"])
|
||||
assert "output_agent_logs=False" in result.output # should print correct value
|
||||
|
||||
# config should reflect that output_agent_logs is True
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["output_agent_logs"] is False
|
||||
|
||||
result = cli(["dev-mode", "config", "-agent"])
|
||||
assert "output_agent_logs=True" in result.output # should print correct value
|
||||
|
||||
# config should reflect that output_agent_logs is True
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["output_agent_logs"]
|
||||
|
||||
result = cli(["dev-mode", "config", "-nagent"])
|
||||
assert "output_agent_logs=False" in result.output # should print correct value
|
||||
|
||||
# config should reflect that output_agent_logs is True
|
||||
assert PRIMAITE_CONFIG["developer_mode"]["output_agent_logs"] is False
|
||||
|
||||
|
||||
def test_dev_mode_config_pcap_logs_enable_disable():
|
||||
"""Test that the pcap logs output can be enabled or disabled."""
|
||||
# check defaults
|
||||
|
||||
@@ -35,3 +35,7 @@ def test_io_settings():
|
||||
assert env.io.settings.save_step_metadata is False
|
||||
|
||||
assert env.io.settings.write_sys_log_to_terminal is False # false by default
|
||||
|
||||
assert env.io.settings.save_agent_logs is True
|
||||
assert env.io.settings.agent_log_level is LogLevel.INFO
|
||||
assert env.io.settings.write_agent_log_to_terminal is True # Set to True by the config file.
|
||||
|
||||
@@ -16,7 +16,7 @@ from tests import TEST_ASSETS_ROOT
|
||||
TEST_CONFIG = TEST_ASSETS_ROOT / "configs/software_fix_duration.yaml"
|
||||
ONE_ITEM_CONFIG = TEST_ASSETS_ROOT / "configs/fix_duration_one_item.yaml"
|
||||
|
||||
TestApplications = ["TestDummyApplication", "TestBroadcastClient"]
|
||||
TestApplications = ["DummyApplication", "BroadcastTestClient"]
|
||||
|
||||
|
||||
def load_config(config_path: Union[str, Path]) -> PrimaiteGame:
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.game.agent.interface import ProxyAgent
|
||||
from primaite.game.game import PrimaiteGame
|
||||
from primaite.simulator.network.hardware.nodes.host.computer import Computer
|
||||
from primaite.simulator.network.hardware.nodes.host.server import Server
|
||||
from primaite.simulator.system.applications.application import ApplicationOperatingState
|
||||
from primaite.simulator.system.applications.web_browser import WebBrowser
|
||||
from primaite.simulator.system.services.service import ServiceOperatingState
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def game_and_agent_fixture(game_and_agent):
|
||||
"""Create a game with a simple agent that can be controlled by the tests."""
|
||||
game, agent = game_and_agent
|
||||
|
||||
client_1: Computer = game.simulation.network.get_node_by_hostname("client_1")
|
||||
client_1.start_up_duration = 3
|
||||
|
||||
return (game, agent)
|
||||
|
||||
|
||||
def test_application_cannot_perform_actions_unless_running(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test the the request permissions prevent any actions unless application is running."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
browser: WebBrowser = client_1.software_manager.software.get("WebBrowser")
|
||||
|
||||
browser.close()
|
||||
assert browser.operating_state == ApplicationOperatingState.CLOSED
|
||||
|
||||
action = ("NODE_APPLICATION_SCAN", {"node_id": 0, "application_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert browser.operating_state == ApplicationOperatingState.CLOSED
|
||||
|
||||
action = ("NODE_APPLICATION_CLOSE", {"node_id": 0, "application_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert browser.operating_state == ApplicationOperatingState.CLOSED
|
||||
|
||||
action = ("NODE_APPLICATION_FIX", {"node_id": 0, "application_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert browser.operating_state == ApplicationOperatingState.CLOSED
|
||||
|
||||
action = ("NODE_APPLICATION_EXECUTE", {"node_id": 0, "application_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert browser.operating_state == ApplicationOperatingState.CLOSED
|
||||
@@ -0,0 +1,159 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
import uuid
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.game.agent.interface import ProxyAgent
|
||||
from primaite.game.game import PrimaiteGame
|
||||
from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus
|
||||
from primaite.simulator.network.hardware.nodes.host.computer import Computer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def game_and_agent_fixture(game_and_agent):
|
||||
"""Create a game with a simple agent that can be controlled by the tests."""
|
||||
game, agent = game_and_agent
|
||||
|
||||
client_1: Computer = game.simulation.network.get_node_by_hostname("client_1")
|
||||
client_1.start_up_duration = 3
|
||||
|
||||
return (game, agent)
|
||||
|
||||
|
||||
def test_create_file(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the validator allows a files to be created."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
|
||||
random_folder = str(uuid.uuid4())
|
||||
random_file = str(uuid.uuid4())
|
||||
|
||||
assert client_1.file_system.get_file(folder_name=random_folder, file_name=random_file) is None
|
||||
|
||||
action = (
|
||||
"NODE_FILE_CREATE",
|
||||
{"node_id": 0, "folder_name": random_folder, "file_name": random_file},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert client_1.file_system.get_file(folder_name=random_folder, file_name=random_file) is not None
|
||||
|
||||
|
||||
def test_file_delete_action(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the validator allows a file to be deleted."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
file = client_1.file_system.get_file(folder_name="downloads", file_name="cat.png")
|
||||
assert file.deleted is False
|
||||
|
||||
action = (
|
||||
"NODE_FILE_DELETE",
|
||||
{"node_id": 0, "folder_id": 0, "file_id": 0},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert file.deleted
|
||||
|
||||
|
||||
def test_file_scan_action(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the validator allows a file to be scanned."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
file = client_1.file_system.get_file(folder_name="downloads", file_name="cat.png")
|
||||
|
||||
file.corrupt()
|
||||
assert file.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
assert file.visible_health_status == FileSystemItemHealthStatus.GOOD
|
||||
|
||||
action = (
|
||||
"NODE_FILE_SCAN",
|
||||
{"node_id": 0, "folder_id": 0, "file_id": 0},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert file.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
assert file.visible_health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
|
||||
def test_file_repair_action(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the validator allows a folder to be created."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
file = client_1.file_system.get_file(folder_name="downloads", file_name="cat.png")
|
||||
|
||||
file.corrupt()
|
||||
assert file.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
action = (
|
||||
"NODE_FILE_REPAIR",
|
||||
{"node_id": 0, "folder_id": 0, "file_id": 0},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert file.health_status == FileSystemItemHealthStatus.GOOD
|
||||
|
||||
|
||||
def test_file_restore_action(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the validator allows a file to be restored."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
file = client_1.file_system.get_file(folder_name="downloads", file_name="cat.png")
|
||||
|
||||
file.corrupt()
|
||||
assert file.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
action = (
|
||||
"NODE_FILE_RESTORE",
|
||||
{"node_id": 0, "folder_id": 0, "file_id": 0},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert file.health_status == FileSystemItemHealthStatus.GOOD
|
||||
|
||||
|
||||
def test_file_corrupt_action(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the validator allows a file to be corrupted."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
file = client_1.file_system.get_file(folder_name="downloads", file_name="cat.png")
|
||||
|
||||
assert file.health_status == FileSystemItemHealthStatus.GOOD
|
||||
|
||||
action = (
|
||||
"NODE_FILE_CORRUPT",
|
||||
{"node_id": 0, "folder_id": 0, "file_id": 0},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert file.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
|
||||
def test_file_access_action(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the validator allows a file to be accessed."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
file = client_1.file_system.get_file(folder_name="downloads", file_name="cat.png")
|
||||
assert file.num_access == 0
|
||||
|
||||
action = (
|
||||
"NODE_FILE_ACCESS",
|
||||
{"node_id": 0, "folder_name": file.folder_name, "file_name": file.name},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert file.num_access == 1
|
||||
@@ -0,0 +1,123 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
import uuid
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.game.agent.interface import ProxyAgent
|
||||
from primaite.game.game import PrimaiteGame
|
||||
from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus
|
||||
from primaite.simulator.network.hardware.nodes.host.computer import Computer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def game_and_agent_fixture(game_and_agent):
|
||||
"""Create a game with a simple agent that can be controlled by the tests."""
|
||||
game, agent = game_and_agent
|
||||
|
||||
client_1: Computer = game.simulation.network.get_node_by_hostname("client_1")
|
||||
client_1.start_up_duration = 3
|
||||
|
||||
return (game, agent)
|
||||
|
||||
|
||||
def test_create_folder(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the validator allows a folder to be created."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
|
||||
random_folder = str(uuid.uuid4())
|
||||
|
||||
assert client_1.file_system.get_folder(folder_name=random_folder) is None
|
||||
|
||||
action = (
|
||||
"NODE_FOLDER_CREATE",
|
||||
{
|
||||
"node_id": 0,
|
||||
"folder_name": random_folder,
|
||||
},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert client_1.file_system.get_folder(folder_name=random_folder) is not None
|
||||
|
||||
|
||||
def test_folder_scan_action(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test to make sure that the validator checks if the folder exists before scanning."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
|
||||
folder = client_1.file_system.get_folder(folder_name="downloads")
|
||||
assert folder.health_status == FileSystemItemHealthStatus.GOOD
|
||||
assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD
|
||||
|
||||
folder.corrupt()
|
||||
|
||||
assert folder.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
assert folder.visible_health_status == FileSystemItemHealthStatus.GOOD
|
||||
|
||||
action = (
|
||||
"NODE_FOLDER_SCAN",
|
||||
{
|
||||
"node_id": 0, # client_1,
|
||||
"folder_id": 0, # downloads
|
||||
},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
for i in range(folder.scan_duration + 1):
|
||||
game.step()
|
||||
|
||||
assert folder.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
assert folder.visible_health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
|
||||
def test_folder_repair_action(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test to make sure that the validator checks if the folder exists before repairing."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
|
||||
folder = client_1.file_system.get_folder(folder_name="downloads")
|
||||
folder.corrupt()
|
||||
assert folder.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
action = (
|
||||
"NODE_FOLDER_REPAIR",
|
||||
{
|
||||
"node_id": 0, # client_1,
|
||||
"folder_id": 0, # downloads
|
||||
},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert folder.health_status == FileSystemItemHealthStatus.GOOD
|
||||
|
||||
|
||||
def test_folder_restore_action(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test to make sure that the validator checks if the folder exists before restoring."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
|
||||
folder = client_1.file_system.get_folder(folder_name="downloads")
|
||||
folder.corrupt()
|
||||
|
||||
assert folder.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
action = (
|
||||
"NODE_FOLDER_RESTORE",
|
||||
{
|
||||
"node_id": 0, # client_1,
|
||||
"folder_id": 0, # downloads
|
||||
},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert folder.health_status == FileSystemItemHealthStatus.RESTORING
|
||||
@@ -0,0 +1,95 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.game.agent.interface import ProxyAgent
|
||||
from primaite.game.game import PrimaiteGame
|
||||
from primaite.simulator.network.hardware.nodes.host.computer import Computer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def game_and_agent_fixture(game_and_agent):
|
||||
"""Create a game with a simple agent that can be controlled by the tests."""
|
||||
game, agent = game_and_agent
|
||||
|
||||
client_1: Computer = game.simulation.network.get_node_by_hostname("client_1")
|
||||
client_1.start_up_duration = 3
|
||||
|
||||
return (game, agent)
|
||||
|
||||
|
||||
def test_nic_cannot_be_turned_off_if_not_on(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that a NIC cannot be disabled if it is not enabled."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
nic = client_1.network_interface[1]
|
||||
nic.disable()
|
||||
assert nic.enabled is False
|
||||
|
||||
action = (
|
||||
"HOST_NIC_DISABLE",
|
||||
{
|
||||
"node_id": 0, # client_1
|
||||
"nic_id": 0, # the only nic (eth-1)
|
||||
},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert nic.enabled is False
|
||||
|
||||
|
||||
def test_nic_cannot_be_turned_on_if_already_on(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that a NIC cannot be enabled if it is already enabled."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
nic = client_1.network_interface[1]
|
||||
assert nic.enabled
|
||||
|
||||
action = (
|
||||
"HOST_NIC_ENABLE",
|
||||
{
|
||||
"node_id": 0, # client_1
|
||||
"nic_id": 0, # the only nic (eth-1)
|
||||
},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert nic.enabled
|
||||
|
||||
|
||||
def test_that_a_nic_can_be_enabled_and_disabled(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Tests that a NIC can be enabled and disabled."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
nic = client_1.network_interface[1]
|
||||
assert nic.enabled
|
||||
|
||||
action = (
|
||||
"HOST_NIC_DISABLE",
|
||||
{
|
||||
"node_id": 0, # client_1
|
||||
"nic_id": 0, # the only nic (eth-1)
|
||||
},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert nic.enabled is False
|
||||
|
||||
action = (
|
||||
"HOST_NIC_ENABLE",
|
||||
{
|
||||
"node_id": 0, # client_1
|
||||
"nic_id": 0, # the only nic (eth-1)
|
||||
},
|
||||
)
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert nic.enabled
|
||||
@@ -1 +1,94 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.game.agent.interface import ProxyAgent
|
||||
from primaite.game.game import PrimaiteGame
|
||||
from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState
|
||||
from primaite.simulator.network.hardware.nodes.host.computer import Computer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def game_and_agent_fixture(game_and_agent):
|
||||
"""Create a game with a simple agent that can be controlled by the tests."""
|
||||
game, agent = game_and_agent
|
||||
|
||||
client_1: Computer = game.simulation.network.get_node_by_hostname("client_1")
|
||||
client_1.start_up_duration = 3
|
||||
|
||||
return (game, agent)
|
||||
|
||||
|
||||
def test_node_startup_shutdown(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the node can be shut down and started up."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
|
||||
assert client_1.operating_state == NodeOperatingState.ON
|
||||
|
||||
# turn it off
|
||||
action = ("NODE_SHUTDOWN", {"node_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert client_1.operating_state == NodeOperatingState.SHUTTING_DOWN
|
||||
|
||||
for i in range(client_1.shut_down_duration + 1):
|
||||
action = ("DONOTHING", {"node_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert client_1.operating_state == NodeOperatingState.OFF
|
||||
|
||||
# turn it on
|
||||
action = ("NODE_STARTUP", {"node_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert client_1.operating_state == NodeOperatingState.BOOTING
|
||||
|
||||
for i in range(client_1.start_up_duration + 1):
|
||||
action = ("DONOTHING", {"node_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert client_1.operating_state == NodeOperatingState.ON
|
||||
|
||||
|
||||
def test_node_cannot_be_started_up_if_node_is_already_on(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that a node cannot be started up if it is already on."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
assert client_1.operating_state == NodeOperatingState.ON
|
||||
|
||||
# turn it on
|
||||
action = ("NODE_STARTUP", {"node_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert client_1.operating_state == NodeOperatingState.ON
|
||||
|
||||
|
||||
def test_node_cannot_be_shut_down_if_node_is_already_off(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that a node cannot be shut down if it is already off."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
client_1 = game.simulation.network.get_node_by_hostname("client_1")
|
||||
client_1.power_off()
|
||||
|
||||
for i in range(client_1.shut_down_duration + 1):
|
||||
action = ("DONOTHING", {"node_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert client_1.operating_state == NodeOperatingState.OFF
|
||||
|
||||
# turn it ff
|
||||
action = ("NODE_SHUTDOWN", {"node_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert client_1.operating_state == NodeOperatingState.OFF
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite.game.agent.interface import ProxyAgent
|
||||
from primaite.game.game import PrimaiteGame
|
||||
from primaite.simulator.network.hardware.nodes.host.computer import Computer
|
||||
from primaite.simulator.network.hardware.nodes.host.server import Server
|
||||
from primaite.simulator.system.services.service import ServiceOperatingState
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def game_and_agent_fixture(game_and_agent):
|
||||
"""Create a game with a simple agent that can be controlled by the tests."""
|
||||
game, agent = game_and_agent
|
||||
|
||||
client_1: Computer = game.simulation.network.get_node_by_hostname("client_1")
|
||||
client_1.start_up_duration = 3
|
||||
|
||||
return (game, agent)
|
||||
|
||||
|
||||
def test_service_start(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the validator makes sure that the service is stopped before starting the service."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
server_1: Server = game.simulation.network.get_node_by_hostname("server_1")
|
||||
dns_server = server_1.software_manager.software.get("DNSServer")
|
||||
|
||||
dns_server.pause()
|
||||
assert dns_server.operating_state == ServiceOperatingState.PAUSED
|
||||
|
||||
action = ("NODE_SERVICE_START", {"node_id": 1, "service_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert dns_server.operating_state == ServiceOperatingState.PAUSED
|
||||
|
||||
dns_server.stop()
|
||||
|
||||
assert dns_server.operating_state == ServiceOperatingState.STOPPED
|
||||
|
||||
action = ("NODE_SERVICE_START", {"node_id": 1, "service_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert dns_server.operating_state == ServiceOperatingState.RUNNING
|
||||
|
||||
|
||||
def test_service_resume(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test that the validator checks if the service is paused before resuming."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
server_1: Server = game.simulation.network.get_node_by_hostname("server_1")
|
||||
dns_server = server_1.software_manager.software.get("DNSServer")
|
||||
|
||||
action = ("NODE_SERVICE_RESUME", {"node_id": 1, "service_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert dns_server.operating_state == ServiceOperatingState.RUNNING
|
||||
|
||||
dns_server.pause()
|
||||
|
||||
assert dns_server.operating_state == ServiceOperatingState.PAUSED
|
||||
|
||||
action = ("NODE_SERVICE_RESUME", {"node_id": 1, "service_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
|
||||
assert dns_server.operating_state == ServiceOperatingState.RUNNING
|
||||
|
||||
|
||||
def test_service_cannot_perform_actions_unless_running(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]):
|
||||
"""Test to make sure that the service cannot perform certain actions while not running."""
|
||||
game, agent = game_and_agent_fixture
|
||||
|
||||
server_1: Server = game.simulation.network.get_node_by_hostname("server_1")
|
||||
dns_server = server_1.software_manager.software.get("DNSServer")
|
||||
|
||||
dns_server.stop()
|
||||
assert dns_server.operating_state == ServiceOperatingState.STOPPED
|
||||
|
||||
action = ("NODE_SERVICE_SCAN", {"node_id": 1, "service_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert dns_server.operating_state == ServiceOperatingState.STOPPED
|
||||
|
||||
action = ("NODE_SERVICE_PAUSE", {"node_id": 1, "service_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert dns_server.operating_state == ServiceOperatingState.STOPPED
|
||||
|
||||
action = ("NODE_SERVICE_RESUME", {"node_id": 1, "service_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert dns_server.operating_state == ServiceOperatingState.STOPPED
|
||||
|
||||
action = ("NODE_SERVICE_RESTART", {"node_id": 1, "service_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert dns_server.operating_state == ServiceOperatingState.STOPPED
|
||||
|
||||
action = ("NODE_SERVICE_FIX", {"node_id": 1, "service_id": 0})
|
||||
agent.store_action(action)
|
||||
game.step()
|
||||
assert dns_server.operating_state == ServiceOperatingState.STOPPED
|
||||
@@ -155,7 +155,7 @@ def test_nic_monitored_traffic(simulation):
|
||||
assert traffic_obs["icmp"]["outbound"] == 0
|
||||
|
||||
# send a ping
|
||||
pc.ping(target_ip_address=pc2.network_interface[1].ip_address)
|
||||
assert pc.ping(target_ip_address=pc2.network_interface[1].ip_address)
|
||||
traffic_obs = nic_obs.observe(simulation.describe_state()).get("TRAFFIC")
|
||||
|
||||
assert traffic_obs["icmp"]["inbound"] == 1
|
||||
@@ -178,7 +178,7 @@ def test_nic_monitored_traffic(simulation):
|
||||
traffic_obs = nic_obs.observe(simulation.describe_state()).get("TRAFFIC")
|
||||
assert traffic_obs["icmp"]["inbound"] == 0
|
||||
assert traffic_obs["icmp"]["outbound"] == 0
|
||||
assert traffic_obs["tcp"][53]["inbound"] == 0
|
||||
assert traffic_obs["tcp"][53]["inbound"] == 1
|
||||
assert traffic_obs["tcp"][53]["outbound"] == 1 # getting a webpage sent a dns request out
|
||||
|
||||
simulation.pre_timestep(2) # apply timestep to whole sim
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
import yaml
|
||||
|
||||
from primaite.game.game import PrimaiteGame
|
||||
from primaite.simulator.network.airspace import (
|
||||
AirspaceEnvironmentType,
|
||||
AirSpaceFrequency,
|
||||
calculate_total_channel_capacity,
|
||||
ChannelWidth,
|
||||
)
|
||||
from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter
|
||||
from tests import TEST_ASSETS_ROOT
|
||||
|
||||
|
||||
def test_wireless_wan_wifi_5_80_channel_width_urban():
|
||||
config_path = TEST_ASSETS_ROOT / "configs" / "wireless_wan_wifi_5_80_channel_width_urban.yaml"
|
||||
|
||||
with open(config_path, "r") as f:
|
||||
config_dict = yaml.safe_load(f)
|
||||
network = PrimaiteGame.from_config(cfg=config_dict).simulation.network
|
||||
|
||||
airspace = network.airspace
|
||||
|
||||
assert airspace.airspace_environment_type == AirspaceEnvironmentType.URBAN
|
||||
|
||||
router_1: WirelessRouter = network.get_node_by_hostname("router_1")
|
||||
router_2: WirelessRouter = network.get_node_by_hostname("router_2")
|
||||
|
||||
expected_speed = calculate_total_channel_capacity(
|
||||
channel_width=ChannelWidth.WIDTH_80_MHZ,
|
||||
frequency=AirSpaceFrequency.WIFI_5,
|
||||
environment_type=AirspaceEnvironmentType.URBAN,
|
||||
)
|
||||
|
||||
assert router_1.wireless_access_point.speed == expected_speed
|
||||
assert router_2.wireless_access_point.speed == expected_speed
|
||||
|
||||
pc_a = network.get_node_by_hostname("pc_a")
|
||||
pc_b = network.get_node_by_hostname("pc_b")
|
||||
|
||||
assert pc_a.ping(pc_a.default_gateway), "PC A should ping its default gateway successfully."
|
||||
assert pc_b.ping(pc_b.default_gateway), "PC B should ping its default gateway successfully."
|
||||
|
||||
assert pc_a.ping(pc_b.network_interface[1].ip_address), "PC A should ping PC B across routers successfully."
|
||||
assert pc_b.ping(pc_a.network_interface[1].ip_address), "PC B should ping PC A across routers successfully."
|
||||
|
||||
|
||||
def test_wireless_wan_wifi_5_80_channel_width_blocked():
|
||||
config_path = TEST_ASSETS_ROOT / "configs" / "wireless_wan_wifi_5_80_channel_width_blocked.yaml"
|
||||
|
||||
with open(config_path, "r") as f:
|
||||
config_dict = yaml.safe_load(f)
|
||||
network = PrimaiteGame.from_config(cfg=config_dict).simulation.network
|
||||
|
||||
airspace = network.airspace
|
||||
|
||||
assert airspace.airspace_environment_type == AirspaceEnvironmentType.BLOCKED
|
||||
|
||||
router_1: WirelessRouter = network.get_node_by_hostname("router_1")
|
||||
router_2: WirelessRouter = network.get_node_by_hostname("router_2")
|
||||
|
||||
expected_speed = calculate_total_channel_capacity(
|
||||
channel_width=ChannelWidth.WIDTH_80_MHZ,
|
||||
frequency=AirSpaceFrequency.WIFI_5,
|
||||
environment_type=AirspaceEnvironmentType.BLOCKED,
|
||||
)
|
||||
|
||||
assert router_1.wireless_access_point.speed == expected_speed
|
||||
assert router_2.wireless_access_point.speed == expected_speed
|
||||
|
||||
pc_a = network.get_node_by_hostname("pc_a")
|
||||
pc_b = network.get_node_by_hostname("pc_b")
|
||||
|
||||
assert pc_a.ping(pc_a.default_gateway), "PC A should ping its default gateway successfully."
|
||||
assert pc_b.ping(pc_b.default_gateway), "PC B should ping its default gateway successfully."
|
||||
|
||||
assert not pc_a.ping(pc_b.network_interface[1].ip_address), "PC A should ping PC B across routers unsuccessfully."
|
||||
assert not pc_b.ping(pc_a.network_interface[1].ip_address), "PC B should ping PC A across routers unsuccessfully."
|
||||
|
||||
|
||||
def test_wireless_wan_blocking_and_unblocking_airspace():
|
||||
config_path = TEST_ASSETS_ROOT / "configs" / "wireless_wan_wifi_5_80_channel_width_urban.yaml"
|
||||
|
||||
with open(config_path, "r") as f:
|
||||
config_dict = yaml.safe_load(f)
|
||||
network = PrimaiteGame.from_config(cfg=config_dict).simulation.network
|
||||
|
||||
airspace = network.airspace
|
||||
|
||||
assert airspace.airspace_environment_type == AirspaceEnvironmentType.URBAN
|
||||
|
||||
pc_a = network.get_node_by_hostname("pc_a")
|
||||
pc_b = network.get_node_by_hostname("pc_b")
|
||||
|
||||
assert pc_a.ping(pc_b.network_interface[1].ip_address), "PC A should ping PC B across routers successfully."
|
||||
assert pc_b.ping(pc_a.network_interface[1].ip_address), "PC B should ping PC A across routers successfully."
|
||||
|
||||
airspace.airspace_environment_type = AirspaceEnvironmentType.BLOCKED
|
||||
|
||||
assert not pc_a.ping(pc_b.network_interface[1].ip_address), "PC A should ping PC B across routers unsuccessfully."
|
||||
assert not pc_b.ping(pc_a.network_interface[1].ip_address), "PC B should ping PC A across routers unsuccessfully."
|
||||
|
||||
airspace.airspace_environment_type = AirspaceEnvironmentType.URBAN
|
||||
|
||||
assert pc_a.ping(pc_b.network_interface[1].ip_address), "PC A should ping PC B across routers successfully."
|
||||
assert pc_b.ping(pc_a.network_interface[1].ip_address), "PC B should ping PC A across routers successfully."
|
||||
@@ -0,0 +1,138 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
from primaite.simulator.file_system.file_type import FileType
|
||||
from primaite.simulator.network.hardware.nodes.network.router import ACLAction
|
||||
from primaite.simulator.system.services.ftp.ftp_client import FTPClient
|
||||
from primaite.simulator.system.services.ftp.ftp_server import FTPServer
|
||||
from tests.integration_tests.network.test_wireless_router import wireless_wan_network
|
||||
from tests.integration_tests.system.test_ftp_client_server import ftp_client_and_ftp_server
|
||||
|
||||
|
||||
def test_wireless_link_loading(wireless_wan_network):
|
||||
client, server, router_1, router_2 = wireless_wan_network
|
||||
|
||||
# Configure Router 1 ACLs
|
||||
router_1.acl.add_rule(action=ACLAction.PERMIT, position=1)
|
||||
|
||||
# Configure Router 2 ACLs
|
||||
router_2.acl.add_rule(action=ACLAction.PERMIT, position=1)
|
||||
|
||||
airspace = router_1.airspace
|
||||
|
||||
client.software_manager.install(FTPClient)
|
||||
ftp_client: FTPClient = client.software_manager.software.get("FTPClient")
|
||||
ftp_client.start()
|
||||
|
||||
server.software_manager.install(FTPServer)
|
||||
ftp_server: FTPServer = server.software_manager.software.get("FTPServer")
|
||||
ftp_server.start()
|
||||
|
||||
client.file_system.create_file(file_name="mixtape", size=10 * 10**6, file_type=FileType.MP3, folder_name="music")
|
||||
|
||||
assert ftp_client.send_file(
|
||||
src_file_name="mixtape.mp3",
|
||||
src_folder_name="music",
|
||||
dest_ip_address=server.network_interface[1].ip_address,
|
||||
dest_file_name="mixtape.mp3",
|
||||
dest_folder_name="music",
|
||||
)
|
||||
|
||||
# Reset the physical links between the host nodes and the routers
|
||||
client.network_interface[1]._connected_link.pre_timestep(1)
|
||||
server.network_interface[1]._connected_link.pre_timestep(1)
|
||||
|
||||
assert ftp_client.send_file(
|
||||
src_file_name="mixtape.mp3",
|
||||
src_folder_name="music",
|
||||
dest_ip_address=server.network_interface[1].ip_address,
|
||||
dest_file_name="mixtape1.mp3",
|
||||
dest_folder_name="music",
|
||||
)
|
||||
|
||||
# Reset the physical links between the host nodes and the routers
|
||||
client.network_interface[1]._connected_link.pre_timestep(1)
|
||||
server.network_interface[1]._connected_link.pre_timestep(1)
|
||||
|
||||
assert ftp_client.send_file(
|
||||
src_file_name="mixtape.mp3",
|
||||
src_folder_name="music",
|
||||
dest_ip_address=server.network_interface[1].ip_address,
|
||||
dest_file_name="mixtape2.mp3",
|
||||
dest_folder_name="music",
|
||||
)
|
||||
|
||||
# Reset the physical links between the host nodes and the routers
|
||||
client.network_interface[1]._connected_link.pre_timestep(1)
|
||||
server.network_interface[1]._connected_link.pre_timestep(1)
|
||||
|
||||
assert not ftp_client.send_file(
|
||||
src_file_name="mixtape.mp3",
|
||||
src_folder_name="music",
|
||||
dest_ip_address=server.network_interface[1].ip_address,
|
||||
dest_file_name="mixtape3.mp3",
|
||||
dest_folder_name="music",
|
||||
)
|
||||
|
||||
# Reset the physical links between the host nodes and the routers
|
||||
client.network_interface[1]._connected_link.pre_timestep(1)
|
||||
server.network_interface[1]._connected_link.pre_timestep(1)
|
||||
|
||||
airspace.reset_bandwidth_load()
|
||||
|
||||
assert ftp_client.send_file(
|
||||
src_file_name="mixtape.mp3",
|
||||
src_folder_name="music",
|
||||
dest_ip_address=server.network_interface[1].ip_address,
|
||||
dest_file_name="mixtape3.mp3",
|
||||
dest_folder_name="music",
|
||||
)
|
||||
|
||||
|
||||
def test_wired_link_loading(ftp_client_and_ftp_server):
|
||||
ftp_client, computer, ftp_server, server = ftp_client_and_ftp_server
|
||||
|
||||
link = computer.network_interface[1]._connected_link # noqa
|
||||
|
||||
assert link.is_up
|
||||
|
||||
link.pre_timestep(1)
|
||||
|
||||
computer.file_system.create_file(
|
||||
file_name="mixtape", size=10 * 10**6, file_type=FileType.MP3, folder_name="music"
|
||||
)
|
||||
link_load = link.current_load
|
||||
assert link_load == 0.0
|
||||
|
||||
assert ftp_client.send_file(
|
||||
src_file_name="mixtape.mp3",
|
||||
src_folder_name="music",
|
||||
dest_ip_address=server.network_interface[1].ip_address,
|
||||
dest_file_name="mixtape.mp3",
|
||||
dest_folder_name="music",
|
||||
)
|
||||
|
||||
new_link_load = link.current_load
|
||||
assert new_link_load > link_load
|
||||
|
||||
assert not ftp_client.send_file(
|
||||
src_file_name="mixtape.mp3",
|
||||
src_folder_name="music",
|
||||
dest_ip_address=server.network_interface[1].ip_address,
|
||||
dest_file_name="mixtape1.mp3",
|
||||
dest_folder_name="music",
|
||||
)
|
||||
|
||||
link.pre_timestep(2)
|
||||
|
||||
link_load = link.current_load
|
||||
assert link_load == 0.0
|
||||
|
||||
assert ftp_client.send_file(
|
||||
src_file_name="mixtape.mp3",
|
||||
src_folder_name="music",
|
||||
dest_ip_address=server.network_interface[1].ip_address,
|
||||
dest_file_name="mixtape1.mp3",
|
||||
dest_folder_name="music",
|
||||
)
|
||||
|
||||
new_link_load = link.current_load
|
||||
assert new_link_load > link_load
|
||||
@@ -14,7 +14,7 @@ from primaite.simulator.system.applications.application import Application
|
||||
from primaite.simulator.system.services.service import Service
|
||||
|
||||
|
||||
class TestBroadcastService(Service):
|
||||
class BroadcastTestService(Service):
|
||||
"""A service for sending broadcast and unicast messages over a network."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@@ -41,14 +41,14 @@ class TestBroadcastService(Service):
|
||||
super().send(payload="broadcast", dest_ip_address=ip_network, dest_port=Port.HTTP, ip_protocol=self.protocol)
|
||||
|
||||
|
||||
class TestBroadcastClient(Application, identifier="TestBroadcastClient"):
|
||||
class BroadcastTestClient(Application, identifier="BroadcastTestClient"):
|
||||
"""A client application to receive broadcast and unicast messages."""
|
||||
|
||||
payloads_received: List = []
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# Set default client properties
|
||||
kwargs["name"] = "TestBroadcastClient"
|
||||
kwargs["name"] = "BroadcastTestClient"
|
||||
kwargs["port"] = Port.HTTP
|
||||
kwargs["protocol"] = IPProtocol.TCP
|
||||
super().__init__(**kwargs)
|
||||
@@ -75,8 +75,8 @@ def broadcast_network() -> Network:
|
||||
start_up_duration=0,
|
||||
)
|
||||
client_1.power_on()
|
||||
client_1.software_manager.install(TestBroadcastClient)
|
||||
application_1 = client_1.software_manager.software["TestBroadcastClient"]
|
||||
client_1.software_manager.install(BroadcastTestClient)
|
||||
application_1 = client_1.software_manager.software["BroadcastTestClient"]
|
||||
application_1.run()
|
||||
|
||||
client_2 = Computer(
|
||||
@@ -87,8 +87,8 @@ def broadcast_network() -> Network:
|
||||
start_up_duration=0,
|
||||
)
|
||||
client_2.power_on()
|
||||
client_2.software_manager.install(TestBroadcastClient)
|
||||
application_2 = client_2.software_manager.software["TestBroadcastClient"]
|
||||
client_2.software_manager.install(BroadcastTestClient)
|
||||
application_2 = client_2.software_manager.software["BroadcastTestClient"]
|
||||
application_2.run()
|
||||
|
||||
server_1 = Server(
|
||||
@@ -100,8 +100,8 @@ def broadcast_network() -> Network:
|
||||
)
|
||||
server_1.power_on()
|
||||
|
||||
server_1.software_manager.install(TestBroadcastService)
|
||||
service: TestBroadcastService = server_1.software_manager.software["BroadcastService"]
|
||||
server_1.software_manager.install(BroadcastTestService)
|
||||
service: BroadcastTestService = server_1.software_manager.software["BroadcastService"]
|
||||
service.start()
|
||||
|
||||
switch_1 = Switch(hostname="switch_1", num_ports=6, start_up_duration=0)
|
||||
@@ -117,14 +117,14 @@ def broadcast_network() -> Network:
|
||||
@pytest.fixture(scope="function")
|
||||
def broadcast_service_and_clients(
|
||||
broadcast_network,
|
||||
) -> Tuple[TestBroadcastService, TestBroadcastClient, TestBroadcastClient]:
|
||||
client_1: TestBroadcastClient = broadcast_network.get_node_by_hostname("client_1").software_manager.software[
|
||||
"TestBroadcastClient"
|
||||
) -> Tuple[BroadcastTestService, BroadcastTestClient, BroadcastTestClient]:
|
||||
client_1: BroadcastTestClient = broadcast_network.get_node_by_hostname("client_1").software_manager.software[
|
||||
"BroadcastTestClient"
|
||||
]
|
||||
client_2: TestBroadcastClient = broadcast_network.get_node_by_hostname("client_2").software_manager.software[
|
||||
"TestBroadcastClient"
|
||||
client_2: BroadcastTestClient = broadcast_network.get_node_by_hostname("client_2").software_manager.software[
|
||||
"BroadcastTestClient"
|
||||
]
|
||||
service: TestBroadcastService = broadcast_network.get_node_by_hostname("server_1").software_manager.software[
|
||||
service: BroadcastTestService = broadcast_network.get_node_by_hostname("server_1").software_manager.software[
|
||||
"BroadcastService"
|
||||
]
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ def populated_node(application_class) -> Tuple[Application, Computer]:
|
||||
computer.power_on()
|
||||
computer.software_manager.install(application_class)
|
||||
|
||||
app = computer.software_manager.software.get("TestDummyApplication")
|
||||
app = computer.software_manager.software.get("DummyApplication")
|
||||
app.run()
|
||||
|
||||
return app, computer
|
||||
@@ -39,7 +39,7 @@ def test_application_on_offline_node(application_class):
|
||||
)
|
||||
computer.software_manager.install(application_class)
|
||||
|
||||
app: Application = computer.software_manager.software.get("TestDummyApplication")
|
||||
app: Application = computer.software_manager.software.get("DummyApplication")
|
||||
|
||||
computer.power_off()
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ def test_port_scan_full_subnet_all_ports_and_protocols(example_network):
|
||||
|
||||
actual_result = client_1_nmap.port_scan(
|
||||
target_ip_address=IPv4Network("192.168.10.0/24"),
|
||||
target_port=[Port.ARP, Port.HTTP, Port.FTP, Port.DNS, Port.NTP],
|
||||
)
|
||||
|
||||
expected_result = {
|
||||
|
||||
@@ -13,7 +13,7 @@ from primaite.simulator.network.hardware.node_operating_state import NodeOperati
|
||||
from primaite.simulator.network.hardware.nodes.host.host_node import HostNode
|
||||
from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router
|
||||
from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from tests.conftest import TestDummyApplication, TestService
|
||||
from tests.conftest import DummyApplication, TestService
|
||||
|
||||
|
||||
def test_successful_node_file_system_creation_request(example_network):
|
||||
@@ -47,14 +47,14 @@ def test_successful_application_requests(example_network):
|
||||
net = example_network
|
||||
|
||||
client_1 = net.get_node_by_hostname("client_1")
|
||||
client_1.software_manager.install(TestDummyApplication)
|
||||
client_1.software_manager.software.get("TestDummyApplication").run()
|
||||
client_1.software_manager.install(DummyApplication)
|
||||
client_1.software_manager.software.get("DummyApplication").run()
|
||||
|
||||
resp_1 = net.apply_request(["node", "client_1", "application", "TestDummyApplication", "scan"])
|
||||
resp_1 = net.apply_request(["node", "client_1", "application", "DummyApplication", "scan"])
|
||||
assert resp_1 == RequestResponse(status="success", data={})
|
||||
resp_2 = net.apply_request(["node", "client_1", "application", "TestDummyApplication", "fix"])
|
||||
resp_2 = net.apply_request(["node", "client_1", "application", "DummyApplication", "fix"])
|
||||
assert resp_2 == RequestResponse(status="success", data={})
|
||||
resp_3 = net.apply_request(["node", "client_1", "application", "TestDummyApplication", "compromise"])
|
||||
resp_3 = net.apply_request(["node", "client_1", "application", "DummyApplication", "compromise"])
|
||||
assert resp_3 == RequestResponse(status="success", data={})
|
||||
|
||||
|
||||
|
||||
137
tests/unit_tests/_primaite/_game/_agent/test_agent_log.py
Normal file
137
tests/unit_tests/_primaite/_game/_agent/test_agent_log.py
Normal file
@@ -0,0 +1,137 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from primaite import PRIMAITE_CONFIG
|
||||
from primaite.game.agent.agent_log import AgentLog
|
||||
from primaite.simulator import LogLevel, SIM_OUTPUT
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def override_dev_mode_temporarily():
|
||||
"""Temporarily turn off dev mode for this test."""
|
||||
primaite_dev_mode = PRIMAITE_CONFIG["developer_mode"]["enabled"]
|
||||
PRIMAITE_CONFIG["developer_mode"]["enabled"] = False
|
||||
yield # run tests
|
||||
PRIMAITE_CONFIG["developer_mode"]["enabled"] = primaite_dev_mode
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def agentlog() -> AgentLog:
|
||||
return AgentLog(agent_name="test_agent")
|
||||
|
||||
|
||||
def test_debug_agent_log_level(agentlog, capsys):
|
||||
"""Test that the debug log level logs debug agent logs and above."""
|
||||
SIM_OUTPUT.agent_log_level = LogLevel.DEBUG
|
||||
SIM_OUTPUT.write_agent_log_to_terminal = True
|
||||
|
||||
test_string = str(uuid4())
|
||||
|
||||
agentlog.debug(msg=test_string)
|
||||
agentlog.info(msg=test_string)
|
||||
agentlog.warning(msg=test_string)
|
||||
agentlog.error(msg=test_string)
|
||||
agentlog.critical(msg=test_string)
|
||||
|
||||
captured = "".join(capsys.readouterr())
|
||||
|
||||
assert test_string in captured
|
||||
assert "DEBUG" in captured
|
||||
assert "INFO" in captured
|
||||
assert "WARNING" in captured
|
||||
assert "ERROR" in captured
|
||||
assert "CRITICAL" in captured
|
||||
|
||||
|
||||
def test_info_agent_log_level(agentlog, capsys):
|
||||
"""Test that the debug log level logs debug agent logs and above."""
|
||||
SIM_OUTPUT.agent_log_level = LogLevel.INFO
|
||||
SIM_OUTPUT.write_agent_log_to_terminal = True
|
||||
|
||||
test_string = str(uuid4())
|
||||
|
||||
agentlog.debug(msg=test_string)
|
||||
agentlog.info(msg=test_string)
|
||||
agentlog.warning(msg=test_string)
|
||||
agentlog.error(msg=test_string)
|
||||
agentlog.critical(msg=test_string)
|
||||
|
||||
captured = "".join(capsys.readouterr())
|
||||
|
||||
assert test_string in captured
|
||||
assert "DEBUG" not in captured
|
||||
assert "INFO" in captured
|
||||
assert "WARNING" in captured
|
||||
assert "ERROR" in captured
|
||||
assert "CRITICAL" in captured
|
||||
|
||||
|
||||
def test_warning_agent_log_level(agentlog, capsys):
|
||||
"""Test that the debug log level logs debug agent logs and above."""
|
||||
SIM_OUTPUT.agent_log_level = LogLevel.WARNING
|
||||
SIM_OUTPUT.write_agent_log_to_terminal = True
|
||||
|
||||
test_string = str(uuid4())
|
||||
|
||||
agentlog.debug(msg=test_string)
|
||||
agentlog.info(msg=test_string)
|
||||
agentlog.warning(msg=test_string)
|
||||
agentlog.error(msg=test_string)
|
||||
agentlog.critical(msg=test_string)
|
||||
|
||||
captured = "".join(capsys.readouterr())
|
||||
|
||||
assert test_string in captured
|
||||
assert "DEBUG" not in captured
|
||||
assert "INFO" not in captured
|
||||
assert "WARNING" in captured
|
||||
assert "ERROR" in captured
|
||||
assert "CRITICAL" in captured
|
||||
|
||||
|
||||
def test_error_agent_log_level(agentlog, capsys):
|
||||
"""Test that the debug log level logs debug agent logs and above."""
|
||||
SIM_OUTPUT.agent_log_level = LogLevel.ERROR
|
||||
SIM_OUTPUT.write_agent_log_to_terminal = True
|
||||
|
||||
test_string = str(uuid4())
|
||||
|
||||
agentlog.debug(msg=test_string)
|
||||
agentlog.info(msg=test_string)
|
||||
agentlog.warning(msg=test_string)
|
||||
agentlog.error(msg=test_string)
|
||||
agentlog.critical(msg=test_string)
|
||||
|
||||
captured = "".join(capsys.readouterr())
|
||||
|
||||
assert test_string in captured
|
||||
assert "DEBUG" not in captured
|
||||
assert "INFO" not in captured
|
||||
assert "WARNING" not in captured
|
||||
assert "ERROR" in captured
|
||||
assert "CRITICAL" in captured
|
||||
|
||||
|
||||
def test_critical_agent_log_level(agentlog, capsys):
|
||||
"""Test that the debug log level logs debug agent logs and above."""
|
||||
SIM_OUTPUT.agent_log_level = LogLevel.CRITICAL
|
||||
SIM_OUTPUT.write_agent_log_to_terminal = True
|
||||
|
||||
test_string = str(uuid4())
|
||||
|
||||
agentlog.debug(msg=test_string)
|
||||
agentlog.info(msg=test_string)
|
||||
agentlog.warning(msg=test_string)
|
||||
agentlog.error(msg=test_string)
|
||||
agentlog.critical(msg=test_string)
|
||||
|
||||
captured = "".join(capsys.readouterr())
|
||||
|
||||
assert test_string in captured
|
||||
assert "DEBUG" not in captured
|
||||
assert "INFO" not in captured
|
||||
assert "WARNING" not in captured
|
||||
assert "ERROR" not in captured
|
||||
assert "CRITICAL" in captured
|
||||
@@ -26,7 +26,7 @@ def test_file_scan_request(populated_file_system):
|
||||
assert file.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
assert file.visible_health_status == FileSystemItemHealthStatus.GOOD
|
||||
|
||||
fs.apply_request(request=["file", file.name, "scan"])
|
||||
fs.apply_request(request=["folder", folder.name, "file", file.name, "scan"])
|
||||
|
||||
assert file.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
assert file.visible_health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
@@ -37,12 +37,12 @@ def test_file_checkhash_request(populated_file_system):
|
||||
"""Test that an agent can request a file hash check."""
|
||||
fs, folder, file = populated_file_system
|
||||
|
||||
fs.apply_request(request=["file", file.name, "checkhash"])
|
||||
fs.apply_request(request=["folder", folder.name, "file", file.name, "checkhash"])
|
||||
|
||||
assert file.health_status == FileSystemItemHealthStatus.GOOD
|
||||
file.sim_size = 0
|
||||
|
||||
fs.apply_request(request=["file", file.name, "checkhash"])
|
||||
fs.apply_request(request=["folder", folder.name, "file", file.name, "checkhash"])
|
||||
|
||||
assert file.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
@@ -54,7 +54,7 @@ def test_file_repair_request(populated_file_system):
|
||||
file.corrupt()
|
||||
assert file.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
fs.apply_request(request=["file", file.name, "repair"])
|
||||
fs.apply_request(request=["folder", folder.name, "file", file.name, "repair"])
|
||||
assert file.health_status == FileSystemItemHealthStatus.GOOD
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ def test_file_restore_request(populated_file_system):
|
||||
assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None
|
||||
assert fs.get_file(folder_name=folder.name, file_name=file.name).deleted is False
|
||||
|
||||
fs.apply_request(request=["file", file.name, "corrupt"])
|
||||
fs.apply_request(request=["folder", folder.name, "file", file.name, "corrupt"])
|
||||
assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
fs.apply_request(request=["restore", "file", folder.name, file.name])
|
||||
@@ -81,7 +81,7 @@ def test_file_restore_request(populated_file_system):
|
||||
def test_file_corrupt_request(populated_file_system):
|
||||
"""Test that an agent can request a file corruption."""
|
||||
fs, folder, file = populated_file_system
|
||||
fs.apply_request(request=["file", file.name, "corrupt"])
|
||||
fs.apply_request(request=["folder", folder.name, "file", file.name, "corrupt"])
|
||||
assert file.health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ def test_deleted_file_cannot_be_interacted_with(populated_file_system):
|
||||
fs, folder, file = populated_file_system
|
||||
assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None
|
||||
|
||||
fs.apply_request(request=["file", file.name, "corrupt"])
|
||||
fs.apply_request(request=["folder", folder.name, "file", file.name, "corrupt"])
|
||||
assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
assert (
|
||||
fs.get_file(folder_name=folder.name, file_name=file.name).visible_health_status
|
||||
|
||||
@@ -39,3 +39,39 @@ def test_folder_delete_request(populated_file_system):
|
||||
assert fs.get_file_by_id(folder_uuid=folder.uuid, file_uuid=file.uuid) is None
|
||||
|
||||
fs.show(full=True)
|
||||
|
||||
|
||||
def test_folder_exists_request_validator(populated_file_system):
|
||||
"""Tests that the _FolderExistsValidator works as intended."""
|
||||
fs, folder, file = populated_file_system
|
||||
validator = FileSystem._FolderExistsValidator(file_system=fs)
|
||||
|
||||
assert validator(request=["test_folder"], context={}) # test_folder exists
|
||||
assert validator(request=["fake_folder"], context={}) is False # fake_folder does not exist
|
||||
|
||||
assert validator.fail_message == "Cannot perform request on folder because it does not exist."
|
||||
|
||||
|
||||
def test_file_exists_request_validator(populated_file_system):
|
||||
"""Tests that the _FolderExistsValidator works as intended."""
|
||||
fs, folder, file = populated_file_system
|
||||
validator = FileSystem._FileExistsValidator(file_system=fs)
|
||||
|
||||
assert validator(request=["test_folder", "test_file.txt"], context={}) # test_file.txt exists
|
||||
assert validator(request=["test_folder", "fake_file.txt"], context={}) is False # fake_file.txt does not exist
|
||||
|
||||
assert validator.fail_message == "Cannot perform request on a file that does not exist."
|
||||
|
||||
|
||||
def test_folder_not_deleted_request_validator(populated_file_system):
|
||||
"""Tests that the _FolderExistsValidator works as intended."""
|
||||
fs, folder, file = populated_file_system
|
||||
validator = FileSystem._FolderNotDeletedValidator(file_system=fs)
|
||||
|
||||
assert validator(request=["test_folder"], context={}) # test_folder is not deleted
|
||||
|
||||
fs.delete_folder(folder_name="test_folder")
|
||||
|
||||
assert validator(request=["test_folder"], context={}) is False # test_folder is deleted
|
||||
|
||||
assert validator.fail_message == "Cannot perform request on folder because it is deleted."
|
||||
|
||||
@@ -166,15 +166,40 @@ def test_deleted_folder_and_its_files_cannot_be_interacted_with(populated_file_s
|
||||
fs, folder, file = populated_file_system
|
||||
assert fs.get_file(folder_name=folder.name, file_name=file.name) is not None
|
||||
|
||||
fs.apply_request(request=["file", file.name, "corrupt"])
|
||||
fs.apply_request(request=["folder", folder.name, "file", file.name, "corrupt"])
|
||||
assert fs.get_file(folder_name=folder.name, file_name=file.name).health_status == FileSystemItemHealthStatus.CORRUPT
|
||||
|
||||
fs.apply_request(request=["delete", "folder", folder.name])
|
||||
assert fs.get_file(folder_name=folder.name, file_name=file.name) is None
|
||||
|
||||
fs.apply_request(request=["file", file.name, "repair"])
|
||||
fs.apply_request(request=["folder", folder.name, "file", file.name, "repair"])
|
||||
|
||||
deleted_folder = fs.deleted_folders.get(folder.uuid)
|
||||
deleted_file = deleted_folder.deleted_files.get(file.uuid)
|
||||
|
||||
assert deleted_file.health_status is not FileSystemItemHealthStatus.GOOD
|
||||
|
||||
|
||||
def test_file_exists_request_validator(populated_file_system):
|
||||
"""Tests that the _FolderExistsValidator works as intended."""
|
||||
fs, folder, file = populated_file_system
|
||||
validator = Folder._FileExistsValidator(folder=folder)
|
||||
|
||||
assert validator(request=["test_file.txt"], context={}) # test_file.txt exists
|
||||
assert validator(request=["fake_file.txt"], context={}) is False # fake_file.txt does not exist
|
||||
|
||||
assert validator.fail_message == "Cannot perform request on a file that does not exist."
|
||||
|
||||
|
||||
def test_file_not_deleted_request_validator(populated_file_system):
|
||||
"""Tests that the _FolderExistsValidator works as intended."""
|
||||
fs, folder, file = populated_file_system
|
||||
validator = Folder._FileNotDeletedValidator(folder=folder)
|
||||
|
||||
assert validator(request=["test_file.txt"], context={}) # test_file.txt is not deleted
|
||||
|
||||
fs.delete_file(folder_name="test_folder", file_name="test_file.txt")
|
||||
|
||||
assert validator(request=["fake_file.txt"], context={}) is False # test_file.txt is deleted
|
||||
|
||||
assert validator.fail_message == "Cannot perform request on a file that is deleted."
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
import pytest
|
||||
|
||||
from primaite.simulator.network.hardware.base import NetworkInterface, Node
|
||||
from primaite.simulator.network.hardware.nodes.host.computer import Computer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def node() -> Node:
|
||||
return Computer(hostname="test", ip_address="192.168.1.2", subnet_mask="255.255.255.0")
|
||||
|
||||
|
||||
def test_nic_enabled_validator(node):
|
||||
"""Test the NetworkInterface enabled validator."""
|
||||
network_interface = node.network_interface[1]
|
||||
validator = NetworkInterface._EnabledValidator(network_interface=network_interface)
|
||||
|
||||
assert validator(request=[], context={}) is False # not enabled
|
||||
|
||||
network_interface.enabled = True
|
||||
|
||||
assert validator(request=[], context={}) # enabled
|
||||
|
||||
|
||||
def test_nic_disabled_validator(node):
|
||||
"""Test the NetworkInterface enabled validator."""
|
||||
network_interface = node.network_interface[1]
|
||||
validator = NetworkInterface._DisabledValidator(network_interface=network_interface)
|
||||
|
||||
assert validator(request=[], context={}) # not enabled
|
||||
|
||||
network_interface.enabled = True
|
||||
|
||||
assert validator(request=[], context={}) is False # enabled
|
||||
@@ -155,3 +155,39 @@ def test_reset_node(node):
|
||||
assert node.operating_state == NodeOperatingState.BOOTING
|
||||
|
||||
assert node.operating_state == NodeOperatingState.ON
|
||||
|
||||
|
||||
def test_node_is_on_validator(node):
|
||||
"""Test that the node is on validator."""
|
||||
node.power_on()
|
||||
|
||||
for i in range(node.start_up_duration + 1):
|
||||
node.apply_timestep(i)
|
||||
|
||||
validator = Node._NodeIsOnValidator(node=node)
|
||||
|
||||
assert validator(request=[], context={})
|
||||
|
||||
node.power_off()
|
||||
for i in range(node.shut_down_duration + 1):
|
||||
node.apply_timestep(i)
|
||||
|
||||
assert validator(request=[], context={}) is False
|
||||
|
||||
|
||||
def test_node_is_off_validator(node):
|
||||
"""Test that the node is on validator."""
|
||||
node.power_on()
|
||||
|
||||
for i in range(node.start_up_duration + 1):
|
||||
node.apply_timestep(i)
|
||||
|
||||
validator = Node._NodeIsOffValidator(node=node)
|
||||
|
||||
assert validator(request=[], context={}) is False
|
||||
|
||||
node.power_off()
|
||||
for i in range(node.shut_down_duration + 1):
|
||||
node.apply_timestep(i)
|
||||
|
||||
assert validator(request=[], context={})
|
||||
|
||||
@@ -1 +1,15 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
from primaite.simulator.system.applications.application import Application, ApplicationOperatingState
|
||||
|
||||
|
||||
def test_application_state_validator(application):
|
||||
"""Test the application state validator."""
|
||||
validator = Application._StateValidator(application=application, state=ApplicationOperatingState.CLOSED)
|
||||
assert validator(request=[], context={}) # application is closed
|
||||
application.run()
|
||||
assert validator(request=[], context={}) is False # application is running - expecting closed
|
||||
|
||||
validator = Application._StateValidator(application=application, state=ApplicationOperatingState.RUNNING)
|
||||
assert validator(request=[], context={}) # application is running
|
||||
application.close()
|
||||
assert validator(request=[], context={}) is False # application is closed - expecting running
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK
|
||||
from primaite.simulator.system.services.service import ServiceOperatingState
|
||||
from primaite.simulator.system.services.service import Service, ServiceOperatingState
|
||||
from primaite.simulator.system.software import SoftwareHealthState
|
||||
|
||||
|
||||
@@ -92,3 +92,21 @@ def test_service_fix(service):
|
||||
assert service.health_state_actual == SoftwareHealthState.FIXING
|
||||
service.apply_timestep(2)
|
||||
assert service.health_state_actual == SoftwareHealthState.GOOD
|
||||
|
||||
|
||||
def test_service_state_validator(service):
|
||||
"""Test the service state validator."""
|
||||
validator = Service._StateValidator(service=service, state=ServiceOperatingState.STOPPED)
|
||||
assert validator(request=[], context={}) # service is stopped
|
||||
service.start()
|
||||
assert validator(request=[], context={}) is False # service is running - expecting stopped
|
||||
|
||||
validator = Service._StateValidator(service=service, state=ServiceOperatingState.RUNNING)
|
||||
assert validator(request=[], context={}) # service is running
|
||||
service.pause()
|
||||
assert validator(request=[], context={}) is False # service is paused - expecting running
|
||||
|
||||
validator = Service._StateValidator(service=service, state=ServiceOperatingState.PAUSED)
|
||||
assert validator(request=[], context={}) # service is paused
|
||||
service.resume()
|
||||
assert validator(request=[], context={}) is False # service is running - expecting paused
|
||||
|
||||
Reference in New Issue
Block a user