Projet

Général

Profil

0001-family-clean-app-remove-unused-models-56015.patch

Lauréline Guérin, 27 septembre 2021 14:42

Télécharger (27,2 ko)

Voir les différences:

Subject: [PATCH] family: clean app, remove unused models (#56015)

 combo/apps/family/README                      |   8 --
 combo/apps/family/__init__.py                 |  31 ----
 combo/apps/family/forms.py                    |  27 ----
 combo/apps/family/migrations/0008_clean.py    |  14 ++
 combo/apps/family/models.py                   |  34 +----
 combo/apps/family/templates/family/infos.html |  66 ---------
 .../family/templates/family/link_form.html    |  27 ----
 .../apps/family/templates/family/person.html  |  36 -----
 .../templates/family/unlink_confirm.html      |  11 --
 combo/apps/family/urls.py                     |  24 ----
 combo/apps/family/utils.py                    |  61 --------
 combo/apps/family/views.py                    |  72 ----------
 tests/settings.py                             |   2 -
 tests/test_manager.py                         |  25 +---
 tests/test_public.py                          | 135 ------------------
 tests/test_search.py                          |   2 +-
 16 files changed, 20 insertions(+), 555 deletions(-)
 delete mode 100644 combo/apps/family/README
 delete mode 100644 combo/apps/family/forms.py
 create mode 100644 combo/apps/family/migrations/0008_clean.py
 delete mode 100644 combo/apps/family/templates/family/infos.html
 delete mode 100644 combo/apps/family/templates/family/link_form.html
 delete mode 100644 combo/apps/family/templates/family/person.html
 delete mode 100644 combo/apps/family/templates/family/unlink_confirm.html
 delete mode 100644 combo/apps/family/urls.py
 delete mode 100644 combo/apps/family/utils.py
 delete mode 100644 combo/apps/family/views.py
combo/apps/family/README
1
Combo family cell
2
=================
3

  
4
To be visible, this cell needs a 'passerelle' entry in settings.KNOWN_SERVICES
5
(see ../wcs/README) and a FAMILY_SERVICE attribute in settings:
6

  
7
  FAMILY_SERVICE = {'root': '/connector/instance/'}
8

  
combo/apps/family/__init__.py
1
# combo - content management system
2
# Copyright (C) 2015  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import django.apps
18
from django.utils.translation import ugettext_lazy as _
19

  
20

  
21
class AppConfig(django.apps.AppConfig):
22
    name = 'combo.apps.family'
23
    verbose_name = _('Family')
24

  
25
    def get_before_urls(self):
26
        from . import urls
27

  
28
        return urls.urlpatterns
29

  
30

  
31
default_app_config = 'combo.apps.family.AppConfig'
combo/apps/family/forms.py
1
# combo - content management system
2
# Copyright (C) 2015  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django import forms
18
from django.utils.translation import ugettext_lazy as _
19

  
20

  
21
class FamilyLinkForm(forms.Form):
22
    family_id = forms.CharField(
23
        label=_('Family identifier'), widget=forms.TextInput(attrs={'required': 'required'})
24
    )
25
    family_code = forms.CharField(
26
        label=_('Secret code'), widget=forms.PasswordInput(attrs={'required': 'required'})
27
    )
combo/apps/family/migrations/0008_clean.py
1
from django.db import migrations
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('family', '0007_weekly_agenda_user_template'),
8
    ]
9

  
10
    operations = [
11
        migrations.DeleteModel(
12
            name='FamilyInfosCell',
13
        ),
14
    ]
combo/apps/family/models.py
19 19
from django.utils.translation import ugettext_lazy as _
20 20

  
21 21
from combo.data.library import register_cell_class
22
from combo.data.models import CellBase, JsonCellBase
23

  
24
from .utils import get_family, is_family_enabled
25

  
26

  
27
@register_cell_class
28
class FamilyInfosCell(CellBase):
29
    default_template_name = 'family/infos.html'
30
    user_dependant = True
31

  
32
    class Meta:
33
        verbose_name = _('Family Information Cell')
