import datetime import json import logging import os import re from unittest import mock import pytest import requests from django.apps import apps from django.conf import settings from django.contrib.auth.models import User from django.db import connection from django.forms.widgets import Media from django.template.exceptions import TemplateDoesNotExist from django.test import override_settings from django.test.client import RequestFactory from django.test.utils import CaptureQueriesContext from django.urls import reverse from django.utils.encoding import force_bytes, force_text from django.utils.timezone import now from combo.data.library import get_cell_classes from combo.data.models import ( CellBase, ConfigJsonCell, FeedCell, JsonCell, LinkCell, LinkListCell, MenuCell, Page, TextCell, ValidityInfo, ) from combo.utils import NothingInCacheException from .test_manager import login pytestmark = pytest.mark.django_db @pytest.fixture def context(): ctx = {'request': RequestFactory().get('/')} ctx['request'].user = None ctx['request'].session = {} return ctx def mock_json_response(content, **kwargs): content = force_bytes(content) text = force_text(content) return mock.Mock(content=content, text=text, json=lambda: json.loads(text), **kwargs) def test_cell_reference(): page = Page() page.save() cell = TextCell() cell.page = page cell.text = 'foobar' cell.order = 0 cell.save() assert CellBase.get_cell(cell.get_reference()) == cell def test_media(): class TextCelleWithMedia(TextCell): class Media: js = ['coincoin.js'] class Meta: # to prevent error in Models metaclass as the current module is not # in a registered applicatoin proxy = True app_label = 'data' cells = [TextCelleWithMedia() for i in range(3)] assert ( force_text(sum((cell.media for cell in cells), Media())) == '' ) def test_additional_label(): page = Page() page.save() cell = TextCell() cell.page = page cell.text = '

foobar

' cell.order = 0 cell.save() assert cell.get_additional_label() == 'foobar' cell = TextCell() cell.page = page cell.text = '

%s

' % 'foo' * 30 cell.order = 0 cell.save() assert len(cell.get_additional_label()) < 100 assert '...' in cell.get_additional_label() def test_text_cell_variadic_url(): page = Page() page.save() cell = TextCell(page=page, order=0) cell.text = 'test test2 Example Site' cell.title = '' cell.save() assert cell.render(ctx).strip() == 'http://example.net/' cell.link_page = page cell.save() assert cell.render(ctx).strip() == 'example page' cell.title = 'altertitle' cell.save() assert cell.render(ctx).strip() == 'altertitle' cell.anchor = 'anchor' cell.save() assert cell.render(ctx).strip() == 'altertitle' cell.link_page = None cell.save() assert cell.render(ctx).strip() == 'altertitle' @pytest.mark.parametrize('bypass', [True, False]) def test_link_cell_validity(settings, bypass): page = Page.objects.create(title='example page', slug='example-page') cell = LinkCell.objects.create( page=page, title='Example Site', order=0, bypass_url_validity_check=bypass, ) # no link defined if bypass: assert ValidityInfo.objects.exists() is False else: validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_url_not_defined' assert validity_info.invalid_since is not None # no link defined but anchor set cell.anchor = 'plop' cell.save() assert ValidityInfo.objects.exists() is False # internal link - no check cell.link_page = page with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.return_value = mock_json cell.save() assert requests_get.call_args_list == [] assert ValidityInfo.objects.exists() is False # external link cell.link_page = None cell.url = 'http://example.net/' with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.return_value = mock_json cell.save() if bypass: assert ValidityInfo.objects.exists() is False else: validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_url_not_found' assert validity_info.invalid_since is not None with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=200) requests_get.return_value = mock_json cell.save() assert ValidityInfo.objects.exists() is False with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=500) requests_get.return_value = mock_json cell.save() assert ValidityInfo.objects.exists() is False with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=400) requests_get.return_value = mock_json cell.save() if bypass: assert ValidityInfo.objects.exists() is False else: validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_url_invalid' assert validity_info.invalid_since is not None # external link with a single variable cell.link_page = None cell.anchor = '' settings.TEMPLATE_VARS = {'var1': 'foo'} cell.url = 'http://foo?varone={{var1}}' with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=200) requests_get.return_value = mock_json cell.save() assert ValidityInfo.objects.exists() is False with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.return_value = mock_json cell.save() if bypass: assert ValidityInfo.objects.exists() is False else: assert ValidityInfo.objects.exists() is True # external link with two variables settings.TEMPLATE_VARS = {'var1': 'foo', 'var2': 'bar'} cell.url = 'http://foo?varone={{var1}}&vartwo={{var2}}' with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.return_value = mock_json cell.save() assert ValidityInfo.objects.exists() is False def test_link_list_cell(): page = Page.objects.create(title='example page', slug='example-page') cell = LinkListCell.objects.create(order=0, page=page) item = LinkCell.objects.create( page=page, placeholder=cell.link_placeholder, title='Example Site', url='http://example.net/', order=0, extra_css_class='foobar', ) ctx = {'page_cells': [item]} assert '' in cell.render( ctx ) item.title = '' item.extra_css_class = '' assert '' in cell.render( ctx ) item.link_page = page assert '' in cell.render(ctx) item.title = 'altertitle' assert '' in cell.render(ctx) item.anchor = 'anchor' assert '' in cell.render(ctx) item.link_page = None assert '' in cell.render( ctx ) item2 = LinkCell.objects.create( page=page, placeholder=cell.link_placeholder, title='Example Site', url='http://example.net/', order=1, ) ctx = {'page_cells': [item, item2]} assert 'class="add-more-items--button">+' not in cell.render(ctx) cell.limit = 1 assert 'class="add-more-items--button">+' in cell.render(ctx) def test_link_list_cell_validity(): page = Page.objects.create(title='example page', slug='example-page') cell = LinkListCell.objects.create(order=0, page=page) item = LinkCell.objects.create(page=page, placeholder=cell.link_placeholder, order=0) item.mark_as_valid() cell.check_validity() assert ValidityInfo.objects.exists() is False item.mark_as_invalid('foo_bar_reason') cell.check_validity() validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_link_invalid' assert validity_info.invalid_since is not None validity_info.invalid_since = now() - datetime.timedelta(days=2) validity_info.save() request = RequestFactory().get('/') assert cell.is_visible(request) # particular case: cell is visible def test_feed_cell_validity(context): page = Page.objects.create(title='example page', slug='example-page') cell = FeedCell.objects.create(page=page, placeholder='content', order=1) cell.get_cell_extra_context(context) validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_url_not_defined' assert validity_info.invalid_since is not None cell.url = 'http://example.net/' cell.save() with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.return_value = mock_json cell.get_cell_extra_context(context) validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_url_not_found' assert validity_info.invalid_since is not None with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=200, content='') requests_get.return_value = mock_json cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is False with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=500) requests_get.return_value = mock_json cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is False with mock.patch('combo.data.models.requests.get') as requests_get: mock_json = mock.Mock(status_code=400) requests_get.return_value = mock_json cell.get_cell_extra_context(context) validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_url_invalid' assert validity_info.invalid_since is not None # check validity info is cleared when the cell is saved cell.save() assert ValidityInfo.objects.exists() is False def test_manager_feed_cell_tabs(app, admin_user): page = Page.objects.create(title='example page', slug='example-page') FeedCell.objects.create(page=page, placeholder='content', order=1) app = login(app) resp = app.get('/manage/pages/%s/' % page.pk) assert not resp.pyquery('[data-tab-slug="general"] input[name$="title"]') assert resp.pyquery('[data-tab-slug="appearance"] input[name$="title"]') def test_menu_cell(): Page.objects.all().delete() parent = Page.objects.create( title='Page1', slug='page1', template_name='standard', exclude_from_navigation=False ) page = Page.objects.create( title='Page2', slug='page2', template_name='standard', parent=parent, exclude_from_navigation=False ) page = Page.objects.create( title='Page3', slug='page3', template_name='standard', parent=parent, public=False, exclude_from_navigation=False, ) cell = MenuCell.objects.create(root_page=parent, order=0, page=parent) request = RequestFactory().get('/page1/') request.user = None ctx = {'page': parent, 'request': request} assert 'menu-page2' in cell.render(ctx) assert 'menu-page3' not in cell.render(ctx) request.user = User(username='foo', email='foo@example.net') assert 'menu-page2' in cell.render(ctx) assert 'menu-page3' in cell.render(ctx) # check cell is deferred in skeletons if some pages are private ctx = {'page': parent, 'request': request, 'render_skeleton': True} with pytest.raises(NothingInCacheException): cell.render(ctx) # and that it's rendered directly if all pages are public page.public = True page.save() ctx = {'page': parent, 'request': request, 'render_skeleton': True} assert 'menu-page2' in cell.render(ctx) assert 'menu-page3' in cell.render(ctx) def test_variant_templates(): page = Page(title='example page', slug='example-page') page.save() cell = TextCell() cell.page = page cell.text = '

