From d90de2558d5c3075612061278df7f3bc2fc2b1d4 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Wed, 25 Mar 2020 15:22:07 +0100
Subject: [PATCH] tests: add tests on views and commands (#41025)

---
 tests/test_commands.py       | 149 +++++++++++++++++++++++++++++++++++
 tests/test_nanterre.py       |  15 ++++
 tests/test_nanterre_views.py | 105 ++++++++++++++++++++++++
 tests/test_zoo.py            |  38 ++++++++-
 4 files changed, 304 insertions(+), 3 deletions(-)
 create mode 100644 tests/test_commands.py
 create mode 100644 tests/test_nanterre_views.py

diff --git a/tests/test_commands.py b/tests/test_commands.py
new file mode 100644
index 0000000..d894df1
--- /dev/null
+++ b/tests/test_commands.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+
+import mock
+import pytest
+import sys
+
+from zoo.zoo_data.models import Entity, Transaction, Relation
+from zoo.zoo_nanterre import utils
+
+from django.core.management import call_command
+from django.db.transaction import atomic
+from django.utils.six import StringIO
+
+
+def get_output_of_command(command, *args, **kwargs):
+    old_stdout = sys.stdout
+    output = sys.stdout = StringIO()
+    call_command(command, *args, **kwargs)
+    sys.stdout = old_stdout
+    return output.getvalue()
+
+
+def test_runjobs_command(db):
+    call_command('runjobs')
+
+    with mock.patch('zoo.zoo_data.management.commands.runjobs.Job.redo',
+                    side_effect=Exception):
+        with pytest.raises(Exception):
+            call_command('runjobs')
+
+
+def test_runscript_command(tmpdir):
+    script_path = '%s/script.py' % str(tmpdir)
+    open(script_path, 'w').write('# N/A')
+    call_command('runscript', script_path)
+
+
+def test_rsu_cron_command(settings, nanterre_classic_family):
+    settings.ZOO_NANTERRE_PASSAGE_A_LA_MAJORITE = True
+    call_command('rsu-cron', '-v2')
+
+
+def test_rsu_dupplicate_command(nanterre_classic_family):
+    call_command('rsu-duplicates', '-v2', 'list', '--dedup')
+    call_command('rsu-duplicates', '-v2', 'list')
+
+    with pytest.raises(AssertionError, match='Cannot filter a query once a slice has been taken'):
+        call_command('rsu-duplicates', 'list', '--count', '2')
+    with pytest.raises(TypeError, match="'datetime.datetime' object is not callable"):
+        call_command('rsu-duplicates', '-v2', 'list', '--days', '2')
+
+    call_command('rsu-duplicates', '-v2', 'find', '--limit', '0.1')
+    call_command('rsu-duplicates', '-v2', 'delete', '--limit', '0.1')
+
+
+def test_rsu_integrity_check_command(rsu_schema, nanterre_classic_family):
+    output = get_output_of_command('rsu-integrity-check')
+    assert output == ''
+
+    jean = nanterre_classic_family['jean']
+    marie = nanterre_classic_family['marie']
+    lilou = nanterre_classic_family['lilou']
+    kevin = nanterre_classic_family['kevin']
+    with atomic():
+        tr = Transaction.get_transaction()
+    address2 = Entity.objects.create(
+        created=tr,
+        schema=rsu_schema[utils.ADRESSE_ENT],
+        content={
+            'at': '',
+            'streetnumber': '169',
+            'streetnumberext': '',
+            'streetname': 'RUE DU CHATEAU',
+            'ext1': '',
+            'ext2': '',
+            'streetmatriculation': '00169',
+            'zipcode': '75014',
+            'inseecode': '75014',
+            'city': 'PARIS',
+            'country': 'FRANCE',
+        })
+    address3 = Entity.objects.create(
+        created=tr,
+        schema=rsu_schema[utils.ADRESSE_ENT],
+        content={
+            'at': '',
+            'streetnumber': '170',
+            'streetnumberext': 'à côté',
+            'streetname': 'RUE DU CHATEAU',
+            'ext1': '',
+            'ext2': '',
+            'streetmatriculation': '00170',
+            'zipcode': '75014',
+            'inseecode': '75014',
+            'city': 'PARIS',
+            'country': 'FRANCE',
+        })
+    Relation.objects.create(
+        created=tr,
+        schema=rsu_schema[utils.HABITE_REL],
+        content={'principale': False},
+        left=jean,
+        right=address2)
+    Relation.objects.create(
+        created=tr,
+        schema=rsu_schema[utils.HABITE_REL],
+        content={'principale': False},
+        left=kevin,
+        right=address2)
+    Relation.objects.create(
+        created=tr,
+        schema=rsu_schema[utils.HABITE_REL],
+        content={'principale': False},
+        left=kevin,
+        right=address3)
+    Relation.objects.create(
+        created=tr,
+        schema=rsu_schema[utils.UNION_REL],
+        content={'status': 'pacs/mariage'},
+        left=jean,
+        right=marie)
+    Relation.objects.create(
+        created=tr,
+        schema=rsu_schema[utils.RESPONSABILITE_LEGALE_REL],
+        content={'status': 'parent'},
+        left=lilou,
+        right=kevin)
+    Relation.objects.filter(
+        schema__slug='habite',
+        left_id=marie.id
+    ).delete()
+    Relation.objects.filter(
+        schema__slug='habite',
+        left_id=lilou.id
+    ).delete()
+    output = get_output_of_command('rsu-integrity-check')
+
+    assert "le couple %s / %s est en union plusieurs fois" % (jean.id, marie.id) in output
+    assert "l'individu %s est en union avec plus d'une personne" % jean.id in output
+    assert "l'adulte %s a plus d'une adresse" % jean.id in output
+    assert "l'enfant %s a plus de deux parents" % kevin.id in output
+    assert "l'enfant %s a plus de deux adresses" % kevin.id in output
+    assert "l'adulte %s n'a pas d'adresse" % marie.id in output
+    assert "l'enfant %s n'a pas d'adresse" % lilou.id in output
+
+
+@mock.patch('zoo.zoo_nanterre.utils.psycopg2.connect')
+def test_rsu_load_dump_command(mocked_connect, nanterre_classic_family):
+    call_command('rsu-load-dump',  '-v3', 'pg_dsn=nanterre_rsu','authentic_fixture_path')
diff --git a/tests/test_nanterre.py b/tests/test_nanterre.py
index ebd1c3a..86f8c94 100644
--- a/tests/test_nanterre.py
+++ b/tests/test_nanterre.py
@@ -63,16 +63,26 @@ def test_person_search_api(app, db, rsu):
             assert individu['date_de_modification'] == individu['date_de_creation']
 
     birthdate = rsu[0].content['date_de_naissance']
     response = app.get(url + '?q=%s' % birthdate)
     assert response.json['err'] == 0
     assert len(response.json['data'])
     assert any(data['id'] == rsu[0].id for data in response.json['data'])
 
+    response = app.get(url + '?nom=%s' % rsu[0].content['nom_de_naissance'])
+    assert len(response.json['data']) > 0
+    response = app.get(url + '?date_de_naissance=%s' % rsu[0].content['date_de_naissance'])
+    assert len(response.json['data']) > 0
+    response = app.get(url + '?cle=42', status=200)
+    response = app.get(url + '?email=%s' % rsu[0].content['email'])
+    assert len(response.json['data']) > 0
+    response = app.get(url + '?NameID=my_a2_username', status=200)
+    response = app.get(url + '?cle_?=*', status=200)
+
 
 def test_create_individu(settings, transactional_db, app, app_noauth, rsu_schema):
 
     def get_reseau(identifier):
         reseau_url = reverse('rsu-api-reseau', kwargs={
             'identifier': identifier
         })
         response = app.get(reseau_url)
@@ -443,16 +453,21 @@ def test_create_individu(settings, transactional_db, app, app_noauth, rsu_schema
     # l'adresse du deuxième adulte a été supprimé
     assert Entity.objects.filter(id=second_adult_address.id).count() == 0
 
     # déclaration d'adresse principale pour le premier enfant
     declaration_adresse_principale_url = reverse('rsu-api-declaration-adresse-principale', kwargs={
         'identifier': enfant_id
     })
 
+    # requette get
+    response = app.get(declaration_adresse_principale_url)
+    assert response.content_type == 'application/json'
+    assert not response.json['err']
+
     data = get_reseau(enfant_id)
     assert len(data['adresses']) == 1
     assert data['adresses'][0]['principale'] is False
 
     # sur première adresse ok
     response = app.post_json(declaration_adresse_principale_url, params={
         'adresse_principale': 1,
     })
diff --git a/tests/test_nanterre_views.py b/tests/test_nanterre_views.py
new file mode 100644
index 0000000..d40c1f8
--- /dev/null
+++ b/tests/test_nanterre_views.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+
+from zoo.zoo_data.models import Entity, Transaction, Relation
+from zoo.zoo_nanterre import utils
+
+from django.db.transaction import atomic
+
+
+def test_rsu_demo_view(app, nanterre_classic_family):
+    resp = app.get('/rsu/demo/', status=200)
+    assert resp.html.find_all('h1')[1].text == u'Démo pour le RSU'
+    assert resp.html.find('a', {'href': 'search/'}).text == 'Recherche'
+
+
+def test_rsu_demo_search_view_limit(app, rsu):
+    resp = app.get('/rsu/demo/search/', status=200)
+    assert u'Aucune fiche trouvée' in resp.text
+
+    # largest
+    resp = app.get('/rsu/demo/search/', {'query': 'Léon', 'limit': 0.1}, status=200)
+    nb_res_1 = len(resp.html.find_all('div', {'class': 'individu'}))
+
+    # finer
+    resp = app.get('/rsu/demo/search/', {'query': 'Léon', 'limit': 0.2}, status=200)
+    nb_res_2 = len(resp.html.find_all('div', {'class': 'individu'}))
+    assert nb_res_1 > nb_res_2 > 0
+
+
+def test_rsu_demo_search_view(app, nanterre_classic_family):
+    url = '/rsu/demo/search/'
+
+    # name
+    resp = app.get(url, {'query': 'Lou', 'limit': 0.1}, status=200)
+    assert len(resp.html.find_all('div', {'class': 'individu'})) == 1
+
+    # email
+    resp = app.get(url, {'query': 'marie.darmette@example.com', 'limit': 0.9}, status=200)
+    assert len(resp.html.find_all('div', {'class': 'individu'})) == 2
+
+    # id
+    resp = app.get(url, {'query': '#1', 'limit': 0.9}, status=200)
+
+    # birthdate
+    resp = app.get(url, {'query': '01/01/2008', 'limit': 0.9}, status=200)
+    assert len(resp.html.find_all('div', {'class': 'individu'})) == 1
+    resp = app.get(url, {'query': '01/01/08', 'limit': 0.9}, status=200)
+    assert len(resp.html.find_all('div', {'class': 'individu'})) == 1
+    resp = app.get(url, {'query': '01/01/82', 'limit': 0.9}, status=200)
+    assert len(resp.html.find_all('div', {'class': 'individu'})) == 2
+    resp = app.get(url, {'query': '01/82', 'limit': 0.9}, status=200)
+    assert len(resp.html.find_all('div', {'class': 'individu'})) == 2
+    resp = app.get(url, {'query': '82', 'limit': 0.9}, status=200)
+    assert len(resp.html.find_all('div', {'class': 'individu'})) == 2
+    resp = app.get(url, {'query': '08 kev', 'limit': 0.1}, status=200)
+    assert len(resp.html.find_all('div', {'class': 'individu'})) == 1
+
+
+def test_rsu_import_control_view(app, nanterre_classic_family):
+    resp = app.get('/rsu/import_control/', status=200)
+    assert resp.html.find_all('h1')[1].text == u"Contrôle de l'import"
+    ints = [
+        ("Nombre d'individus", 4),
+        ("Nombre d'individus majeurs", 2),
+        ("Nombre d'individus mineurs", 2),
+        (u'Fédérations technocarte', 0),
+    ]
+    for i, (key, value) in enumerate(ints):
+        assert resp.html.find_all('dt')[i].text == key
+        assert int(resp.html.find_all('dd')[i].text) == value
+
+
+def test_adresse(app, rsu_schema, nanterre_classic_family):
+    with atomic():
+        tr = Transaction.get_transaction()
+    address = Entity.objects.create(
+        created=tr,
+        schema=rsu_schema[utils.ADRESSE_ENT],
+        content={
+            'at': '',
+            'streetnumber': '169',
+            'streetnumberext': '',
+            'streetname': 'RUE DU CHATEAU',
+            'ext1': '',
+            'ext2': '',
+            'streetmatriculation': '00169',
+            'zipcode': '75014',
+            'inseecode': '75014',
+            'city': 'PARIS',
+            'country': 'FRANCE',
+        })
+    assert utils.adresse(nanterre_classic_family['jean'])
+    Relation.objects.create(
+        created=tr,
+        schema=rsu_schema[utils.HABITE_REL],
+        content={'principale': False},
+        left=nanterre_classic_family['jean'],
+        right=address)
+    assert not utils.adresse(nanterre_classic_family['jean'])
+
+
+def test_add_age(app, nanterre_classic_family, freezer):
+    freezer.move_to('2008-01-02T00:00:00')
+    resp = app.get('/rsu/demo/search/', {'query': '2008-01-01', 'limit': 0.9}, status=200)
+    assert len(resp.html.find_all('div', {'class': 'individu'})) == 1
+    assert resp.html.find('span', {'class': 'individu-age'}).text == "moins d'un mois"
diff --git a/tests/test_zoo.py b/tests/test_zoo.py
index b727532..ab2e6ba 100644
--- a/tests/test_zoo.py
+++ b/tests/test_zoo.py
@@ -32,28 +32,32 @@ def person_schema(db):
                 'bad_type_property': {
                     'type': 'not string type'
                 }
             }
         })
     return schema
 
 