34

  
35
    class Media:
36
        js = (
37
            'xstatic/jquery-ui.min.js',
38
            'js/gadjo.js',
39
        )
40

  
41
    @classmethod
42
    def is_enabled(cls):
43
        return is_family_enabled()
44

  
45
    def get_cell_extra_context(self, context):
46
        if context.get('placeholder_search_mode'):
47
            return {}
48
        user = self.get_concerned_user(context)
49
        if not user or user.is_anonymous:
50
            return {}
51
        response = get_family(user=user, raise_if_not_cached=not (context.get('synchronous')))
52
        if response.status_code == 200:
53
            return {'family': response.json()}
54
        return {'error': _('An error occured while retrieving family details.')}
22
from combo.data.models import JsonCellBase
55 23

  
56 24

  
57 25
@register_cell_class
combo/apps/family/templates/family/infos.html
1
{% load i18n %}
2
{% block cell-content %}
3
<h2>{% trans "Informations related to your family" %}</h2>
4

  
5
{% trans "Top content for unlinked users" as top_content %}
6
{% placeholder "family_unlinked_user" name=top_content render=False %}
7

  
8
<div>
9
  {% if not user.is_authenticated %}
10
  {% placeholder "family_unlinked_user" %}
11
  {% elif error %}
12
  <p>{{error}}</p>
13
  {% elif not family.data %}
14
  {% url 'family-link' as link_url %}
15
  {% placeholder "family_unlinked_user" %}
16
  <div class="family-link">
17
    <a href="{{ link_url }}" rel="popup">{% trans "Link to your family" %}</a>
18
  </div>
19
  {% else %}
20
  <div class="family_unlink">
21
    <a href="{% url 'family-unlink' %}" rel="popup">{% trans "Unlink" %}</a>
22
  </div>
23
  {% with data=family.data %}
24
  {% if data.address or data.quotient %}
25
  <div class="family-data">
26
    {% if data.address %}
27
    <div class="address">
28
      <h4>{% trans "Address" %}</h4>
29
      <p>{{ data.address }}</p>
30
    </div>
31
    {% endif %}
32
    {% if data.quotient %}
33
    <p class="family-quotient">
34
      <span class="label">{% trans "Family quotient:" %}</span>
35
      <span class="value">{{ data.quotient }}</span>
36
    </p>
37
    {% endif %}
38
  </div>
39
  {% endif %}
40
  <div class="family_members">
41
  {% if data.adults %}
42
  <div class="family_adults">
43
    <h3>{% trans "Adults" %}</h3>
44
    {% for adult in data.adults %}
45
    <div>
46
      {% include 'family/person.html' with person=adult %}
47
    </div>
48
    {% endfor %}
49
  </div>
50
  {% endif %}
51

  
52
  {% if data.children %}
53
  <div class="family_children">
54
    <h3>{% trans "Children" %}</h3>
55
    {% for child in data.children %}
56
    <div>
57
      {% include 'family/person.html' with person=child %}
58
    </div>
59
    {% endfor %}
60
  </div>
61
  {% endif %}
62
  </div>
63
  {% endwith %}
64
  {% endif %}
65
</div>
66
{% endblock %}
combo/apps/family/templates/family/link_form.html
1
{% load i18n %}
2
<div id="content">
3
  <div id="appbar">
4
    <h2>{% trans "Link to a family" %}</h2>
5
  </div>
6
  <form method="post" class="quixote" action='{% url "family-link" %}'>
7
    {% csrf_token %}
8
    {% if family_link_intro_text %}
9
      {{family_link_intro_text|linebreaks}}
10
    {% else %}
11
      <p>
12
      {% blocktrans %}
13
      Enter your family credentials below.
14
      {% endblocktrans %}
15
      </p>
16
    {% endif %}
17
    {% for field in form %}
18
    <div class="widget grid-1-2">
19
      <div class="title"><label>{{field.label_tag}}</label></div>
20
      <div class="content">{{field}}</div>
21
    </div>
22
    {% endfor %}
23
    <div class="buttons">
