chrono/tests/api/test_applification_api.py

507 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import io
import json
import re
import tarfile
import pytest
from django.contrib.auth.models import Group
from django.contrib.contenttypes.models import ContentType
from chrono.agendas.models import Agenda, Category, Desk, EventsType, Resource, UnavailabilityCalendar
from chrono.apps.export_import.models import Application, ApplicationElement
pytestmark = pytest.mark.django_db
def test_object_types(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.get('/api/export-import/')
assert resp.json == {
'data': [
{
'id': 'agendas',
'text': 'Agendas',
'singular': 'Agenda',
'urls': {'list': 'http://testserver/api/export-import/agendas/'},
},
{
'id': 'agendas_categories',
'minor': True,
'singular': 'Category (agendas)',
'text': 'Categories (agendas)',
'urls': {'list': 'http://testserver/api/export-import/agendas_categories/'},
},
{
'id': 'events_types',
'minor': True,
'singular': 'Type dévènements',
'text': 'Types dévènements',
'urls': {'list': 'http://testserver/api/export-import/events_types/'},
},
{
'id': 'resources',
'minor': True,
'singular': 'Ressource',
'text': 'Ressources',
'urls': {'list': 'http://testserver/api/export-import/resources/'},
},
{
'id': 'unavailability_calendars',
'minor': True,
'singular': 'Unavailability calendar',
'text': 'Calendrier dindisponibilités',
'urls': {'list': 'http://testserver/api/export-import/unavailability_calendars/'},
},
]
}
def test_list_agendas(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
Agenda.objects.create(label='Rdv', slug='rdv', kind='meetings')
Agenda.objects.create(label='Event', slug='event', kind='events')
resp = app.get('/api/export-import/agendas/')
assert resp.json == {
'data': [
{
'id': 'event',
'text': 'Event',
'type': 'agendas',
'urls': {
'export': 'http://testserver/api/export-import/agendas/event/',
'dependencies': 'http://testserver/api/export-import/agendas/event/dependencies/',
'redirect': 'http://testserver/api/export-import/agendas/event/redirect/',
},
},
{
'id': 'rdv',
'text': 'Rdv',
'type': 'agendas',
'urls': {
'export': 'http://testserver/api/export-import/agendas/rdv/',
'dependencies': 'http://testserver/api/export-import/agendas/rdv/dependencies/',
'redirect': 'http://testserver/api/export-import/agendas/rdv/redirect/',
},
},
]
}
def test_export_agenda(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
group1 = Group.objects.create(name='group1')
group2 = Group.objects.create(name='group2')
Agenda.objects.create(label='Rdv', slug='rdv', kind='meetings', edit_role=group1, view_role=group2)
resp = app.get('/api/export-import/agendas/rdv/')
assert resp.json['data']['label'] == 'Rdv'
assert resp.json['data']['permissions'] == {'view': 'group2', 'edit': 'group1'}
def test_export_minor_components(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
Category.objects.create(slug='cat', label='Category')
Resource.objects.create(slug='foo', label='Foo')
EventsType.objects.create(slug='foo', label='Foo')
UnavailabilityCalendar.objects.create(slug='foo', label='Foo')
resp = app.get('/api/export-import/agendas_categories/cat/')
assert resp.json['data']['label'] == 'Category'
resp = app.get('/api/export-import/resources/foo/')
assert resp.json['data']['label'] == 'Foo'
resp = app.get('/api/export-import/events_types/foo/')
assert resp.json['data']['label'] == 'Foo'
resp = app.get('/api/export-import/unavailability_calendars/foo/')
assert resp.json['data']['label'] == 'Foo'
def test_agenda_dependencies_category(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
category = Category.objects.create(slug='cat', label='Category')
Agenda.objects.create(label='Rdv', slug='rdv', kind='meetings', category=category)
resp = app.get('/api/export-import/agendas/rdv/dependencies/')
assert resp.json == {
'data': [
{
'id': 'cat',
'text': 'Category',
'type': 'agendas_categories',
'urls': {
'dependencies': 'http://testserver/api/export-import/agendas_categories/cat/dependencies/',
'export': 'http://testserver/api/export-import/agendas_categories/cat/',
'redirect': 'http://testserver/api/export-import/agendas_categories/cat/redirect/',
},
}
],
'err': 0,
}
def test_agenda_dependencies_resources(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
meetings_agenda = Agenda.objects.create(label='Rdv', slug='rdv', kind='meetings')
meetings_agenda.resources.add(Resource.objects.create(slug='foo', label='Foo'))
resp = app.get('/api/export-import/agendas/rdv/dependencies/')
assert resp.json == {
'data': [
{
'id': 'foo',
'text': 'Foo',
'type': 'resources',
'urls': {
'dependencies': 'http://testserver/api/export-import/resources/foo/dependencies/',
'export': 'http://testserver/api/export-import/resources/foo/',
'redirect': 'http://testserver/api/export-import/resources/foo/redirect/',
},
}
],
'err': 0,
}
def test_agenda_dependencies_unavailability_calendars(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
meetings_agenda = Agenda.objects.create(label='Rdv', slug='rdv', kind='meetings')
desk = Desk.objects.create(slug='foo', label='Foo', agenda=meetings_agenda)
desk.unavailability_calendars.add(UnavailabilityCalendar.objects.create(slug='foo', label='Foo'))
resp = app.get('/api/export-import/agendas/rdv/dependencies/')
assert resp.json == {
'data': [
{
'id': 'foo',
'text': 'Foo',
'type': 'unavailability_calendars',
'urls': {
'dependencies': 'http://testserver/api/export-import/unavailability_calendars/foo/dependencies/',
'export': 'http://testserver/api/export-import/unavailability_calendars/foo/',
'redirect': 'http://testserver/api/export-import/unavailability_calendars/foo/redirect/',
},
}
],
'err': 0,
}
def test_agenda_dependencies_groups(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
group1 = Group.objects.create(name='group1')
group2 = Group.objects.create(name='group2')
Agenda.objects.create(label='Rdv', slug='rdv', kind='meetings', edit_role=group1, view_role=group2)
resp = app.get('/api/export-import/agendas/rdv/dependencies/')
# note: with hobo.agent.common installed, 'groups' will contain group slugs,
# not group id
assert resp.json == {
'data': [
{'id': group2.id, 'text': group2.name, 'type': 'roles', 'urls': {}},
{'id': group1.id, 'text': group1.name, 'type': 'roles', 'urls': {}},
],
'err': 0,
}
def test_agenda_dependencies_virtual_agendas(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
rdv1 = Agenda.objects.create(label='Rdv1', slug='rdv1', kind='meetings')
rdv2 = Agenda.objects.create(label='Rdv2', slug='rdv2', kind='meetings')
virt = Agenda.objects.create(label='Virt', slug='virt', kind='virtual')
virt.real_agendas.add(rdv1)
virt.real_agendas.add(rdv2)
resp = app.get('/api/export-import/agendas/virt/dependencies/')
assert resp.json == {
'data': [
{
'id': 'rdv1',
'text': 'Rdv1',
'type': 'agendas',
'urls': {
'dependencies': 'http://testserver/api/export-import/agendas/rdv1/dependencies/',
'export': 'http://testserver/api/export-import/agendas/rdv1/',
'redirect': 'http://testserver/api/export-import/agendas/rdv1/redirect/',
},
},
{
'id': 'rdv2',
'text': 'Rdv2',
'type': 'agendas',
'urls': {
'dependencies': 'http://testserver/api/export-import/agendas/rdv2/dependencies/',
'export': 'http://testserver/api/export-import/agendas/rdv2/',
'redirect': 'http://testserver/api/export-import/agendas/rdv2/redirect/',
},
},
],
'err': 0,
}
def test_agenda_dependencies_events_type(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
events_type = EventsType.objects.create(slug='foo', label='Foo')
Agenda.objects.create(label='Evt', slug='evt', kind='events', events_type=events_type)
resp = app.get('/api/export-import/agendas/evt/dependencies/')
assert resp.json == {
'data': [
{
'id': 'foo',
'text': 'Foo',
'type': 'events_types',
'urls': {
'dependencies': 'http://testserver/api/export-import/events_types/foo/dependencies/',
'export': 'http://testserver/api/export-import/events_types/foo/',
'redirect': 'http://testserver/api/export-import/events_types/foo/redirect/',
},
}
],
'err': 0,
}
def test_agenda_redirect(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
agenda = Agenda.objects.create(label='Rdv', slug='rdv', kind='meetings')
redirect_url = '/api/export-import/agendas/rdv/redirect/'
resp = app.get(redirect_url, status=302)
assert resp.location == f'/manage/agendas/{agenda.pk}/'
def create_bundle(app, user, visible=True, version_number='42.0'):
app.authorization = ('Basic', ('john.doe', 'password'))
group, _ = Group.objects.get_or_create(name='plop')
category, _ = Category.objects.get_or_create(slug='foo', label='Foo')
meetings_agenda, _ = Agenda.objects.get_or_create(
slug='rdv', label='Rdv', kind='meetings', edit_role=group, category=category
)
resource, _ = Resource.objects.get_or_create(slug='foo', label='Foo')
meetings_agenda.resources.add(resource)
desk, _ = Desk.objects.get_or_create(slug='foo', label='Foo', agenda=meetings_agenda)
unavailability_calendar, _ = UnavailabilityCalendar.objects.get_or_create(slug='foo', label='Foo')
desk.unavailability_calendars.add(unavailability_calendar)
events_type, _ = EventsType.objects.get_or_create(slug='foo', label='Foo')
events_agenda, _ = Agenda.objects.get_or_create(
label='Evt', slug='evt', kind='events', events_type=events_type
)
Desk.objects.get_or_create(agenda=events_agenda, slug='_exceptions_holder')
sub_agenda, _ = Agenda.objects.get_or_create(slug='sub', label='Sub', kind='meetings')
virtual_agenda, _ = Agenda.objects.get_or_create(label='Virt', slug='virt', kind='virtual')
virtual_agenda.real_agendas.add(sub_agenda)
components = [
(meetings_agenda, False),
(category, True),
(resource, True),
(unavailability_calendar, True),
(events_agenda, False),
(events_type, True),
(virtual_agenda, False),
(sub_agenda, True),
]
tar_io = io.BytesIO()
with tarfile.open(mode='w', fileobj=tar_io) as tar:
manifest_json = {
'application': 'Test',
'slug': 'test',
'icon': 'foo.png',
'description': 'Foo Bar',
'documentation_url': 'http://foo.bar',
'visible': visible,
'version_number': version_number,
'version_notes': 'foo bar blah',
'elements': [],
}
for component, auto_dependency in components:
manifest_json['elements'].append(
{
'type': component.application_component_type,
'slug': component.slug,
'name': component.label,
'auto-dependency': auto_dependency,
}
)
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)
icon_fd = io.BytesIO(
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
)
tarinfo = tarfile.TarInfo('foo.png')
tarinfo.size = len(icon_fd.getvalue())
tar.addfile(tarinfo, fileobj=icon_fd)
for component, _ in components:
component_export = app.get(
'/api/export-import/%s/%s/' % (component.application_component_type, component.slug)
).content
tarinfo = tarfile.TarInfo('%s/%s' % (component.application_component_type, component.slug))
tarinfo.size = len(component_export)
tar.addfile(tarinfo, fileobj=io.BytesIO(component_export))
bundle = tar_io.getvalue()
return bundle
@pytest.fixture
def bundle(app, user):
return create_bundle(app, user)
def test_bundle_import(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
bundles = []
for version_number in ['42.0', '42.1']:
bundles.append(create_bundle(app, user, version_number=version_number))
Agenda.objects.all().delete()
Category.objects.all().delete()
Resource.objects.all().delete()
EventsType.objects.all().delete()
UnavailabilityCalendar.objects.all().delete()
resp = app.put('/api/export-import/bundle-import/', bundles[0])
assert Agenda.objects.all().count() == 4
assert resp.json['err'] == 0
assert Application.objects.count() == 1
application = Application.objects.latest('pk')
assert application.slug == 'test'
assert application.name == 'Test'
assert application.description == 'Foo Bar'
assert application.documentation_url == 'http://foo.bar'
assert application.version_number == '42.0'
assert application.version_notes == 'foo bar blah'
assert re.match(r'applications/icons/foo(_\w+)?.png', application.icon.name)
assert application.editable is False
assert application.visible is True
assert ApplicationElement.objects.count() == 8
# check editable flag is kept on install
application.editable = True
application.save()
# create link to element not present in manifest: it should be unlinked
last_agenda = Agenda.objects.latest('pk')
ApplicationElement.objects.create(
application=application,
content_type=ContentType.objects.get_for_model(Agenda),
object_id=last_agenda.pk + 1,
)
# check update
resp = app.put('/api/export-import/bundle-import/', bundles[1])
assert Agenda.objects.all().count() == 4
assert resp.json['err'] == 0
assert Application.objects.count() == 1
application = Application.objects.latest('pk')
assert application.editable is False
assert ApplicationElement.objects.count() == 8
assert (
ApplicationElement.objects.filter(
application=application,
content_type=ContentType.objects.get_for_model(Agenda),
object_id=last_agenda.pk + 1,
).exists()
is False
)
def test_bundle_declare(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
bundle = create_bundle(app, user, visible=False)
resp = app.put('/api/export-import/bundle-declare/', bundle)
assert Agenda.objects.all().count() == 4
assert resp.json['err'] == 0
assert Application.objects.count() == 1
application = Application.objects.latest('pk')
assert application.slug == 'test'
assert application.name == 'Test'
assert application.description == 'Foo Bar'
assert application.documentation_url == 'http://foo.bar'
assert application.version_number == '42.0'
assert application.version_notes == 'foo bar blah'
assert re.match(r'applications/icons/foo(_\w+)?.png', application.icon.name)
assert application.editable is True
assert application.visible is False
assert ApplicationElement.objects.count() == 8
bundle = create_bundle(app, user, visible=True)
# create link to element not present in manifest: it should be unlinked
last_page = Agenda.objects.latest('pk')
ApplicationElement.objects.create(
application=application,
content_type=ContentType.objects.get_for_model(Agenda),
object_id=last_page.pk + 1,
)
# and remove agendas to have unkown references in manifest
Agenda.objects.all().delete()
resp = app.put('/api/export-import/bundle-declare/', bundle)
assert Application.objects.count() == 1
application = Application.objects.latest('pk')
assert application.visible is True
assert ApplicationElement.objects.count() == 4 # category, events_type, unavailability_calendar, resource
def test_bundle_unlink(app, user, bundle):
app.authorization = ('Basic', ('john.doe', 'password'))
application = Application.objects.create(
name='Test',
slug='test',
version_number='42.0',
)
other_application = Application.objects.create(
name='Other Test',
slug='other-test',
version_number='42.0',
)
agenda = Agenda.objects.latest('pk')
ApplicationElement.objects.create(
application=application,
content_object=agenda,
)
ApplicationElement.objects.create(
application=application,
content_type=ContentType.objects.get_for_model(Agenda),
object_id=agenda.pk + 1,
)
ApplicationElement.objects.create(
application=other_application,
content_object=agenda,
)
ApplicationElement.objects.create(
application=other_application,
content_type=ContentType.objects.get_for_model(Agenda),
object_id=agenda.pk + 1,
)
assert Application.objects.count() == 2
assert ApplicationElement.objects.count() == 4
app.post('/api/export-import/unlink/', {'application': 'test'})
assert Application.objects.count() == 1
assert ApplicationElement.objects.count() == 2
assert ApplicationElement.objects.filter(
application=other_application,
content_type=ContentType.objects.get_for_model(Agenda),
object_id=agenda.pk,
).exists()
assert ApplicationElement.objects.filter(
application=other_application,
content_type=ContentType.objects.get_for_model(Agenda),
object_id=agenda.pk + 1,
).exists()
# again
app.post('/api/export-import/unlink/', {'application': 'test'})
assert Application.objects.count() == 1
assert ApplicationElement.objects.count() == 2
def test_bundle_check(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
assert app.put('/api/export-import/bundle-check/').json == {'err': 0, 'data': {}}