diff --git a/charmcraft/services/store.py b/charmcraft/services/store.py index f5c562504..ce1ddd627 100644 --- a/charmcraft/services/store.py +++ b/charmcraft/services/store.py @@ -24,6 +24,7 @@ import craft_store from craft_cli import emit from craft_store import models +from craft_store.errors import StoreServerError from overrides import override from charmcraft import const, env, errors, store @@ -244,7 +245,22 @@ def get_libraries_metadata( store_lib["patch"] = patch_version store_libs.append(store_lib) - return self.anonymous_client.fetch_libraries_metadata(store_libs) + try: + return self.anonymous_client.fetch_libraries_metadata(store_libs) + except StoreServerError as exc: + lib_names = [lib.lib for lib in libraries] + # Type ignore here because error_list is supposed to have string keys, but + # for whatever reason the store returns a null code for this one. + # https://bugs.launchpad.net/snapstore-server/+bug/1925065 + if exc.error_list[None]["message"] == ( # type: ignore[index] + "Items need to include 'library_id' or 'package_id'" + ): + raise errors.LibraryError( + "One or more declared charm-libs could not be found in the store.", + details="Declared charm-libs: " + ", ".join(lib_names), + resolution="Check the charm and library names in charmcraft.yaml", + ) from exc + raise def get_libraries_metadata_by_name( self, libraries: Sequence[CharmLib] diff --git a/tests/unit/services/test_store.py b/tests/unit/services/test_store.py index 55dadff43..f9776ffe9 100644 --- a/tests/unit/services/test_store.py +++ b/tests/unit/services/test_store.py @@ -20,8 +20,10 @@ from unittest import mock import craft_store +import craft_store.errors import distro import pytest +import requests from craft_cli.pytest_plugin import RecordingEmitter from craft_store import models from hypothesis import given, strategies @@ -34,12 +36,12 @@ @pytest.fixture -def store(service_factory) -> services.StoreService: +def store(service_factory, mock_store_anonymous_client) -> services.StoreService: store = services.StoreService( app=application.APP_METADATA, services=service_factory ) store.client = mock.Mock(spec_set=client.Client) - store.anonymous_client = mock.Mock(spec_set=client.AnonymousClient) + store.anonymous_client = mock_store_anonymous_client return store @@ -305,3 +307,17 @@ def test_fetch_libraries_metadata(monkeypatch, store, libs, expected_call): store.anonymous_client.fetch_libraries_metadata.assert_called_once_with( expected_call ) + + +def test_get_libraries_metadata_name_error( + monkeypatch, store: services.StoreService, mock_store_anonymous_client: mock.Mock +) -> None: + bad_response = requests.Response() + bad_response.status_code = 400 + bad_response._content = b'{"error-list": [{"code": null, "message": "Items need to include \'library_id\' or \'package_id\'"}]}' + mock_store_anonymous_client.fetch_libraries_metadata.side_effect = ( + craft_store.errors.StoreServerError(bad_response) + ) + + with pytest.raises(errors.LibraryError, match="One or more declared"): + store.get_libraries_metadata([CharmLib(lib="boop.snoot", version="-1")])