24
      <button class="submit-button">{% trans "Submit" %}</button>
25
    </div>
26
  </form>
27
</div>
combo/apps/family/templates/family/person.html
1
{% load i18n %}
2
<p class="name"><span>{{ person.first_name }} {{ person.last_name }}</span></p>
3
{% if person.birthdate %}
4
<p class="birthdate">
5
  <span class="label">{% trans "Born on" %}</span>
6
  <span class="value">{{ person.birthdate }}</span>
7
</p>
8
{% endif %}
9

  
10
<div class="address">
11
  <h4>{% trans "Address" %}</h4>
12
  <p>{{ person.address }}</p>
13
</div>
14

  
15
{% if person.phone or person.cellphone or person.email %}
16
<div class="contact">
17
{% if person.phone %}
18
  <p class="phone">
19
    <span class="label">{% trans "Phone:" %}</span>
20
    <span class="value">{{ person.phone }}</span>
21
  </p>
22
{% endif %}
23
{% if person.cellphone %}
24
  <p class="cellphone">
25
    <span class="label">{% trans "Cellphone:" %}</span>
26
    <span class="value">{{ person.cellphone }}</span>
27
  </p>
28
{% endif %}
29
{% if person.email %}
30
  <p class="email">
31
    <span class="label">{% trans "Email:" %}</span>
32
    <span class="value"{{ person.email }}</span>
33
  </p>
34
{% endif %}
35
</div>
36
{% endif %}
combo/apps/family/templates/family/unlink_confirm.html
1
{% load i18n %}
2

  
3
<div id="content">
4
<form method="post">
5
  {% csrf_token %}
6
  {% trans "Unlink your personal account from this family account?" %}
7
  <div class="buttons">
8
    <button class="delete-button">{% trans 'Unlink' %}</button>
9
  </div>
10
</form>
11
</div>
combo/apps/family/urls.py
1
# combo - content management system
2
# Copyright (C) 2015  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.conf.urls import url
18

  
19
from .views import FamilyLinkView, FamilyUnlinkView
20

  
21
urlpatterns = [
22
    url(r'^family/link/?$', FamilyLinkView.as_view(), name='family-link'),
23
    url(r'^family/unlink/?$', FamilyUnlinkView.as_view(), name='family-unlink'),
24
]
combo/apps/family/utils.py
1
# combo - content management system
2
# Copyright (C) 2015-2016  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.conf import settings
18

  
19
from combo.utils import requests
20

  
21

  
22
def get_passerelle_service():
23
    try:
24
        return [x for x in settings.KNOWN_SERVICES['passerelle'].values() if not x.get('secondary')][0]
25
    except (AttributeError, IndexError, KeyError):
26
        return None
27

  
28

  
29
def is_family_enabled():
30
    return get_passerelle_service() and hasattr(settings, 'FAMILY_SERVICE')
31

  
32

  
33
def remote_service(endpoint, **kwargs):
34
    path = settings.FAMILY_SERVICE.get('root') + endpoint
35
    return requests.get(
36
        path, remote_service=get_passerelle_service(), headers={'accept': 'application/json'}, **kwargs
37
    )
38

  
39

  
40
def get_family(**kwargs):
41
    endpoint = 'family/'
42
    return remote_service(endpoint, **kwargs)
43

  
44

  
45
def link_family(user, family_id, family_code):
46
    endpoint = 'family/link/'
47
    kwargs = {
48
        'user': user,
49
        'invalidate_cache': True,
50
        'params': {
51
            'login': family_id,
52
            'password': family_code,
53
        },
54
    }
55
    return remote_service(endpoint, **kwargs)
56

  
57

  
58
def unlink_family(user):
59
    endpoint = 'family/unlink/'
60
    kwargs = {'user': user, 'invalidate_cache': True}
61
    return remote_service(endpoint, **kwargs)