foobar

' cell.order = 0 cell.save() ctx = {} assert cell.render(ctx).strip() == '

foobar

' templates_settings = [settings.TEMPLATES[0].copy()] templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))] with override_settings(TEMPLATES=templates_settings): assert cell.render(ctx).strip() == '

foobar

' cell.slug = 'foobar' cell.save() assert cell.render(ctx).strip() == '

foobar

' assert cell.render(ctx).strip() == '

foobar

' def test_extra_template(): page = Page(title='example page', slug='example-page') page.save() cell = TextCell() cell.page = page cell.text = '

foobar

' cell.order = 0 cell.save() ctx = {} templates_settings = [settings.TEMPLATES[0].copy()] templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))] with override_settings( COMBO_CELL_TEMPLATES={ 'data_textcell': { 'extra': { 'label': 'Extra', 'template': 'combo/cells/foobar/text-cell.html', 'extra-css-classes': 'plop', } } }, TEMPLATES=templates_settings, ): cell.slug = '' cell.template_name = 'extra' cell.save() assert cell.render(ctx).strip() == '

foobar

' assert 'plop' in cell.css_class_names def mocked_request(*args, **kwargs): pass def test_json_cell(caplog): caplog.set_level(logging.DEBUG) page = Page(title='example page', slug='example-page') page.save() cell = JsonCell() cell.page = page cell.url = 'http://example.net/' cell.title = 'Example Site' cell.order = 0 cell.save() cell._json_content = None assert cell.default_template_name == 'combo/json-error-cell.html' cell._json_content = {} assert cell.default_template_name == 'combo/json-cell.html' cell._json_content = {'data': []} assert cell.default_template_name == 'combo/json-cell.html' cell._json_content = {'data': [{'url': 'xxx', 'text': 'xxx'}]} assert cell.default_template_name == 'combo/json-list-cell.html' cell._json_content = {'data': [{'foo': 'xxx', 'bar': 'xxx'}]} assert cell.default_template_name == 'combo/json-cell.html' with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': [{'url': 'xxx', 'text': 'xxx'}]} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) context = cell.get_cell_extra_context({}) assert context['json'] == data assert context['json_url'] == 'http://example.net/' assert context['json_status'] == 200 assert 'json_error' not in context requests_get.return_value = mock_json_response(content='', status_code=204) # 204 : No Content context = cell.get_cell_extra_context({}) assert context['json'] is None assert context['json_url'] == 'http://example.net/' assert context['json_status'] == 204 assert 'json_error' not in context requests_get.return_value = mock_json_response(content='not found', status_code=404, headers={}) context = cell.get_cell_extra_context({}) assert context['json'] is None assert context['json_url'] == 'http://example.net/' assert context['json_status'] == 404 assert 'json_error' not in context requests_get.return_value = mock_json_response( content=json.dumps(data), status_code=404, headers={'content-type': 'application/json'} ) context = cell.get_cell_extra_context({}) assert context['json'] is None assert context['json_url'] == 'http://example.net/' assert context['json_status'] == 404 assert context['json_error'] == data def mocked_requests_connection_error(*args, **kwargs): raise requests.ConnectionError('boom') requests_get.side_effect = mocked_requests_connection_error context = cell.get_cell_extra_context({}) assert context['json'] is None assert context['json_status'] == -1 assert context['json_url'] == 'http://example.net/' assert context['json_error'] == 'boom' assert isinstance(context['json_exception'], requests.ConnectionError) cell.url = '' # no URL -> no request, no data, no status requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) context = cell.get_cell_extra_context({}) assert context['json'] is None assert context['json_url'] == '' assert 'json_status' not in context assert 'json_error' not in context with pytest.raises(NothingInCacheException): cell.url = 'http://test3' cell.render({}) with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': [{'url': 'http://a.b', 'text': 'xxx'}]} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) cell.url = 'http://test4' result = cell.render({'synchronous': True}) assert 'http://a.b' in result assert requests_get.call_count == 1 cell.template_string = '{{json.data.0.text}}' result = cell.render({'synchronous': True}) assert result == 'xxx' with mock.patch('combo.utils.requests.get') as requests_get: requests_get.return_value = mock_json_response(content='garbage', status_code=200) cell.url = 'http://test5' result = cell.render({'synchronous': True}) assert result == '' context = cell.get_cell_extra_context({}) assert context['json'] is None assert context['json_url'] == 'http://test5' assert context['json_status'] == 200 assert context['json_error'] == 'invalid_json' # URL is a template, using [variables] cell.cache_duration = 10 data = {'data': [{'url': 'xxx', 'text': 'xxx'}]} cell.url = 'http://testuser?[foobar]' with mock.patch('combo.utils.requests.get') as requests_get: requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) context = cell.get_cell_extra_context({'foobar': 'barfoo'}) assert context['json'] == data assert context['json_url'] == 'http://testuser?barfoo' assert context['json_status'] == 200 assert requests_get.call_args[0][0] == 'http://testuser?barfoo' assert requests_get.call_args[1]['cache_duration'] == 10 assert requests_get.call_count == 1 caplog.clear() with mock.patch('combo.utils.requests.get') as requests_get: context = cell.get_cell_extra_context({}) # can't get URL, 'foobar' variable is missing assert context['json'] is None assert requests_get.call_count == 0 assert caplog.record_tuples == [ ( 'combo.data.models', logging.DEBUG, 'error in templated URL (http://testuser?[foobar]): unknown variable foobar', ) ] request = RequestFactory().get('/') request.user = User(username='foo', email='foo@example.net') cell.url = 'http://testuser?email=[user_email]' with mock.patch('combo.utils.requests.get') as requests_get: requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) context = cell.get_cell_extra_context({'request': request}) assert context['json'] == data assert context['json_url'] == 'http://testuser?email=foo%40example.net' assert requests_get.call_count == 1 assert requests_get.call_args[0][0] == 'http://testuser?email=foo%40example.net' assert requests_get.call_args[1]['cache_duration'] == 10 # URL is a template cell.url = 'http://testuser?{{ concerned_user|name_id }}' caplog.clear() with mock.patch('combo.utils.requests.get') as requests_get: context = cell.get_cell_extra_context({}) # can't get URL, variable is missing assert context['json'] is None assert requests_get.call_count == 0 assert caplog.record_tuples == [ ( 'combo.data.models', logging.DEBUG, 'error in templated URL (http://testuser?{{ concerned_user|name_id }}): name_id', ) ] cell.url = 'http://testuser?{% for %}' caplog.clear() with mock.patch('combo.utils.requests.get') as requests_get: context = cell.get_cell_extra_context({}) # can't get URL, variable is missing assert context['json'] is None assert requests_get.call_count == 0 assert len(caplog.records) == 1 assert caplog.record_tuples == [ ( 'combo.data.models', logging.ERROR, 'error in templated URL (http://testuser?{% for %}): syntax error', ) ] def test_json_cell_with_varnames(app): page = Page(title='example page', slug='index') page.save() cell = JsonCell() cell.page = page cell.title = 'Example Site' cell.order = 0 cell.varnames_str = 'var1, var2, ' cell.url = 'http://foo?varone=[var1]&vartwo=[var2]' cell.template_string = '/var1={{var1}}/var2={{var2}}/' cell.save() assert cell.varnames == ['var1', 'var2'] with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()} ) resp = app.get(url + '?var1=foo&var2=bar') # request query string is here assert requests_get.call_count == 1 assert requests_get.call_args[0][0] == 'http://foo?varone=foo&vartwo=bar' assert '/var1=foo/' in resp.text assert '/var2=bar/' in resp.text def test_json_cell_make_public_url(app): page = Page(title='example page', slug='index') page.save() cell = JsonCell() cell.page = page cell.title = 'Example Site' cell.order = 0 cell.url = 'https://example.net' # url from known_services cell.template_string = '{% make_public_url url="http://127.0.0.1:8999/" %}' cell.save() with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()} ) resp = app.get(url) assert '/api/wcs/file/' in resp.text assert 'http://127.0.0.1:8999' not in resp.text # url from unknown service cell.template_string = '{% make_public_url url="https://example.net" %}' cell.save() with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()} ) resp = app.get(url) assert 'https://example.net' in resp.text assert '/api/wcs/file/' not in resp.text # url with empty string cell.template_string = 'X{% make_public_url url="" %}Y' cell.save() with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()} ) resp = app.get(url) assert 'XY' in resp.text assert '/api/wcs/file/' not in resp.text # url as None cell.template_string = 'X{% make_public_url url=None %}Y' cell.save() with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()} ) resp = app.get(url) assert 'XNoneY' in resp.text assert '/api/wcs/file/' not in resp.text # url as "None" as string cell.template_string = '{% make_public_url url="None" %}' cell.save() with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()} ) resp = app.get(url) assert resp.text == 'None' def test_json_cell_validity(context): page = Page.objects.create(title='example page', slug='example-page') cell = JsonCell.objects.create( page=page, placeholder='content', order=1, varnames_str='var1, var2, ', url='http://foo?varone=[var1]&vartwo=[var2]', ) with mock.patch('combo.data.models.requests.get') as requests_get: cell.get_cell_extra_context(context) assert requests_get.call_args_list == [] # invalid context assert ValidityInfo.objects.exists() is False context['synchronous'] = True # to get fresh content cell.varnames_str = '' cell.url = 'http://foo' with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.side_effect = [mock_json] cell.get_cell_extra_context(context) validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_url_not_found' assert validity_info.invalid_since is not None with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is False with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=500) requests_get.return_value = mock_json cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is False with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=400) requests_get.return_value = mock_json cell.get_cell_extra_context(context) validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_url_invalid' assert validity_info.invalid_since is not None # url with a single variable context['var1'] = 'foo' cell.varnames_str = 'var1' cell.url = 'http://foo?varone={{var1}}' with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=200) requests_get.return_value = mock_json cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is False with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.side_effect = [mock_json] cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is True # check validity info is cleared when the cell is saved cell.save() assert ValidityInfo.objects.exists() is False # url with two variables context['var2'] = 'bar' cell.varnames_str = 'var1, var2' cell.url = 'http://foo?varone={{var1}}&vartwo={{var2}}' with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.side_effect = [mock_json] cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is False def test_manager_json_cell_tabs(app, admin_user): page = Page.objects.create(title='example page', slug='example-page') JsonCell.objects.create( page=page, placeholder='content', order=1, varnames_str='var1, var2, ', url='http://foo?varone=[var1]&vartwo=[var2]', ) app = login(app) resp = app.get('/manage/pages/%s/' % page.pk) assert not resp.pyquery('[data-tab-slug="general"] input[name$="title"]') assert resp.pyquery('[data-tab-slug="appearance"] input[name$="title"]') def test_config_json_cell(): page = Page(title='example page', slug='example-page') page.save() request = RequestFactory().get('/') with override_settings(JSON_CELL_TYPES={'foobar': {'name': 'Foobar', 'url': 'http://test/'}}): cell = ConfigJsonCell() cell.key = 'foobar' cell.parameters = {'blah': 'plop'} assert cell.get_label() == 'Foobar' assert cell.url == 'http://test/' assert cell.default_template_name == 'combo/json/foobar.html' assert cell.css_class_names.split() == ['config-json-cell', 'configjsoncell', 'foobar'] with mock.patch('combo.utils.requests.get') as requests_get: requests_get.return_value = mock_json_response( content=json.dumps({'hello': 'world'}), status_code=200 ) context = cell.get_cell_extra_context({'request': request}) assert context['json'] == {'hello': 'world'} assert context['json_url'] == 'http://test/' assert context['json_status'] == 200 assert context['parameters'] == {'blah': 'plop'} assert context['blah'] == 'plop' with override_settings( JSON_CELL_TYPES={'foobar': {'name': 'Foobar', 'url': 'http://test/', 'cache_duration': 10}} ): cell = ConfigJsonCell() cell.key = 'foobar' cell.parameters = {'blah': 'plop'} assert cell.get_label() == 'Foobar' assert cell.url == 'http://test/' assert cell.default_template_name == 'combo/json/foobar.html' assert cell.cache_duration == 10 def test_config_json_cell_with_varnames(app): page = Page(title='example page', slug='index') page.save() templates_settings = [settings.TEMPLATES[0].copy()] templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))] with override_settings( JSON_CELL_TYPES={ 'test-config-json-cell': { 'name': 'Foobar', 'url': 'http://foo?varone=[var1]&vartwo=[var2]', 'varnames': ['var1', 'var2'], } }, TEMPLATES=templates_settings, ): cell = ConfigJsonCell() cell.key = 'test-config-json-cell' cell.page = page cell.title = 'Example Site' cell.order = 0 cell.save() assert cell.varnames == ['var1', 'var2'] with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}, ) resp = app.get(url + '?var1=foo&var2=bar') # request query string is here assert requests_get.call_count == 1 assert requests_get.call_args[0][0] == 'http://foo?varone=foo&vartwo=bar' assert '/var1=foo/' in resp.text assert '/var2=bar/' in resp.text resp = app.get(url + '?var2=plop') # use var1 default value assert requests_get.call_count == 1 # no request, var1 is missing assert '/var1=/' in resp.text assert '/var2=plop/' in resp.text cell.parameters = {'var1': 'defaultvalue1'} cell.save() resp = app.get(url + '?var2=plop') # use var1 default value assert requests_get.call_count == 2 assert requests_get.call_args[0][0] == 'http://foo?varone=defaultvalue1&vartwo=plop' assert '/var1=defaultvalue1/' in resp.text assert '/var2=plop/' in resp.text def test_config_json_cell_with_param_in_url(app): page = Page(title='example page', slug='index') page.save() templates_settings = [settings.TEMPLATES[0].copy()] templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))] with override_settings( JSON_CELL_TYPES={ 'test-config-json-cell': { 'name': 'Foobar', 'url': 'http://foo?var=[identifier]', 'log_errors': False, 'timeout': 42, 'form': [{"varname": "identifier", "type": "string", "label": "Identifier"}], } }, TEMPLATES=templates_settings, ): cell = ConfigJsonCell() cell.key = 'test-config-json-cell' cell.parameters = {'identifier': 'plop'} cell.page = page cell.title = 'Example Site' cell.order = 0 cell.save() with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}, ) app.get(url) assert requests_get.call_count == 1 assert requests_get.call_args[0][0] == 'http://foo?var=plop' assert requests_get.call_args[-1]['log_errors'] is False assert requests_get.call_args[-1]['timeout'] == 42 def test_config_json_cell_with_template_string(settings, context): settings.JSON_CELL_TYPES = { 'test-config-json-cell': { 'name': 'Foobar', 'url': 'http://foo', 'form': [ {"varname": "identifier", "type": "string", "label": "Identifier"}, {"varname": "template_string", "type": "text", "label": "Template"}, ], }, } page = Page.objects.create(title='example page', slug='example-page') cell = ConfigJsonCell.objects.create( page=page, placeholder='content', order=1, key='test-config-json-cell', parameters={'identifier': 'plop', 'template_string': 'Foo Bar {{ identifier }}'}, ) with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) assert cell.render({}) == 'Foo Bar plop' settings.JSON_CELL_TYPES = { # template_string is not defined anymore 'test-config-json-cell': { 'name': 'Foobar', 'url': 'http://foo', 'form': [ {"varname": "identifier", "type": "string", "label": "Identifier"}, ], }, } cell = ConfigJsonCell.objects.get(pk=cell.pk) # reload cell with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) with pytest.raises(TemplateDoesNotExist): cell.render({}) def test_config_json_cell_with_global(settings, app): settings.JSON_CELL_TYPES = { 'test-config-json-cell': { 'name': 'Foobar', 'url': 'http://foo', 'make_global': 'new_global_context', 'form': [ {"varname": "template_string", "type": "text", "label": "Template"}, ], }, 'test-config-json-cell-2': { 'name': 'Foobar 2', 'url': 'http://foo', 'form': [ {"varname": "template_string", "type": "text", "label": "Template"}, ], }, } page = Page.objects.create(title='example page', slug='example-page') cell1 = ConfigJsonCell.objects.create( page=page, placeholder='content', order=1, key='test-config-json-cell', parameters={'template_string': 'Foo Bar'}, ) cell2 = ConfigJsonCell.objects.create( page=page, placeholder='content', order=2, key='test-config-json-cell-2', parameters={'template_string': 'Foo Bar'}, ) with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.pk, 'cell_reference': cell1.get_reference()}, ) resp = app.get(url) assert resp.context['new_global_context'] == data url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.pk, 'cell_reference': cell2.get_reference()}, ) resp = app.get(url) assert resp.context['new_global_context'] == data def test_config_json_cell_with_repeat(settings, app): settings.JSON_CELL_TYPES = { 'test-config-json-cell': { 'name': 'Foobar', 'url': 'http://foo', 'form': [ {"varname": "template_string", "type": "text", "label": "Template"}, ], }, 'test-config-json-cell-2': { 'name': 'Foobar 2', 'url': 'http://foo', 'repeat': '3', 'form': [ {"varname": "template_string", "type": "text", "label": "Template"}, ], }, } page = Page.objects.create(title='example page', slug='example-page') cell1 = ConfigJsonCell.objects.create( page=page, placeholder='content', order=1, key='test-config-json-cell', parameters={'template_string': 'Foo Bar'}, ) cell2 = ConfigJsonCell.objects.create( page=page, placeholder='content', order=2, key='test-config-json-cell-2', parameters={'template_string': 'Foo Bar {{ repeat_index }}'}, ) with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) resp = app.get(page.get_online_url()) assert len(resp.context['cells']) == 4 assert resp.context['cells'][0].pk == cell1.pk extra_ctx = re.findall(r'data-extra-context="(.*)"', resp.text) cell_url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.pk, 'cell_reference': cell2.get_reference()}, ) for i in range(0, 3): assert resp.context['cells'][i + 1].pk == cell2.pk assert resp.context['cells'][i + 1].repeat_index == i cell_resp = app.get(cell_url + '?ctx=' + extra_ctx[i + 1]) assert cell_resp.context['repeat_index'] == i assert cell_resp.text == 'Foo Bar %s' % i # repeat: not an int settings.JSON_CELL_TYPES['test-config-json-cell-2']['repeat'] = 'foo' with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) resp = app.get(page.get_online_url()) assert len(resp.context['cells']) == 2 # repeat: template error settings.JSON_CELL_TYPES['test-config-json-cell-2']['repeat'] = '{% foo %}' with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) resp = app.get(page.get_online_url()) assert len(resp.context['cells']) == 2 def test_config_json_cell_validity(settings, context): settings.JSON_CELL_TYPES = { 'test-config-json-cell': { 'name': 'Foobar', 'url': 'http://foo?varone=[var1]&vartwo=[var2]', 'varnames': ['var1', 'var2'], }, } templates_settings = [settings.TEMPLATES[0].copy()] templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))] settings.TEMPLATES = templates_settings page = Page.objects.create(title='example page', slug='example-page') cell = ConfigJsonCell.objects.create( page=page, placeholder='content', order=1, key='test-config-json-cell', parameters={'identifier': 'plop'}, ) assert cell.varnames == ['var1', 'var2'] with mock.patch('combo.data.models.requests.get') as requests_get: cell.get_cell_extra_context(context) assert requests_get.call_args_list == [] # invalid context assert ValidityInfo.objects.exists() is False context['synchronous'] = True # to get fresh content settings.JSON_CELL_TYPES['test-config-json-cell']['url'] = 'http://foo' settings.JSON_CELL_TYPES['test-config-json-cell']['varnames'] = [] with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.side_effect = [mock_json] cell.get_cell_extra_context(context) validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_url_not_found' assert validity_info.invalid_since is not None with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': []} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is False with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=500) requests_get.return_value = mock_json cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is False with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=400) requests_get.return_value = mock_json cell.get_cell_extra_context(context) validity_info = ValidityInfo.objects.latest('pk') assert validity_info.invalid_reason_code == 'data_url_invalid' assert validity_info.invalid_since is not None # url with a single variable context['var1'] = 'foo' settings.JSON_CELL_TYPES['test-config-json-cell']['varnames'] = ['var1'] settings.JSON_CELL_TYPES['test-config-json-cell']['url'] = 'http://foo?varone={{var1}}' with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=200) requests_get.return_value = mock_json cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is False with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.side_effect = [mock_json] cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is True # url with two variables context['var2'] = 'bar' settings.JSON_CELL_TYPES['test-config-json-cell']['varnames'] = ['var1', 'var2'] settings.JSON_CELL_TYPES['test-config-json-cell']['url'] = 'http://foo?varone={{var1}}&vartwo={{var2}}' with mock.patch('combo.utils.requests.get') as requests_get: mock_json = mock.Mock(status_code=404) requests_get.side_effect = [mock_json] cell.get_cell_extra_context(context) assert ValidityInfo.objects.exists() is False def test_json_force_async(): cell = JsonCell() cell.url = 'http://example.net/test-force-async' cell.template_string = '{{json.hello}}' cell.force_async = True with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get: requests_get.return_value = mock_json_response( content=json.dumps({'hello': 'world'}), status_code=200 ) with pytest.raises(NothingInCacheException): cell.render({}) assert cell.render({'synchronous': True}) == 'world' # check force async is effective with pytest.raises(NothingInCacheException): cell.render({}) # disable force_async cell.force_async = False assert cell.render({}) == 'world' cell = JsonCell() cell.url = 'http://example.net/test-force-async-2' cell.template_string = '{{json.hello}}' cell.force_async = False with mock.patch('combo.utils.requests_wrapper.RequestsSession.request') as requests_get: requests_get.return_value = mock_json_response( content=json.dumps({'hello': 'world2'}), status_code=200 ) # raise if nothing in cache with pytest.raises(NothingInCacheException): cell.render({}) # force stuff in cache assert cell.render({'synchronous': True}) == 'world2' # rerun with stuff in cache assert cell.render({}) == 'world2' def test_config_json_cell_additional_url(app): page = Page(title='example page', slug='index') page.save() templates_settings = [settings.TEMPLATES[0].copy()] templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))] with override_settings( JSON_CELL_TYPES={ 'test-config-json-cell-2': { 'name': 'Foobar', 'url': 'http://foo', 'additional-data': [ {'key': 'plop', 'url': 'http://bar', 'log_errors': False, 'timeout': 42}, ], } }, TEMPLATES=templates_settings, ): cell = ConfigJsonCell() cell.key = 'test-config-json-cell-2' cell.page = page cell.title = 'Example Site' cell.order = 0 cell.save() with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': 'toto'} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}, ) resp = app.get(url) assert resp.text.strip() == '/var1=toto/var2=toto/' assert len(requests_get.mock_calls) == 2 assert requests_get.mock_calls[0][1][0] == 'http://foo' assert requests_get.mock_calls[0][-1]['log_errors'] is True assert requests_get.mock_calls[0][-1]['timeout'] is None assert requests_get.mock_calls[1][1][0] == 'http://bar' assert requests_get.mock_calls[1][-1]['log_errors'] is False assert requests_get.mock_calls[1][-1]['timeout'] == 42 with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': 'toto'} requests_get.return_value = mock_json_response( content=json.dumps(data), status_code=404, headers={'content-type': 'application/json'} ) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}, ) resp = app.get(url) assert resp.text.strip() == '/var1=/var2=/' assert len(requests_get.mock_calls) == 2 assert requests_get.mock_calls[0][1][0] == 'http://foo' assert requests_get.mock_calls[1][1][0] == 'http://bar' context = cell.get_cell_extra_context({}) assert context['json'] is None assert context['json_url'] == 'http://foo' assert context['json_status'] == 404 assert context['json_error'] == data assert context['plop'] is None assert context['plop_url'] == 'http://bar' assert context['plop_status'] == 404 assert context['plop_error'] == data with mock.patch('combo.utils.requests.get') as requests_get: data = {'data': 'toto'} requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) context = cell.get_cell_extra_context({}) assert context['json'] == data assert context['json_url'] == 'http://foo' assert context['json_status'] == 200 assert 'json_error' not in context assert context['plop'] == data assert context['plop_url'] == 'http://bar' assert context['plop_status'] == 200 assert 'plop_error' not in context # additional-data url depends on others results, with Django-syntax URL with override_settings( JSON_CELL_TYPES={ 'test-config-json-cell-2': { 'name': 'Foobar', 'url': 'http://foo', 'additional-data': [ {'key': 'plop', 'url': 'http://{{json.data}}', 'log_errors': False, 'timeout': 42}, { 'key': 'plop2', 'url': '{% if plop %}http://{{json.data}}/{{plop.data}}{% endif %}', 'log_errors': False, 'timeout': 10, }, ], } }, TEMPLATES=templates_settings, ): cell = ConfigJsonCell() cell.key = 'test-config-json-cell-2' cell.page = page cell.title = 'Example Site' cell.order = 0 cell.save() data = {'data': 'bar'} with mock.patch('combo.utils.requests.get') as requests_get: requests_get.return_value = mock_json_response(content=json.dumps(data), status_code=200) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}, ) resp = app.get(url) assert resp.text.strip() == '/var1=bar/var2=bar/' assert len(requests_get.mock_calls) == 3 assert requests_get.mock_calls[0][1][0] == 'http://foo' assert requests_get.mock_calls[0][-1]['log_errors'] is True assert requests_get.mock_calls[0][-1]['timeout'] is None assert requests_get.mock_calls[1][1][0] == 'http://bar' assert requests_get.mock_calls[1][-1]['log_errors'] is False assert requests_get.mock_calls[1][-1]['timeout'] == 42 assert requests_get.mock_calls[2][1][0] == 'http://bar/bar' assert requests_get.mock_calls[2][-1]['log_errors'] is False assert requests_get.mock_calls[2][-1]['timeout'] == 10 context = cell.get_cell_extra_context({}) assert context['json'] == data assert context['json_url'] == 'http://foo' assert context['json_status'] == 200 assert context['plop'] == data assert context['plop_url'] == 'http://bar' assert context['plop_status'] == 200 assert context['plop2'] == data assert context['plop2_url'] == 'http://bar/bar' assert context['plop2_status'] == 200 with mock.patch('combo.utils.requests.get') as requests_get: requests_get.return_value = mock_json_response( content=json.dumps(data), status_code=404, headers={'content-type': 'application/json'} ) url = reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.id, 'cell_reference': cell.get_reference()}, ) resp = app.get(url) assert resp.text.strip() == '/var1=/var2=/' # can not create plop and plop2 url: only one request for "json" assert len(requests_get.mock_calls) == 2 assert requests_get.mock_calls[0][1][0] == 'http://foo' context = cell.get_cell_extra_context({}) assert context['json'] is None assert context['json_url'] == 'http://foo' assert context['json_status'] == 404 assert context['json_error'] == data assert context['plop'] is None assert context['plop_url'] == 'http://' assert context['plop_status'] == 404 assert context['plop_error'] == data # plop2 url is empty, no request: None value, no status assert context['plop2'] is None assert context['plop2_url'] == '' assert 'plop2_status' not in context assert 'plop2_error' not in context def test_config_json_invalid_key_cell(): page = Page(title='example page', slug='example-page') page.save() with override_settings(JSON_CELL_TYPES={'foobar': {'name': 'Foobar', 'url': 'http://test/'}}): cell = ConfigJsonCell() cell.key = 'foobar' cell.parameters = {'blah': 'plop'} cell.page = page cell.order = 0 cell.save() assert len(page.get_cells()) == 1 assert len(page.get_cells()) == 0 def test_page_cell_placeholder_restricted_visibility(app, admin_user): page = Page(title='Test', slug='test', template_name='standard') page.save() json_cell = JsonCell(page=page, placeholder='content', order=0, url='http://example.com') json_cell.template_string = '{% placeholder "foobar" name="Foobar" %}' json_cell.save() TextCell( page=page, placeholder='foobar', text='

