Search code examples
pythonpython-3.xflaskrequesthttp-status-code-400

Flask Endpoint Returning Status 400 While Executing Just Fine


I am currrently writing a unit test for a function that processes a JSON string of filters to build a CSV file using flask-csv. When I tested the function in the application it worked just fine, triggering the download with the right filters, and returning the file in the expected format. Checking the output in response.data also turns out fine, but I keep getting a BadRequest (400) when running the test.

This would be the endpoint function:

csv-download.py

import json
import logging

import flask_csv
import pandas as pd
from flask import Blueprint
from jinja2 import Template
from pydantic import ValidationError
from models import CSVModel

home_blueprint = Blueprint("home", __name__, url_prefix="/")

@home_blueprint.route("csv_download", methods=["GET", "POST"])
def download_csv():

    filter_str = request.args.get("filters")
    content = request.args.get("content")

    try:
        model = CSVModel(csv_fliters=filter_str, csv_content=content)
    except ValidationError as e:
        raise BadInputException(e.json())

    filters = json.loads(filter_str)


    with open(
        "bq_queries/myquery.bq", "r", encoding="utf-8"
    ) as file:
        query_string = file.read()

    query = Template(query_string)

    # this function is replaced by a mock.patch in the test
    data = load_from_query(apply_filters(query, filters, "None"), client)

    today = pd.Timestamp.today().strftime("%Y%m%d")
    start = pd.to_datetime(filters["order_date"][0]).strftime("%Y%m%d")
    end = pd.to_datetime(filters["order_date"][1]).strftime("%Y%m%d")

    response = flask_csv.send_csv(
        data.to_dict(orient="records"),
        f"{today}_{start}_until_{end}_{content}.csv".format(),
        list(data.columns),
    )

    return response

This function is tested like this:

test_csv_download.py

import pytest  
from flask.testing import FlaskClient
from unittest.mock import patch

@patch('customer_feedback_hub.home.views.load_from_query')
def test_csv_download_(mock_load, client: FlaskClient) -> None:
    """
    Test verifying that the CSV-Download Endpoint works.

    """

    import pandas as pd
    import json

    mock_load.return_value = pd.DataFrame({'A': [1,2], 'B': [3,4]})

    test_filters = {'subentity_id': 1, 'order_date': ['2019-10-30', '2019-10-31']}

    suffix = "test"



    dl_filters = json.dumps(test_filters)

    response = client.get("/csv_download" +
    "?filters={}&content={}".format(dl_filters, suffix))

    # response format taken directly from:
    # https://github.com/Shir0kamii/Flask-CSV/blob/master/test_flask_csv.py

    expected = b"""A,B
    1,3
    2,4
    """.replace(b'\n', b"\r\n")

    response.data == expected

    assert response.status_code == 200

The message for the test failing:

====================================================================== FAILURES =======================================================================
_________________________________________________________________ test_csv_download_ __________________________________________________________________

mock_load = <MagicMock name='load_from_query' id='139751143251392'>, client = <FlaskClient <Flask 'customer_feedback_hub.app'>>

    @patch('customer_feedback_hub.home.views.load_from_query')
    def test_csv_download_(mock_load, client: FlaskClient) -> None:
        """
        Test verifying that the CSV-Download Endpoint works.

        """

        import pandas as pd
        import json

        mock_load.return_value = pd.DataFrame({'A': [1,2], 'B': [3,4]})

        test_filters = {'subentity_id': 1, 'order_date': ['2019-10-30', '2019-10-31']}

        suffix = "test"


        # start = pd.to_datetime(test_filters["order_date"][0]).strftime("%Y%m%d")
        # end = pd.to_datetime(test_filters["order_date"][1]).strftime("%Y%m%d")


        dl_filters = json.dumps(test_filters)

        response = client.get("/csv_download" +
        "?filters={}&content={}".format(dl_filters, suffix))

        # response format taken directly from:
        # https://github.com/Shir0kamii/Flask-CSV/blob/master/test_flask_csv.py

        expected = b"""A,B
        1,3
        2,4
        """.replace(b'\n', b"\r\n")

        response.data == expected

>       assert response.status_code == 200
E       assert 400 == 200
E         -400
E         +200

tests/home/test_views.py:79: AssertionError

Also, for the sake of completeness, the definition of CSVModel used in the original function, which I use to validate that the string indeed can be loaded as a JSON string

models.py

import json

from pydantic import BaseModel, Extra, NoneStr, validator


class CSVModel(BaseModel):
    """
    Example Pydantic model for validating json
    """

    csv_filters: str
    csv_content: NoneStr = None

    class Config:
        extra = Extra.forbid

    @validator("csv_filters")
    def filters_are_json(cls, v):
        try:
            dic = json.loads(v)
            dic.items()

        except Exception:
            raise ValueError("No JSON!")

Maybe I'm missing something obvious, but I'd be happy to have another pair of eyes on this.


Solution

  • You have

    response.data == expected
    

    But you probably want

    assert response.data == expected
    

    And I suspect that assertion will fail and tell you more. :)