combo/apps/family/views.py
1
# combo - content management system
2
# Copyright (C) 2015  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17

  
18
from django.contrib import messages
19
from django.http import HttpResponseRedirect
20
from django.utils.translation import ugettext_lazy as _
21
from django.views.generic import FormView, TemplateView
22

  
23
from .forms import FamilyLinkForm
24
from .utils import link_family, unlink_family
25

  
26
ERROR_MESSAGES = {
27
    100: _('Wrong credentials: make sure you typed them correctly.'),
28
    101: _('This family account is blocked.'),
29
}
30

  
31

  
32
class FamilyLinkView(FormView):
33
    form_class = FamilyLinkForm
34
    template_name = 'family/link_form.html'
35

  
36
    def get_success_url(self):
37
        if self.request.META.get('HTTP_REFERER'):
38
            return self.request.META['HTTP_REFERER']
39
        # return to current location if no referer
40
        return '.'
41

  
42
    def form_valid(self, form):
43
        response = link_family(
44
            self.request.user, form.cleaned_data['family_id'], form.cleaned_data['family_code']
45
        )
46

  
47
        if not response.ok or response.json().get('err'):
48
            error_code = response.json().get('err')
49
            error_message = ERROR_MESSAGES.get(
50
                error_code, _('Failed to link to family. Please check your credentials and retry later.')
51
            )
52
            messages.error(self.request, error_message)
53
        else:
54
            messages.info(self.request, _('Your account was successfully linked.'))
55
        return super().form_valid(form)
56

  
57

  
58
class FamilyUnlinkView(TemplateView):
59
    template_name = 'family/unlink_confirm.html'
60

  
61
    def post(self, request, *args, **kwargs):
62
        response = unlink_family(request.user)
63

  
64
        if not response.ok or response.json().get('err'):
65
            messages.error(request, _('An error occured when unlinking.'))
66
        else:
67
            messages.info(request, _('Your account was successfully unlinked.'))
68

  
69
        if self.request.META.get('HTTP_REFERER'):
70
            return HttpResponseRedirect(self.request.META['HTTP_REFERER'])
71
        # fallback to homepage if no referer
72
        return HttpResponseRedirect('/')
tests/settings.py
79 79
    MIGRATION_MODULES = DisableMigrations()
80 80

  
81 81

  
82
FAMILY_SERVICE = {'root': '/'}
83

  
84 82
BOOKING_CALENDAR_CELL_ENABLED = True
85 83
LEGACY_CHART_CELL_ENABLED = True
86 84

  
tests/test_manager.py
27 27
from webtest import Upload
28 28

  
29 29
from combo.apps.assets.models import Asset
30
from combo.apps.family.models import FamilyInfosCell
31 30
from combo.apps.maps.models import MapLayer
32 31
from combo.apps.search.models import SearchCell
33 32
from combo.data.forms import LinkCellForm
......
878 877
    resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
879 878
    with CaptureQueriesContext(connection) as ctx:
880 879
        resp = resp.form.submit()
881
        assert len(ctx.captured_queries) in [307, 308]
880
        assert len(ctx.captured_queries) in [302, 303]
882 881
    assert Page.objects.count() == 4
883 882
    assert PageSnapshot.objects.all().count() == 4
884 883

  
......
889 888
    resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
890 889
    with CaptureQueriesContext(connection) as ctx:
891 890
        resp = resp.form.submit()
892
        assert len(ctx.captured_queries) == 276
891
        assert len(ctx.captured_queries) == 272
893 892
    assert set(Page.objects.get(slug='one').related_cells['cell_types']) == {'data_textcell', 'data_linkcell'}
894 893
    assert Page.objects.count() == 4
895 894
    assert LinkCell.objects.count() == 2
......
2139 2138
    assert re.findall('<h2>(.*)</h2>', resp.text) == ['Page - One', 'Content', 'JSON Prototype / Foobar']
2140 2139

  
2141 2140

  
2142
def test_page_familycell_placeholder(app, admin_user):
2143
    page = Page(title='My family', slug='my-family', template_name='standard')
2144
    page.save()
2145
    cell = FamilyInfosCell(page=page, placeholder='content', order=0)
2146
    cell.save()
2147

  
2148
    app = login(app)