Public text

', order=0, restricted_to_unlogged=True ).save() TextCell(page=page, placeholder='foobar', text='

Private text

', order=1, public=False).save() resp = app.get( reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.pk, 'cell_reference': json_cell.get_reference()}, ) ) assert "

Public text

" in resp.text assert "

Private text

" not in resp.text app = login(app) resp = app.get( reverse( 'combo-public-ajax-page-cell', kwargs={'page_pk': page.pk, 'cell_reference': json_cell.get_reference()}, ) ) assert "

Public text

" not in resp.text assert "

Private text

" in resp.text def test_related_cell_types_tracking(): page = Page(title='example page', slug='example-page') page.save() assert page.related_cells['cell_types'] == [] TextCell(page=page, placeholder='content', order=0, text='hello').save() assert Page.objects.get(id=page.id).related_cells['cell_types'] == ['data_textcell'] TextCell(page=page, placeholder='content', order=1, text='hello').save() assert Page.objects.get(id=page.id).related_cells['cell_types'] == ['data_textcell'] LinkCell(page=page, placeholder='content', order=0, title='Test', url='http://example.net').save() assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_textcell', 'data_linkcell'} with CaptureQueriesContext(connection) as ctx: assert len(CellBase.get_cells(page=Page.objects.get(id=page.id))) == 3 assert len(ctx.captured_queries) == 1 + 2 TextCell.objects.get(order=1).delete() assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_textcell', 'data_linkcell'} TextCell.objects.get(order=0).delete() assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_linkcell'} with CaptureQueriesContext(connection) as ctx: assert len(CellBase.get_cells(page=Page.objects.get(id=page.id))) == 1 assert len(ctx.captured_queries) == 1 + 1 # remove tracker, check it is rebuilt correctly page.related_cells = {} page.save() with CaptureQueriesContext(connection) as ctx: assert len(CellBase.get_cells(page=Page.objects.get(id=page.id))) == 1 assert len(ctx.captured_queries) == len(get_cell_classes()) Page.objects.get(id=page.id).get_cells() TextCell(page=page, placeholder='content', order=0, text='hello').save() assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_textcell', 'data_linkcell'} def test_link_list_cell_duplicate(): page = Page.objects.create(title='xxx', slug='new', template_name='standard') cell = LinkListCell.objects.create(order=0, page=page) item = LinkCell.objects.create( page=page, placeholder=cell.link_placeholder, title='Example Site', url='http://example.net/', link_page=page, order=1, ) new_cell = cell.duplicate() assert LinkCell.objects.count() == 2 assert len(new_cell.get_items()) == 1 new_item = new_cell.get_items()[0] assert new_item.page == page assert new_item.placeholder == new_cell.link_placeholder assert new_item.pk != item.pk assert new_item.title == item.title assert new_item.url == item.url assert new_item.link_page == item.link_page def test_cell_is_visible(): request = RequestFactory().get('/') page = Page.objects.create() cell = TextCell.objects.create(page=page, order=0) assert cell.is_visible(request) is True # invalid cell since just now validity_info = ValidityInfo.objects.create(content_object=cell) validity_info.invalid_reason_code = 'FOO' validity_info.invalid_since = now() validity_info.save() assert cell.is_visible(request) is True # invalid cell since two days validity_info.invalid_since = now() - datetime.timedelta(days=2) validity_info.save() assert cell.is_visible(request) is False def test_cell_invalidity_marker(): page = Page.objects.create() cell = TextCell.objects.create(page=page, order=0) cell.mark_as_invalid('foo_bar_reason') validity_info = ValidityInfo.objects.latest('pk') old_reason = validity_info.invalid_reason_code old_date = validity_info.invalid_since cell.mark_as_invalid('another_foo_bar_reason', force=False) validity_info.refresh_from_db() assert old_reason == validity_info.invalid_reason_code assert old_date == validity_info.invalid_since cell.mark_as_invalid('another_foo_bar_reason') validity_info.refresh_from_db() assert validity_info.invalid_reason_code == 'another_foo_bar_reason' assert old_date < validity_info.invalid_since cell.mark_as_valid() assert ValidityInfo.objects.exists() is False def test_hourly(): appconfig = apps.get_app_config('data') page = Page.objects.create(title='xxx', slug='test_current_forms_cell_render', template_name='standard') cell_classes = [c for c in appconfig.get_models() if c in get_cell_classes()] for klass in cell_classes: klass.objects.create(page=page, placeholder='content', order=0) for klass in cell_classes: if klass in [LinkCell, LinkListCell]: with mock.patch('combo.data.models.%s.check_validity' % klass.__name__) as check_validity: appconfig.hourly() assert check_validity.call_args_list == [mock.call()] else: assert hasattr(klass, 'check_validity') is False def test_cell_assets(settings, app, admin_user): page = Page.objects.create(title='xxx', slug='test_cell_assets', template_name='standard') text_cell = TextCell.objects.create(page=page, order=0, slug='foo') list_cell = LinkListCell.objects.create(page=page, order=2, slug='bar') item = LinkCell.objects.create( page=page, placeholder=list_cell.link_placeholder, title='Example Site', link_page=page, order=1, ) app = login(app) settings.COMBO_CELL_ASSET_SLOTS = {} resp = app.get('/manage/assets/') assert 'have any asset yet.' in resp.text # only text cells are defined for assets settings.COMBO_CELL_ASSET_SLOTS = {'data_textcell': {'picture': {'prefix': 'Picture'}}} resp = app.get('/manage/assets/') assert 'Picture — %s' % text_cell.get_label_for_asset() in resp.text assert 'Picture — %s' % list_cell.get_label_for_asset() not in resp.text assert 'Picture — %s' % item.get_label_for_asset() not in resp.text # text and list link cells are defined for assets settings.COMBO_CELL_ASSET_SLOTS = { 'data_textcell': {'picture': {'prefix': 'Picture'}}, 'data_linklistcell': {'picture': {'prefix': 'Picture'}}, 'data_linkcell': {'picture': {'prefix': 'Picture', 'suffix': 'test'}}, } resp = app.get('/manage/assets/') assert 'Picture — %s' % text_cell.get_label_for_asset() in resp.text assert 'Picture — %s' % list_cell.get_label_for_asset() in resp.text # but items are excluded assert item.get_slug_for_asset() is None # slug for asset is always None for items assert 'Picture — %s' % item.get_label_for_asset() not in resp.text # test slugs link_cell = LinkCell.objects.create( page=page, url='http://example.net/', order=1, ) resp = app.get('/manage/assets/') assert link_cell.get_slug_for_asset() == '' assert 'Picture — %s' % link_cell.get_label_for_asset() not in resp.text link_cell.slug = 'foo' link_cell.save() resp = app.get('/manage/assets/') assert link_cell.get_slug_for_asset() == 'foo' assert 'Picture — %s (test)' % link_cell.get_label_for_asset() in resp.text link_cell.slug = '' link_cell.url = '' link_cell.link_page = page link_cell.save() resp = app.get('/manage/assets/') assert link_cell.get_slug_for_asset() == 'test_cell_assets' assert 'Picture — %s (test)' % link_cell.get_label_for_asset() in resp.text