diff --git a/gvm/protocols/gmp/_gmpnext.py b/gvm/protocols/gmp/_gmpnext.py
index 63b27c74..7584d896 100644
--- a/gvm/protocols/gmp/_gmpnext.py
+++ b/gvm/protocols/gmp/_gmpnext.py
@@ -8,7 +8,7 @@
from .._protocol import T
from ._gmp227 import GMPv227
-from .requests.next import AgentInstallers
+from .requests.next import AgentGroups, AgentInstallers, Agents
class GMPNext(GMPv227[T]):
@@ -78,3 +78,199 @@ def get_agent_installer_file(self, agent_installer_id: EntityID) -> T:
return self._send_request_and_transform_response(
AgentInstallers.get_agent_installer_file(agent_installer_id)
)
+
+ def get_agents(
+ self,
+ *,
+ filter_string: Optional[str] = None,
+ filter_id: Optional[EntityID] = None,
+ details: Optional[bool] = None,
+ ) -> T:
+ """Request a list of agents.
+
+ Args:
+ filter_string: Filter term to use for the query.
+ filter_id: UUID of an existing filter to use for the query.
+ details: Whether to include detailed agent info.
+ """
+ return self._send_request_and_transform_response(
+ Agents.get_agents(
+ filter_string=filter_string,
+ filter_id=filter_id,
+ details=details,
+ )
+ )
+
+ def modify_agents(
+ self,
+ agent_ids: list[EntityID],
+ *,
+ authorized: Optional[bool] = None,
+ min_interval: Optional[int] = None,
+ heartbeat_interval: Optional[int] = None,
+ schedule: Optional[str] = None,
+ comment: Optional[str] = None,
+ ) -> T:
+ """Modify multiple agents
+
+ Args:
+ agent_ids: List of agent UUIDs to modify
+ authorized: Whether the agent is authorized
+ min_interval: Minimum scan interval
+ heartbeat_interval: Interval for sending heartbeats
+ schedule: Cron-style schedule for agent
+ comment: Comment for the agents
+ """
+ return self._send_request_and_transform_response(
+ Agents.modify_agents(
+ agent_ids=agent_ids,
+ authorized=authorized,
+ min_interval=min_interval,
+ heartbeat_interval=heartbeat_interval,
+ schedule=schedule,
+ comment=comment,
+ )
+ )
+
+ def delete_agents(self, agent_ids: list[EntityID]) -> T:
+ """Delete multiple agents
+
+ Args:
+ agent_ids: List of agent UUIDs to delete
+ """
+ return self._send_request_and_transform_response(
+ Agents.delete_agents(agent_ids=agent_ids)
+ )
+
+ def get_agent_groups(
+ self,
+ *,
+ filter_string: Optional[str] = None,
+ filter_id: Optional[EntityID] = None,
+ trash: Optional[bool] = None,
+ ) -> T:
+ """Request a list of agent groups.
+
+ Args:
+ filter_string: Filter expression to use.
+ filter_id: UUID of a predefined filter.
+ trash: If True, return trashed agent groups.
+
+ Returns:
+ Request object to fetch agent groups.
+ """
+ return self._send_request_and_transform_response(
+ AgentGroups.get_agent_groups(
+ filter_string=filter_string,
+ filter_id=filter_id,
+ trash=trash,
+ )
+ )
+
+ def get_agent_group(self, agent_group_id: EntityID) -> T:
+ """Request a single agent group by ID.
+
+ Args:
+ agent_group_id: UUID of the agent group.
+
+ Raises:
+ RequiredArgument: If agent_group_id is not provided.
+
+ Returns:
+ Request object to fetch the specific agent group.
+ """
+ return self._send_request_and_transform_response(
+ AgentGroups.get_agent_group(
+ agent_group_id=agent_group_id,
+ )
+ )
+
+ def create_agent_group(
+ self,
+ name: str,
+ agent_ids: list[str],
+ *,
+ comment: Optional[str] = None,
+ ) -> T:
+ """Create a new agent group.
+
+ Args:
+ name: Name of the new agent group.
+ agent_ids: List of agent UUIDs to include in the group (required).
+ comment: Optional comment for the group.
+
+ Raises:
+ RequiredArgument: If name or agent_ids is not provided.
+ """
+ return self._send_request_and_transform_response(
+ AgentGroups.create_agent_group(
+ name=name,
+ comment=comment,
+ agent_ids=agent_ids,
+ )
+ )
+
+ def modify_agent_group(
+ self,
+ agent_group_id: EntityID,
+ *,
+ name: Optional[str] = None,
+ comment: Optional[str] = None,
+ agent_ids: Optional[list[str]] = None,
+ ) -> T:
+ """Modify an existing agent group.
+
+ Args:
+ agent_group_id: UUID of the group to modify.
+ name: Optional new name for the group.
+ comment: Optional comment for the group.
+ agent_ids: Optional list of agent UUIDs to set for the group.
+
+ Raises:
+ RequiredArgument: If agent_group_id is not provided.
+ """
+ return self._send_request_and_transform_response(
+ AgentGroups.modify_agent_group(
+ agent_group_id=agent_group_id,
+ name=name,
+ comment=comment,
+ agent_ids=agent_ids,
+ )
+ )
+
+ def delete_agent_group(
+ self,
+ agent_group_id: EntityID,
+ ultimate: bool = False,
+ ) -> T:
+ """Delete an existing agent group.
+
+ Args:
+ agent_group_id: UUID of the group to delete.
+ ultimate: Whether to permanently delete or move to trashcan.
+
+ Raises:
+ RequiredArgument: If agent_group_id is not provided.
+ """
+ return self._send_request_and_transform_response(
+ AgentGroups.delete_agent_group(
+ agent_group_id=agent_group_id,
+ ultimate=ultimate,
+ )
+ )
+
+ def clone_agent_group(
+ self,
+ agent_group_id: EntityID,
+ ) -> T:
+ """Clone an existing agent group
+
+ Args:
+ agent_group_id: UUID of an existing agent group to clone from
+
+ Returns:
+ Request: GMP command to create a new agent group based on a copy
+ """
+ return self._send_request_and_transform_response(
+ AgentGroups.clone_agent_group(agent_group_id)
+ )
diff --git a/gvm/protocols/gmp/requests/next/__init__.py b/gvm/protocols/gmp/requests/next/__init__.py
index e60c1a17..b42ae874 100644
--- a/gvm/protocols/gmp/requests/next/__init__.py
+++ b/gvm/protocols/gmp/requests/next/__init__.py
@@ -2,7 +2,9 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
+from gvm.protocols.gmp.requests.next._agent_groups import AgentGroups
from gvm.protocols.gmp.requests.next._agent_installers import AgentInstallers
+from gvm.protocols.gmp.requests.next._agents import Agents
from .._entity_id import EntityID
from .._version import Version
@@ -77,7 +79,9 @@
)
__all__ = (
+ "AgentGroups",
"AgentInstallers",
+ "Agents",
"Aggregates",
"AggregateStatistic",
"Alerts",
diff --git a/gvm/protocols/gmp/requests/next/_agent_groups.py b/gvm/protocols/gmp/requests/next/_agent_groups.py
new file mode 100644
index 00000000..0753ef23
--- /dev/null
+++ b/gvm/protocols/gmp/requests/next/_agent_groups.py
@@ -0,0 +1,193 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from typing import Optional
+
+from gvm.errors import RequiredArgument
+from gvm.protocols.core import Request
+from gvm.protocols.gmp.requests._entity_id import EntityID
+from gvm.utils import to_bool
+from gvm.xml import XmlCommand
+
+
+class AgentGroups:
+ @classmethod
+ def create_agent_group(
+ cls,
+ name: str,
+ agent_ids: list[str],
+ *,
+ comment: Optional[str] = None,
+ ) -> Request:
+ """Create a new agent group.
+
+ Args:
+ name: Name of the new agent group.
+ agent_ids: List of agent UUIDs to include in the group (required).
+ comment: Optional comment for the group.
+
+ Raises:
+ RequiredArgument: If name or agent_ids is not provided.
+ """
+ if not name:
+ raise RequiredArgument(
+ function=cls.create_agent_group.__name__, argument="name"
+ )
+
+ if not agent_ids:
+ raise RequiredArgument(
+ function=cls.create_agent_group.__name__, argument="agent_ids"
+ )
+
+ cmd = XmlCommand("create_agent_group")
+ cmd.add_element("name", name)
+
+ if comment:
+ cmd.add_element("comment", comment)
+
+ agents_element = cmd.add_element("agents")
+ for agent_id in agent_ids:
+ agents_element.add_element("agent", attrs={"id": agent_id})
+
+ return cmd
+
+ @classmethod
+ def clone_agent_group(cls, agent_group_id: EntityID) -> Request:
+ """Clone an existing agent group
+
+ Args:
+ agent_group_id: UUID of an existing agent group to clone from
+
+ Returns:
+ Request: GMP command to create a new agent group based on a copy
+ """
+ if not agent_group_id:
+ raise RequiredArgument(
+ function=cls.clone_agent_group.__name__,
+ argument="agent_group_id",
+ )
+
+ cmd = XmlCommand("create_agent_group")
+ cmd.add_element("copy", str(agent_group_id))
+
+ return cmd
+
+ @classmethod
+ def modify_agent_group(
+ cls,
+ agent_group_id: EntityID,
+ *,
+ name: Optional[str] = None,
+ comment: Optional[str] = None,
+ agent_ids: Optional[list[str]] = None,
+ ) -> Request:
+ """Modify an existing agent group.
+
+ Args:
+ agent_group_id: UUID of the group to modify.
+ name: Optional new name for the group.
+ comment: Optional comment for the group.
+ agent_ids: Optional list of agent UUIDs to set for the group.
+
+ Raises:
+ RequiredArgument: If agent_group_id is not provided.
+ """
+ if not agent_group_id:
+ raise RequiredArgument(
+ function=cls.modify_agent_group.__name__,
+ argument="agent_group_id",
+ )
+
+ cmd = XmlCommand("modify_agent_group")
+ cmd.set_attribute("agent_group_id", str(agent_group_id))
+
+ if name:
+ cmd.add_element("name", name)
+
+ if comment:
+ cmd.add_element("comment", comment)
+
+ if agent_ids:
+ agents_element = cmd.add_element("agents")
+ for agent_id in agent_ids:
+ agents_element.add_element("agent", attrs={"id": agent_id})
+
+ return cmd
+
+ @classmethod
+ def delete_agent_group(
+ cls,
+ agent_group_id: EntityID,
+ *,
+ ultimate: Optional[bool] = False,
+ ) -> Request:
+ """Delete an existing agent group.
+
+ Args:
+ agent_group_id: UUID of the group to delete.
+ ultimate: Whether to permanently delete or move to trashcan.
+
+ Raises:
+ RequiredArgument: If agent_group_id is not provided.
+ """
+ if not agent_group_id:
+ raise RequiredArgument(
+ function=cls.delete_agent_group.__name__,
+ argument="agent_group_id",
+ )
+
+ cmd = XmlCommand("delete_agent_group")
+ cmd.set_attribute("agent_group_id", str(agent_group_id))
+ cmd.set_attribute("ultimate", to_bool(ultimate))
+
+ return cmd
+
+ @classmethod
+ def get_agent_groups(
+ cls,
+ *,
+ filter_string: Optional[str] = None,
+ filter_id: Optional[EntityID] = None,
+ trash: Optional[bool] = None,
+ ) -> Request:
+ """Request a list of agent groups.
+
+ Args:
+ filter_string: Filter expression to use.
+ filter_id: UUID of a predefined filter.
+ trash: If True, return trashed agent groups.
+
+ Returns:
+ Request object to fetch agent groups.
+ """
+ cmd = XmlCommand("get_agent_groups")
+ cmd.add_filter(filter_string, filter_id)
+
+ if trash is not None:
+ cmd.set_attribute("trash", to_bool(trash))
+
+ return cmd
+
+ @classmethod
+ def get_agent_group(cls, agent_group_id: EntityID) -> Request:
+ """Request a single agent group by ID.
+
+ Args:
+ agent_group_id: UUID of the agent group.
+
+ Raises:
+ RequiredArgument: If agent_group_id is not provided.
+
+ Returns:
+ Request object to fetch the specific agent group.
+ """
+ if not agent_group_id:
+ raise RequiredArgument(
+ function=cls.get_agent_group.__name__, argument="agent_group_id"
+ )
+
+ cmd = XmlCommand("get_agent_groups")
+ cmd.set_attribute("agent_group_id", str(agent_group_id))
+
+ return cmd
diff --git a/gvm/protocols/gmp/requests/next/_agents.py b/gvm/protocols/gmp/requests/next/_agents.py
new file mode 100644
index 00000000..47b7db96
--- /dev/null
+++ b/gvm/protocols/gmp/requests/next/_agents.py
@@ -0,0 +1,101 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from typing import Optional
+
+from gvm.errors import RequiredArgument
+from gvm.protocols.core import Request
+from gvm.protocols.gmp.requests._entity_id import EntityID
+from gvm.utils import to_bool
+from gvm.xml import XmlCommand
+
+
+class Agents:
+ @classmethod
+ def get_agents(
+ cls,
+ *,
+ filter_string: Optional[str] = None,
+ filter_id: Optional[EntityID] = None,
+ details: Optional[bool] = None,
+ ) -> Request:
+ """Request a list of agents.
+
+ Args:
+ filter_string: Filter term to use for the query.
+ filter_id: UUID of an existing filter to use for the query.
+ details: Whether to include detailed agent info.
+ """
+ cmd = XmlCommand("get_agents")
+ cmd.add_filter(filter_string, filter_id)
+
+ if details is not None:
+ cmd.set_attribute("details", to_bool(details))
+
+ return cmd
+
+ @classmethod
+ def modify_agents(
+ cls,
+ agent_ids: list[EntityID],
+ *,
+ authorized: Optional[bool] = None,
+ min_interval: Optional[int] = None,
+ heartbeat_interval: Optional[int] = None,
+ schedule: Optional[str] = None,
+ comment: Optional[str] = None,
+ ) -> Request:
+ """Modify multiple agents
+
+ Args:
+ agent_ids: List of agent UUIDs to modify
+ authorized: Whether the agent is authorized
+ min_interval: Minimum scan interval
+ heartbeat_interval: Interval for sending heartbeats
+ schedule: Cron-style schedule for agent
+ comment: Comment for the agents
+ """
+ if not agent_ids:
+ raise RequiredArgument(
+ function=cls.modify_agents.__name__, argument="agent_ids"
+ )
+
+ cmd = XmlCommand("modify_agents")
+ xml_agents = cmd.add_element("agents")
+
+ for agent_id in agent_ids:
+ xml_agents.add_element("agent", attrs={"id": agent_id})
+
+ if authorized is not None:
+ cmd.add_element("authorized", to_bool(authorized))
+ if min_interval is not None:
+ cmd.add_element("min_interval", str(min_interval))
+ if heartbeat_interval is not None:
+ cmd.add_element("heartbeat_interval", str(heartbeat_interval))
+ if schedule:
+ cmd.add_element("schedule", schedule)
+ if comment:
+ cmd.add_element("comment", comment)
+
+ return cmd
+
+ @classmethod
+ def delete_agents(cls, agent_ids: list[EntityID]) -> Request:
+ """Delete multiple agents
+
+ Args:
+ agent_ids: List of agent UUIDs to delete
+ """
+ if not agent_ids:
+ raise RequiredArgument(
+ function=cls.delete_agents.__name__, argument="agent_ids"
+ )
+
+ cmd = XmlCommand("delete_agents")
+ xml_agents = cmd.add_element("agents")
+
+ for agent_id in agent_ids:
+ xml_agents.add_element("agent", attrs={"id": agent_id})
+
+ return cmd
diff --git a/tests/protocols/gmpnext/entities/agent_groups/__init__.py b/tests/protocols/gmpnext/entities/agent_groups/__init__.py
new file mode 100644
index 00000000..7cb84d4c
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agent_groups/__init__.py
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/tests/protocols/gmpnext/entities/agent_groups/test_clone_agent_group.py b/tests/protocols/gmpnext/entities/agent_groups/test_clone_agent_group.py
new file mode 100644
index 00000000..21af153f
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agent_groups/test_clone_agent_group.py
@@ -0,0 +1,18 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from gvm.errors import RequiredArgument
+
+
+class GmpCloneAgentGroupTestMixin:
+ def test_clone_agent_group(self):
+ self.gmp.clone_agent_group(agent_group_id="group-1")
+
+ self.connection.send.has_been_called_with(
+ b"group-1"
+ )
+
+ def test_clone_agent_group_without_id(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.clone_agent_group(agent_group_id=None)
diff --git a/tests/protocols/gmpnext/entities/agent_groups/test_create_agent_group.py b/tests/protocols/gmpnext/entities/agent_groups/test_create_agent_group.py
new file mode 100644
index 00000000..a6b9d1f5
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agent_groups/test_create_agent_group.py
@@ -0,0 +1,40 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from gvm.errors import RequiredArgument
+
+
+class GmpCreateAgentGroupTestMixin:
+ def test_create_agent_group(self):
+ self.gmp.create_agent_group(
+ name="ExampleGroup",
+ agent_ids=["agent-1", "agent-2"],
+ comment="Sample comment",
+ )
+
+ self.connection.send.has_been_called_with(
+ b""
+ b"ExampleGroup"
+ b"Sample comment"
+ b''
+ b""
+ )
+
+ def test_create_agent_group_without_name(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.create_agent_group(name="", agent_ids=["agent-1"])
+
+ def test_create_agent_group_without_agents(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.create_agent_group(name="Group", agent_ids=[])
+
+ def test_create_agent_group_without_comment(self):
+ self.gmp.create_agent_group(name="GroupX", agent_ids=["a1", "a2"])
+
+ self.connection.send.has_been_called_with(
+ b""
+ b"GroupX"
+ b''
+ b""
+ )
diff --git a/tests/protocols/gmpnext/entities/agent_groups/test_delete_agent_group.py b/tests/protocols/gmpnext/entities/agent_groups/test_delete_agent_group.py
new file mode 100644
index 00000000..70d9322e
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agent_groups/test_delete_agent_group.py
@@ -0,0 +1,25 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from gvm.errors import RequiredArgument
+
+
+class GmpDeleteAgentGroupTestMixin:
+ def test_delete_agent_group_soft(self):
+ self.gmp.delete_agent_group(agent_group_id="group-1", ultimate=False)
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_delete_agent_group_hard(self):
+ self.gmp.delete_agent_group(agent_group_id="group-1", ultimate=True)
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_delete_agent_group_without_id(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.delete_agent_group(agent_group_id=None)
diff --git a/tests/protocols/gmpnext/entities/agent_groups/test_get_agent_group.py b/tests/protocols/gmpnext/entities/agent_groups/test_get_agent_group.py
new file mode 100644
index 00000000..a066dc1f
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agent_groups/test_get_agent_group.py
@@ -0,0 +1,18 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from gvm.errors import RequiredArgument
+
+
+class GmpGetAgentGroupTestMixin:
+ def test_get_agent_group(self):
+ self.gmp.get_agent_group(agent_group_id="group-1")
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_get_agent_group_without_id(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.get_agent_group(agent_group_id=None)
diff --git a/tests/protocols/gmpnext/entities/agent_groups/test_get_agent_groups.py b/tests/protocols/gmpnext/entities/agent_groups/test_get_agent_groups.py
new file mode 100644
index 00000000..203a3417
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agent_groups/test_get_agent_groups.py
@@ -0,0 +1,43 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+
+class GmpGetAgentGroupsTestMixin:
+ def test_get_agent_groups(self):
+ self.gmp.get_agent_groups()
+
+ self.connection.send.has_been_called_with(b"")
+
+ def test_get_agent_groups_with_filter_string(self):
+ self.gmp.get_agent_groups(filter_string="name=group")
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_get_agent_groups_with_filter_id(self):
+ self.gmp.get_agent_groups(filter_id="filter-1")
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_get_agent_groups_with_trash(self):
+ self.gmp.get_agent_groups(trash=True)
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_get_agent_groups_with_trash_false(self):
+ self.gmp.get_agent_groups(trash=False)
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_get_agent_groups_without_trash(self):
+ self.gmp.get_agent_groups()
+
+ self.connection.send.has_been_called_with(b"")
diff --git a/tests/protocols/gmpnext/entities/agent_groups/test_modify_agent_group.py b/tests/protocols/gmpnext/entities/agent_groups/test_modify_agent_group.py
new file mode 100644
index 00000000..83aa3dbd
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agent_groups/test_modify_agent_group.py
@@ -0,0 +1,69 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from gvm.errors import RequiredArgument
+
+
+class GmpModifyAgentGroupTestMixin:
+ def test_modify_agent_group(self):
+ self.gmp.modify_agent_group(
+ agent_group_id="group-1",
+ name="NewName",
+ comment="Updated comment",
+ agent_ids=["agent-1", "agent-2"],
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"NewName"
+ b"Updated comment"
+ b''
+ b""
+ )
+
+ def test_modify_agent_group_without_id(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.modify_agent_group(agent_group_id=None)
+
+ def test_modify_agent_group_without_name(self):
+ self.gmp.modify_agent_group(
+ agent_group_id="group-1",
+ comment="Updated comment",
+ agent_ids=["agent-1", "agent-2"],
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"Updated comment"
+ b''
+ b""
+ )
+
+ def test_modify_agent_group_without_comment(self):
+ self.gmp.modify_agent_group(
+ agent_group_id="group-1",
+ name="NewName",
+ agent_ids=["agent-1", "agent-2"],
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"NewName"
+ b''
+ b""
+ )
+
+ def test_modify_agent_group_without_agent_ids(self):
+ self.gmp.modify_agent_group(
+ agent_group_id="group-1",
+ name="NewName",
+ comment="Updated comment",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"NewName"
+ b"Updated comment"
+ b""
+ )
diff --git a/tests/protocols/gmpnext/entities/agents/__init__.py b/tests/protocols/gmpnext/entities/agents/__init__.py
new file mode 100644
index 00000000..7cb84d4c
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agents/__init__.py
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/tests/protocols/gmpnext/entities/agents/test_delete_agents.py b/tests/protocols/gmpnext/entities/agents/test_delete_agents.py
new file mode 100644
index 00000000..c829de88
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agents/test_delete_agents.py
@@ -0,0 +1,20 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from gvm.errors import RequiredArgument
+
+
+class GmpDeleteAgentsTestMixin:
+ def test_delete_agents(self):
+ self.gmp.delete_agents(agent_ids=["agent-123", "agent-456"])
+
+ self.connection.send.has_been_called_with(
+ b""
+ b''
+ b""
+ )
+
+ def test_delete_agents_without_ids(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.delete_agents(agent_ids=[])
diff --git a/tests/protocols/gmpnext/entities/agents/test_get_agents.py b/tests/protocols/gmpnext/entities/agents/test_get_agents.py
new file mode 100644
index 00000000..e9a6866c
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agents/test_get_agents.py
@@ -0,0 +1,32 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+
+class GmpGetAgentsTestMixin:
+ def test_get_agents(self):
+ self.gmp.get_agents()
+
+ self.connection.send.has_been_called_with(b"")
+
+ def test_get_agents_with_filter_string(self):
+ self.gmp.get_agents(filter_string="name=agent")
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_get_agents_with_filter_id(self):
+ self.gmp.get_agents(filter_id="f1")
+
+ self.connection.send.has_been_called_with(b'')
+
+ def test_get_agents_with_details_true(self):
+ self.gmp.get_agents(details=True)
+
+ self.connection.send.has_been_called_with(b'')
+
+ def test_get_agents_with_details_false(self):
+ self.gmp.get_agents(details=False)
+
+ self.connection.send.has_been_called_with(b'')
diff --git a/tests/protocols/gmpnext/entities/agents/test_modify_agents.py b/tests/protocols/gmpnext/entities/agents/test_modify_agents.py
new file mode 100644
index 00000000..50777d93
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/agents/test_modify_agents.py
@@ -0,0 +1,41 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from gvm.errors import RequiredArgument
+
+
+class GmpModifyAgentsTestMixin:
+ def test_modify_agents_basic(self):
+ self.gmp.modify_agents(agent_ids=["agent-123"])
+
+ self.connection.send.has_been_called_with(
+ b""
+ b''
+ b""
+ )
+
+ def test_modify_agents_with_all_fields(self):
+ self.gmp.modify_agents(
+ agent_ids=["agent-123", "agent-456"],
+ authorized=True,
+ min_interval=300,
+ heartbeat_interval=600,
+ schedule="@every 6h",
+ comment="Updated agents",
+ )
+
+ self.connection.send.has_been_called_with(
+ b""
+ b''
+ b"1"
+ b"300"
+ b"600"
+ b"@every 6h"
+ b"Updated agents"
+ b""
+ )
+
+ def test_modify_agents_without_ids(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.modify_agents(agent_ids=[])
diff --git a/tests/protocols/gmpnext/entities/test_agent_group.py b/tests/protocols/gmpnext/entities/test_agent_group.py
new file mode 100644
index 00000000..77f4b9b6
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/test_agent_group.py
@@ -0,0 +1,48 @@
+# SPDX-FileCopyrightText: 2023-2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+from ...gmpnext import GMPTestCase
+from .agent_groups.test_clone_agent_group import (
+ GmpCloneAgentGroupTestMixin,
+)
+from .agent_groups.test_create_agent_group import (
+ GmpCreateAgentGroupTestMixin,
+)
+from .agent_groups.test_delete_agent_group import (
+ GmpDeleteAgentGroupTestMixin,
+)
+from .agent_groups.test_get_agent_group import (
+ GmpGetAgentGroupTestMixin,
+)
+from .agent_groups.test_get_agent_groups import (
+ GmpGetAgentGroupsTestMixin,
+)
+from .agent_groups.test_modify_agent_group import (
+ GmpModifyAgentGroupTestMixin,
+)
+
+
+class GMPGetAgentGroupsTestCase(GmpGetAgentGroupsTestMixin, GMPTestCase):
+ pass
+
+
+class GMPGetAgentGroupTestCase(GmpGetAgentGroupTestMixin, GMPTestCase):
+ pass
+
+
+class GMPCreateAgentGroupTestCase(GmpCreateAgentGroupTestMixin, GMPTestCase):
+ pass
+
+
+class GMPCloneAgentGroupTestCase(GmpCloneAgentGroupTestMixin, GMPTestCase):
+ pass
+
+
+class GMPModifyAgentGroupTestCase(GmpModifyAgentGroupTestMixin, GMPTestCase):
+ pass
+
+
+class GMPDeleteAgentGroupTestCase(GmpDeleteAgentGroupTestMixin, GMPTestCase):
+ pass
diff --git a/tests/protocols/gmpnext/entities/test_agents.py b/tests/protocols/gmpnext/entities/test_agents.py
new file mode 100644
index 00000000..accf9e5f
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/test_agents.py
@@ -0,0 +1,27 @@
+# SPDX-FileCopyrightText: 2023-2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+from ...gmpnext import GMPTestCase
+from .agents.test_delete_agents import (
+ GmpDeleteAgentsTestMixin,
+)
+from .agents.test_get_agents import (
+ GmpGetAgentsTestMixin,
+)
+from .agents.test_modify_agents import (
+ GmpModifyAgentsTestMixin,
+)
+
+
+class GMPGetAgentsTestCase(GmpGetAgentsTestMixin, GMPTestCase):
+ pass
+
+
+class GMPModifyAgentsTestCase(GmpModifyAgentsTestMixin, GMPTestCase):
+ pass
+
+
+class GMPDeleteAgentsTestCase(GmpDeleteAgentsTestMixin, GMPTestCase):
+ pass