Repository: fractal-analytics-platform/fractal-client (language: python)

Issue:
Command `version` should not fail if fractal server is not alive
Right now we have this
```sh
poetry run fractal version
ConnectError at http://localhost:8000/api/alive/
Original error: '[Errno 61] Connection refused'
Hint: is http://localhost:8000 alive?
```

We something like 
```python
def version(client: Client, **kwargs) -> Interface:
    try:
        res = client.get(f"{settings.FRACTAL_SERVER}/api/alive/")
        data = res.json()
        server_str = (
            f"\turl: {settings.FRACTAL_SERVER}"
            f"\tversion: {data['version']}"
        )
    except ConnectError:
        server_str = f"\tConnection to '{settings.FRACTAL_SERVER}' refused"

    return Interface(
        retcode=0,
        data=(
            f"Fractal client\n\tversion: {__VERSION__}\n"
            f"Fractal server:\n{server_str}"
            
        ),
    )
```
we get
```
poetry run fractal version
Fractal client
	version: 2.3.0a1
Fractal server:
	Connection to 'http://localhost:8000' refused
```


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9497b6b6..dd1c6025 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@
 * Add `--new-ssh-settings-json` option to `fractal user edit` (\#715).
 * Add `--private` option to task-creating commands (\#717).
 * Drop `task delete` command (\#717).
+* Handle missing server in `fractal version` (\#724).
 * Testing:
     * Run all tests against a single `fractal-server` instance (\#717).
     * Run tests in random module order, based on `pytest-randomly` (\#717).
diff --git a/fractal_client/cmd/__init__.py b/fractal_client/cmd/__init__.py
index 4960a880..bed075ff 100644
--- a/fractal_client/cmd/__init__.py
+++ b/fractal_client/cmd/__init__.py
@@ -1,4 +1,5 @@
 from httpx import Client
+from httpx import ConnectError
 
 from ..authclient import AuthClient
 from ..config import settings
@@ -298,16 +299,20 @@ def job(
 
 
 def version(client: Client, **kwargs) -> Interface:
-    res = client.get(f"{settings.FRACTAL_SERVER}/api/alive/")
-    data = res.json()
+    try:
+        res = client.get(f"{settings.FRACTAL_SERVER}/api/alive/")
+        data = res.json()
+        server_str = (
+            f"\turl: {settings.FRACTAL_SERVER}\tversion: {data['version']}"
+        )
+    except ConnectError:
+        server_str = f"\tConnection to '{settings.FRACTAL_SERVER}' refused"
 
     return Interface(
         retcode=0,
         data=(
             f"Fractal client\n\tversion: {__VERSION__}\n"
-            "Fractal server:\n"
-            f"\turl: {settings.FRACTAL_SERVER}"
-            f"\tversion: {data['version']}"
+            f"Fractal server:\n{server_str}"
         ),
     )
 


Test patch:
diff --git a/tests/fixtures_testserver.py b/tests/fixtures_testserver.py
index 39191ea7..93112691 100644
--- a/tests/fixtures_testserver.py
+++ b/tests/fixtures_testserver.py
@@ -95,7 +95,7 @@ def testserver(tester, tmpdir_factory, request):
     while True:
         try:
             res = handle(shlex.split("fractal version"))
-            if res.retcode == 0:
+            if "refused" not in res.data:
                 break
             else:
                 raise ConnectError("fractal-server not ready")
diff --git a/tests/test_client.py b/tests/test_client.py
index 50b2389e..64af97fc 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -6,6 +6,7 @@
 
 from fractal_client import __VERSION__
 from fractal_client.client import handle
+from fractal_client.config import Settings
 
 
 def test_debug(invoke):
@@ -21,6 +22,19 @@ def test_version(invoke):
     assert iface.retcode == 0
 
 
+def test_version_connect_error(invoke, monkeypatch):
+
+    mock_settings = Settings()
+    mock_settings.FRACTAL_SERVER = "http://localhost:9999"
+    monkeypatch.setattr("fractal_client.cmd.settings", mock_settings)
+
+    iface = invoke("version")
+    debug(iface.data)
+    assert f"version: {__VERSION__}" in iface.data
+    assert "refused" in iface.data
+    assert iface.retcode == 0
+
+
 def test_server():
     """
     GIVEN a testserver