diff --git a/src/open_inwoner/openklant/tests/data.py b/src/open_inwoner/openklant/tests/data.py index fa65deb559..714cda44e2 100644 --- a/src/open_inwoner/openklant/tests/data.py +++ b/src/open_inwoner/openklant/tests/data.py @@ -144,8 +144,9 @@ def __init__(self): type="SomeType", kanaal="MAIL", status=Status.afgehandeld, - antwoord="", + antwoord="foo", onderwerp="e_suite_subject_code", + registratiedatum="2022-01-01T12:00:00Z", ) self.contactmoment2 = generate_oas_component_cached( "cmc", diff --git a/src/open_inwoner/openklant/tests/factories.py b/src/open_inwoner/openklant/tests/factories.py index e54286d210..84b1e52394 100644 --- a/src/open_inwoner/openklant/tests/factories.py +++ b/src/open_inwoner/openklant/tests/factories.py @@ -1,5 +1,7 @@ import factory +from open_inwoner.accounts.tests.factories import UserFactory + class ContactFormSubjectFactory(factory.django.DjangoModelFactory): class Meta: @@ -7,3 +9,11 @@ class Meta: subject = factory.Faker("sentence") subject_code = factory.Faker("word") + + +class KlantContactMomentLocalFactory(factory.django.DjangoModelFactory): + class Meta: + model = "openklant.KlantContactMomentLocal" + + kcm_url = factory.Faker("url") + user = factory.SubFactory(UserFactory) diff --git a/src/open_inwoner/openklant/tests/test_helpers.py b/src/open_inwoner/openklant/tests/test_helpers.py new file mode 100644 index 0000000000..2285cc898b --- /dev/null +++ b/src/open_inwoner/openklant/tests/test_helpers.py @@ -0,0 +1,42 @@ +from django.test import TestCase + +import requests_mock + +from open_inwoner.openklant.models import KlantContactMomentLocal +from open_inwoner.openklant.tests.data import MockAPIReadData +from open_inwoner.openklant.wrap import ( + fetch_klantcontactmomenten, + get_local_kcm_mapping, +) +from open_inwoner.utils.test import ClearCachesMixin, DisableRequestLogMixin + + +@requests_mock.Mocker() +class HelpersTestCase(ClearCachesMixin, DisableRequestLogMixin, TestCase): + @classmethod + def setUpTestData(cls): + super().setUpTestData() + MockAPIReadData.setUpServices() + + def test_get_local_kcm_mapping(self, m): + data = MockAPIReadData().install_mocks(m) + + kcms = fetch_klantcontactmomenten(user_bsn=data.user.bsn) + + with self.subTest("running the first time will create KlantContactMomentLocal"): + mapping = get_local_kcm_mapping(kcms, data.user) + + self.assertEqual(KlantContactMomentLocal.objects.count(), 1) + + kcm_local = KlantContactMomentLocal.objects.get() + + self.assertEqual(mapping, {kcms[0].url: kcm_local}) + self.assertEqual(kcm_local.user, data.user) + self.assertEqual(kcm_local.kcm_url, kcms[0].url) + self.assertEqual(kcm_local.is_seen, False) + + with self.subTest("running function again will ignore existing entries"): + mapping = get_local_kcm_mapping(kcms, data.user) + + self.assertEqual(KlantContactMomentLocal.objects.count(), 1) + self.assertEqual(mapping, {kcms[0].url: kcm_local}) diff --git a/src/open_inwoner/openklant/tests/test_views.py b/src/open_inwoner/openklant/tests/test_views.py index f4130af5d9..8ababe3e54 100644 --- a/src/open_inwoner/openklant/tests/test_views.py +++ b/src/open_inwoner/openklant/tests/test_views.py @@ -1,15 +1,23 @@ from datetime import datetime +from unittest.mock import patch from uuid import uuid4 from django.test import modify_settings, override_settings from django.urls import reverse -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext as _ import requests_mock from django_webtest import WebTest +from freezegun import freeze_time +from zgw_consumers.api_models.base import factory from open_inwoner.accounts.tests.factories import UserFactory -from open_inwoner.openklant.models import ContactFormSubject, OpenKlantConfig +from open_inwoner.openklant.api_models import ContactMoment, Klant, KlantContactMoment +from open_inwoner.openklant.models import ( + ContactFormSubject, + KlantContactMomentLocal, + OpenKlantConfig, +) from open_inwoner.openklant.tests.data import MockAPIReadData from open_inwoner.openzaak.models import OpenZaakConfig from open_inwoner.utils.test import ( @@ -18,13 +26,18 @@ set_kvk_branch_number_in_session, ) +from .factories import KlantContactMomentLocalFactory + @requests_mock.Mocker() +@patch( + "open_inwoner.accounts.views.contactmoments.get_local_kcm_mapping", autospec=True +) @override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls") @modify_settings( MIDDLEWARE={"remove": ["open_inwoner.kvk.middleware.KvKLoginMiddleware"]} ) -class FetchKlantDataTestCase(ClearCachesMixin, DisableRequestLogMixin, WebTest): +class ContactMomentViewsTestCase(ClearCachesMixin, DisableRequestLogMixin, WebTest): maxDiff = None @classmethod @@ -39,7 +52,8 @@ def setUpTestData(cls): config=OpenKlantConfig.get_solo(), ) - def test_list_for_bsn(self, m): + # @patch("open_inwoner.accounts.views.contactmoments.get_local_kcm_mapping", autospec=True) + def test_list_for_bsn(self, m, mock_get_local_kcm_mapping): data = MockAPIReadData().install_mocks(m) detail_url = reverse( @@ -51,8 +65,56 @@ def test_list_for_bsn(self, m): kcms = response.context["contactmomenten"] cm_data = data.contactmoment + self.assertEqual(len(kcms), 1) + self.assertEqual( + kcms[0], + { + "registered_date": datetime.fromisoformat(cm_data["registratiedatum"]), + "channel": cm_data["kanaal"].title(), + "text": cm_data["tekst"], + "onderwerp": self.contactformsubject.subject, + "antwoord": cm_data["antwoord"], + "identificatie": cm_data["identificatie"], + "type": cm_data["type"], + "status": _("Afgehandeld"), + "url": detail_url, + "new_answer_available": False, + }, + ) + + kcm = factory(KlantContactMoment, data.klant_contactmoment) + kcm.contactmoment = factory(ContactMoment, data.contactmoment) + kcm.klant = factory(Klant, data.klant) + + mock_get_local_kcm_mapping.assert_called_once_with([kcm], data.user) + + status_item = response.pyquery.find(f"p:contains('{_('Status')}')").parent() + self.assertEqual(status_item.text(), f"{_('Status')}\n{_('Afgehandeld')}") + self.assertNotIn(_("Nieuw antwoord beschikbaar"), response.text) + + @freeze_time("2022-01-01") + def test_list_for_bsn_new_answer_available(self, m, mock_get_local_kcm_mapping): + data = MockAPIReadData().install_mocks(m) + + mock_get_local_kcm_mapping.return_value = { + data.klant_contactmoment["url"]: KlantContactMomentLocalFactory.create( + user=data.user, kcm_url=data.klant_contactmoment["url"], is_seen=False + ) + } + + detail_url = reverse( + "cases:contactmoment_detail", + kwargs={"kcm_uuid": data.klant_contactmoment["uuid"]}, + ) + list_url = reverse("cases:contactmoment_list") + response = self.app.get(list_url, user=data.user) + + kcms = response.context["contactmomenten"] + cm_data = data.contactmoment + + self.assertEqual(len(kcms), 1) self.assertEqual( kcms[0], { @@ -65,14 +127,22 @@ def test_list_for_bsn(self, m): "type": cm_data["type"], "status": _("Afgehandeld"), "url": detail_url, + "new_answer_available": True, }, ) + kcm = factory(KlantContactMoment, data.klant_contactmoment) + kcm.contactmoment = factory(ContactMoment, data.contactmoment) + kcm.klant = factory(Klant, data.klant) + + mock_get_local_kcm_mapping.assert_called_once_with([kcm], data.user) + status_item = response.pyquery.find(f"p:contains('{_('Status')}')").parent() self.assertEqual(status_item.text(), f"{_('Status')}\n{_('Afgehandeld')}") + self.assertIn(_("Nieuw antwoord beschikbaar"), response.text) - def test_list_for_kvk_or_rsin(self, m): + def test_list_for_kvk_or_rsin(self, m, mock_get_local_kcm_mapping): for use_rsin_for_innNnpId_query_parameter in [True, False]: with self.subTest( use_rsin_for_innNnpId_query_parameter=use_rsin_for_innNnpId_query_parameter @@ -109,11 +179,12 @@ def test_list_for_kvk_or_rsin(self, m): "type": cm_data["type"], "status": _("Afgehandeld"), "url": detail_url, + "new_answer_available": False, }, ) @set_kvk_branch_number_in_session("1234") - def test_list_for_vestiging(self, m): + def test_list_for_vestiging(self, m, mock_get_local_kcm_mapping): data = MockAPIReadData().install_mocks(m) self.client.force_login(user=data.eherkenning_user) @@ -152,10 +223,11 @@ def test_list_for_vestiging(self, m): "type": cm_data["type"], "status": _("Afgehandeld"), "url": detail_url, + "new_answer_available": False, }, ) - def test_show_detail_for_bsn(self, m): + def test_show_detail_for_bsn(self, m, mock_get_local_kcm_mapping): data = MockAPIReadData().install_mocks(m) detail_url = reverse( @@ -180,10 +252,11 @@ def test_show_detail_for_bsn(self, m): "type": cm_data["type"], "status": _("Afgehandeld"), "url": detail_url, + "new_answer_available": False, }, ) - def test_show_detail_for_bsn_with_zaak(self, m): + def test_show_detail_for_bsn_with_zaak(self, m, mock_get_local_kcm_mapping): data = MockAPIReadData().install_mocks(m, link_objectcontactmomenten=True) detail_url = reverse( @@ -210,6 +283,7 @@ def test_show_detail_for_bsn_with_zaak(self, m): "type": cm_data["type"], "status": _("Afgehandeld"), "url": detail_url, + "new_answer_available": False, }, ) @@ -224,7 +298,16 @@ def test_show_detail_for_bsn_with_zaak(self, m): ), ) - def test_show_detail_for_bsn_with_zaak_reformat_esuite_id(self, m): + kcm_local = KlantContactMomentLocal.objects.get( + kcm_url=data.klant_contactmoment["url"] + ) + + self.assertEqual(kcm_local.user, data.user) + self.assertEqual(kcm_local.is_seen, True) + + def test_show_detail_for_bsn_with_zaak_reformat_esuite_id( + self, m, mock_get_local_kcm_mapping + ): data = MockAPIReadData().install_mocks(m, link_objectcontactmomenten=True) oz_config = OpenZaakConfig.get_solo() @@ -259,6 +342,7 @@ def test_show_detail_for_bsn_with_zaak_reformat_esuite_id(self, m): "type": cm_data["type"], "status": _("Afgehandeld"), "url": detail_url, + "new_answer_available": False, }, ) @@ -273,11 +357,15 @@ def test_show_detail_for_bsn_with_zaak_reformat_esuite_id(self, m): ), ) - def test_show_detail_for_kvk_or_rsin(self, m): + def test_show_detail_for_kvk_or_rsin(self, m, mock_get_local_kcm_mapping): for use_rsin_for_innNnpId_query_parameter in [True, False]: with self.subTest( use_rsin_for_innNnpId_query_parameter=use_rsin_for_innNnpId_query_parameter ): + # Avoid having a `KlantContactMomentLocal` with the same URL for + # different users + KlantContactMomentLocal.objects.all().delete() + config = OpenKlantConfig.get_solo() config.use_rsin_for_innNnpId_query_parameter = ( use_rsin_for_innNnpId_query_parameter @@ -307,11 +395,12 @@ def test_show_detail_for_kvk_or_rsin(self, m): "type": cm_data["type"], "status": _("Afgehandeld"), "url": detail_url, + "new_answer_available": False, }, ) @set_kvk_branch_number_in_session("1234") - def test_show_detail_for_vestiging(self, m): + def test_show_detail_for_vestiging(self, m, mock_get_local_kcm_mapping): data = MockAPIReadData().install_mocks(m) self.client.force_login(user=data.eherkenning_user) @@ -346,11 +435,14 @@ def test_show_detail_for_vestiging(self, m): "type": cm_data["type"], "status": _("Afgehandeld"), "url": detail_url, + "new_answer_available": False, }, ) @set_kvk_branch_number_in_session("1234") - def test_cannot_access_detail_for_hoofdvestiging_as_vestiging(self, m): + def test_cannot_access_detail_for_hoofdvestiging_as_vestiging( + self, m, mock_get_local_kcm_mapping + ): data = MockAPIReadData().install_mocks(m) self.client.force_login(user=data.eherkenning_user) @@ -372,24 +464,24 @@ def test_cannot_access_detail_for_hoofdvestiging_as_vestiging(self, m): self.assertEqual(response.status_code, 404) - def test_list_requires_bsn_or_kvk(self, m): + def test_list_requires_bsn_or_kvk(self, m, mock_get_local_kcm_mapping): user = UserFactory() list_url = reverse("cases:contactmoment_list") response = self.app.get(list_url, user=user) self.assertRedirects(response, reverse("pages-root")) - def test_list_requires_login(self, m): + def test_list_requires_login(self, m, mock_get_local_kcm_mapping): list_url = reverse("cases:contactmoment_list") response = self.app.get(list_url) self.assertRedirects(response, f"{reverse('login')}?next={list_url}") - def test_detail_requires_bsn_or_kvk(self, m): + def test_detail_requires_bsn_or_kvk(self, m, mock_get_local_kcm_mapping): user = UserFactory() url = reverse("cases:contactmoment_detail", kwargs={"kcm_uuid": uuid4()}) response = self.app.get(url, user=user) self.assertRedirects(response, reverse("pages-root")) - def test_detail_requires_login(self, m): + def test_detail_requires_login(self, m, mock_get_local_kcm_mapping): url = reverse("cases:contactmoment_detail", kwargs={"kcm_uuid": uuid4()}) response = self.app.get(url) self.assertRedirects(response, f"{reverse('login')}?next={url}")