applification: bundle malformé (#88132) #246

Merged
lguerin merged 2 commits from wip/88132-application-errors into main 2024-03-25 10:05:32 +01:00
6 changed files with 312 additions and 133 deletions

View File

@ -32,6 +32,7 @@ from rest_framework.response import Response
from combo.apps.export_import.models import Application, ApplicationAsyncJob, ApplicationElement
from combo.apps.wcs.utils import WCSError
from combo.data.models import Page, PageSnapshot
from combo.utils.api import APIErrorBadRequest
from combo.utils.misc import is_portal_agent
klasses = {klass.application_component_type: klass for klass in [Page]}
@ -39,7 +40,7 @@ klasses['roles'] = Group
class Index(GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
permission_classes = (permissions.IsAdminUser,)
def get(self, request, *args, **kwargs):
if is_portal_agent():
@ -127,7 +128,7 @@ def get_component_bundle_entry(request, component, order):
class ListComponents(GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
permission_classes = (permissions.IsAdminUser,)
def get(self, request, *args, **kwargs):
klass = klasses[kwargs['component_type']]
@ -145,7 +146,7 @@ list_components = ListComponents.as_view()
class ExportComponent(GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
permission_classes = (permissions.IsAdminUser,)
def get(self, request, uuid, *args, **kwargs):
serialisation = get_object_or_404(Page, uuid=uuid).get_serialized_page()
@ -156,7 +157,7 @@ export_component = ExportComponent.as_view()
class ComponentDependencies(GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
permission_classes = (permissions.IsAdminUser,)
def get(self, request, uuid, *args, **kwargs):
klass = klasses[kwargs['component_type']]
@ -199,94 +200,100 @@ def component_redirect(request, component_type, uuid):
class BundleCheck(GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
permission_classes = (permissions.IsAdminUser,)
def put(self, request, *args, **kwargs):
tar_io = io.BytesIO(request.read())
page_type = 'portal-agent-pages' if is_portal_agent() else 'pages'
with tarfile.open(fileobj=tar_io) as tar:
manifest = json.loads(tar.extractfile('manifest.json').read().decode())
application_slug = manifest.get('slug')
application_version = manifest.get('version_number')
if not application_slug or not application_version:
return Response({'data': {}})
differences = []
unknown_elements = []
no_history_elements = []
legacy_elements = []
content_type = ContentType.objects.get_for_model(Page)
for element in manifest.get('elements'):
if element.get('type') != page_type:
continue
try:
with tarfile.open(fileobj=tar_io) as tar:
try:
page = Page.objects.get(uuid=element['slug'])
except Page.DoesNotExist:
unknown_elements.append(
{
'type': element['type'],
'slug': element['slug'],
}
manifest = json.loads(tar.extractfile('manifest.json').read().decode())
except KeyError:
raise APIErrorBadRequest(_('Invalid tar file, missing manifest'))

Ok ici l’ensemble de l’API est déjà écrite alors ça ne changera pas, mais je pense qu’on aurait raisonnablement pu envoyer autre chose que des http 200 sur ces erreurs de requête d’applification ratées (sans pour autant que ça prête à confusion entre l’applicatif et le technique).

Ok ici l’ensemble de l’API est déjà écrite alors ça ne changera pas, mais je pense qu’on aurait raisonnablement pu envoyer autre chose que des http 200 sur ces erreurs de requête d’applification ratées (sans pour autant que ça prête à confusion entre l’applicatif et le technique).

bien vu, hobo ne gère pas les 200 avec err!=0, j'ai changé ça

bien vu, hobo ne gère pas les 200 avec err!=0, j'ai changé ça
application_slug = manifest.get('slug')
application_version = manifest.get('version_number')
if not application_slug or not application_version:
return Response({'data': {}})
differences = []
unknown_elements = []
no_history_elements = []
legacy_elements = []
content_type = ContentType.objects.get_for_model(Page)
for element in manifest.get('elements'):
if element.get('type') != page_type:
continue
try:
page = Page.objects.get(uuid=element['slug'])

Pareil ici, ce n’est pas la PR qui introduit ça et de toute façon l’API est déjà écrite, mais pour un relecteur peu éclairé comme moi cette confusion entre uuid et slug n’aide pas trop :/

Pareil ici, ce n’est pas la PR qui introduit ça et de toute façon l’API est déjà écrite, mais pour un relecteur peu éclairé comme moi cette confusion entre uuid et slug n’aide pas trop :/

yes, contrainte hobo

yes, contrainte hobo
except Page.DoesNotExist:
unknown_elements.append(
{
'type': element['type'],
'slug': element['slug'],
}
)
continue
elements_qs = ApplicationElement.objects.filter(
application__slug=application_slug,
content_type=content_type,
object_id=page.pk,
)
continue
elements_qs = ApplicationElement.objects.filter(
application__slug=application_slug,
content_type=content_type,
object_id=page.pk,
)
if not elements_qs.exists():
# object exists, but not linked to the application
legacy_elements.append(
{
'type': element['type'],
'slug': element['slug'],
# information needed here, Relation objects may not exist yet in hobo
'text': page.title,
'url': request.build_absolute_uri(
reverse(
'api-export-import-component-redirect',
kwargs={
'uuid': str(page.uuid),
'component_type': page.application_component_type,
},
)
),
}
)
continue
snapshot_for_app = (
PageSnapshot.objects.filter(
page=page,
application_slug=application_slug,
application_version=application_version,
)
.order_by('timestamp')
.last()
)
if not snapshot_for_app:
# no snapshot for this bundle
no_history_elements.append(
{
'type': element['type'],
'slug': element['slug'],
}
)
continue
last_snapshot = PageSnapshot.objects.filter(page=page).latest('timestamp')
if snapshot_for_app.pk != last_snapshot.pk:
differences.append(
{
'type': element['type'],
'slug': element['slug'],
'url': '%s%s?version1=%s&version2=%s'
% (
request.build_absolute_uri('/')[:-1],
reverse('combo-manager-page-history-compare', args=[page.pk]),
snapshot_for_app.pk,
last_snapshot.pk,
),
}
if not elements_qs.exists():
# object exists, but not linked to the application
legacy_elements.append(
{
'type': element['type'],
'slug': element['slug'],
# information needed here, Relation objects may not exist yet in hobo
'text': page.title,
'url': request.build_absolute_uri(
reverse(
'api-export-import-component-redirect',
kwargs={
'uuid': str(page.uuid),
'component_type': page.application_component_type,
},
)
),
}
)
continue
snapshot_for_app = (
PageSnapshot.objects.filter(
page=page,
application_slug=application_slug,
application_version=application_version,
)
.order_by('timestamp')
.last()
)
if not snapshot_for_app:
# no snapshot for this bundle
no_history_elements.append(
{
'type': element['type'],
'slug': element['slug'],
}
)
continue
last_snapshot = PageSnapshot.objects.filter(page=page).latest('timestamp')
if snapshot_for_app.pk != last_snapshot.pk:
differences.append(
{
'type': element['type'],
'slug': element['slug'],
'url': '%s%s?version1=%s&version2=%s'
% (
request.build_absolute_uri('/')[:-1],
reverse('combo-manager-page-history-compare', args=[page.pk]),
snapshot_for_app.pk,
last_snapshot.pk,
),
}
)
except tarfile.TarError:
raise APIErrorBadRequest(_('Invalid tar file'))
return Response(
{
@ -304,14 +311,20 @@ bundle_check = BundleCheck.as_view()
class BundleImport(GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
permission_classes = (permissions.IsAdminUser,)
action = 'import_bundle'
def put(self, request, *args, **kwargs):
tar_io = io.BytesIO(request.read())
with tarfile.open(fileobj=tar_io) as tar:
manifest = json.loads(tar.extractfile('manifest.json').read().decode())
application_slug = manifest.get('slug')
try:
with tarfile.open(fileobj=tar_io) as tar:
try:
manifest = json.loads(tar.extractfile('manifest.json').read().decode())
except KeyError:
raise APIErrorBadRequest(_('Invalid tar file, missing manifest'))
application_slug = manifest.get('slug')
except tarfile.TarError:
raise APIErrorBadRequest(_('Invalid tar file'))
job = ApplicationAsyncJob(
action=self.action,
)
@ -325,7 +338,6 @@ bundle_import = BundleImport.as_view()
class BundleDeclare(BundleImport):
permission_classes = (permissions.IsAuthenticated,)
action = 'declare_bundle'
@ -333,7 +345,7 @@ bundle_declare = BundleDeclare.as_view()
class BundleUnlink(GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
permission_classes = (permissions.IsAdminUser,)
def post(self, request, *args, **kwargs):
if request.POST.get('application'):

View File

@ -33,6 +33,10 @@ from django.utils.translation import gettext_lazy as _
from combo.utils.misc import is_portal_agent
class BundleKeyError(Exception):
pass
class Application(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=100, unique=True)
@ -59,10 +63,10 @@ class Application(models.Model):
slug=manifest.get('slug'), defaults={'editable': editable}
)
application.name = manifest.get('application')
application.description = manifest.get('description')
application.documentation_url = manifest.get('documentation_url')
application.description = manifest.get('description') or ''
application.documentation_url = manifest.get('documentation_url') or ''
application.version_number = manifest.get('version_number') or 'unknown'
application.version_notes = manifest.get('version_notes')
application.version_notes = manifest.get('version_notes') or ''
if not editable:
application.editable = editable
application.visible = manifest.get('visible', True)
@ -171,8 +175,6 @@ class ApplicationAsyncJob(models.Model):
last_update_timestamp = models.DateTimeField(auto_now=True)
completion_timestamp = models.DateTimeField(default=None, null=True)
raise_exception = True
def run(self, spool=False):
if 'uwsgi' in sys.modules and spool:
from combo.utils.spooler import run_async_job
@ -183,11 +185,12 @@ class ApplicationAsyncJob(models.Model):
self.save()
try:
getattr(self, self.action)()
except BundleKeyError as e:
self.status = 'failed'
self.exception = str(e)
except Exception:
self.status = 'failed'
self.exception = traceback.format_exc()
if self.raise_exception:
raise
finally:
if self.status == 'running':
self.status = 'completed'
@ -212,9 +215,17 @@ class ApplicationAsyncJob(models.Model):
for element in manifest.get('elements'):
if element.get('type') != page_type:
continue
pages.append(
json.loads(tar.extractfile(f'{page_type}/{element["slug"]}').read().decode()).get('data')
)
try:
pages.append(
json.loads(tar.extractfile(f'{page_type}/{element["slug"]}').read().decode()).get(
'data'
)
)
except KeyError:
raise BundleKeyError(
'Invalid tar file, missing component %s/%s.' % (page_type, element['slug'])
)
# init cache of application elements, from manifest
self.application_elements = set()
# install pages

View File

@ -391,6 +391,8 @@ CHART_FILTERS_CELL_ENABLED = True
# default country code for phonenumbers' user phone parsing
DEFAULT_COUNTRY_CODE = '33'
REST_FRAMEWORK = {'EXCEPTION_HANDLER': 'combo.utils.api.exception_handler'}
def debug_show_toolbar(request):
from debug_toolbar.middleware import show_toolbar as dt_show_toolbar # pylint: disable=import-error

60
combo/utils/api.py Normal file
View File

@ -0,0 +1,60 @@
# combo - content management system
# Copyright (C) 2017-2024 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import gettext_lazy as _
from rest_framework.response import Response as DRFResponse
from rest_framework.views import exception_handler as DRF_exception_handler
class Response(DRFResponse):
def __init__(self, data=None, *args, **kwargs):
# add reason for compatibility (https://dev.entrouvert.org/issues/24025)
if data is not None and 'err_class' in data:
data['reason'] = data['err_class']
super().__init__(data=data, *args, **kwargs)
class APIError(Exception):
http_status = 200
def __init__(self, message, *args, err=1, err_class=None, errors=None):
self.err_desc = _(message) % args
self.err = err
self.err_class = err_class or message % args
self.errors = errors
super().__init__(self.err_desc)
def to_response(self):
data = {
'err': self.err,
'err_class': self.err_class,
'err_desc': self.err_desc,
}
if self.errors:
data['errors'] = self.errors
return Response(data, status=self.http_status)
class APIErrorBadRequest(APIError):
http_status = 400
def exception_handler(exc, context):
if isinstance(exc, APIError):
return exc.to_response()
return DRF_exception_handler(exc, context)

View File

@ -125,7 +125,8 @@ REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
]
],
'EXCEPTION_HANDLER': 'combo.utils.api.exception_handler',
}
DEFAULT_COUNTRY_CODE = '33'

View File

@ -28,8 +28,8 @@ from .wcs.utils import MockedRequestResponse, mocked_requests_send
pytestmark = pytest.mark.django_db
def test_object_types(settings, app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_object_types(settings, app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
resp = app.get('/api/export-import/')
assert resp.json == {
'data': [
@ -71,8 +71,8 @@ def test_object_types(settings, app, john_doe):
}
def test_list(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_list(app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
Page.objects.all().delete()
page = Page(title='Test', slug='test', template_name='standard')
page.save()
@ -115,8 +115,8 @@ def test_list(app, john_doe):
}
def test_export_page(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_export_page(app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
Page.objects.all().delete()
page = Page(title='Test', slug='test', template_name='standard')
page.save()
@ -126,8 +126,8 @@ def test_export_page(app, john_doe):
app.get('/api/export-import/pages/%s/' % uuid.uuid4(), status=404)
def test_export_page_with_role(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_export_page_with_role(app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
group = Group(name='plop')
group.save()
Page.objects.all().delete()
@ -138,8 +138,8 @@ def test_export_page_with_role(app, john_doe):
assert resp.json['data']['fields']['groups'] == ['plop']
def test_page_dependencies(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_page_dependencies(app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
page = Page.objects.create(title='Test', slug='test', template_name='standard')
resp = app.get(f'/api/export-import/pages/{page.uuid}/dependencies/')
assert resp.json == {'data': [], 'err': 0}
@ -147,8 +147,8 @@ def test_page_dependencies(app, john_doe):
app.get('/api/export-import/pages/%s/dependencies/' % uuid.uuid4(), status=404)
def test_page_dependencies_groups(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_page_dependencies_groups(app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
group1 = Group(name='plop1')
group1.save()
group2 = Group(name='plop2')
@ -172,8 +172,8 @@ def test_page_dependencies_groups(app, john_doe):
}
def test_page_dependencies_children(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_page_dependencies_children(app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
page = Page(title='Test', slug='test', template_name='standard')
page.save()
page2 = Page(title='Child', slug='child', template_name='standard', parent=page)
@ -198,8 +198,7 @@ def test_page_dependencies_children(app, john_doe):
}
def test_page_redirect(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_page_redirect(app):
page = Page(title='Test', slug='test', template_name='standard')
page.save()
redirect_url = f'/api/export-import/pages/{page.uuid}/redirect/'
@ -257,7 +256,7 @@ def create_bundle(elements, *args, **kwargs):
@pytest.fixture
def bundle(app, john_doe):
def bundle(app, admin_user):
page, dummy = Page.objects.get_or_create(
slug='test', defaults={'title': 'Test Page', 'template_name': 'standard'}
)
@ -270,8 +269,8 @@ def bundle(app, john_doe):
)
def test_bundle_import(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_bundle_import(app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
bundles = []
for version_number in ['42.0', '42.1']:
@ -347,9 +346,48 @@ def test_bundle_import(app, john_doe):
assert last_snapshot.application_slug == 'test'
assert last_snapshot.application_version == '42.1'
# bad file format
resp = app.put('/api/export-import/bundle-import/', b'garbage', status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'Invalid tar file'
def test_bundle_import_pages_position(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
# missing manifest
tar_io = io.BytesIO()
with tarfile.open(mode='w', fileobj=tar_io) as tar:
foo_fd = io.BytesIO(json.dumps({'foo': 'bar'}, indent=2).encode())
tarinfo = tarfile.TarInfo('foo.json')
tarinfo.size = len(foo_fd.getvalue())
tar.addfile(tarinfo, fileobj=foo_fd)
resp = app.put('/api/export-import/bundle-import/', tar_io.getvalue(), status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'Invalid tar file, missing manifest'
# missing component
tar_io = io.BytesIO()
with tarfile.open(mode='w', fileobj=tar_io) as tar:
manifest_json = {
'application': 'Test',
'slug': 'test',
'elements': [{'type': 'pages', 'slug': str(uuid.uuid4()), 'name': 'foo'}],
}
manifest_fd = io.BytesIO(json.dumps(manifest_json, indent=2).encode())
tarinfo = tarfile.TarInfo('manifest.json')
tarinfo.size = len(manifest_fd.getvalue())
tar.addfile(tarinfo, fileobj=manifest_fd)
resp = app.put('/api/export-import/bundle-import/', tar_io.getvalue())
job_url = resp.json['url']
resp = app.get(job_url)
assert resp.json['data']['status'] == 'failed'
job = ApplicationAsyncJob.objects.get(uuid=job_url.split('/')[-3])
assert job.status == 'failed'
assert (
job.exception
== 'Invalid tar file, missing component pages/%s.' % manifest_json['elements'][0]['slug']
)
def test_bundle_import_pages_position(app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
page1 = Page.objects.create(
slug='test1',
title='Test Page 1',
@ -891,8 +929,8 @@ def test_bundle_import_pages_position(app, john_doe):
assert Page.objects.get(uuid=page2.uuid).parent is None
def test_bundle_declare(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_bundle_declare(app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
page, dummy = Page.objects.get_or_create(
slug='test', defaults={'title': 'Test Page', 'template_name': 'standard'}
@ -952,9 +990,48 @@ def test_bundle_declare(app, john_doe):
assert application.visible is True
assert ApplicationElement.objects.count() == 0
# bad file format
resp = app.put('/api/export-import/bundle-declare/', b'garbage', status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'Invalid tar file'
def test_bundle_unlink(app, john_doe, bundle):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
# missing manifest
tar_io = io.BytesIO()
with tarfile.open(mode='w', fileobj=tar_io) as tar:
foo_fd = io.BytesIO(json.dumps({'foo': 'bar'}, indent=2).encode())
tarinfo = tarfile.TarInfo('foo.json')
tarinfo.size = len(foo_fd.getvalue())
tar.addfile(tarinfo, fileobj=foo_fd)
resp = app.put('/api/export-import/bundle-declare/', tar_io.getvalue(), status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'Invalid tar file, missing manifest'
# missing component
tar_io = io.BytesIO()
with tarfile.open(mode='w', fileobj=tar_io) as tar:
manifest_json = {
'application': 'Test',
'slug': 'test',
'elements': [{'type': 'pages', 'slug': str(uuid.uuid4()), 'name': 'foo'}],
}
manifest_fd = io.BytesIO(json.dumps(manifest_json, indent=2).encode())
tarinfo = tarfile.TarInfo('manifest.json')
tarinfo.size = len(manifest_fd.getvalue())
tar.addfile(tarinfo, fileobj=manifest_fd)
resp = app.put('/api/export-import/bundle-declare/', tar_io.getvalue())
job_url = resp.json['url']
resp = app.get(job_url)
assert resp.json['data']['status'] == 'failed'
job = ApplicationAsyncJob.objects.get(uuid=job_url.split('/')[-3])
assert job.status == 'failed'
assert (
job.exception
== 'Invalid tar file, missing component pages/%s.' % manifest_json['elements'][0]['slug']
)
def test_bundle_unlink(app, admin_user, bundle):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
application = Application.objects.create(
name='Test',
@ -1008,8 +1085,8 @@ def test_bundle_unlink(app, john_doe, bundle):
assert ApplicationElement.objects.count() == 2
def test_bundle_check(app, john_doe):
app.authorization = ('Basic', (john_doe.username, john_doe.username))
def test_bundle_check(app, admin_user):
app.authorization = ('Basic', (admin_user.username, admin_user.username))
bundles = []
for version_number in ['1.0', '2.0']:
@ -1153,9 +1230,25 @@ def test_bundle_check(app, john_doe):
}
}
# bad file format
resp = app.put('/api/export-import/bundle-check/', b'garbage', status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'Invalid tar file'
# missing manifest
tar_io = io.BytesIO()
with tarfile.open(mode='w', fileobj=tar_io) as tar:
foo_fd = io.BytesIO(json.dumps({'foo': 'bar'}, indent=2).encode())
tarinfo = tarfile.TarInfo('foo.json')
tarinfo.size = len(foo_fd.getvalue())
tar.addfile(tarinfo, fileobj=foo_fd)
resp = app.put('/api/export-import/bundle-check/', tar_io.getvalue(), status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'Invalid tar file, missing manifest'
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
def test_page_dependencies_card_models(mock_send, app, john_doe):
def test_page_dependencies_card_models(mock_send, app, admin_user):
page = Page.objects.create(
title='Test',
slug='test',
@ -1174,7 +1267,7 @@ def test_page_dependencies_card_models(mock_send, app, john_doe):
}
assert card_dep in page.get_dependencies()
app.authorization = ('Basic', (john_doe.username, john_doe.username))
app.authorization = ('Basic', (admin_user.username, admin_user.username))
with mock.patch('requests.Session.get') as requests_get:
requests_get.side_effect = ConnectionError()
resp = app.get(f'/api/export-import/pages/{page.uuid}/dependencies/', status=400)
@ -1269,7 +1362,7 @@ def test_page_dependencies_linkslist_cell():
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
def test_page_dependencies_form_cell(mock_send, app, john_doe):
def test_page_dependencies_form_cell(mock_send, app, admin_user):
page = Page.objects.create(title='Test', slug='test', template_name='standard')
cell = WcsFormCell(page=page, placeholder='content', order=0, formdef_reference='default:form-title')
cell.save()
@ -1283,7 +1376,7 @@ def test_page_dependencies_form_cell(mock_send, app, john_doe):
'redirect': 'http://127.0.0.1:8999/api/export-import/forms/form-title/redirect/',
},
} in page.get_dependencies()
app.authorization = ('Basic', (john_doe.username, john_doe.username))
app.authorization = ('Basic', (admin_user.username, admin_user.username))
resp = app.get(f'/api/export-import/pages/{page.uuid}/dependencies/')
assert resp.json['data'][0]['type'] == 'forms'