2149
    resp = app.get('/manage/pages/%s/' % page.id)
2150
    assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content', "family_unlinked_user"]
2151
    assert re.findall('<h2>(.*)</h2>', resp.text) == [
2152
        'Page - My family',
2153
        'Content',
2154
        'Family Information Cell / Top content for unlinked users',
2155
    ]
2156

  
2157

  
2158 2141
def test_page_discover_placeholder_with_error_cells(app, admin_user):
2159 2142
    page = Page(title='One', slug='one', template_name='standard')
2160 2143
    page.save()
......
2220 2203

  
2221 2204
    with CaptureQueriesContext(connection) as ctx:
2222 2205
        resp2 = resp.click('view', index=1)
2223
        assert len(ctx.captured_queries) == 71
2206
        assert len(ctx.captured_queries) == 70
2224 2207
    assert Page.snapshots.latest('pk').related_cells == {'cell_types': ['data_textcell']}
2225 2208
    assert resp2.text.index('Hello world') < resp2.text.index('Foobar3')
2226 2209

  
......
2281 2264
    resp = resp.click('restore', index=6)
2282 2265
    with CaptureQueriesContext(connection) as ctx:
2283 2266
        resp = resp.form.submit().follow()
2284
        assert len(ctx.captured_queries) == 146
2267
        assert len(ctx.captured_queries) == 144
2285 2268

  
2286 2269
    resp2 = resp.click('See online')
2287 2270
    assert resp2.text.index('Foobar1') < resp2.text.index('Foobar2') < resp2.text.index('Foobar3')
