wcs/tests/test_snapshots.py

1051 lines
38 KiB
Python

import io
import os
import shutil
import xml.etree.ElementTree as ET
import pytest
from quixote.http_request import Upload
from wcs.blocks import BlockDef
from wcs.carddef import CardDef
from wcs.categories import Category
from wcs.data_sources import NamedDataSource
from wcs.fields import CommentField, ItemField, StringField
from wcs.formdef import FormDef
from wcs.mail_templates import MailTemplate
from wcs.qommon.form import UploadedFile
from wcs.qommon.misc import localstrftime
from wcs.wf.form import WorkflowFormFieldsFormDef
from wcs.workflows import Workflow, WorkflowVariablesFieldsFormDef
from wcs.wscalls import NamedWsCall
from .admin_pages.test_all import create_role, create_superuser
from .utilities import clean_temporary_pub, create_temporary_pub, get_app, login
@pytest.fixture
def pub(emails):
pub = create_temporary_pub(lazy_mode=True)
pub.cfg['identification'] = {'methods': ['password']}
pub.cfg['language'] = {'language': 'en'}
pub.write_cfg()
FormDef.wipe()
CardDef.wipe()
NamedDataSource.wipe()
pub.snapshot_class.wipe()
pub.user_class.wipe()
return pub
@pytest.fixture
def formdef_with_history(pub):
formdef = FormDef()
formdef.name = 'testform'
formdef.fields = [StringField(id='1', label='Test', type='string')]
formdef.store()
for i in range(5):
formdef.name = 'testform %s' % i
formdef.description = 'this is a description (%s)' % i
formdef.store()
return formdef
def teardown_module(module):
clean_temporary_pub()
def test_snapshot_basics(pub):
formdef = FormDef()
formdef.name = 'testform'
# start with a big content
formdef.fields = [CommentField(id='0', label='Test ' * 500, type='comment')]
formdef.store()
# first occurence, complete snapshot stored
assert pub.snapshot_class.count() == 1
snapshot1 = pub.snapshot_class.get_latest('formdef', formdef.id)
assert snapshot1.serialization is not None
assert '>testform<' in snapshot1.serialization
assert snapshot1.patch is None
assert snapshot1.instance # possible to restore
# no changes
formdef.store()
assert pub.snapshot_class.count() == 1
# patch only
formdef.name = 'testform2'
formdef.store()
assert pub.snapshot_class.count() == 2
snapshot2 = pub.snapshot_class.get_latest('formdef', formdef.id)
assert snapshot2.serialization is None
assert '>testform2<' in snapshot2.patch
assert snapshot2.instance # possible to restore
# no diff with latest snap but label is given
pub.snapshot_class.snap(instance=formdef, label="foo bar")
assert pub.snapshot_class.count() == 3
snapshot3 = pub.snapshot_class.get_latest('formdef', formdef.id)
assert snapshot3.serialization is None
assert '>testform2<' in snapshot3.patch
assert snapshot2.patch == snapshot3.patch
assert snapshot3.instance # possible to restore
# patch is longer as serialization, store serialization
formdef.name = 'testform3'
formdef.fields += [StringField(id=str(i + 1), label='Test %s' % i, type='string') for i in range(0, 10)]
formdef.store()
assert pub.snapshot_class.count() == 4
snapshot4 = pub.snapshot_class.get_latest('formdef', formdef.id)
assert snapshot4.serialization is not None
assert '>testform3<' in snapshot4.serialization
assert snapshot4.patch is None
assert snapshot4.instance # possible to restore
# no diff with latest snap but label is given
pub.snapshot_class.snap(instance=formdef, label="foo bar")
assert pub.snapshot_class.count() == 5
snapshot5 = pub.snapshot_class.get_latest('formdef', formdef.id)
assert snapshot5.serialization is None
assert snapshot5.patch == '' # no difference with latest snap, which has a serialization
assert snapshot5.instance # possible to restore
# add snapshots with patches
snapshot6 = None
for i in range(10):
formdef.name = 'testform%s' % (i + 6)
formdef.fields.append(StringField(id=str(i + 11), label='Test %s' % (i + 10), type='string'))
formdef.store()
snapshot = pub.snapshot_class.get_latest('formdef', formdef.id)
assert snapshot.patch is None or len(snapshot.patch) < len(snapshot.get_serialization()) / 10
snapshot6 = snapshot6 or snapshot
assert pub.snapshot_class.count() == 15
# patch is longer as serialization, store serialization
formdef.name = 'testform16'
formdef.fields += [
StringField(id=str(i + 20), label='Test %s' % (i + 20), type='string') for i in range(0, 30)
]
formdef.store()
assert pub.snapshot_class.count() == 16
snapshot16 = pub.snapshot_class.get_latest('formdef', formdef.id)
assert snapshot16.serialization is not None
assert '>testform16<' in snapshot16.serialization
assert snapshot16.patch is None
assert snapshot16.instance # possible to restore
# check that snapshot6 restoration:
# don't take snapshot15 as latest_complete
latest_complete = snapshot6.get_latest(snapshot6.object_type, snapshot6.object_id, complete=True)
assert latest_complete.id == snapshot16.id
latest_complete = snapshot6.get_latest(
snapshot6.object_type, snapshot6.object_id, complete=True, max_timestamp=snapshot6.timestamp
)
assert latest_complete.id == snapshot4.id
assert snapshot6.instance # possible to restore
assert [int(f.id) for f in snapshot6.instance.fields] == list(range(0, 12))
def test_snapshot_diff(pub):
create_superuser(pub)
create_role(pub)
formdef = FormDef()
formdef.name = 'testform'
formdef.fields = []
formdef.store()
assert pub.snapshot_class.count() == 1
snapshot1 = pub.snapshot_class.get_latest('formdef', formdef.id)
formdef.fields = [StringField(id=1, label='Test', type='string')]
formdef.store()
assert pub.snapshot_class.count() == 2
snapshot2 = pub.snapshot_class.get_latest('formdef', formdef.id)
formdef.fields += [StringField(id=2, label='Test bis', type='string')]
formdef.store()
assert pub.snapshot_class.count() == 3
snapshot3 = pub.snapshot_class.get_latest('formdef', formdef.id)
app = login(get_app(pub))
resp = app.get('/backoffice/forms/%s/history/' % formdef.id)
assert 'name="version1" value="%s"' % snapshot3.id in resp
assert 'name="version2" value="%s"' % snapshot3.id not in resp
assert 'name="version1" value="%s"' % snapshot2.id in resp
assert 'name="version2" value="%s"' % snapshot2.id in resp
assert 'name="version1" value="%s"' % snapshot1.id not in resp
assert 'name="version2" value="%s"' % snapshot1.id in resp
resp = app.get(
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s'
% (formdef.id, snapshot1.id, snapshot3.id)
)
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot1.id, snapshot1.id) in resp
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot3.id, snapshot3.id) in resp
assert resp.text.count('diff_sub') == 1
assert resp.text.count('diff_add') == 24
resp = app.get(
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s'
% (formdef.id, snapshot3.id, snapshot1.id)
)
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot1.id, snapshot1.id) in resp
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot3.id, snapshot3.id) in resp
assert resp.text.count('diff_sub') == 1
assert resp.text.count('diff_add') == 24
resp = app.get(
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s'
% (formdef.id, snapshot2.id, snapshot3.id)
)
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot2.id, snapshot2.id) in resp
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot3.id, snapshot3.id) in resp
assert resp.text.count('diff_sub') == 0
assert resp.text.count('diff_add') == 11
formdef.fields = [StringField(id=1, label='Test', type='string')]
formdef.store()
assert pub.snapshot_class.count() == 4
snapshot4 = pub.snapshot_class.get_latest('formdef', formdef.id)
resp = app.get(
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s'
% (formdef.id, snapshot3.id, snapshot4.id)
)
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot3.id, snapshot3.id) in resp
assert 'Snapshot <a href="%s/view/">%s</a>' % (snapshot4.id, snapshot4.id) in resp
assert resp.text.count('diff_sub') == 11
assert resp.text.count('diff_add') == 0
resp = app.get('/backoffice/forms/%s/history/compare' % (formdef.id), status=404)
resp = app.get(
'/backoffice/forms/%s/history/compare?version1=%s' % (formdef.id, snapshot4.id), status=404
)
resp = app.get(
'/backoffice/forms/%s/history/compare?version2=%s' % (formdef.id, snapshot4.id), status=404
)
resp = app.get(
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s' % (formdef.id, snapshot3.id, 0),
status=404,
)
resp = app.get(
'/backoffice/forms/%s/history/compare?version1=%s&version2=%s' % (formdef.id, 0, snapshot4.id),
status=404,
)
def test_snapshot_instance(pub):
formdef = FormDef()
formdef.name = 'testform'
formdef.fields = []
formdef.store()
carddef = CardDef()
carddef.name = 'testcard'
carddef.fields = []
carddef.store()
# remove existing snapshots as they may be duplicated if table_name was
# generated in a different second.
pub.snapshot_class.wipe()
carddef.name = 'testcard2'
carddef.store()
for i in range(10):
formdef.name = 'testform %s' % i
formdef.store()
assert pub.snapshot_class.count() == 11
snapshots = pub.snapshot_class.select_object_history(formdef)
assert len(snapshots) == 10
for i in range(10):
assert snapshots[i].serialization is None # not loaded
assert snapshots[i].patch is None # not loaded
assert pub.snapshot_class.get(snapshots[i].id).instance.name == 'testform %s' % (9 - i)
snapshots = pub.snapshot_class.select_object_history(carddef)
assert len(snapshots) == 1
def test_snapshot_user(pub):
user = pub.user_class()
user.name = 'User Name'
user.email = 'foo@localhost'
user.store()
carddef = CardDef()
carddef.name = 'testcard'
carddef.fields = []
carddef.store()
snapshot = pub.snapshot_class.select_object_history(carddef)[0]
assert snapshot.user is None
snapshot.user_id = user.id
snapshot.store()
snapshot = pub.snapshot_class.select_object_history(carddef)[0]
assert str(snapshot.user) == 'User Name'
snapshot.user_id = 'nope'
snapshot.store()
snapshot = pub.snapshot_class.select_object_history(carddef)[0]
assert str(snapshot.user) == 'unknown user'
def test_form_snapshot_comments(pub):
create_superuser(pub)
create_role(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/forms/')
resp = resp.click('New Form')
resp.form['name'] = 'form title'
resp = resp.form.submit().follow()
# .store() then .disabled = True, then .store() again -> 2.
assert pub.snapshot_class.count() == 2
resp = resp.click('Confirmation Page')
assert resp.form['confirmation'].checked
resp.form['confirmation'].checked = False
resp = resp.form.submit().follow()
assert pub.snapshot_class.count() == 3
assert (
pub.snapshot_class.select(order_by='-timestamp')[0].comment
== 'Changed "Confirmation Page" parameters'
)
resp = resp.click(href='fields/')
resp.forms[0]['label'] = 'foobar'
resp.forms[0]['type'] = 'string'
resp = resp.forms[0].submit().follow()
assert pub.snapshot_class.select(order_by='-timestamp')[0].comment == 'New field "foobar"'
resp.forms[0]['label'] = 'foo' * 30
resp.forms[0]['type'] = 'string'
resp = resp.forms[0].submit().follow()
assert (
pub.snapshot_class.select(order_by='-timestamp')[0].comment
== 'New field "foofoofoofoofoofoofoofoofoo(…)"'
)
def test_form_snapshot_history(pub, formdef_with_history):
create_superuser(pub)
create_role(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/forms/%s/' % formdef_with_history.id)
resp = resp.click('History')
assert [x.attrib['class'] for x in resp.pyquery.find('ul.snapshots-list li')] == [
'new-day',
'collapsed',
'collapsed',
'collapsed',
'collapsed',
'collapsed',
]
def test_form_snapshot_export(pub, formdef_with_history):
create_superuser(pub)
create_role(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[2]
resp_export = resp.click(href='%s/export' % snapshot.id)
assert resp_export.content_type == 'application/x-wcs-snapshot'
assert '>testform 2<' in resp_export.text
def test_form_snapshot_restore(pub, formdef_with_history):
create_superuser(pub)
create_role(pub)
app = login(get_app(pub))
# restore as new
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[2]
resp = resp.click(href='%s/restore' % snapshot.id)
assert resp.form['action'].value == 'as-new'
resp = resp.form.submit('submit')
assert FormDef.count() == 2
formdef = FormDef.get(resp.location.split('/')[-2])
assert formdef.url_name != formdef_with_history.url_name
assert not hasattr(formdef, 'snapshot_object')
# restore over
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[2]
resp = resp.click(href='%s/restore' % snapshot.id)
resp.form['action'].value = 'overwrite'
resp = resp.form.submit('submit')
assert FormDef.count() == 2
formdef = FormDef.get(resp.location.split('/')[-2])
assert formdef.id == formdef_with_history.id
assert formdef.url_name == formdef_with_history.url_name
def test_form_snapshot_restore_with_import_error(pub):
create_superuser(pub)
create_role(pub)
app = login(get_app(pub))
formdef = FormDef()
formdef.name = 'testform'
formdef.fields = [ItemField(id='1', label='Test', type='item', data_source={'type': 'unknown'})]
formdef.store()
assert pub.snapshot_class.count() == 1
snapshot = pub.snapshot_class.select_object_history(formdef)[0]
resp = app.get('/backoffice/forms/%s/history/%s/restore' % (formdef.id, snapshot.id))
resp = resp.form.submit('submit')
assert 'Can not restore snapshot (Unknown referenced objects [Unknown datasources: unknown])' in resp
def test_block_snapshot_browse(pub):
create_superuser(pub)
create_role(pub)
BlockDef.wipe()
blockdef = BlockDef()
blockdef.name = 'testblock'
blockdef.fields = []
blockdef.store()
assert pub.snapshot_class.count() == 1
# check calling .store() without changes doesn't create snapshots
blockdef.store()
assert pub.snapshot_class.count() == 1
app = login(get_app(pub))
resp = app.get('/backoffice/forms/blocks/%s/history/' % blockdef.id)
snapshot = pub.snapshot_class.select_object_history(blockdef)[0]
resp = resp.click(href='%s/view/' % snapshot.id)
assert 'This block of fields is readonly.' in resp.text
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
def test_card_snapshot_browse(pub):
create_superuser(pub)
create_role(pub)
CardDef.wipe()
carddef = CardDef()
carddef.name = 'testcard'
carddef.fields = []
carddef.store()
assert pub.snapshot_class.count() == 1
# check calling .store() without changes doesn't create snapshots
carddef.store()
assert pub.snapshot_class.count() == 1
pub.custom_view_class.wipe()
custom_view = pub.custom_view_class()
custom_view.title = 'shared form view'
custom_view.formdef = carddef
custom_view.columns = {'list': [{'id': 'id'}]}
custom_view.filters = {}
custom_view.visibility = 'any'
custom_view.store()
# new version has custom views
carddef.name = 'test 1'
carddef.store()
# delete custom views
pub.custom_view_class.wipe()
app = login(get_app(pub))
resp = app.get('/backoffice/cards/%s/history/' % carddef.id)
snapshot = pub.snapshot_class.select_object_history(carddef)[0]
resp = resp.click(href='%s/view/' % snapshot.id)
assert 'This card model is readonly' in resp.text
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
# check option dialogs only have a cancel button
resp = resp.click('User support')
assert [x[0].name for x in resp.form.fields.values() if x[0].tag == 'button'] == ['cancel']
assert pub.custom_view_class.count() == 0 # custom views are not restore on preview
def test_datasource_snapshot_browse(pub):
create_superuser(pub)
create_role(pub)
NamedDataSource.wipe()
datasource = NamedDataSource(name='test')
datasource.data_source = {'type': 'formula', 'value': repr([('1', 'un'), ('2', 'deux')])}
datasource.store()
assert pub.snapshot_class.count() == 1
# check calling .store() without changes doesn't create snapshots
datasource.store()
assert pub.snapshot_class.count() == 1
app = login(get_app(pub))
resp = app.get('/backoffice/forms/data-sources/%s/history/' % datasource.id)
snapshot = pub.snapshot_class.select_object_history(datasource)[0]
resp = resp.click(href='%s/view/' % snapshot.id)
assert 'This data source is readonly' in resp.text
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
with pytest.raises(IndexError):
resp = resp.click('Edit')
with pytest.raises(IndexError):
resp = resp.click('Duplicate')
def test_form_snapshot_browse(pub, formdef_with_history):
create_superuser(pub)
create_role(pub)
app = login(get_app(pub))
pub.custom_view_class.wipe()
custom_view = pub.custom_view_class()
custom_view.title = 'shared form view'
custom_view.formdef = formdef_with_history
custom_view.columns = {'list': [{'id': 'id'}]}
custom_view.filters = {}
custom_view.visibility = 'any'
custom_view.store()
# version 5 has custom views
formdef_with_history.name = 'testform 5'
formdef_with_history.description = 'this is a description (5)'
formdef_with_history.store()
assert pub.snapshot_class.count() == 7
# check calling .store() without changes doesn't create snapshots
formdef_with_history.store()
assert pub.snapshot_class.count() == 7
# delete custom views
pub.custom_view_class.wipe()
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[0]
resp = resp.click(href='%s/view/' % snapshot.id)
assert 'This form is readonly' in resp.text
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
assert '<a class="button disabled" href="#">&Lt;</a>' in resp.text
assert '<a class="button disabled" href="#">&LT;</a>' in resp.text
assert '<a class="button" href="../../%s/view/">&GT;</a>' % (snapshot.id - 1) in resp.text
assert '<a class="button" href="../../%s/view/">&Gt;</a>' % (snapshot.id - 6) in resp.text
resp = resp.click(href='../../%s/view/' % (snapshot.id - 1))
assert '<a class="button" href="../../%s/view/">&Lt;</a>' % snapshot.id in resp.text
assert '<a class="button" href="../../%s/view/">&LT;</a>' % snapshot.id in resp.text
assert '<a class="button" href="../../%s/view/">&GT;</a>' % (snapshot.id - 2) in resp.text
assert '<a class="button" href="../../%s/view/">&Gt;</a>' % (snapshot.id - 6) in resp.text
resp = resp.click(href='../../%s/view/' % (snapshot.id - 6))
assert '<a class="button" href="../../%s/view/">&Lt;</a>' % snapshot.id in resp.text
assert '<a class="button" href="../../%s/view/">&LT;</a>' % (snapshot.id - 5) in resp.text
assert '<a class="button disabled" href="#">&GT;</a>' in resp.text
assert '<a class="button disabled" href="#">&Gt;</a>' in resp.text
resp = resp.click(href='../../%s/view/' % snapshot.id)
resp = resp.click('Description')
assert resp.form['description'].value == 'this is a description (5)'
assert [x[0].name for x in resp.form.fields.values() if x[0].tag == 'button'] == ['cancel']
assert pub.custom_view_class.count() == 0 # custom views are not restore on preview
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
resp = resp.click(href='%s/view/' % (snapshot.id - 1))
resp = resp.click(href='fields/')
assert 'sortable readonly' in resp.text
assert '<a class="button" href="../../../%s/view/fields/">&Lt;</a>' % snapshot.id in resp.text
assert '<a class="button" href="../../../%s/view/fields/">&LT;</a>' % snapshot.id in resp.text
assert '<a class="button" href="../../../%s/view/fields/">&GT;</a>' % (snapshot.id - 2) in resp.text
assert '<a class="button" href="../../../%s/view/fields/">&Gt;</a>' % (snapshot.id - 6) in resp.text
def test_form_snapshot_browse_with_import_error(pub):
create_superuser(pub)
create_role(pub)
app = login(get_app(pub))
formdef = FormDef()
formdef.name = 'testform'
formdef.fields = [ItemField(id='1', label='Test', type='item', data_source={'type': 'unknown'})]
formdef.store()
assert pub.snapshot_class.count() == 1
snapshot = pub.snapshot_class.select_object_history(formdef)[0]
# no error for missing datasource
resp = app.get('/backoffice/forms/%s/history/%s/view/' % (formdef.id, snapshot.id), status=200)
# other FormdefImportError
formdef.fields = [ItemField(id='1', label='Test', type='foobar')]
formdef.store()
assert pub.snapshot_class.count() == 2
snapshot = pub.snapshot_class.select_object_history(formdef)[0]
resp = app.get('/backoffice/forms/%s/history/%s/view/' % (formdef.id, snapshot.id), status=302)
assert resp.location == 'http://example.net/backoffice/forms/%s/history/' % formdef.id
resp = resp.follow()
assert 'Can not display snapshot (Unknown referenced objects [Unknown field types: foobar])' in resp
def test_workflow_snapshot_browse(pub):
create_superuser(pub)
create_role(pub)
Workflow.wipe()
workflow = Workflow(name='test')
workflow.store()
assert pub.snapshot_class.count() == 1
# check calling .store() without changes doesn't create snapshots
workflow.store()
assert pub.snapshot_class.count() == 1
app = login(get_app(pub))
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
snapshot = pub.snapshot_class.select_object_history(workflow)[0]
resp = resp.click(href='%s/view/' % snapshot.id)
assert 'This workflow is readonly' in resp.text
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
def test_workflow_snapshot_browse_with_missing_role(pub):
create_superuser(pub)
app = login(get_app(pub))
pub.role_class.wipe()
Workflow.wipe()
wf = Workflow(name='status')
ac1 = wf.add_global_action('Action', 'ac1')
trigger = ac1.triggers[0]
assert trigger.key == 'manual'
trigger.roles = ['foobar']
wf.store()
assert pub.role_class.count() == 0
assert pub.snapshot_class.count() == 1
snapshot = pub.snapshot_class.select_object_history(wf)[0]
app.get('/backoffice/workflows/%s/history/%s/view/' % (wf.id, snapshot.id), status=200)
assert pub.role_class.count() == 1 # missing role was created
pub.cfg['sp'] = {'idp-manage-roles': True}
pub.write_cfg()
trigger.roles = ['foobarbaz']
wf.store()
assert pub.role_class.count() == 1
assert pub.snapshot_class.count() == 2
snapshot = pub.snapshot_class.select_object_history(wf)[0]
app.get('/backoffice/workflows/%s/history/%s/view/' % (wf.id, snapshot.id), status=200)
assert pub.role_class.count() == 1 # missing role was not created
def test_workflow_snapshot_restore(pub):
create_superuser(pub)
create_role(pub)
Workflow.wipe()
workflow = Workflow(name='test')
workflow.store()
app = login(get_app(pub))
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
snapshot = pub.snapshot_class.select_object_history(workflow)[0]
resp = resp.click(href='%s/view/' % snapshot.id)
# restore over
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
snapshot = pub.snapshot_class.select_object_history(workflow)[-1]
resp = resp.click(href='%s/restore' % snapshot.id)
resp.form['action'].value = 'overwrite'
resp = resp.form.submit('submit')
assert Workflow.count() == 1
assert not hasattr(Workflow.get(workflow.id), 'snapshot_object')
def test_workflow_snapshot_restore_with_import_error(pub):
create_superuser(pub)
app = login(get_app(pub))
Workflow.wipe()
wf = Workflow(name='status')
st1 = wf.add_status('Status1', 'st1')
display_form = st1.add_action('form', id='_x')
display_form.varname = 'xxx'
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
display_form.formdef.fields = [
ItemField(id='1', label='Test', type='item', data_source={'type': 'unknown'})
]
wf.store()
assert pub.snapshot_class.count() == 1
snapshot = pub.snapshot_class.select_object_history(wf)[0]
resp = app.get('/backoffice/workflows/%s/history/%s/restore' % (wf.id, snapshot.id))
resp = resp.form.submit('submit')
assert 'Can not restore snapshot (Unknown referenced objects [Unknown datasources: unknown])' in resp
def test_workflow_snapshot_restore_with_missing_role(pub):
create_superuser(pub)
app = login(get_app(pub))
pub.role_class.wipe()
Workflow.wipe()
wf = Workflow(name='status')
ac1 = wf.add_global_action('Action', 'ac1')
trigger = ac1.triggers[0]
assert trigger.key == 'manual'
trigger.roles = ['foobar']
wf.store()
assert pub.role_class.count() == 0
assert pub.snapshot_class.count() == 1
snapshot = pub.snapshot_class.select_object_history(wf)[0]
resp = app.get('/backoffice/workflows/%s/history/%s/restore' % (wf.id, snapshot.id), status=200)
resp.form['action'].value = 'overwrite'
resp = resp.form.submit('submit')
assert pub.role_class.count() == 1 # missing role was created
wf = Workflow.get(resp.location.split('/')[-2])
assert wf.global_actions[0].triggers[0].roles == ['1']
assert pub.snapshot_class.count() == 2
pub.cfg['sp'] = {'idp-manage-roles': True}
pub.write_cfg()
wf.global_actions[0].triggers[0].roles = ['foobarbaz']
wf.store()
assert pub.role_class.count() == 1
assert pub.snapshot_class.count() == 3
snapshot = pub.snapshot_class.select_object_history(wf)[0]
resp = app.get('/backoffice/workflows/%s/history/%s/restore' % (wf.id, snapshot.id), status=200)
resp.form['action'].value = 'overwrite'
resp = resp.form.submit('submit')
assert pub.role_class.count() == 1 # missing role was not created
wf = Workflow.get(resp.location.split('/')[-2])
assert wf.global_actions[0].triggers[0].roles == ['foobarbaz']
# no error raised due to unknown role
app.get(
'/backoffice/workflows/%s/global-actions/ac1/triggers/%s/'
% (wf.id, wf.global_actions[0].triggers[0].id)
)
def test_workflow_with_model_snapshot_browse(pub):
create_superuser(pub)
create_role(pub)
Workflow.wipe()
if os.path.exists(os.path.join(pub.app_dir, 'models')):
shutil.rmtree(os.path.join(pub.app_dir, 'models'))
workflow = Workflow(name='test')
st1 = workflow.add_status('Status1', 'st1')
export_to = st1.add_action('export_to_model')
export_to.label = 'test'
upload = Upload('/foo/bar', content_type='application/vnd.oasis.opendocument.text')
file_content = b'''PK\x03\x04\x14\x00\x00\x08\x00\x00\'l\x8eG^\xc62\x0c\'\x00'''
upload.fp = io.BytesIO()
upload.fp.write(file_content)
upload.fp.seek(0)
export_to.model_file = UploadedFile('models', 'tmp', upload)
# export/import to get models stored in the expected way
workflow.store()
workflow = Workflow.import_from_xml_tree(
ET.fromstring(ET.tostring(workflow.export_to_xml(include_id=True))), include_id=True
)
workflow.store()
assert len(os.listdir(os.path.join(pub.app_dir, 'models'))) == 2
workflow = Workflow.import_from_xml_tree(
ET.fromstring(ET.tostring(workflow.export_to_xml(include_id=True))), include_id=True
)
workflow.store()
assert len(os.listdir(os.path.join(pub.app_dir, 'models'))) == 2
app = login(get_app(pub))
for i in range(3):
# check document model is not overwritten
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
snapshot = pub.snapshot_class.select_object_history(workflow)[0]
resp = resp.click(href='%s/view/' % snapshot.id)
assert 'This workflow is readonly' in resp.text
filenames = os.listdir(os.path.join(pub.app_dir, 'models'))
assert len(filenames) == 3 + i
assert (
len([f for f in filenames if f.startswith('export_to_model-snapshot') and f.endswith('.upload')])
== 1 + i
)
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
resp = resp.click(href='%s/restore' % snapshot.id)
assert resp.form['action'].value == 'as-new'
resp = resp.form.submit('submit')
assert Workflow.count() == 2
Workflow.get(2)
assert list(workflow.get_all_items())[0].key == 'export_to_model'
assert list(workflow.get_all_items())[0].model_file.filename == 'export_to_model-1-st1-1.upload'
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
resp_export = resp.click(href='%s/export' % snapshot.id)
assert resp_export.content_type == 'application/x-wcs-snapshot'
workflow = Workflow.import_from_xml_tree(ET.fromstring(resp_export.text))
workflow.store()
assert list(workflow.get_all_items())[0].key == 'export_to_model'
assert list(workflow.get_all_items())[0].model_file.filename != 'export_to_model-1-st1-1.upload'
assert 'snapshot' not in list(workflow.get_all_items())[0].model_file.filename
def test_workflow_with_form_snapshot_browse(pub):
create_superuser(pub)
create_role(pub)
Workflow.wipe()
wf = Workflow(name='status')
st1 = wf.add_status('Status1', 'st1')
display_form = st1.add_action('form', id='_x')
display_form.varname = 'xxx'
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
display_form.formdef.fields.append(StringField(id='1', label='Test', type='string'))
wf.store()
snapshot = pub.snapshot_class.select_object_history(wf)[0]
app = login(get_app(pub))
resp = app.get(
'/backoffice/workflows/%s/history/%s/view/status/st1/items/_x/fields/' % (wf.id, snapshot.id)
)
assert 'The fields are readonly' in resp.text # ok, no error
assert 'sortable readonly' in resp.text
def test_wscall_snapshot_browse(pub):
create_superuser(pub)
create_role(pub)
NamedWsCall.wipe()
wscall = NamedWsCall(name='test')
wscall.store()
assert pub.snapshot_class.count() == 1
# check calling .store() without changes doesn't create snapshots
wscall.store()
assert pub.snapshot_class.count() == 1
app = login(get_app(pub))
resp = app.get('/backoffice/settings/wscalls/%s/history/' % wscall.id)
snapshot = pub.snapshot_class.select_object_history(wscall)[0]
resp = resp.click(href='%s/view/' % snapshot.id)
assert 'This webservice call is readonly' in resp.text
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
with pytest.raises(IndexError):
resp = resp.click('Edit')
def test_form_snapshot_save(pub, formdef_with_history):
create_superuser(pub)
create_role(pub)
app = login(get_app(pub))
resp = app.get('/backoffice/forms/%s/' % formdef_with_history.id)
resp = resp.click('Save snapshot')
resp.form['label'] = 'test snapshot'
resp = resp.form.submit('submit')
# add more snapshots
formdef = FormDef.get(id=formdef_with_history.id)
for i in range(10, 15):
formdef.description = 'this is a description (%s)' % i
formdef.store()
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
assert [x.attrib['class'] for x in resp.pyquery.find('ul.snapshots-list li')] == [
'new-day',
'collapsed',
'collapsed',
'collapsed',
'collapsed',
'has-label',
'collapsed',
'collapsed',
'collapsed',
'collapsed',
'collapsed',
'collapsed',
]
def test_snaphost_workflow_status_item_comments(pub):
create_superuser(pub)
create_role(pub)
Workflow.wipe()
workflow = Workflow(name='test')
workflow.add_status(name='baz')
workflow.add_status(name='hop')
global_action = workflow.add_global_action('Action', 'ac1')
register_comment = global_action.add_action('register-comment')
workflow.store()
app = login(get_app(pub))
resp = app.get('/backoffice/workflows/1/')
resp = resp.click('baz')
resp.forms[0]['action-interaction'] = 'Webservice'
resp = resp.forms[0].submit()
resp = resp.follow()
resp = resp.click('Edit', href='items/1/')
resp.form['url'] = 'http://example.org'
resp = resp.forms[0].submit('submit')
resp = resp.follow()
resp = resp.follow()
resp = resp.click('Edit', href='items/1/')
resp.form['label'] = 'foo'
resp = resp.forms[0].submit('submit')
resp = resp.follow()
resp = resp.follow()
resp = resp.click(href='items/1/copy')
resp.form['status'] = 'hop'
resp = resp.forms[0].submit('submit')
resp = resp.follow()
resp = resp.click(href='items/1/delete')
resp = resp.form.submit('submit')
resp = app.get('/backoffice/workflows/1/global-actions/ac1/items/%s/' % register_comment.id)
resp.forms[0]['comment'] = 'xxx'
resp = resp.form.submit('submit')
resp = app.get('/backoffice/workflows/%s/history/' % workflow.id)
comments = [
x.text[18 : x.text.find('\n')]
for x in resp.html.find('ul', {'class': 'snapshots-list'}).find_all('span', {'class': 'label'})
]
assert comments == [
'Change in action "History Message" in global action "Action"',
'Deletion of action "Webservice (foo)" in status "baz"',
'Copy of action "Webservice (foo)" from status "baz" to status "hop"',
'Change in action "Webservice (foo)" in status "baz"',
'Change in action "Webservice" in status "baz"',
'New action "Webservice" in status "baz"',
'',
]
def test_snapshot_workflow_variable(pub):
create_superuser(pub)
create_role(pub)
Workflow.wipe()
workflow = Workflow(name='test')
workflow.store()
workflow.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow)
workflow.variables_formdef.fields = [
StringField(id='0', label='foo', type='string', varname='foo'),
]
workflow.store()
app = login(get_app(pub))
snapshot = pub.snapshot_class.get_latest('workflow', workflow.id)
resp = app.get(
'/backoffice/workflows/%s/history/%s/view/variables/fields/0/' % (workflow.id, snapshot.id)
)
assert '>Submit<' not in resp
resp = app.get('/backoffice/workflows/%s/history/%s/view/variables/fields/' % (workflow.id, snapshot.id))
assert 'This workflow is readonly' in resp.text
assert 'sortable readonly' in resp.text
def test_pickle_erroneous_snapshot_object(pub):
# check snapshot object attribute is not restored
formdef = FormDef()
formdef.name = 'basic formdef'
formdef.snapshot_object = 'whatever'
formdef.store()
assert not hasattr(FormDef.get(formdef.id), 'snapshot_object')
def test_mail_template_snapshot_restore(pub):
create_superuser(pub)
create_role(pub)
app = login(get_app(pub))
mail_template = MailTemplate(name='test')
mail_template.store()
for i in range(2):
mail_template.name = 'test %s' % i
mail_template.store()
assert pub.snapshot_class.count() == 3
# restore as new
resp = app.get('/backoffice/workflows/mail-templates/%s/history/' % mail_template.id)
snapshot = pub.snapshot_class.select_object_history(mail_template)[2]
resp = resp.click(href='%s/restore' % snapshot.id)
assert resp.form['action'].value == 'as-new'
resp = resp.form.submit('submit')
assert MailTemplate.count() == 2
mail_template2 = MailTemplate.get(resp.location.split('/')[-2])
assert mail_template2.name == 'test'
assert mail_template2.id != mail_template.id
# restore over
resp = app.get('/backoffice/workflows/mail-templates/%s/history/' % mail_template.id)
snapshot = pub.snapshot_class.select_object_history(mail_template)[2]
resp = resp.click(href='%s/restore' % snapshot.id)
resp.form['action'].value = 'overwrite'
resp = resp.form.submit('submit')
assert MailTemplate.count() == 2
mail_template2 = MailTemplate.get(resp.location.split('/')[-2])
assert mail_template2.id == mail_template.id
def test_mail_template_snapshot_browse(pub):
create_superuser(pub)
create_role(pub)
MailTemplate.wipe()
mail_template = MailTemplate(name='test')
mail_template.store()
assert pub.snapshot_class.count() == 1
# check calling .store() without changes doesn't create snapshots
mail_template.store()
assert pub.snapshot_class.count() == 1
app = login(get_app(pub))
resp = app.get('/backoffice/workflows/mail-templates/%s/history/' % mail_template.id)
snapshot = pub.snapshot_class.select_object_history(mail_template)[0]
resp = resp.click(href='%s/view/' % snapshot.id)
assert 'This mail template is readonly' in resp.text
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
with pytest.raises(IndexError):
resp = resp.click('Edit')
def test_category_snapshot_browse(pub):
create_superuser(pub)
create_role(pub)
Category.wipe()
category = Category(name='test')
category.store()
assert pub.snapshot_class.count() == 1
# check calling .store() without changes doesn't create snapshots
category.store()
assert pub.snapshot_class.count() == 1
app = login(get_app(pub))
resp = app.get('/backoffice/forms/categories/%s/history/' % category.id)
snapshot = pub.snapshot_class.select_object_history(category)[0]
resp = resp.click(href='%s/view/' % snapshot.id)
assert 'This category is readonly' in resp.text
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
with pytest.raises(IndexError):
resp = resp.click('Edit')