How to test external POST call that receives query parameters as arrays instead of single values?

219 Views Asked by At

Has anybody successfully managed to write tests for a Paddle webhook in a Django application?

Paddle sends messages as POST requests to a webhook. According to Paddle's webhook testing an exemplary request looks like this:

[alert_id] => 1865992389
[alert_name] => payment_succeeded
[...] => ...

The webhook at my Django application receives this as the request.POST=<QueryDict> parameter:

{'alert_id': ['1865992389'], 'alert_name': ['payment_succeeded'], '...': ['...']}

That is, all values are received as arrays, not values.

Contrary, my webhook test looks like the message format I would expect, i.e. uses values instead of arrays:

    response = client.post('/pricing/paddlewebhook/',
                           {'alert_name': 'payment_succeeded',
                            'alert_id': '1865992389',
                            '...': '...',
                            },
                           content_type='application/x-www-form-urlencoded')
    assert response.status_code == 200  # Match paddle's expectation

This is received by the webhook as request.POST=<QueryDict> parameter:

{'alert_name': 'payment_succeeded', 'alert_id': '1865992389', '...': '...'}

The webhook itself is a simple POST method of a class-based view:

# Django wants us to apply the csrf_exempt decorator all methods via dispatch() instead of just to post().
@method_decorator(csrf_exempt, name='dispatch')
class PaddleWebhook(View):
    def post(self, request, *args, **kwargs):
        logger.info("request.POST=%s", request.POST)
        # ...

Is it really suitable testing behaviour to change the test (and the view's post()) to include array values or am I missing something obvious about external calls to POST webhooks?

1

There are 1 best solutions below

0
On

A possible solution ("working for me") is to define and use an intermediate method to "adjust" the incoming parameter dict:

    def _extractArrayParameters(self, params_dict: dict) -> dict:
        """Returns a parameter dict that contains values directly instead of single element arrays.
        Needed as messages from paddle are received as single element arrays.
        """
        ret = dict()
        for k, v in params_dict.items():
            # Convert all single element arrays directly to values.
            if isinstance(v, list) and len(v) == 1:
                ret[k] = v[0]
            else:
                ret[k] = v
        return ret

However, this feels dirty, even though it makes all tests pass.