tests/test_public.py
23 23
import requests
24 24

  
25 25
from combo.apps.assets.models import Asset
26
from combo.apps.family.models import FamilyInfosCell
27 26
from combo.data.models import (
28 27
    ConfigJsonCell,
29 28
    FeedCell,
......
833 832
                assert resp2.content == b'a,b\n1,2'
834 833

  
835 834

  
836
def test_familyinfos_cell_with_placeholders(app, admin_user):
837
    Page.objects.all().delete()
838
    page = Page(title='Family', slug='index', template_name='standard')
839
    page.save()
840
    family_cell = FamilyInfosCell(page=page, placeholder='content', order=0)
841
    family_cell.save()
842
    TextCell(
843
        page=page,
844
        placeholder='family_unlinked_user',
845
        text='<p>Hello anonymous user</p>',
846
        order=0,
847
        restricted_to_unlogged=True,
848
        public=True,
849
    ).save()
850
    TextCell(
851
        page=page,
852
        placeholder='family_unlinked_user',
853
        text='<p>You are not linked</p>',
854
        order=1,
855
        public=False,
856
        restricted_to_unlogged=False,
857
    ).save()
858

  
859
    with override_settings(FAMILY_SERVICE={'root': '/family/'}):
860
        with mock.patch('combo.utils.requests.send') as requests_send:
861
            resp = app.get('/')
862
            assert "<p>Hello anonymous user</p>" in resp.text
863
            assert "<p>You are not linked</p>" not in resp.text
864

  
865
            app = login(app)
866
            data = {'err': 0, 'data': None}
867
            requests_send.return_value = mock.Mock(
868
                json=lambda: data, content=json.dumps(data), status_code=200
869
            )
870
            # make sure no data are loaded
871
            resp = app.get('/')
872
            assert resp.html.body.find('div', {'class': 'familyinfoscell'}).text.strip() == 'Loading...'
873
            # put data in cache
874
            resp = app.get(
875
                reverse(
876
                    'combo-public-ajax-page-cell',
877
                    kwargs={'page_pk': page.pk, 'cell_reference': family_cell.get_reference()},
878
                )
879
            )
880
            resp = app.get('/')
881
            assert "<p>Hello anonymous user</p>" not in resp.text
882
            assert "<p>You are not linked</p>" in resp.text
883

  
884

  
885
@mock.patch('combo.utils.requests.get')
886
def test_familycell_link(requests_get, app, settings, admin_user):
887
    settings.FAMILY_SERVICE = {'root': '/family/'}
888
    Page.objects.all().delete()
889
    page = Page(title='Family', slug='index', template_name='standard')
890
    page.save()
891
    family_cell = FamilyInfosCell(page=page, placeholder='content', order=0)
892
    family_cell.save()
893
    TextCell(
894
        page=page,
895
        placeholder='family_unlinked_user',
896
        text='<p>Hello anonymous user</p>',
897
        order=0,
898
        restricted_to_unlogged=True,
899
        public=True,
900
    ).save()
901
    TextCell(
902
        page=page,
903
        placeholder='family_unlinked_user',
904
        text='<p>You are not linked</p>',
905
        order=1,
906
        public=False,
907
        restricted_to_unlogged=False,
908
    ).save()
909
    app = login(app)
910
    # when linking fails
911
    requests_get.return_value = mock.Mock(ok=False, json=lambda: {'err': 102})
912
    resp = app.get('/family/link/')
913
    resp.form['family_id'] = '123'
914
    resp.form['family_code'] = 'l33t'
915
    resp = resp.form.submit().follow()
916
    messages = resp.context['messages']
917
    assert len(messages._loaded_messages) == 1
918
    assert (
919
        messages._loaded_messages[0].message
920
        == 'Failed to link to family. Please check your credentials and retry later.'
921
    )
922
    requests_get.return_value = mock.Mock(ok=True, json=lambda: {})
923
    resp.form['family_id'] = '123'
924
    resp.form['family_code'] = 'l33t'
925
    resp = resp.form.submit().follow()
926
    messages = resp.context['messages']
927
    assert len(messages._loaded_messages) == 2
928
    assert messages._loaded_messages[1].message == 'Your account was successfully linked.'
929

  
930

  
931
@mock.patch('combo.utils.requests.get')
932
def test_familycell_unlink(requests_get, app, settings, admin_user):
933
    settings.FAMILY_SERVICE = {'root': '/family/'}
934
    Page.objects.all().delete()
935
    page = Page(title='Family', slug='index', template_name='standard')
936
    page.save()
937
    family_cell = FamilyInfosCell(page=page, placeholder='content', order=0)
938
    family_cell.save()
939
    TextCell(
940
        page=page,
941
        placeholder='family_unlinked_user',
942
        text='<p>Hello anonymous user</p>',
943
        order=0,
944
        restricted_to_unlogged=True,
945
        public=True,
946
    ).save()
947
    TextCell(
948
        page=page,
949
        placeholder='family_unlinked_user',
950
        text='<p>You are not linked</p>',
951
        order=1,
952
        public=False,
953
        restricted_to_unlogged=False,
954
    ).save()
955
    app = login(app)
956
    requests_get.return_value = mock.Mock(ok=False, json=lambda: {'err': 102})
957
    resp = app.get('/family/unlink/')
958
    resp = resp.form.submit().follow()
959
    messages = resp.context['messages']
960
    assert len(messages._loaded_messages) == 1
961
    assert messages._loaded_messages[0].message == 'An error occured when unlinking.'
962
    requests_get.return_value = mock.Mock(ok=True, json=lambda: {})
963
    resp = app.get('/family/unlink/')
964
    resp = resp.form.submit().follow()
965
    messages = resp.context['messages']
966
    assert len(messages._loaded_messages) == 2
967
    assert messages._loaded_messages[1].message == 'Your account was successfully unlinked.'
968

  
969

  
970 835
def test_synchronous_placeholder(app):
971 836
    page = Page(title='foo', slug='foo', template_name='standard', order=0)
972 837
    page.save()
tests/test_search.py
1371 1371
    assert IndexedCell.objects.count() == 50
1372 1372
    with CaptureQueriesContext(connection) as ctx:
1373 1373
        index_site()
1374
        assert len(ctx.captured_queries) == 223
1374
        assert len(ctx.captured_queries) == 222
1375 1375

  
1376 1376
    SearchCell.objects.create(
1377 1377
        page=page, placeholder='content', order=0, _search_services={'data': ['search1']}
1378
-