diff --git a/dsc/client.py b/dsc/client.py index 2965f74..62c56f0 100644 --- a/dsc/client.py +++ b/dsc/client.py @@ -281,9 +281,18 @@ class SeafileClient: try: libraries = json.loads(out.decode()) except (UnicodeDecodeError, ValueError) as err: - raise DaemonError(f"Cannot read the library list from seaf-cli: {err}") + raise DaemonError( + f"Cannot read the library list from seaf-cli: {err}" + ) from err - return {lib["id"] for lib in libraries} + # Valid JSON is not necessarily the expected shape, and an unexpected + # one must fail the same way rather than as a bare TypeError. + try: + return {lib["id"] for lib in libraries} + except (KeyError, TypeError) as err: + raise DaemonError( + f"Unexpected library list from seaf-cli: {err}" + ) from err def configure(self, args: argparse.Namespace, check_for_daemon: bool = True): need_restart = False diff --git a/tests/test_commands.py b/tests/test_commands.py index 48edf8f..082f16f 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -213,3 +213,25 @@ def test_local_libraries_reports_a_failed_command(client, monkeypatch): run_returning(monkeypatch, b"", returncode=1) with pytest.raises(DaemonError): client.get_local_libraries() + + +@pytest.mark.parametrize("payload", [ + b'{"name": "Docs"}', # an object where a list is expected + b'[{"name": "Docs"}]', # an entry with no id + b'["Docs"]', # entries that are not objects + b'[null]', + b'null', + b'42', +]) +def test_local_libraries_reports_unexpected_json_shapes(client, monkeypatch, payload): + """Valid JSON of the wrong shape must not escape as a raw TypeError.""" + run_returning(monkeypatch, payload) + with pytest.raises(DaemonError): + client.get_local_libraries() + + +def test_local_libraries_error_keeps_the_original_cause(client, monkeypatch): + run_returning(monkeypatch, b"not json at all") + with pytest.raises(DaemonError) as err: + client.get_local_libraries() + assert err.value.__cause__ is not None