applications: check legacy elements on install (#82373)
gitea/hobo/pipeline/head This commit looks good Details

This commit is contained in:
Lauréline Guérin 2023-10-19 16:33:39 +02:00
parent 2660050395
commit 3d1536687d
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
6 changed files with 235 additions and 94 deletions

View File

@ -365,11 +365,13 @@ class Version(models.Model):
# this version was already successfully installed, check itself
return self
def check_install(self, job=None):
# search for version to check
version_to_check = self.get_version_to_check()
if version_to_check is None:
return
def check_install(self, initial=False, job=None):
version_to_check = self
if not initial:
# search for version to check
version_to_check = self.get_version_to_check()
if version_to_check is None:
return
bundle_content = version_to_check.bundle.read()
target_url = 'api/export-import/bundle-check/'
@ -547,7 +549,9 @@ class AsyncJob(models.Model):
elif self.action == 'deploy':
self.version.deploy(self)
elif self.action == 'check-install':
self.version.check_install(self)
self.version.check_install(job=self)
elif self.action == 'check-first-install':
self.version.check_install(job=self, initial=True)
except ApplicationError as e:
self.status = 'failed'
self.exception = e.msg
@ -569,14 +573,18 @@ class AsyncJob(models.Model):
diffs = collections.defaultdict(dict)
not_found = collections.defaultdict(list)
no_history = collections.defaultdict(list)
result_legacy = []
for service in self.details.values():
for data in service.values():
for diff in data.get('differences') or []:
diffs[diff['type']][diff['slug']] = diff['url']
for unknown in data.get('unknown_elements') or []:
not_found[unknown['type']].append(unknown['slug'])
for history in data.get('no_history_elements') or []:
no_history[history['type']].append(history['slug'])
if self.action == 'check-install':
# don't look at differences etc on first install
for diff in data.get('differences') or []:
diffs[diff['type']][diff['slug']] = diff['url']
for unknown in data.get('unknown_elements') or []:
not_found[unknown['type']].append(unknown['slug'])
for history in data.get('no_history_elements') or []:
no_history[history['type']].append(history['slug'])
result_legacy += data.get('legacy_elements') or []
result_diffs = []
result_not_found = []
result_no_history = []
@ -594,6 +602,9 @@ class AsyncJob(models.Model):
relations,
key=lambda a: (a.auto_dependency, types.index(a.element.type), slugify(a.element.name)),
)
for legacy in result_legacy:
legacy['type_label'] = type_labels.get(legacy['type'])
result_legacy = sorted(result_legacy, key=lambda a: (types.index(a['type']), slugify(a['text'])))
# build information to display
for relation in relations:
diff = diffs.get(relation.element.type, {}).get(relation.element.slug)
@ -603,4 +614,4 @@ class AsyncJob(models.Model):
result_not_found.append(relation)
if relation.element.slug in no_history.get(relation.element.type, []):
result_no_history.append(relation)
return result_diffs, result_not_found, result_no_history
return result_diffs, result_not_found, result_no_history, result_legacy

View File

@ -3,11 +3,11 @@
{% block breadcrumb %}
{{ block.super }}
<a href="{% url 'application-confirm-install' app_slug=app.slug version_pk=version.pk %}">{% trans "Update" %}</a>
<a href="{% url 'application-confirm-install' app_slug=app.slug version_pk=version.pk %}">{% if last_job.action == 'check-install' %}{% trans "Update" %}{% else %}{% trans "Install" %}{% endif %}</a>
{% endblock %}
{% block appbar %}
<h2>{{ app.name }} - {% trans "Update" %}</h2>
<h2>{{ app.name }} - {% if last_job.action == 'check-install' %}{% trans "Update" %}{% else %}{% trans "Install" %}{% endif %}</h2>
{% endblock %}
{% block content %}
@ -21,7 +21,7 @@
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="buttons">
{% if last_job.status == 'completed' %}<button class="submit-button">{% trans 'Run update' %}</button>{% endif %}
{% if last_job.status == 'completed' %}<button class="submit-button">{% if last_job.action == 'check-install' %}{% trans "Run update" %}{% else %}{% trans "Run install" %}{% endif %}</button>{% endif %}
<a class="cancel" href="{% url 'application-manifest' app_slug=app.slug %}">{% trans 'Cancel' %}</a>
</div>
</form>

View File

