From d534fc0c15aad6bc89c756d4c3207ae01ee8ee7f Mon Sep 17 00:00:00 2001 From: Alex Petenchea Date: Tue, 12 Aug 2025 06:17:28 +0000 Subject: [PATCH 1/4] Getting started on administration API --- arangoasync/database.py | 23 +++++++++++++++++++++++ arangoasync/exceptions.py | 8 ++++++-- docs/admin.rst | 23 +++++++++++++++++++++++ tests/test_database.py | 4 ++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 docs/admin.rst diff --git a/arangoasync/database.py b/arangoasync/database.py index be057c4..ebf21c5 100644 --- a/arangoasync/database.py +++ b/arangoasync/database.py @@ -40,6 +40,7 @@ PermissionResetError, PermissionUpdateError, ServerEncryptionError, + ServerEngineError, ServerStatusError, ServerTLSError, ServerTLSReloadError, @@ -2437,6 +2438,28 @@ def response_handler(resp: Response) -> bool: return await self._executor.execute(request, response_handler) + async def engine(self) -> Result[Json]: + """Returns the storage engine the server is configured to use. + + Returns: + dict: Database engine details. + + Raises: + ServerEngineError: If the operation fails. + + References: + - `get-the-storage-engine-type `__ + """ # noqa: E501 + request = Request(method=Method.GET, endpoint="/_api/engine") + + def response_handler(resp: Response) -> Json: + if not resp.is_success: + raise ServerEngineError(resp, request) + result: Json = self.deserializer.loads(resp.raw_body) + return result + + return await self._executor.execute(request, response_handler) + class StandardDatabase(Database): """Standard database API wrapper. diff --git a/arangoasync/exceptions.py b/arangoasync/exceptions.py index 99340dd..f9e68f9 100644 --- a/arangoasync/exceptions.py +++ b/arangoasync/exceptions.py @@ -547,12 +547,16 @@ class SerializationError(ArangoClientError): """Failed to serialize the request.""" +class ServerConnectionError(ArangoServerError): + """Failed to connect to ArangoDB server.""" + + class ServerEncryptionError(ArangoServerError): """Failed to reload user-defined encryption keys.""" -class ServerConnectionError(ArangoServerError): - """Failed to connect to ArangoDB server.""" +class ServerEngineError(ArangoServerError): + """Failed to retrieve database engine.""" class ServerStatusError(ArangoServerError): diff --git a/docs/admin.rst b/docs/admin.rst new file mode 100644 index 0000000..28fe7d6 --- /dev/null +++ b/docs/admin.rst @@ -0,0 +1,23 @@ +Server Administration +--------------------- + +ArangoDB provides operations for server administration and monitoring. +Most of these operations can only be performed by admin users via the +``_system`` database. + +**Example:** + +.. code-block:: python + + from arangoasync import ArangoClient + from arangoasync.auth import Auth + + # Initialize the client for ArangoDB. + async with ArangoClient(hosts="http://localhost:8529") as client: + auth = Auth(username="root", password="passwd") + + # Connect to "_system" database as root user. + sys_db = await client.db("_system", auth=auth) + + # Retrieve the database engine. + await sys_db.engine() diff --git a/tests/test_database.py b/tests/test_database.py index 7058ac1..4d0675f 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -64,6 +64,10 @@ async def test_database_misc_methods(sys_db, db, bad_db, cluster, db_version): with pytest.raises(CollectionKeyGeneratorsError): await bad_db.key_generators() + # Administration + result = await db.engine() + assert isinstance(result, dict) + @pytest.mark.asyncio async def test_create_drop_database( From e7fa7a9a5056cb9e01a0f21df534dfff6baf7eca Mon Sep 17 00:00:00 2001 From: Alex Petenchea Date: Fri, 15 Aug 2025 13:55:57 +0000 Subject: [PATCH 2/4] Adding more Administration methods --- arangoasync/database.py | 231 ++++++++++++++++++++++++++++++++++++++ arangoasync/exceptions.py | 36 ++++++ docs/admin.rst | 18 +++ tests/test_database.py | 52 +++++++++ 4 files changed, 337 insertions(+) diff --git a/arangoasync/database.py b/arangoasync/database.py index ebf21c5..4b747f1 100644 --- a/arangoasync/database.py +++ b/arangoasync/database.py @@ -6,6 +6,7 @@ ] +from datetime import datetime from typing import Any, List, Optional, Sequence, TypeVar, cast from warnings import warn @@ -30,6 +31,7 @@ DatabaseDeleteError, DatabaseListError, DatabasePropertiesError, + DatabaseSupportInfoError, GraphCreateError, GraphDeleteError, GraphListError, @@ -39,9 +41,17 @@ PermissionListError, PermissionResetError, PermissionUpdateError, + ServerAvailableOptionsGetError, + ServerCheckAvailabilityError, + ServerCurrentOptionsGetError, ServerEncryptionError, ServerEngineError, + ServerLicenseGetError, + ServerLicenseSetError, + ServerModeError, + ServerModeSetError, ServerStatusError, + ServerTimeError, ServerTLSError, ServerTLSReloadError, ServerVersionError, @@ -2460,6 +2470,227 @@ def response_handler(resp: Response) -> Json: return await self._executor.execute(request, response_handler) + async def time(self) -> Result[datetime]: + """Return server system time. + + Returns: + datetime.datetime: Server system time. + + Raises: + ServerTimeError: If the operation fails. + + References: + - `get-the-system-time `__ + """ # noqa: E501 + request = Request(method=Method.GET, endpoint="/_admin/time") + + def response_handler(resp: Response) -> datetime: + if not resp.is_success: + raise ServerTimeError(resp, request) + return datetime.fromtimestamp( + self.deserializer.loads(resp.raw_body)["time"] + ) + + return await self._executor.execute(request, response_handler) + + async def check_availability(self) -> Result[str]: + """Return ArangoDB server availability mode. + + Returns: + str: Server availability mode, either "readonly" or "default". + + Raises: + ServerCheckAvailabilityError: If the operation fails. + + References: + - `check-server-availability `__ + """ # noqa: E501 + request = Request( + method=Method.GET, + endpoint="/_admin/server/availability", + ) + + def response_handler(resp: Response) -> str: + if not resp.is_success: + raise ServerCheckAvailabilityError(resp, request) + result: Json = self.deserializer.loads(resp.raw_body) + return str(result["mode"]) + + return await self._executor.execute(request, response_handler) + + async def support_info(self) -> Result[Json]: + """Retrieves deployment information for support purposes. + + Note: + As this API may reveal sensitive data about the deployment, it can only be accessed from inside the _system database. + + Returns: + dict: Deployment information + + Raises: + DatabaseSupportInfoError: If the operation fails. + + References: + - `get-information-about-the-deployment `__ + """ # noqa: E501 + request = Request(method=Method.GET, endpoint="/_admin/support-info") + + def response_handler(resp: Response) -> Json: + if not resp.is_success: + raise DatabaseSupportInfoError(resp, request) + + result: Json = self.deserializer.loads(resp.raw_body) + return result + + return await self._executor.execute(request, response_handler) + + async def options(self) -> Result[Json]: + """Return the currently-set server options. + + Returns: + dict: Server options. + + Raises: + ServerCurrentOptionsGetError: If the operation fails. + + References: + - `get-the-startup-option-configuration `__ + """ # noqa: E501 + request = Request(method=Method.GET, endpoint="/_admin/options") + + def response_handler(resp: Response) -> Json: + if not resp.is_success: + raise ServerCurrentOptionsGetError(resp, request) + result: Json = self.deserializer.loads(resp.raw_body) + return result + + return await self._executor.execute(request, response_handler) + + async def options_available(self) -> Result[Json]: + """Return a description of all available server options. + + Returns: + dict: Server options description. + + Raises: + ServerAvailableOptionsGetError: If the operation fails. + + References: + - `get-the-available-startup-options `__ + """ # noqa: E501 + request = Request(method=Method.GET, endpoint="/_admin/options-description") + + def response_handler(resp: Response) -> Json: + if not resp.is_success: + raise ServerAvailableOptionsGetError(resp, request) + result: Json = self.deserializer.loads(resp.raw_body) + return result + + return await self._executor.execute(request, response_handler) + + async def mode(self) -> Result[str]: + """Return the server mode ("default" or "readonly"). + + Returns: + str: Server mode, either "default" or "readonly". + + Raises: + ServerModeError: If the operation fails. + + References: + - `return-whether-or-not-a-server-is-in-read-only-mode `__ + """ # noqa: E501 + request = Request(method=Method.GET, endpoint="/_admin/server/mode") + + def response_handler(resp: Response) -> str: + if not resp.is_success: + raise ServerModeError(resp, request) + return str(self.deserializer.loads(resp.raw_body)["mode"]) + + return await self._executor.execute(request, response_handler) + + async def set_mode(self, mode: str) -> Result[str]: + """Set the server mode to read-only or default. + + Args: + mode (str): Server mode. Possible values are "default" or "readonly". + + Returns: + str: New server mode. + + Raises: + ServerModeSetError: If the operation fails. + + References: + - `set-the-server-mode-to-read-only-or-default `__ + """ # noqa: E501 + request = Request( + method=Method.PUT, + endpoint="/_admin/server/mode", + data=self.serializer.dumps({"mode": mode}), + ) + + def response_handler(resp: Response) -> str: + if not resp.is_success: + raise ServerModeSetError(resp, request) + result: Json = self.deserializer.loads(resp.raw_body) + return str(result["mode"]) + + return await self._executor.execute(request, response_handler) + + async def license(self) -> Result[Json]: + """View the license information and status of an Enterprise Edition instance. + + Returns: + dict: Server license information. + + Raises: + ServerLicenseGetError: If the operation fails. + + References: + - `get-information-about-the-current-license `__ + """ # noqa: E501 + request = Request(method=Method.GET, endpoint="/_admin/license") + + def response_handler(resp: Response) -> Json: + if not resp.is_success: + raise ServerLicenseGetError(resp, request) + result: Json = self.deserializer.loads(resp.raw_body) + return result + + return await self._executor.execute(request, response_handler) + + async def set_license(self, license: str, force: Optional[bool] = False) -> None: + """Set a new license for an Enterprise Edition instance. + + Args: + license (str) -> Base64-encoded license string, wrapped in double-quotes. + force (bool | None) -> Set to `True` to change the license even if it + expires sooner than the current one. + + Raises: + ServerLicenseSetError: If the operation fails. + + References: + - `set-a-new-license `__ + """ # noqa: E501 + params: Params = {} + if force is not None: + params["force"] = force + + request = Request( + method=Method.PUT, + endpoint="/_admin/license", + params=params, + data=license, + ) + + def response_handler(resp: Response) -> None: + if not resp.is_success: + raise ServerLicenseSetError(resp, request) + + await self._executor.execute(request, response_handler) + class StandardDatabase(Database): """Standard database API wrapper. diff --git a/arangoasync/exceptions.py b/arangoasync/exceptions.py index f9e68f9..7dfa44c 100644 --- a/arangoasync/exceptions.py +++ b/arangoasync/exceptions.py @@ -335,6 +335,10 @@ class DatabasePropertiesError(ArangoServerError): """Failed to retrieve database properties.""" +class DatabaseSupportInfoError(ArangoServerError): + """Failed to retrieve support info for deployment.""" + + class DeserializationError(ArangoClientError): """Failed to deserialize the server response.""" @@ -547,10 +551,22 @@ class SerializationError(ArangoClientError): """Failed to serialize the request.""" +class ServerAvailableOptionsGetError(ArangoServerError): + """Failed to retrieve available server options.""" + + +class ServerCheckAvailabilityError(ArangoServerError): + """Failed to retrieve server availability mode.""" + + class ServerConnectionError(ArangoServerError): """Failed to connect to ArangoDB server.""" +class ServerCurrentOptionsGetError(ArangoServerError): + """Failed to retrieve currently-set server options.""" + + class ServerEncryptionError(ArangoServerError): """Failed to reload user-defined encryption keys.""" @@ -559,6 +575,22 @@ class ServerEngineError(ArangoServerError): """Failed to retrieve database engine.""" +class ServerModeError(ArangoServerError): + """Failed to retrieve server mode.""" + + +class ServerModeSetError(ArangoServerError): + """Failed to set server mode.""" + + +class ServerLicenseGetError(ArangoServerError): + """Failed to retrieve server license.""" + + +class ServerLicenseSetError(ArangoServerError): + """Failed to set server license.""" + + class ServerStatusError(ArangoServerError): """Failed to retrieve server status.""" @@ -571,6 +603,10 @@ class ServerTLSReloadError(ArangoServerError): """Failed to reload TLS.""" +class ServerTimeError(ArangoServerError): + """Failed to retrieve server system time.""" + + class ServerVersionError(ArangoServerError): """Failed to retrieve server version.""" diff --git a/docs/admin.rst b/docs/admin.rst index 28fe7d6..1855059 100644 --- a/docs/admin.rst +++ b/docs/admin.rst @@ -21,3 +21,21 @@ Most of these operations can only be performed by admin users via the # Retrieve the database engine. await sys_db.engine() + + # Retrieve the server time.. + time = await sys_db.time() + + # Check server availability + availability = sys_db.check_availability() + + # Support info + info = sys_db.support_info() + + # Get the startup option configuration + options = await sys_db.options() + + # Get the available startup options + options = await sys_db.options_available() + + # Return whether or not a server is in read-only mode + mode = await sys_db.mode() diff --git a/tests/test_database.py b/tests/test_database.py index 4d0675f..142dadc 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,4 +1,5 @@ import asyncio +import datetime import pytest from packaging import version @@ -13,9 +14,19 @@ DatabaseDeleteError, DatabaseListError, DatabasePropertiesError, + DatabaseSupportInfoError, JWTSecretListError, JWTSecretReloadError, + ServerAvailableOptionsGetError, + ServerCheckAvailabilityError, + ServerCurrentOptionsGetError, + ServerEngineError, + ServerLicenseGetError, + ServerLicenseSetError, + ServerModeError, + ServerModeSetError, ServerStatusError, + ServerTimeError, ServerVersionError, ) from arangoasync.typings import CollectionType, KeyOptions, UserInfo @@ -65,9 +76,50 @@ async def test_database_misc_methods(sys_db, db, bad_db, cluster, db_version): await bad_db.key_generators() # Administration + with pytest.raises(ServerEngineError): + await bad_db.engine() result = await db.engine() assert isinstance(result, dict) + with pytest.raises(ServerTimeError): + await bad_db.time() + time = await db.time() + assert isinstance(time, datetime.datetime) + + with pytest.raises(ServerCheckAvailabilityError): + await bad_db.check_availability() + assert isinstance(await db.check_availability(), str) + + with pytest.raises(DatabaseSupportInfoError): + await bad_db.support_info() + info = await sys_db.support_info() + assert isinstance(info, dict) + + with pytest.raises(ServerCurrentOptionsGetError): + await bad_db.options() + options = await sys_db.options() + assert isinstance(options, dict) + with pytest.raises(ServerAvailableOptionsGetError): + await bad_db.options_available() + options_available = await sys_db.options_available() + assert isinstance(options_available, dict) + + with pytest.raises(ServerModeError): + await bad_db.mode() + mode = await sys_db.mode() + assert isinstance(mode, str) + with pytest.raises(ServerModeSetError): + await bad_db.set_mode("foo") + mode = await sys_db.set_mode("default") + assert isinstance(mode, str) + + with pytest.raises(ServerLicenseGetError): + await bad_db.license() + license = await sys_db.license() + assert isinstance(license, dict) + with pytest.raises(ServerLicenseSetError): + await sys_db.set_license('"abc"') + @pytest.mark.asyncio async def test_create_drop_database( From 3ca5d5d957bd4ad71afb9d3eec06de2e48ee1626 Mon Sep 17 00:00:00 2001 From: Alex Petenchea Date: Sat, 16 Aug 2025 10:47:15 +0000 Subject: [PATCH 3/4] Finishing up Administration API --- arangoasync/database.py | 170 ++++++++++++++++++++++++++++++++++++++ arangoasync/exceptions.py | 24 ++++++ docs/admin.rst | 6 ++ tests/test_database.py | 55 ++++++++++-- 4 files changed, 246 insertions(+), 9 deletions(-) diff --git a/arangoasync/database.py b/arangoasync/database.py index 4b747f1..449b789 100644 --- a/arangoasync/database.py +++ b/arangoasync/database.py @@ -27,6 +27,7 @@ CollectionDeleteError, CollectionKeyGeneratorsError, CollectionListError, + DatabaseCompactError, DatabaseCreateError, DatabaseDeleteError, DatabaseListError, @@ -44,12 +45,17 @@ ServerAvailableOptionsGetError, ServerCheckAvailabilityError, ServerCurrentOptionsGetError, + ServerEchoError, ServerEncryptionError, ServerEngineError, + ServerExecuteError, ServerLicenseGetError, ServerLicenseSetError, ServerModeError, ServerModeSetError, + ServerReloadRoutingError, + ServerShutdownError, + ServerShutdownProgressError, ServerStatusError, ServerTimeError, ServerTLSError, @@ -2691,6 +2697,170 @@ def response_handler(resp: Response) -> None: await self._executor.execute(request, response_handler) + async def shutdown(self, soft: Optional[bool] = None) -> None: + """Initiate server shutdown sequence. + + Args: + soft (bool | None): If set to `True`, this initiates a soft shutdown. + + Raises: + ServerShutdownError: If the operation fails. + + References: + - `start-the-shutdown-sequence `__ + """ # noqa: E501 + params: Params = {} + if soft is not None: + params["soft"] = soft + + request = Request( + method=Method.DELETE, + endpoint="/_admin/shutdown", + params=params, + ) + + def response_handler(resp: Response) -> None: + if not resp.is_success: + raise ServerShutdownError(resp, request) + + await self._executor.execute(request, response_handler) + + async def shutdown_progress(self) -> Result[Json]: + """Query the soft shutdown progress. + + Returns: + dict: Information about the shutdown progress. + + Raises: + ServerShutdownProgressError: If the operation fails. + + References: + - `query-the-soft-shutdown-progress `__ + """ # noqa: E501 + request = Request(method=Method.GET, endpoint="/_admin/shutdown") + + def response_handler(resp: Response) -> Json: + if not resp.is_success: + raise ServerShutdownProgressError(resp, request) + + result: Json = self.deserializer.loads(resp.raw_body) + return result + + return await self._executor.execute(request, response_handler) + + async def compact( + self, + change_level: Optional[bool] = None, + compact_bottom_most_level: Optional[bool] = None, + ) -> None: + """Compact all databases. This method requires superuser access. + + Note: + This command can cause a full rewrite of all data in all databases, + which may take very long for large databases. + + Args: + change_level (bool | None): Whether or not compacted data should be + moved to the minimum possible level. Default value is `False`. + compact_bottom_most_level (bool | None): Whether or not to compact the bottom-most level of data. + Default value is `False`. + + Returns: + dict: Information about the compaction process. + + Raises: + DatabaseCompactError: If the operation fails. + + References: + - `compact-all-databases `__ + """ # noqa: E501 + data = {} + if change_level is not None: + data["changeLevel"] = change_level + if compact_bottom_most_level is not None: + data["compactBottomMostLevel"] = compact_bottom_most_level + + request = Request( + method=Method.PUT, + endpoint="/_admin/compact", + data=self.serializer.dumps(data), + ) + + def response_handler(resp: Response) -> None: + if not resp.is_success: + raise DatabaseCompactError(resp, request) + + await self._executor.execute(request, response_handler) + + async def reload_routing(self) -> None: + """Reload the routing information. + + Raises: + ServerReloadRoutingError: If the operation fails. + + References: + - `reload-the-routing-table `__ + """ # noqa: E501 + request = Request(method=Method.POST, endpoint="/_admin/routing/reload") + + def response_handler(resp: Response) -> None: + if not resp.is_success: + raise ServerReloadRoutingError(resp, request) + + await self._executor.execute(request, response_handler) + + async def echo(self, body: Optional[Json] = None) -> Result[Json]: + """Return an object with the servers request information. + + Args: + body (dict | None): Optional body of the request. + + Returns: + dict: Details of the request. + + Raises: + ServerEchoError: If the operation fails. + + References: + - `echo-a-request `__ + """ # noqa: E501 + data = body if body is not None else {} + request = Request(method=Method.POST, endpoint="/_admin/echo", data=data) + + def response_handler(resp: Response) -> Json: + if not resp.is_success: + raise ServerEchoError(resp, request) + result: Json = self.deserializer.loads(resp.raw_body) + return result + + return await self._executor.execute(request, response_handler) + + async def execute(self, command: str) -> Result[Any]: + """Execute raw Javascript command on the server. + + Args: + command (str): Javascript command to execute. + + Returns: + Return value of **command**, if any. + + Raises: + ServerExecuteError: If the execution fails. + + References: + - `execute-a-script `__ + """ # noqa: E501 + request = Request( + method=Method.POST, endpoint="/_admin/execute", data=command.encode("utf-8") + ) + + def response_handler(resp: Response) -> Any: + if not resp.is_success: + raise ServerExecuteError(resp, request) + return self.deserializer.loads(resp.raw_body) + + return await self._executor.execute(request, response_handler) + class StandardDatabase(Database): """Standard database API wrapper. diff --git a/arangoasync/exceptions.py b/arangoasync/exceptions.py index 7dfa44c..96a432a 100644 --- a/arangoasync/exceptions.py +++ b/arangoasync/exceptions.py @@ -319,6 +319,10 @@ class CursorStateError(ArangoClientError): """The cursor object was in a bad state.""" +class DatabaseCompactError(ArangoServerError): + """Failed to compact databases.""" + + class DatabaseCreateError(ArangoServerError): """Failed to create database.""" @@ -567,6 +571,10 @@ class ServerCurrentOptionsGetError(ArangoServerError): """Failed to retrieve currently-set server options.""" +class ServerEchoError(ArangoServerError): + """Failed to retrieve details on last request.""" + + class ServerEncryptionError(ArangoServerError): """Failed to reload user-defined encryption keys.""" @@ -575,6 +583,10 @@ class ServerEngineError(ArangoServerError): """Failed to retrieve database engine.""" +class ServerExecuteError(ArangoServerError): + """Failed to execute raw JavaScript command.""" + + class ServerModeError(ArangoServerError): """Failed to retrieve server mode.""" @@ -591,6 +603,18 @@ class ServerLicenseSetError(ArangoServerError): """Failed to set server license.""" +class ServerReloadRoutingError(ArangoServerError): + """Failed to reload routing details.""" + + +class ServerShutdownError(ArangoServerError): + """Failed to initiate shutdown sequence.""" + + +class ServerShutdownProgressError(ArangoServerError): + """Failed to retrieve soft shutdown progress.""" + + class ServerStatusError(ArangoServerError): """Failed to retrieve server status.""" diff --git a/docs/admin.rst b/docs/admin.rst index 1855059..6a494d1 100644 --- a/docs/admin.rst +++ b/docs/admin.rst @@ -39,3 +39,9 @@ Most of these operations can only be performed by admin users via the # Return whether or not a server is in read-only mode mode = await sys_db.mode() + + # Get license information + license = await sys_db.license() + + # Execute Javascript on the server + result = await sys_db.execute("return 1") diff --git a/tests/test_database.py b/tests/test_database.py index 142dadc..5daa837 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -4,12 +4,14 @@ import pytest from packaging import version +from arangoasync.client import ArangoClient from arangoasync.collection import StandardCollection from arangoasync.exceptions import ( CollectionCreateError, CollectionDeleteError, CollectionKeyGeneratorsError, CollectionListError, + DatabaseCompactError, DatabaseCreateError, DatabaseDeleteError, DatabaseListError, @@ -20,11 +22,16 @@ ServerAvailableOptionsGetError, ServerCheckAvailabilityError, ServerCurrentOptionsGetError, + ServerEchoError, ServerEngineError, + ServerExecuteError, ServerLicenseGetError, ServerLicenseSetError, ServerModeError, ServerModeSetError, + ServerReloadRoutingError, + ServerShutdownError, + ServerShutdownProgressError, ServerStatusError, ServerTimeError, ServerVersionError, @@ -34,7 +41,9 @@ @pytest.mark.asyncio -async def test_database_misc_methods(sys_db, db, bad_db, cluster, db_version): +async def test_database_misc_methods( + sys_db, db, bad_db, cluster, db_version, url, sys_db_name, token +): # Status status = await sys_db.status() assert status["server"] == "arango" @@ -95,14 +104,15 @@ async def test_database_misc_methods(sys_db, db, bad_db, cluster, db_version): info = await sys_db.support_info() assert isinstance(info, dict) - with pytest.raises(ServerCurrentOptionsGetError): - await bad_db.options() - options = await sys_db.options() - assert isinstance(options, dict) - with pytest.raises(ServerAvailableOptionsGetError): - await bad_db.options_available() - options_available = await sys_db.options_available() - assert isinstance(options_available, dict) + if db_version >= version.parse("3.12.0"): + with pytest.raises(ServerCurrentOptionsGetError): + await bad_db.options() + options = await sys_db.options() + assert isinstance(options, dict) + with pytest.raises(ServerAvailableOptionsGetError): + await bad_db.options_available() + options_available = await sys_db.options_available() + assert isinstance(options_available, dict) with pytest.raises(ServerModeError): await bad_db.mode() @@ -120,6 +130,33 @@ async def test_database_misc_methods(sys_db, db, bad_db, cluster, db_version): with pytest.raises(ServerLicenseSetError): await sys_db.set_license('"abc"') + with pytest.raises(ServerShutdownError): + await bad_db.shutdown() + with pytest.raises(ServerShutdownProgressError): + await bad_db.shutdown_progress() + + with pytest.raises(ServerReloadRoutingError): + await bad_db.reload_routing() + await sys_db.reload_routing() + + with pytest.raises(ServerEchoError): + await bad_db.echo() + result = await sys_db.echo() + assert isinstance(result, dict) + + with pytest.raises(ServerExecuteError): + await bad_db.execute("return 1") + result = await sys_db.execute("return 1") + assert result == 1 + + with pytest.raises(DatabaseCompactError): + await bad_db.compact() + async with ArangoClient(hosts=url) as client: + db = await client.db( + sys_db_name, auth_method="superuser", token=token, verify=True + ) + await db.compact() + @pytest.mark.asyncio async def test_create_drop_database( From 932c2dd9d1dc7d91e818e9aca0a52d8b5662c9fb Mon Sep 17 00:00:00 2001 From: Alex Petenchea Date: Sat, 16 Aug 2025 10:58:13 +0000 Subject: [PATCH 4/4] Adding admin to to toctree --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index 78afe62..0fab3ac 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -61,6 +61,7 @@ Contents .. toctree:: :maxdepth: 1 + admin user **Miscellaneous**