Skip to content

✨Improve error detail when re-requesting credential #285

✨Improve error detail when re-requesting credential

✨Improve error detail when re-requesting credential #285

GitHub Actions / JUnit Test Report failed Dec 3, 2024 in 0s

875 tests run, 868 passed, 6 skipped, 1 failed.

Annotations

Check failure on line 373 in app/tests/e2e/issuer/test_indy_credentials.py

See this annotation in the file changed.

@github-actions github-actions / JUnit Test Report

test_indy_credentials.test_requesting_already_issued_credential[clean-clean-clean-clean-clean]

fastapi.exceptions.HTTPException: 404: {"detail":"Record not found: cred_ex_v20/100dd442-ac39-44f1-8e72-e8e593580bcf."}
Raw output
self = <shared.util.rich_async_client.RichAsyncClient object at 0x7f2dea786ae0>
method = 'post'
url = '/v1/issuer/credentials/v2-100dd442-ac39-44f1-8e72-e8e593580bcf/request'
kwargs = {}, attempt = 0, response = <Response [404 Not Found]>, code = 404

    async def _request_with_retries(self, method: str, url: str, **kwargs) -> Response:
        for attempt in range(self.retries):
            try:
                response = await getattr(super(), method)(url, **kwargs)
>               return await self._handle_response(response)

shared/util/rich_async_client.py:67: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
shared/util/rich_async_client.py:50: in _handle_response
    response.raise_for_status()  # Raise exception for 4xx and 5xx status codes
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Response [404 Not Found]>

    def raise_for_status(self) -> Response:
        """
        Raise the `HTTPStatusError` if one occurred.
        """
        request = self._request
        if request is None:
            raise RuntimeError(
                "Cannot call `raise_for_status` as the request "
                "instance has not been set on this response."
            )
    
        if self.is_success:
            return self
    
        if self.has_redirect_location:
            message = (
                "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n"
                "Redirect location: '{0.headers[location]}'\n"
                "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}"
            )
        else:
            message = (
                "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n"
                "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}"
            )
    
        status_class = self.status_code // 100
        error_types = {
            1: "Informational response",
            3: "Redirect response",
            4: "Client error",
            5: "Server error",
        }
        error_type = error_types.get(status_class, "Invalid status code")
        message = message.format(self, error_type=error_type)
>       raise HTTPStatusError(message, request=request, response=self)
E       httpx.HTTPStatusError: Client error '404 Not Found' for url 'https://tenant-web.cloudapi.dev.didxtech.com/tenant/v1/issuer/credentials/v2-100dd442-ac39-44f1-8e72-e8e593580bcf/request'
E       For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404

/usr/local/lib/python3.12/site-packages/httpx/_models.py:829: HTTPStatusError

The above exception was the direct cause of the following exception:

alice_member_client = <shared.util.rich_async_client.RichAsyncClient object at 0x7f2dea786ae0>
tenant_admin_client = <shared.util.rich_async_client.RichAsyncClient object at 0x7f2dea784050>
faber_client = <shared.util.rich_async_client.RichAsyncClient object at 0x7f2de9f0b710>
faber_and_alice_connection = FaberAliceConnect(alice_connection_id='958da2fe-e8e0-47b3-8420-9e07472764d8', faber_connection_id='c2a2a32f-8a39-422d-8806-aec799d37035')
credential_definition_id = 'JsKC6vL9i2EQ1TbiuA6WGk:3:CL:2437:tag'

    @pytest.mark.anyio
    @pytest.mark.skipif(
        TestMode.regression_run in TestMode.fixture_params,
        reason="We don't want to modify wallet settings for regression runs",
    )
    async def test_requesting_already_issued_credential(
        alice_member_client: RichAsyncClient,
        tenant_admin_client: RichAsyncClient,
        faber_client: RichAsyncClient,
        faber_and_alice_connection: FaberAliceConnect,
        credential_definition_id: str,
    ):
        # First, configure Alice to not auto-complete credential flow
        alice_wallet_id = get_wallet_id_from_async_client(alice_member_client)
        update_request = {"extra_settings": {"ACAPY_AUTO_STORE_CREDENTIAL": False}}
        update_response = await tenant_admin_client.put(
            f"{TENANTS_BASE_PATH}/{alice_wallet_id}",
            json=update_request,
        )
        assert update_response.status_code == 200
    
        # Create credential offer
        credential = {
            "connection_id": faber_and_alice_connection.faber_connection_id,
            "indy_credential_detail": {
                "credential_definition_id": credential_definition_id,
                "attributes": sample_credential_attributes,
            },
            "save_exchange_record": True,
        }
    
        # Send credential offer
        response = await faber_client.post(
            CREDENTIALS_BASE_PATH,
            json=credential,
        )
        credential_exchange = response.json()
        thread_id = credential_exchange["thread_id"]
    
        # Wait for offer to be received
        await check_webhook_state(
            client=alice_member_client,
            topic="credentials",
            state="offer-received",
            filter_map={
                "thread_id": thread_id,
            },
        )
    
        # Get credential exchange ID
        await asyncio.sleep(0.5)  # credential may take moment to reflect after webhook
        response = await alice_member_client.get(
            CREDENTIALS_BASE_PATH,
            params={"thread_id": thread_id},
        )
        credential_exchange_id = (response.json())[0]["credential_exchange_id"]
    
        # First request should succeed
        request_response = await alice_member_client.post(
            f"{CREDENTIALS_BASE_PATH}/{credential_exchange_id}/request",
        )
        assert request_response.status_code == 200
    
        await asyncio.sleep(1)  # sleep for record to update
    
        # Second request should fail with 409
>       error_response = await alice_member_client.post(
            f"{CREDENTIALS_BASE_PATH}/{credential_exchange_id}/request",
        )

app/tests/e2e/issuer/test_indy_credentials.py:373: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
shared/util/rich_async_client.py:81: in post
    return await self._request_with_retries("post", url, **kwargs)
shared/util/rich_async_client.py:78: in _request_with_retries
    await self._handle_error(e, url, method)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <shared.util.rich_async_client.RichAsyncClient object at 0x7f2dea786ae0>
e = HTTPStatusError("Client error '404 Not Found' for url 'https://tenant-web.cloudapi.dev.didxtech.com/tenant/v1/issuer/c...-8e72-e8e593580bcf/request'\nFor more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404")
url = '/v1/issuer/credentials/v2-100dd442-ac39-44f1-8e72-e8e593580bcf/request'
method = 'post'

    async def _handle_error(self, e: HTTPStatusError, url: str, method: str) -> None:
        code = e.response.status_code
        message = e.response.text
        log_message = (
            f"{self.name} {method} `{url}` failed. "
            f"Status code: {code}. Response: `{message}`."
        )
        logger.error(log_message)
>       raise HTTPException(status_code=code, detail=message) from e
E       fastapi.exceptions.HTTPException: 404: {"detail":"Record not found: cred_ex_v20/100dd442-ac39-44f1-8e72-e8e593580bcf."}

shared/util/rich_async_client.py:61: HTTPException