@ -1,23 +1,31 @@
{% load i18n %}
<div class="pk-information">
{% if diffs %}
<p>{% trans "Local changes:" %}</p>
<ul>
{% for relation, url in diffs %}
<li>
{% blocktrans with type_label=relation.element.type_label name=relation.element.name %}{{ type_label }} "{{ name }}" {% endblocktrans %}
(<a href="{{ url }}">{% trans "see changes" %}</a>)
</li>
{% endfor %}
</ul>
<p>{% trans "If you run the update, all changes will be lost." %}</p>
{% else %}
<p>{% trans "No local changes found." %}</p>
{% endif %}
</div>
{% if diffs or not legacy %}
<div class="pk-information">
{% if diffs %}
<p>{% trans "Local changes:" %}</p>
<ul>
{% for relation, url in diffs %}
<li>
{% blocktrans with type_label=relation.element.type_label name=relation.element.name %}{{ type_label }} "{{ name }}" {% endblocktrans %}
(<a href="{{ url }}">{% trans "see changes" %}</a>)
</li>
{% endfor %}
</ul>
{% if not is_report %}
{% if last_job.action == 'check-install' %}
<p>{% trans "If you run the update, all changes will be lost." %}</p>
{% else %}
<p>{% trans "If you run the install, all changes will be lost." %}</p>
{% endif %}
{% endif %}
{% else %}
<p>{% trans "No local changes found." %}</p>
{% endif %}
</div>
{% endif %}
{% if not_found %}
<div class="pk-attention">
<p>{% trans "Not found elements (removed ?):" %}</p>
<p>{% trans "Not found components (removed ?):" %}</p>
<ul>
{% for relation in not_found %}
<li>
@ -29,7 +37,7 @@
{% endif %}
{% if no_history %}
<div class="pk-attention">
<p>{% trans "Impossible to check local changes for these elements (no history found for this application version):" %}</p>
<p>{% trans "Impossible to check local changes for these components (no history found for this application version):" %}</p>
<ul>
{% for relation in no_history %}
<li>
@ -39,3 +47,23 @@
</ul>
</div>
{% endif %}
{% if legacy %}
<div class="pk-attention">
<p>{% trans "These components alreay exist outside the application:" %}</p>
<ul>
{% for element in legacy %}
<li>
{% blocktrans with type_label=element.type_label name=element.text %}{{ type_label }} "{{ name }}" {% endblocktrans %}
(<a href="{{ element.url }}">{% trans "see component" %}</a>)
</li>
{% endfor %}
</ul>
{% if not is_report %}
{% if last_job.action == 'check-install' %}
<p>{% trans "If you run the update, theese components will be reinstalled." %}</p>
{% else %}
<p>{% trans "If you run the install, theese components will be reinstalled." %}</p>
{% endif %}
{% endif %}
</div>
{% endif %}

View File

@ -31,7 +31,7 @@
{% endif %}
- {% trans "status:" %} {{ job.get_status_display }}
{% if job.status == 'failed' %}[{{ job.exception|truncatechars:50 }}]{% endif %}
{% if job.action == 'check-install' and job.status == 'completed' %}- <a href="{% url 'application-async-job-diffs' app_slug=app.slug pk=job.pk %}">{% trans "see local changes" %}</a>{% endif %}
{% if job.action == 'check-install' or job.action == 'check-first-install' %}{% if job.status == 'completed' %}- <a href="{% url 'application-async-job-diffs' app_slug=app.slug pk=job.pk %}">{% trans "see report" %}</a>{% endif %}{% endif %}
</li>
{% endfor %}
</ul>

View File

