Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions tests/core/management/commands/assets/pois_to_import.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name,street_address,postal_code,city,country,latitude,longitude,location_on_map,website,email,phone_number,appointment_url,category,barrier_free,temporarily_closed,monday_start,monday_end,monday_all_day,monday_closed,monday_appointment_only,tuesday_start,tuesday_end,tuesday_all_day,tuesday_closed,tuesday_appointment_only,wednesday_start,wednesday_end,wednesday_all_day,wednesday_closed,wednesday_appointment_only,thursday_start,thursday_end,thursday_all_day,thursday_closed,thursday_appointment_only,friday_start,friday_end,friday_all_day,friday_closed,friday_appointment_only,saturday_start,saturday_end,saturday_all_day,saturday_closed,saturday_appointment_only,sunday_start,sunday_end,sunday_all_day,sunday_closed,sunday_appointment_only
"Café Tür an Tür",Wertachstr. 29,86153,Augsburg,,,,yes,"https://tuerantuer.de/cafe/",cafe@tuerantuer.de,0821/65075450,,Gastronomie,yes,no,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
"Bellevue di Monaco",Müllerstraße 6,80469,München,,,,yes,"https://bellevuedimonaco.de/",info@bellevuedimonaco.de,089 550 5775-0,"https://bellevuedimonaco.de/veranstaltungen/",Sonstiges,yes,no,,,,yes,,,,,yes,,,,,yes,,,,,yes,,,,,yes,,,,,yes,,,,,yes,
"Café Tür an Tür",Wertachstr. 29,86153,Augsburg,,,,yes,"https://tuerantuer.de/cafe/",cafe@tuerantuer.de,+49 82165075450,,Gastronomie,yes,no,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
"Bellevue di Monaco",Müllerstraße 6,80469,München,,,,yes,"https://bellevuedimonaco.de/",info@bellevuedimonaco.de,+49 8955057750,"https://bellevuedimonaco.de/veranstaltungen/",Sonstiges,yes,no,,,,yes,,,,,yes,,,,,yes,,,,,yes,,,,,yes,,,,,yes,,,,,yes,
Brandenburger Tor,Pariser Platz,10117,Berlin,,,,yes,"https://www.berlin.de/sehenswuerdigkeiten/3560266-3558930-brandenburger-tor.html",,,,Treffpunkt,yes,no,09:00,17:00,,,,,,yes,,,09:00,17:00,,,,,,,yes,,09:00,17:00,,,,,,,yes,,,,,yes,
216 changes: 216 additions & 0 deletions tests/core/management/commands/test_import_pois_from_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
from __future__ import annotations

from typing import TYPE_CHECKING
from unittest.mock import patch

import pytest
from django.core.management.base import CommandError
from geopy.location import Location

from integreat_cms.cms.constants import poicategory, status
from integreat_cms.cms.models import POI, POITranslation, Region
from integreat_cms.nominatim_api.nominatim_api_client import NominatimApiClient

from ..utils import get_command_output

if TYPE_CHECKING:
from collections.abc import Generator
from pathlib import Path

CSV_FILE = "tests/core/management/commands/assets/pois_to_import.csv"
REGION_SLUG = "augsburg"
USERNAME = "root"
POI_NAMES = ["Café Tür an Tür", "Bellevue di Monaco", "Brandenburger Tor"]
MOCKED_COORDINATES = (48.3780446, 10.8879783)
CLOSED = {"timeSlots": [], "allDay": False, "closed": True, "appointmentOnly": False}
ALL_DAY = {"timeSlots": [], "allDay": True, "closed": False, "appointmentOnly": False}
OPEN = {
"timeSlots": [{"start": "09:00", "end": "17:00"}],
"allDay": False,
"closed": False,
"appointmentOnly": False,
}


@pytest.fixture
def mock_nominatim() -> Generator[None]:
"""
Mock the Nominatim API to avoid real requests during the import
"""
location = Location(
"Mocked address",
MOCKED_COORDINATES,
{"address": {"country": "Deutschland"}},
)
with patch.object(NominatimApiClient, "search", return_value=location):
yield


def test_no_argument_fails() -> None:
"""
Tests that the command fails when no argument is supplied.
"""
with pytest.raises(CommandError) as exc_info:
get_command_output("import_pois_from_csv")

assert (
str(exc_info.value)
== "Error: the following arguments are required: csv_filename, region_slug, username"
)


@pytest.mark.django_db
def test_non_existing_region_fails(load_test_data: None) -> None:
"""
Tests that the command fails when the given region does not exist.
"""
with pytest.raises(CommandError) as exc_info:
get_command_output("import_pois_from_csv", CSV_FILE, "non-existing", USERNAME)

assert str(exc_info.value) == 'Region with slug "non-existing" does not exist.'