-def test_new_schema(db, person_schema):
-
-    Entity.objects.create(
+@pytest.fixture
+def person_entity(person_schema):
+    entity = Entity.objects.create(
         schema=person_schema,
         meta={},
         content={
             'first_name': 'Leon',
             'last_name': 'Blum',
             'address': {
                 'street': 'Rue du Château',
             }
         })
+    return entity
+
+
+def test_new_schema(person_schema, person_entity):
     qs = Entity.objects.content_search(person_schema, address__street='chateau')
     assert qs.count() == 1
 
 
 class ActionExample(object):
     def __init__(self, tries=2, content=None):
         self.content = content or {
             'tries': tries,
@@ -110,8 +114,36 @@ def test_demo_schemas_view(app, person_schema):
 def test_demo_schema_view(app, person_schema):
     resp = app.get('/demo/schemas/person/', {'page': 'not a number'}, status=200)
     assert resp.html.find('h1').text == 'person'
     assert resp.html.find('label', {'for': 'id_first_name'}).name
     assert resp.html.find('label', {'for': 'id_last_name'}).name
     assert resp.html.find('label', {'for': 'id_address__street'}).name
     assert not resp.html.find('label', {'for': 'bad_type_property'})
     assert resp.html.find('input', {'type': 'submit'})['value'] == 'Recherche'
+    assert u'Aucune entité trouvée' in resp.text
+
+
+def test_demo_search_on_schema_view(app, person_entity):
+    resp = app.get('/demo/schemas/person/', {'first_name': 'Leo'}, status=200)
+    assert u'Aucune entité trouvée' not in resp.text
+    assert 'Blum' in resp.html.pre.text
+
+    resp = app.get('/demo/schemas/person/', {'limit': 'not a float'}, status=200)
+    assert u'Aucune entité trouvée' in resp.text
+
+
+def test_admin_zoo_data_entity_inactive_view(app, admin, nanterre_classic_family):
+    url = '/admin/zoo_data/entity/inactive/'
+    app.set_user(admin.username)
+    resp = app.get(url, status=200)
+    assert [x.text for x in resp.html.find_all('h1')] == [
+        'Administration technique du RSU', u'Fiches inactives à supprimer']
+
+    resp = app.get(url+'?recompute', status=302)
+    assert resp.location == url
+
+    # unavailable post query
+    resp = app.post(url+'?delete', status=403)
+
+    resp = app.get(url+'?csv', status=200)
+    assert resp.content_type == 'text/csv'
+    assert resp.text == 'id,prenoms,nom_d_usage,nom_de_naissance,date_de_naissance,statut_legal,age\r\n'
-- 
2.25.1