@ -353,19 +353,18 @@ class Install(FormView):
version.save()
self.version = version
if not created:
# check if some objects where locally modified
job = AsyncJob(
label=_('Check local modifications'),
application=self.application,
version=self.version,
action='check-install',
)
job.save()
try:
job.run()
except ApplicationError:
pass
# check if some objects where locally modified or already exist outside the application
job = AsyncJob(
label=_('Check installation'),
application=self.application,
version=self.version,
action='check-first-install' if created else 'check-install',
)
job.save()
try:
job.run()
except ApplicationError:
pass
return super().form_valid(form)
@ -409,35 +408,38 @@ class ConfirmInstall(TemplateView):
kwargs['last_job'] = self.last_job
if self.last_job:
diffs, not_found, no_history = self.last_job.get_diff_details()
diffs, not_found, no_history, legacy = self.last_job.get_diff_details()
kwargs['diffs'] = diffs
kwargs['not_found'] = not_found
kwargs['no_history'] = no_history
kwargs['legacy'] = legacy
return super().get_context_data(**kwargs)
def get(self, request, *args, **kwargs):
if self.last_job is None or self.last_job.action != 'check-install':
if self.last_job is None or self.last_job.action not in ['check-install', 'check-first-install']:
return self.install()
if self.last_job.status == 'completed':
no_history_only = True
no_legacy = True
for service in self.last_job.details.values():
for data in service.values():
if data.get('differences'):
no_history_only = False
break
if data.get('unknown_elements'):
no_history_only = False
break
if data.get('legacy_elements'):
no_history_only = False
no_legacy = False
if not data.get('no_history_elements'):
no_history_only = False
break
if no_history_only is False:
break
if no_history_only is True:
# legacy app, no application information found in elements history, jump to update page.
# legacy app, no application information found in elements history, jump to update page
return self.install()
if self.last_job.action == 'check-first-install' and no_legacy:
# first install, no legacy elements, jump to install page
return self.install()
return super().get(request, *args, **kwargs)
@ -578,16 +580,22 @@ class AsyncJobDiffsView(DetailView):
return (
super()
.get_queryset()
.filter(application__slug=self.kwargs['app_slug'], action='check-install', status='completed')
.filter(
application__slug=self.kwargs['app_slug'],
action__in=['check-install', 'check-first-install'],
status='completed',
)
)
def get_context_data(self, **kwargs):
kwargs['app'] = self.object.application
diffs, not_found, no_history = self.object.get_diff_details()
diffs, not_found, no_history, legacy = self.object.get_diff_details()
kwargs['diffs'] = diffs
kwargs['not_found'] = not_found
kwargs['no_history'] = no_history
kwargs['legacy'] = legacy
kwargs['is_report'] = True
return super().get_context_data(**kwargs)

View File