@pytest.mark.django_db
def test_non_existing_user_fails(load_test_data: None) -> None:
"""
Tests that the command fails when the given user does not exist.
"""
with pytest.raises(CommandError) as exc_info:
get_command_output(
"import_pois_from_csv",
CSV_FILE,
REGION_SLUG,
"non-existing",
)

assert str(exc_info.value) == 'User with username "non-existing" does not exist.'


@pytest.mark.django_db
def test_import_successful(load_test_data: None, mock_nominatim: None) -> None:
"""
Tests that all POIs of the CSV file are imported as drafts of the region's default language
"""
region = Region.objects.get(slug=REGION_SLUG)

assert not POITranslation.objects.filter(
poi__region=region,
title__in=POI_NAMES,
).exists(), "POIs should not exist before import"

out, err = get_command_output(
"import_pois_from_csv",
CSV_FILE,
REGION_SLUG,
USERNAME,
)
assert not err
assert f"✔ Imported CSV file {CSV_FILE}" in out

for name in POI_NAMES:
poi_translation = POITranslation.objects.filter(
poi__region=region,
title=name,
).first()
assert poi_translation, f"POI {name!r} should exist after import"
assert poi_translation.language == region.default_language
assert poi_translation.status == status.DRAFT
assert poi_translation.creator.username == USERNAME


@pytest.mark.django_db
def test_import_address_data(load_test_data: None, mock_nominatim: None) -> None:
"""
Tests that the columns of the CSV file are imported into the correct fields
"""
get_command_output("import_pois_from_csv", CSV_FILE, REGION_SLUG, USERNAME)

poi = POI.objects.get(translations__title=POI_NAMES[0])
assert poi.address == "Wertachstr. 29"
assert poi.postcode == "86153"
assert poi.city == "Augsburg"
# The country is missing in the CSV file and therefore autocompleted by the Nominatim API
assert poi.country == "Deutschland"
assert (poi.latitude, poi.longitude) == MOCKED_COORDINATES
assert poi.location_on_map
assert poi.barrier_free
assert not poi.temporarily_closed
assert (
POI.objects.get(translations__title=POI_NAMES[1]).appointment_url
== "https://bellevuedimonaco.de/veranstaltungen/"
)


@pytest.mark.django_db
def test_import_categories(load_test_data: None, mock_nominatim: None) -> None:
"""
Tests that existing categories are matched by name and unknown ones fall back to the default category
"""
get_command_output("import_pois_from_csv", CSV_FILE, REGION_SLUG, USERNAME)

icons = [
POI.objects.get(translations__title=name).category.icon for name in POI_NAMES
]
# "Gastronomie" exists in the test data, "Sonstiges" and "Treffpunkt" do not,
# so they fall back to the default category
assert icons == [poicategory.GASTRONOMY, poicategory.OTHER, poicategory.OTHER]


@pytest.mark.django_db
def test_import_opening_hours(load_test_data: None, mock_nominatim: None) -> None:
"""
Tests that the opening hour columns are parsed into our JSON structure
"""
get_command_output("import_pois_from_csv", CSV_FILE, REGION_SLUG, USERNAME)

closed_all_week = [CLOSED] * 7
# All opening hour columns of this POI are empty, so it defaults to closed
assert (
POI.objects.get(translations__title=POI_NAMES[0]).opening_hours
== closed_all_week
)
# This POI is explicitly closed on all days
assert (
POI.objects.get(translations__title=POI_NAMES[1]).opening_hours
== closed_all_week
)
# This POI is open on Monday, Wednesday and Friday, open all day on Tuesday
# and closed on Thursday and the weekend
assert POI.objects.get(translations__title=POI_NAMES[2]).opening_hours == [
OPEN,
ALL_DAY,
OPEN,
CLOSED,
OPEN,
CLOSED,
CLOSED,
]


@pytest.mark.django_db
def test_import_invalid_csv_fails(
load_test_data: None,
mock_nominatim: None,
tmp_path: Path,
) -> None:
"""
Tests that the command fails with the form errors if the CSV file contains invalid data
"""
with open(CSV_FILE, encoding="utf-8") as csv_file:
lines = csv_file.read().splitlines()
invalid_csv = tmp_path / "invalid_pois.csv"
invalid_csv.write_text(
"\n".join([lines[0], lines[1].replace("cafe@tuerantuer.de", "invalid-email")]),
encoding="utf-8",
)

with pytest.raises(CommandError) as exc_info:
get_command_output(
"import_pois_from_csv",
str(invalid_csv),
REGION_SLUG,
USERNAME,
)

assert "Enter a valid email address." in str(exc_info.value)
assert not POITranslation.objects.filter(title=POI_NAMES[0]).exists()
Loading