@ -233,6 +233,7 @@ def mocked_http(url, request):
'differences': [],
'unknown_elements': [],
'no_history_elements': [{'type': 'forms', 'slug': 'legacy'}],
'legacy_elements': [],
}
}
),
@ -904,14 +905,33 @@ def test_deploy_application(app, admin_user, settings, app_bundle, app_bundle_wi
login(app)
def mocked_http2(url, request):
if url.path == '/api/export-import/bundle-check/':
return {
'content': json.dumps(
{
'data': {
'differences': [],
'unknown_elements': [],
'no_history_elements': [{'type': 'forms', 'slug': 'legacy'}],
'legacy_elements': [
{'type': 'cards', 'slug': 'test', 'text': 'Test', 'url': 'http://foobar'}
],
}
}
),
'status_code': 200,
}
return mocked_http(url, request)
if action == 'Update':
Application.objects.create(name='Test', slug='test', editable=False)
def install(resp, bundle):
resp = resp.click(action)
resp.form['bundle'] = Upload('app.tar', bundle, 'application/x-tar')
with StatefulHTTMock(mocked_http):
resp = resp.form.submit().follow()
with StatefulHTTMock(mocked_http2):
resp = resp.form.submit()
assert Application.objects.count() == 1
application = Application.objects.get(slug='test')
@ -927,12 +947,44 @@ def test_deploy_application(app, admin_user, settings, app_bundle, app_bundle_wi
else:
assert version.number == '42.0'
assert version.notes == 'foo bar blah'
if action == 'Install':
job = AsyncJob.objects.latest('pk')
assert resp.location.endswith('/applications/manifest/test/confirm/%s/' % version.pk)
with StatefulHTTMock(mocked_http2):
resp = resp.follow()
assert 'Local changes:' not in resp
assert 'Not found components (removed ?):' not in resp
assert 'Impossible to check local changes for these components' not in resp
assert 'These components alreay exist outside the application' in resp
assert 'Card Model "Test"' in resp
assert '(<a href="http://foobar">see component</a>)' in resp
with StatefulHTTMock(mocked_http2):
resp = resp.form.submit()
assert resp.location.endswith('/applications/manifest/test/')
with StatefulHTTMock(mocked_http2):
resp = app.get('/applications/manifest/test/job/%s/check-diffs/' % job.pk)
assert 'Local changes:' not in resp
assert 'Not found components (removed ?):' not in resp
assert 'Impossible to check local changes for these components' not in resp
assert 'These components alreay exist outside the application' in resp
assert 'Card Model "Test"' in resp
assert '(<a href="http://foobar">see component</a>)' in resp
else:
with StatefulHTTMock(mocked_http2):
resp = resp.follow()
if resp.location:
resp.follow()
else:
resp.form.submit()
assert application.elements.count() == 4
job = AsyncJob.objects.latest('pk')
assert job.status == 'completed'
assert job.progression_urls == {'wcs': {'Foobar': 'https://wcs.example.invalid/api/jobs/job-uuid/'}}
assert Parameter.objects.filter(application=application).count() == 2
assert Variable.objects.filter(name__startswith='app_').count() == 2
# check forms are not marked as error (the other object types are not altered in the mocked responses
# and would be marked as errors)
assert [x.error for x in application.relation_set.filter(element__type='forms')] == [False]
@ -940,15 +992,21 @@ def test_deploy_application(app, admin_user, settings, app_bundle, app_bundle_wi
resp = app.get('/applications/')
if action == 'Update':
with StatefulHTTMock(mocked_http):
with StatefulHTTMock(mocked_http2):
resp = resp.click(href='/manifest/test/')
version1 = install(resp, app_bundle)
if action == 'Update':
# update, check-install job then deploy job
assert list(AsyncJob.objects.values_list('action', flat=True)) == ['check-install', 'deploy']
assert list(AsyncJob.objects.values_list('action', flat=True).order_by('pk')) == [
'check-install',
'deploy',
]
else:
# first install, just deploy job
assert list(AsyncJob.objects.values_list('action', flat=True)) == ['deploy']
assert list(AsyncJob.objects.values_list('action', flat=True).order_by('pk')) == [
'check-first-install',
'deploy',
]
assert version1.application.version_set.count() == 1
version2 = install(resp, app_bundle)
assert version2.application.version_set.count() == 1
@ -974,15 +1032,16 @@ def test_deploy_application(app, admin_user, settings, app_bundle, app_bundle_wi
def response_content(url, request):
if url.path == '/api/export-import/bundle-import/':
return {'status_code': 500}
return mocked_http(url, request)
return mocked_http2(url, request)
resp = app.get('/applications/')
if action == 'Update':
with StatefulHTTMock(mocked_http):
with StatefulHTTMock(mocked_http2):
resp = resp.click(href='manifest/test/')
resp = resp.click(action)
resp.form['bundle'] = Upload('app.tar', app_bundle, 'application/x-tar')
with StatefulHTTMock(response_content):
resp = resp.form.submit().follow()
with pytest.raises(DeploymentError) as e:
resp.form.submit().follow()
assert str(e.value) == 'Failed to deploy module wcs (500)'
@ -1012,15 +1071,16 @@ def test_deploy_application(app, admin_user, settings, app_bundle, app_bundle_wi
'content': {'data': [form_def]},
'status_code': 200,
}
return mocked_http(url, request)
return mocked_http2(url, request)
resp = app.get('/applications/')
if action == 'Update':
with StatefulHTTMock(mocked_http):
with StatefulHTTMock(mocked_http2):
resp = resp.click(href='manifest/test/')
resp = resp.click(action)
resp.form['bundle'] = Upload('app.tar', app_bundle, 'application/x-tar')
with StatefulHTTMock(response_content):
resp = resp.form.submit().follow()
resp.form.submit().follow()
application = Application.objects.get(slug='test')
elements = application.elements.all().order_by('type')
@ -1033,15 +1093,16 @@ def test_deploy_application(app, admin_user, settings, app_bundle, app_bundle_wi
def response_content(url, request): # noqa pylint: disable=function-redefined
if url.path == '/api/export-import/forms/':
return {'status_code': 500}
return mocked_http(url, request)
return mocked_http2(url, request)
resp = app.get('/applications/')
if action == 'Update':
with StatefulHTTMock(mocked_http):
with StatefulHTTMock(mocked_http2):
resp = resp.click(href='manifest/test/')
resp = resp.click(action)
resp.form['bundle'] = Upload('app.tar', app_bundle, 'application/x-tar')
with StatefulHTTMock(response_content):
resp = resp.form.submit().follow()
with pytest.raises(ScanError) as e:
resp.form.submit().follow()
assert str(e.value) == 'Failed to get elements of type forms (500)'
@ -1052,7 +1113,7 @@ def test_deploy_application(app, admin_user, settings, app_bundle, app_bundle_wi
# bad file format
resp = app.get('/applications/')
if action == 'Update':
with StatefulHTTMock(mocked_http):
with StatefulHTTMock(mocked_http2):
resp = resp.click(href='manifest/test/')
resp = resp.click(action)
resp.form['bundle'] = Upload('app.tar', b'garbage', 'application/x-tar')
@ -1101,7 +1162,9 @@ def test_update_application(app, admin_user, settings, app_bundle):
assert resp.location.endswith('/applications/manifest/test/confirm/%s/' % last_version.pk)
resp = resp.follow()
assert resp.location.endswith('/applications/manifest/test/')
assert list(AsyncJob.objects.filter(version__number='42.0').values_list('action', flat=True)) == [
assert list(
AsyncJob.objects.filter(version__number='42.0').values_list('action', flat=True).order_by('pk')
) == [
'check-install',
'deploy',
]
@ -1115,6 +1178,9 @@ def test_update_application(app, admin_user, settings, app_bundle):
'differences': [{'type': 'forms', 'slug': 'test', 'url': 'http://foobar'}],
'unknown_elements': [{'type': 'workflows', 'slug': 'test'}],
'no_history_elements': [{'type': 'blocks', 'slug': 'test'}],
'legacy_elements': [
{'type': 'cards', 'slug': 'test', 'text': 'Test', 'url': 'http://foobar'}
],
}
}
),
@ -1130,23 +1196,28 @@ def test_update_application(app, admin_user, settings, app_bundle):
last_version = Version.objects.latest('pk')
assert resp.location.endswith('/applications/manifest/test/confirm/%s/' % last_version.pk)
resp = resp.follow()
assert list(AsyncJob.objects.filter(version__number='42.0').values_list('action', flat=True)) == [
'check-install'
]
assert list(
AsyncJob.objects.filter(version__number='42.0').values_list('action', flat=True).order_by('pk')
) == ['check-install']
job = AsyncJob.objects.latest('pk')
assert 'Local changes:' in resp
assert 'Form "test"' in resp
assert '(<a href="http://foobar">see changes</a>)' in resp
assert 'Not found elements (removed ?):' in resp
assert 'Not found components (removed ?):' in resp
assert 'Workflow "test"' in resp
assert (
'Impossible to check local changes for these elements (no history found for this application version):'
'Impossible to check local changes for these components (no history found for this application version):'
in resp
)
assert 'Block of fields "test"' in resp
assert 'These components alreay exist outside the application' in resp
assert 'Card Model "Test"' in resp
assert '(<a href="http://foobar">see component</a>)' in resp
resp = resp.form.submit()
assert resp.location.endswith('/applications/manifest/test/')
assert list(AsyncJob.objects.filter(version__number='42.0').values_list('action', flat=True)) == [
assert list(
AsyncJob.objects.filter(version__number='42.0').values_list('action', flat=True).order_by('pk')
) == [
'check-install',
'deploy',
]
@ -1155,13 +1226,16 @@ def test_update_application(app, admin_user, settings, app_bundle):
assert 'Local changes:' in resp
assert 'Form "test"' in resp
assert '(<a href="http://foobar">see changes</a>)' in resp
assert 'Not found elements (removed ?):' in resp
assert 'Not found components (removed ?):' in resp
assert 'Workflow "test"' in resp
assert (
'Impossible to check local changes for these elements (no history found for this application version):'
'Impossible to check local changes for these components (no history found for this application version):'
in resp
)
assert 'Block of fields "test"' in resp
assert 'These components alreay exist outside the application' in resp
assert 'Card Model "Test"' in resp
assert '(<a href="http://foobar">see component</a>)' in resp
def response_content(url, request): # noqa pylint: disable=function-redefined
if url.path == '/api/export-import/bundle-check/':
@ -1177,9 +1251,9 @@ def test_update_application(app, admin_user, settings, app_bundle):
'/applications/manifest/test/confirm/%s/' % Version.objects.latest('pk').pk
)
resp = resp.follow()
assert list(AsyncJob.objects.filter(version__number='42.0').values_list('action', flat=True)) == [
'check-install'
]
assert list(
AsyncJob.objects.filter(version__number='42.0').values_list('action', flat=True).order_by('pk')
) == ['check-install']
assert 'Error checking local changes, update can not be run.' in resp
def response_content(url, request): # noqa pylint: disable=function-redefined
@ -1416,29 +1490,34 @@ def test_deploy_application_roles(app, admin_user, settings, app_bundle_roles):
resp.form['bundle'] = Upload('app.tar', app_bundle_roles, 'application/x-tar')
with StatefulHTTMock(httmock.remember_called(mocked_http)):
resp = resp.form.submit().follow()
# roles
assert mocked_http.call['requests'][0].url.startswith(
'https://idp.example.invalid/api/roles/?update'
'https://wcs.example.invalid/api/export-import/bundle-check/?'
)
# roles
assert mocked_http.call['requests'][1].url.startswith(
'https://idp.example.invalid/api/provision/'
'https://idp.example.invalid/api/roles/?update'
)
assert mocked_http.call['requests'][2].url.startswith(
'https://idp.example.invalid/api/roles/?update'
'https://idp.example.invalid/api/provision/'
)
assert mocked_http.call['requests'][3].url.startswith(
'https://idp.example.invalid/api/roles/?update'
)
assert mocked_http.call['requests'][4].url.startswith(
'https://idp.example.invalid/api/provision/'
)
# then form
assert 'wcs.example.invalid' in mocked_http.call['requests'][4].url
assert mocked_http.call['requests'][5].url.startswith(
'https://wcs.example.invalid/api/export-import/bundle-import/?'
)
assert mocked_http.call['requests'][6].url.startswith(
'https://wcs.example.invalid/api/export-import/?'
)
# then element refresh
available_objects = [o for o in WCS_AVAILABLE_OBJECTS['data'] if o['id'] in ['forms', 'roles']]
assert mocked_http.call['count'] == 5 + 1 + len(available_objects)
assert mocked_http.call['count'] == 6 + 1 + len(available_objects)
for i, object_type in enumerate(available_objects):
assert mocked_http.call['requests'][6 + i].url.startswith(
assert mocked_http.call['requests'][7 + i].url.startswith(
'https://wcs.example.invalid/api/export-import/%s/?' % object_type['id']
)
@ -1461,6 +1540,20 @@ def test_deploy_application_roles(app, admin_user, settings, app_bundle_roles):
assert job.exception == 'Failed to create role test-role (500)'
def response_content(url, request): # noqa pylint: disable=function-redefined
if url.path == '/api/export-import/bundle-check/':
return {
'content': json.dumps(
{
'data': {
'differences': [],
'unknown_elements': [],
'no_history_elements': [],
'legacy_elements': [],
}
}
),
'status_code': 200,
}
if url.path == '/api/provision/':
return {'status_code': 500}
return mocked_http(url, request)
@ -1468,6 +1561,7 @@ def test_deploy_application_roles(app, admin_user, settings, app_bundle_roles):
resp = app.get('/applications/install/')
resp.form['bundle'] = Upload('app.tar', app_bundle_roles, 'application/x-tar')
with StatefulHTTMock(response_content):
resp = resp.form.submit().follow()
with pytest.raises(DeploymentError) as e:
resp.form.submit().follow()
assert str(e.value) == 'Failed to provision role test-role (500)'
@ -1482,12 +1576,12 @@ def test_deploy_application_roles(app, admin_user, settings, app_bundle_roles):
resp = resp.click('Install')
resp.form['bundle'] = Upload('app.tar', app_bundle_roles, 'application/x-tar')
with StatefulHTTMock(httmock.remember_called(mocked_http)):
resp.form.submit().follow()
resp.form.submit().follow().follow()
# ou must be specified when calling authentic API
assert mocked_http.call['requests'][0].url.startswith(
assert mocked_http.call['requests'][1].url.startswith(
'https://idp.example.invalid/api/roles/?update_or_create=slug&update_or_create=ou'
)
assert b'"ou": "service-ou"' in mocked_http.call['requests'][0].body
assert b'"ou": "service-ou"' in mocked_http.call['requests'][1].body
# test import on a default OU
Application.objects.all().delete()
@ -1498,8 +1592,8 @@ def test_deploy_application_roles(app, admin_user, settings, app_bundle_roles):
v.service_pk = wcs.id
v.save()
with StatefulHTTMock(httmock.remember_called(mocked_http)):
resp.form.submit().follow()
assert b'"ou": "default"' in mocked_http.call['requests'][0].body
resp.form.submit().follow().follow()
assert b'"ou": "default"' in mocked_http.call['requests'][1].body
def test_job_status_page(app, admin_user, settings):