diff options
-rw-r--r-- | src/booking/tests/test_quick_booking.py | 58 | ||||
-rw-r--r-- | src/dashboard/testing_utils.py | 25 | ||||
-rw-r--r-- | src/dashboard/views.py | 2 | ||||
-rw-r--r-- | src/templates/config_bundle/steps/define_software.html | 25 | ||||
-rw-r--r-- | src/workflow/forms.py | 13 | ||||
-rw-r--r-- | src/workflow/models.py | 9 | ||||
-rw-r--r-- | src/workflow/tests/test_fixtures.py | 2 | ||||
-rw-r--r-- | src/workflow/tests/test_steps.py | 391 | ||||
-rw-r--r-- | src/workflow/tests/test_steps_render.py | 43 | ||||
-rw-r--r-- | src/workflow/tests/test_workflows.py | 7 | ||||
-rw-r--r-- | src/workflow/urls.py | 12 | ||||
-rw-r--r-- | src/workflow/views.py | 3 |
12 files changed, 305 insertions, 285 deletions
diff --git a/src/booking/tests/test_quick_booking.py b/src/booking/tests/test_quick_booking.py index e445860..5ba1744 100644 --- a/src/booking/tests/test_quick_booking.py +++ b/src/booking/tests/test_quick_booking.py @@ -8,6 +8,7 @@ ############################################################################## import datetime +import json from django.test import TestCase, Client @@ -30,7 +31,9 @@ from dashboard.testing_utils import ( class QuickBookingValidFormTestCase(TestCase): @classmethod def setUpTestData(cls): - cls.user = make_user(False, username="newtestuser", password="testpassword") + cls.user = make_user(False, username="newtestuser") + cls.user.set_password("testpassword") + cls.user.save() make_user_profile(cls.user, True) lab_user = make_user(True) @@ -51,7 +54,20 @@ class QuickBookingValidFormTestCase(TestCase): @classmethod def build_post_data(cls): return { - 'filter_field': '{"hosts":[{"host_' + str(cls.host_profile.id) + '":"true"}], "labs": [{"lab_' + str(cls.lab.lab_user.id) + '":"true"}]}', + 'filter_field': json.dumps({ + "host": { + "host_" + str(cls.host_profile.id): { + "selected": True, + "id": cls.host_profile.id + } + }, + "lab": { + "lab_" + str(cls.lab.lab_user.id): { + "selected": True, + "id": cls.lab.lab_user.id + } + } + }), 'purpose': 'my_purpose', 'project': 'my_project', 'length': '3', @@ -70,7 +86,7 @@ class QuickBookingValidFormTestCase(TestCase): return response def setUp(self): - self.client.login(username=self.user.username, password="testpassword") + self.client.login(username="newtestuser", password="testpassword") def assertValidBooking(self, booking): self.assertEqual(booking.owner, self.user) @@ -116,13 +132,40 @@ class QuickBookingValidFormTestCase(TestCase): self.assertIsNone(Booking.objects.first()) def test_with_invalid_host_id(self): - response = self.post({'filter_field': '{"hosts":[{"host_' + str(self.host_profile.id + 100) + '":"true"}], "labs": [{"lab_' + str(self.lab.lab_user.id) + '":"true"}]}'}) + response = self.post({'filter_field': json.dumps({ + "host": { + "host_" + str(self.host_profile.id + 100): { + "selected": True, + "id": self.host_profile.id + 100 + } + }, + "lab": { + "lab_" + str(self.lab.lab_user.id): { + "selected": True, + "id": self.lab.lab_user.id + } + } + })}) self.assertEqual(response.status_code, 200) self.assertIsNone(Booking.objects.first()) def test_with_invalid_lab_id(self): response = self.post({'filter_field': '{"hosts":[{"host_' + str(self.host_profile.id) + '":"true"}], "labs": [{"lab_' + str(self.lab.lab_user.id + 100) + '":"true"}]}'}) + response = self.post({'filter_field': json.dumps({ + "host": { + "host_" + str(self.host_profile.id): { + "selected": True, + "id": self.host_profile.id + } + }, + "lab": { + "lab_" + str(self.lab.lab_user.id + 100): { + "selected": True, + "id": self.lab.lab_user.id + 100 + } + } + })}) self.assertEqual(response.status_code, 200) self.assertIsNone(Booking.objects.first()) @@ -134,17 +177,16 @@ class QuickBookingValidFormTestCase(TestCase): self.assertIsNone(Booking.objects.first()) def test_with_garbage_users_field(self): # expected behavior: treat as though field is empty if it has garbage data - response = self.post({'users': 'X�]QP�槰DP�+m���h�U�_�yJA:.rDi��QN|.��C��n�P��F!��D�����5ȅj�9�LV��'}) # output from /dev/urandom + response = self.post({'users': ['X�]QP�槰DP�+m���h�U�_�yJA:.rDi��QN|.��C��n�P��F!��D�����5ȅj�9�LV��']}) # output from /dev/urandom self.assertEqual(response.status_code, 200) booking = Booking.objects.first() - self.assertIsNotNone(booking) - self.assertValidBooking(booking) + self.assertIsNone(booking) def test_with_valid_form(self): response = self.post() - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, 302) # success should redirect booking = Booking.objects.first() self.assertIsNotNone(booking) self.assertValidBooking(booking) diff --git a/src/dashboard/testing_utils.py b/src/dashboard/testing_utils.py index a96b6d0..0f52daa 100644 --- a/src/dashboard/testing_utils.py +++ b/src/dashboard/testing_utils.py @@ -103,12 +103,16 @@ def make_config_bundle(grb, owner, topology={}, host_set={}, cb = ConfigBundle.objects.create( owner=owner, name="config bundle " + str(ConfigBundle.objects.count()), - description="cb generated by make_config_bundle() method" + description="cb generated by make_config_bundle() method", + bundle=grb ) + scen = scenario or Scenario.objects.first() or make_scenario() + inst = installer or Installer.objects.first() or make_installer([scen]) + opnfv_config = OPNFVConfig.objects.create( - installer=installer, - scenario=scenario, + installer=inst, + scenario=scen, bundle=cb ) @@ -194,7 +198,8 @@ def make_generic_host(grb, host_profile, hostname): def make_user(is_superuser=False, username="testuser", password="testpassword", email="default_email@user.com"): - user = User.objects.create_user(username=username, email=email, password=password) + user = User.objects.get_or_create(username=username, email=email, password=password)[0] + user.is_superuser = is_superuser user.save() @@ -204,14 +209,14 @@ def make_user(is_superuser=False, username="testuser", def make_user_profile(user=None, email_addr="email@email.com", company="company", full_name="John Doe", booking_privledge=True, ssh_file=None): - user = user or User.objects.first() or make_user() - profile = UserProfile.objects.create( + user = user or make_user() + profile = UserProfile.objects.get_or_create( email_addr=email_addr, company=company, full_name=full_name, booking_privledge=booking_privledge, user=user - ) + )[0] profile.ssh_public_key.save("user_ssh_key", ssh_file if ssh_file else ContentFile("public key content string")) return profile @@ -238,7 +243,7 @@ def make_lab(user=None, name="Test_Lab_Instance", vlan_manager = make_vlan_manager() if not user: - user = make_user() + user = make_user(username=name + " user") lab = Lab.objects.create( lab_user=user, @@ -354,7 +359,9 @@ def make_installer(scenarios, name="test installer"): return installer -def make_os(installers, name="test OS"): +def make_os(installers=None, name="test OS"): + if not installers: + installers = [make_installer([make_scenario()])] os = Opsys.objects.create(name=name) for installer in installers: os.sup_installers.add(installer) diff --git a/src/dashboard/views.py b/src/dashboard/views.py index 9416cb4..c387251 100644 --- a/src/dashboard/views.py +++ b/src/dashboard/views.py @@ -12,12 +12,10 @@ from django.shortcuts import get_object_or_404 from django.views.generic import TemplateView from django.shortcuts import render -from django.http import HttpResponseRedirect from account.models import Lab from resource_inventory.models import Image, HostProfile -from workflow.views import create_session from workflow.workflow_manager import ManagerTracker diff --git a/src/templates/config_bundle/steps/define_software.html b/src/templates/config_bundle/steps/define_software.html index 7c47569..43f3f5d 100644 --- a/src/templates/config_bundle/steps/define_software.html +++ b/src/templates/config_bundle/steps/define_software.html @@ -27,20 +27,19 @@ {% block tablejs %} <script> - document.getElementById("radio_{{headnode}}").checked = true; + function radio_pre_submit(){ + var parents = document.getElementsByClassName("table_hidden_input_parent"); + for(const node of parents){ + const radio = node.getElementsByClassName("my_radio")[0]; + const checkbox = radio.nextElementSibling; + if(radio.checked){ + checkbox.value = "True"; + } + } + } + + form_submission_callbacks.push(radio_pre_submit); </script> {% endblock tablejs %} - - -{% block onleave %} -var parents = document.getElementsByClassName("table_hidden_input_parent"); -for(var i=0; i<parents.length; i++){ - var node = parents[i]; - var radio = node.getElementsByClassName("my_radio")[0]; - var checkbox = radio.nextElementSibling; - if(radio.checked){ - checkbox.value = "True"; - } -} diff --git a/src/workflow/forms.py b/src/workflow/forms.py index a2746f9..4d5e9e2 100644 --- a/src/workflow/forms.py +++ b/src/workflow/forms.py @@ -121,7 +121,12 @@ class SearchableSelectMultipleField(forms.Field): raise ValidationError("Nothing was selected") else: return [] - data_as_list = json.loads(data) + try: + data_as_list = json.loads(data) + except json.decoder.JSONDecodeError: + data_as_list = None + if not data_as_list: + raise ValidationError("Contents Not JSON") if self.selectable_limit != -1: if len(data_as_list) > self.selectable_limit: raise ValidationError("Too many items were selected") @@ -271,7 +276,11 @@ class MultipleSelectFilterField(forms.Field): super().__init__(**kwargs) def to_python(self, value): - return json.loads(value) + try: + return json.loads(value) + except json.decoder.JSONDecodeError: + pass + raise ValidationError("content is not valid JSON") class FormUtils: diff --git a/src/workflow/models.py b/src/workflow/models.py index 0521165..9d1fac2 100644 --- a/src/workflow/models.py +++ b/src/workflow/models.py @@ -224,18 +224,9 @@ class WorkflowStep(object): template = get_template(self.template) return template.render(self.get_context(), request) - def post_render(self, request): - self.post(request.POST, request.user) - return self.render(request) - def post(self, post_content, user): raise Exception("WorkflowStep subclass of type " + str(type(self)) + " has no concrete post() method") - def test_render(self, request): - if request.method == "POST": - return self.post_render(request) - return self.render(request) - def validate(self, request): pass diff --git a/src/workflow/tests/test_fixtures.py b/src/workflow/tests/test_fixtures.py new file mode 100644 index 0000000..fe16be7 --- /dev/null +++ b/src/workflow/tests/test_fixtures.py @@ -0,0 +1,2 @@ + +MX_GRAPH_MODEL = '<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="host_c" value="{"name":"c","description":"Intel based ProLiant server from HPE"}" style="editable=0" parent="1" vertex="1" connectable="0"><mxGeometry x="75" y="150" width="110" height="175" as="geometry"><mxPoint x="-50" as="offset"/></mxGeometry></mxCell><mxCell id="2" value="{"name":"ens4f1","description":"speed: 10000M type: onboard"}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="12" width="20" height="20" as="geometry"><mxPoint x="-26" as="offset"/></mxGeometry></mxCell><mxCell id="3" value="{"name":"ens4f0","description":"speed: 10000M type: onboard"}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="37" width="20" height="20" as="geometry"><mxPoint x="-26" as="offset"/></mxGeometry></mxCell><mxCell id="4" value="{"name":"ens1f2","description":"speed: 10000M type: onboard"}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="62" width="20" height="20" as="geometry"><mxPoint x="-26" as="offset"/></mxGeometry></mxCell><mxCell id="5" value="{"name":"ens1f1","description":"speed: 10000M type: onboard"}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="87" width="20" height="20" as="geometry"><mxPoint x="-26" as="offset"/></mxGeometry></mxCell><mxCell id="6" value="{"name":"ens1f0","description":"speed: 10000M type: onboard"}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="112" width="20" height="20" as="geometry"><mxPoint x="-26" as="offset"/></mxGeometry></mxCell><mxCell id="7" value="{"name":"eno49","description":"speed: 10000M type: onboard"}" style="fillColor=blue;editable=0" parent="host_c" vertex="1"><mxGeometry x="90" y="137" width="20" height="20" as="geometry"><mxPoint x="-22" as="offset"/></mxGeometry></mxCell><mxCell id="network_0" value="{"name":"public","public":true}" style="fillColor=red" parent="1" vertex="1"><mxGeometry x="400" y="-10" width="10" height="1700" as="geometry"/></mxCell><mxCell id="8" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="9" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.022222222222222223" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="10" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.044444444444444446" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="11" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.06666666666666667" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="12" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.08888888888888889" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="13" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.11111111111111112" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="14" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.13333333333333333" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="15" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.15555555555555556" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="16" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.17777777777777778" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="17" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.2" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="18" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.22222222222222224" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="19" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.24444444444444446" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="20" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.26666666666666666" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="21" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.2888888888888889" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="22" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.3111111111111111" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="23" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.33333333333333337" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="24" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.35555555555555557" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="25" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.37777777777777777" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="26" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.4" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="27" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.4222222222222222" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="28" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.4444444444444445" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="29" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.4666666666666667" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="30" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.48888888888888893" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="31" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.5111111111111112" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="32" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.5333333333333333" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="33" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.5555555555555556" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="34" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.5777777777777778" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="35" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.6" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="36" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.6222222222222222" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="37" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.6444444444444445" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="38" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.6666666666666667" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="39" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.6888888888888889" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="40" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.7111111111111111" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="41" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.7333333333333334" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="42" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.7555555555555555" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="43" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.7777777777777778" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="44" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.8" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="45" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.8222222222222223" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="46" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.8444444444444444" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="47" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.8666666666666667" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="48" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.888888888888889" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="49" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.9111111111111111" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="50" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.9333333333333333" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="51" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.9555555555555556" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="52" value="" style="fillColor=black;opacity=0" parent="network_0" vertex="1"><mxGeometry y="0.9777777777777779" width="10" height="37.77777777777778" relative="1" as="geometry"/></mxCell><mxCell id="53" value="{"tagged":false}" style="strokeColor=red" parent="1" source="2" target="13" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell></root></mxGraphModel>' diff --git a/src/workflow/tests/test_steps.py b/src/workflow/tests/test_steps.py index cb676c7..39b1f86 100644 --- a/src/workflow/tests/test_steps.py +++ b/src/workflow/tests/test_steps.py @@ -7,229 +7,256 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -from django.test import TestCase -from dashboard.populate_db import Populator -from workflow.tests import constants -from workflow.workflow_factory import WorkflowFactory +""" +This file tests basic functionality of each step class +More in depth case coverage of WorkflowStep.post() must happen elsewhere. +""" + +import json +from unittest import SkipTest, mock + +from django.test import TestCase, RequestFactory +from dashboard.testing_utils import make_lab, make_user, make_os,\ + make_complete_host_profile, make_opnfv_role, make_image, make_grb,\ + make_config_bundle, make_host, make_user_profile, make_generic_host +from workflow import resource_bundle_workflow +from workflow import booking_workflow +from workflow import sw_bundle_workflow from workflow.models import Repository -from workflow.resource_bundle_workflow import Define_Hardware, Define_Nets, Resource_Meta_Info -from workflow.sw_bundle_workflow import SWConf_Resource_Select, Define_Software, Config_Software -from workflow.booking_workflow import Booking_Resource_Select, SWConfig_Select, Booking_Meta -from django.http import QueryDict, HttpRequest -from django.contrib.auth.models import User -from resource_inventory.models import ( - Scenario, - Installer, - OPNFVRole, - Image, - GenericResourceBundle, - ConfigBundle -) - - -class BaseStepTestCase(TestCase): +from workflow.tests import test_fixtures + + +class TestConfig: + """ + Basic class to instantiate and hold reference + to models we will need often + """ + def __init__(self, usr=None): + self.lab = make_lab() + self.user = usr or make_user() + self.os = make_os() + self.host_prof = make_complete_host_profile(self.lab) + self.host = make_host(self.host_prof, self.lab, name="host1") + + # pod description as required by testing lib + self.topology = { + "host1": { + "type": self.host_prof, + "role": make_opnfv_role(), + "image": make_image(self.lab, 3, self.user, self.os, self.host_prof), + "nets": [ + [{"name": "public", "tagged": True, "public": True}] + ] + } + } + self.grb = make_grb(self.topology, self.user, self.lab)[0] + self.generic_host = make_generic_host(self.grb, self.host_prof, "host1") + + +class StepTestCase(TestCase): + + # after setUp is called, this should be an instance of a step + step = None + + post_data = {} # subclasses will set this @classmethod def setUpTestData(cls): - Populator().populate() - - def makeRepo(self): + super().setUpTestData() + cls.factory = RequestFactory() + cls.user_prof = make_user_profile() + cls.user = cls.user_prof.user + + def setUp(self): + super().setUp() + if self.step is None: + raise SkipTest("Step instance not given") repo = Repository() - repo.el[repo.SESSION_USER] = User.objects.filter(username="user 1").first() - return repo + self.add_to_repo(repo) + self.step = self.step(1, repo) - def step_test(self, step_type, data): - step = WorkflowFactory().make_step(step_type, self.makeRepo()) - formData = QueryDict(mutable=True) - formData.update(data) - request = HttpRequest() - request.POST = formData - response = step.post_render(request) - context = step.get_context() - return response, context + def assertCorrectPostBehavior(self, post_data): + """ + allows subclasses to override and make assertions about + the side effects of self.step.post() + post_data is the data passed into post() + """ + return + def add_to_repo(self, repo): + """ + This method is a hook that allows subclasses to modify + the contents of the repo before the step is created. + """ + return -class BookingResourceSelectTestCase(BaseStepTestCase): + def assertValidHtml(self, html_str): + """ + This method should make sure that html_str is a valid + html fragment. + However, I know of no good way of doing this in python + """ + self.assertTrue(isinstance(html_str, str)) + self.assertGreater(len(html_str), 0) - def test_step_with_good_data(self): - grb_model = GenericResourceBundle.objects.filter(owner__username="user 1").first() - grb = [{"small_name": grb_model.name, "expanded_name": "user 1", "id": grb_model.id, "string": ""}] - grb = str(grb).replace("'", '"') - data = {"generic_resource_bundle": grb} - response, context = self.step_test(Booking_Resource_Select, data) - self.assertTrue(True) + def test_render_to_string(self): + request = self.factory.get("/workflow/manager/") + request.user = self.user + response_html = self.step.render_to_string(request) + self.assertValidHtml(response_html) - def test_step_with_bad_data(self): # TODO - data = {} - response, context = self.step_test(Booking_Resource_Select, data) + def test_post(self, data=None): + post_data = data or self.post_data + self.step.post(post_data, self.user) + self.assertCorrectPostBehavior(data) - def test_step_with_empty_data(self): - data = {} - response, context = self.step_test(SWConfig_Select, data) +class SelectStepTestCase(StepTestCase): + # ID of model to be sent to the step's form + # can be an int or a list of ints + obj_id = -1 -class SoftwareConfigSelectTestCase(BaseStepTestCase): + def setUp(self): + super().setUp() - def test_step_with_good_data(self): - config_model = ConfigBundle.objects.filter(owner__username="user 1").first() - config = [{"expanded_name": "user 1", "small_name": config_model.name, "id": config_model.id, "string": ""}] - config = str(config).replace("'", '"') - data = {"software_bundle": config} - response, context = self.step_test(SWConfig_Select, data) + try: + iter(self.obj_id) + except TypeError: + self.obj_id = [self.obj_id] - def test_step_with_bad_data(self): # TODO - data = {} - response, context = self.step_test(SWConfig_Select, data) + field_data = json.dumps(self.obj_id) + self.post_data = { + "searchable_select": [field_data] + } - def test_step_with_empty_data(self): - data = {} - response, context = self.step_test(SWConfig_Select, data) +class DefineHardwareTestCase(StepTestCase): + step = resource_bundle_workflow.Define_Hardware + post_data = { + "filter_field": { + "lab": { + "lab_35": {"selected": True, "id": 35}}, + "host": { + "host_1": {"selected": True, "id": 1}} + } + } -class BookingMetaTestCase(BaseStepTestCase): - def test_step_with_good_data(self): - data = {"length": 7, "project": "LaaS", "purpose": "testing"} - user2 = User.objects.get(username="user 2") - john = User.objects.get(username="johnsmith") - users = [ - {"expanded_name": "", "id": user2.id, "small_name": user2.username, "string": user2.email}, - {"expanded_name": "", "id": john.id, "small_name": john.username, "string": john.email} - ] - users = str(users).replace("'", '"') - data['users'] = users - response, context = self.step_test(Booking_Meta, data) +class DefineNetworkTestCase(StepTestCase): + step = resource_bundle_workflow.Define_Nets + post_data = {"xml": test_fixtures.MX_GRAPH_MODEL} - def test_step_with_bad_data(self): # TODO - data = {} - response, context = self.step_test(Booking_Meta, data) - def test_step_with_empty_data(self): - data = {} - response, context = self.step_test(Booking_Meta, data) +class ResourceMetaTestCase(StepTestCase): + step = resource_bundle_workflow.Resource_Meta_Info + post_data = { + "bundle_name": "my_bundle", + "bundle_description": "My Bundle" + } -class DefineHardwareTestCase(BaseStepTestCase): +class BookingResourceTestCase(SelectStepTestCase): + step = booking_workflow.Booking_Resource_Select - def test_step_with_good_data(self): - hosts = {"host_4": 1, "host_1": 1} - labs = {"lab_1": "true"} - data = {"hosts": hosts, "labs": labs} - response, context = self.step_test(Define_Hardware, data) + def add_to_repo(self, repo): + repo.el[repo.SESSION_USER] = self.user - def test_step_with_bad_data(self): # TODO - data = {} - response, context = self.step_test(Define_Hardware, data) + @classmethod + def setUpTestData(cls): + super().setUpTestData() + conf = TestConfig(usr=cls.user) + cls.obj_id = conf.grb.id - def test_step_with_empty_data(self): - data = {} - response, context = self.step_test(Define_Hardware, data) +class SoftwareSelectTestCase(SelectStepTestCase): + step = booking_workflow.SWConfig_Select -class DefineNetsTestCase(BaseStepTestCase): + def add_to_repo(self, repo): + repo.el[repo.SESSION_USER] = self.user + repo.el[repo.SELECTED_GRESOURCE_BUNDLE] = self.conf.grb - def test_step_with_good_data(self): - xml = constants.POD_XML - data = {"xml": xml} - response, context = self.step_test(Define_Nets, data) + @classmethod + def setUpTestData(cls): + super().setUpTestData() + cls.conf = TestConfig(usr=cls.user) + host_map = {"host1": cls.conf.generic_host} + config_bundle = make_config_bundle(cls.conf.grb, cls.conf.user, cls.conf.topology, host_map)[0] + cls.obj_id = config_bundle.id - def test_step_with_bad_data(self): # TODO - data = {} - response, context = self.step_test(Define_Nets, data) - def test_step_with_empty_data(self): - data = {} - response, context = self.step_test(Define_Nets, data) +class OPNFVSelectTestCase(SelectStepTestCase): + step = booking_workflow.OPNFV_Select + def add_to_repo(self, repo): + repo.el[repo.SELECTED_CONFIG_BUNDLE] = self.config_bundle -class ResourceMetaInfoTestCase(BaseStepTestCase): + @classmethod + def setUpTestData(cls): + super().setUpTestData() + conf = TestConfig(usr=cls.user) + host_map = {"host1": conf.generic_host} + cls.config_bundle, opnfv_config = make_config_bundle(conf.grb, conf.user, conf.topology, host_map) + cls.obj_id = opnfv_config.id - def test_step_with_good_data(self): - data = {"bundle_description": "description", "bundle_name": "my testing bundle"} - response, context = self.step_test(Resource_Meta_Info, data) - def test_step_with_bad_data(self): # TODO - data = {} - response, context = self.step_test(Resource_Meta_Info, data) +class BookingMetaTestCase(StepTestCase): + step = booking_workflow.Booking_Meta + post_data = { + "length": 14, + "purpose": "Testing", + "project": "Lab as a Service", + "users": ["[-1]"] + } - def test_step_with_empty_data(self): - data = {} - response, context = self.step_test(Resource_Meta_Info, data) + def add_to_repo(self, repo): + repo.el[repo.SESSION_MANAGER] = mock.MagicMock() + @classmethod + def setUpTestData(cls): + super().setUpTestData() + new_user = make_user(username="collaborator", email="different@mail.com") + new_user_prof = make_user_profile(user=new_user) + data = "[" + str(new_user_prof.id) + "]" # list of IDs + cls.post_data['users'] = [data] -class SWConfResourceSelectTestCase(BaseStepTestCase): - def test_step_with_good_data(self): - grb_model = GenericResourceBundle.objects.filter(owner__username="user 1").first() - grb = [{"small_name": grb_model.name, "expanded_name": "user 1", "id": grb_model.id, "string": ""}] - grb = str(grb).replace("'", '"') - data = {"generic_resource_bundle": grb} - response, context = self.step_test(SWConf_Resource_Select, data) +class ConfigResourceSelectTestCase(SelectStepTestCase): + step = sw_bundle_workflow.SWConf_Resource_Select - def test_step_with_bad_data(self): # TODO - data = {} - response, context = self.step_test(SWConf_Resource_Select, data) + def add_to_repo(self, repo): + repo.el[repo.SESSION_USER] = self.user - def test_step_with_empty_data(self): - data = {} - response, context = self.step_test(SWConf_Resource_Select, data) + @classmethod + def setUpTestData(cls): + super().setUpTestData() + conf = TestConfig(usr=cls.user) + cls.obj_id = conf.grb.id + + +class DefineSoftwareTestCase(StepTestCase): + step = sw_bundle_workflow.Define_Software + post_data = { + "form-0-image": 1, + "headnode": 1, + "form-0-headnode": "", + "form-TOTAL_FORMS": 1, + "form-INITIAL_FORMS": 1, + "form-MIN_NUM_FORMS": 0, + "form-MAX_NUM_FORMS": 1000, + } + + def add_to_repo(self, repo): + repo.el[repo.SELECTED_GRESOURCE_BUNDLE] = self.conf.grb + @classmethod + def setUpTestData(cls): + super().setUpTestData() + cls.conf = TestConfig(usr=cls.user) -class DefineSoftwareTestCase(BaseStepTestCase): - def makeRepo(self): - """ - put selected grb in repo for step - """ - repo = super(DefineSoftwareTestCase, self).makeRepo() - grb = GenericResourceBundle.objects.filter(owner__username="user 1").first() - repo.el[repo.SWCONF_SELECTED_GRB] = grb - return repo - - def test_step_with_good_data(self): - data = {"form-INITIAL_FORMS": 3, "form-MAX_NUM_FORMS": 1000} - data["form-MIN_NUM_FORMS"] = 0 - data["form-TOTAL_FORMS"] = 3 - an_image_id = Image.objects.get(name="a host image").id - another_image_id = Image.objects.get(name="another host image").id - control = OPNFVRole.objects.get(name="Controller") - compute = OPNFVRole.objects.get(name="Compute") - jumphost = OPNFVRole.objects.get(name="Jumphost") - data['form-0-image'] = an_image_id - data['form-1-image'] = an_image_id - data['form-2-image'] = another_image_id - data['form-0-role'] = compute.id - data['form-1-role'] = control.id - data['form-2-role'] = jumphost.id - response, context = self.step_test(Define_Software, data) - - def test_step_with_bad_data(self): # TODO - data = {"form-INITIAL_FORMS": 0, "form-MAX_NUM_FORMS": 1000} - data["form-MIN_NUM_FORMS"] = 0 - data["form-TOTAL_FORMS"] = 0 - response, context = self.step_test(Define_Software, data) - - def test_step_with_empty_data(self): - data = {"form-INITIAL_FORMS": 0, "form-MAX_NUM_FORMS": 1000} - data["form-MIN_NUM_FORMS"] = 0 - data["form-TOTAL_FORMS"] = 0 - response, context = self.step_test(Define_Software, data) - - -class ConfigSoftwareTestCase(BaseStepTestCase): - - def test_step_with_good_data(self): - data = {"description": "description", "name": "namey"} - installer = Installer.objects.get(name="Fuel") - scenario = Scenario.objects.get(name="os-nosdn-nofeature-noha") - data['installer'] = installer.id - data['scenario'] = scenario.id - response, context = self.step_test(Config_Software, data) - - def test_step_with_bad_data(self): # TODO - data = {} - response, context = self.step_test(Config_Software, data) - - def test_step_with_empty_data(self): - data = {} - response, context = self.step_test(Config_Software, data) +class ConfigSoftwareTestCase(StepTestCase): + step = sw_bundle_workflow.Config_Software + post_data = { + "name": "config_bundle", + "description": "My Config Bundle" + } diff --git a/src/workflow/tests/test_steps_render.py b/src/workflow/tests/test_steps_render.py deleted file mode 100644 index f3df8f2..0000000 --- a/src/workflow/tests/test_steps_render.py +++ /dev/null @@ -1,43 +0,0 @@ -############################################################################## -# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -from django.test import TestCase, Client - - -class SuperViewTestCase(TestCase): - url = "/" - client = Client() - - def test_get(self): - response = self.client.get(self.url) - self.assertLess(response.status_code, 300) - - -class DefineHardwareViewTestCase(SuperViewTestCase): - url = "/wf/workflow/step/define_hardware" - - -class DefineNetworkViewTestCase(SuperViewTestCase): - url = "/wf/workflow/step/define_net" - - -class ResourceMetaViewTestCase(SuperViewTestCase): - url = "/wf/workflow/step/resource_meta" - - -class BookingMetaViewTestCase(SuperViewTestCase): - url = "/wf/workflow/step/booking_meta" - - -class SoftwareSelectViewTestCase(SuperViewTestCase): - url = "/wf/workflow/step/software_select" - - -class ResourceSelectViewTestCase(SuperViewTestCase): - url = "/wf/workflow/step/resource_select" diff --git a/src/workflow/tests/test_workflows.py b/src/workflow/tests/test_workflows.py index 7a53521..293e43d 100644 --- a/src/workflow/tests/test_workflows.py +++ b/src/workflow/tests/test_workflows.py @@ -7,9 +7,9 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## +from unittest import SkipTest from django.test import TestCase from workflow.workflow_factory import WorkflowFactory -from dashboard.populate_db import Populator """ @@ -29,8 +29,9 @@ To remove a workflow: class WorkflowTestCase(TestCase): @classmethod - def setUpTestData(cls): - Populator().populate() + def setUpClass(cls): + super().setUpClass() + raise SkipTest("These tests are no good") def setUp(self): self.clear_workflow() diff --git a/src/workflow/urls.py b/src/workflow/urls.py index 298db95..b1b95a7 100644 --- a/src/workflow/urls.py +++ b/src/workflow/urls.py @@ -9,12 +9,8 @@ from django.conf.urls import url -from django.conf import settings from workflow.views import manager_view, viewport_view, add_workflow, remove_workflow, create_workflow -from workflow.models import Repository -from workflow.resource_bundle_workflow import Define_Hardware, Define_Nets, Resource_Meta_Info -from workflow.booking_workflow import SWConfig_Select, Booking_Resource_Select, Booking_Meta app_name = 'workflow' urlpatterns = [ @@ -25,11 +21,3 @@ urlpatterns = [ url(r'^pop/$', remove_workflow, name='remove_workflow'), url(r'^$', viewport_view, name='viewport') ] - -if settings.TESTING: - urlpatterns.append(url(r'^workflow/step/define_hardware$', Define_Hardware("", Repository()).test_render)) - urlpatterns.append(url(r'^workflow/step/define_net$', Define_Nets("", Repository()).test_render)) - urlpatterns.append(url(r'^workflow/step/resource_meta$', Resource_Meta_Info("", Repository()).test_render)) - urlpatterns.append(url(r'^workflow/step/booking_meta$', Booking_Meta("", Repository()).test_render)) - urlpatterns.append(url(r'^workflow/step/software_select$', SWConfig_Select("", Repository()).test_render)) - urlpatterns.append(url(r'^workflow/step/resource_select$', Booking_Resource_Select("", Repository()).test_render)) diff --git a/src/workflow/views.py b/src/workflow/views.py index 3ab4d30..9ff444d 100644 --- a/src/workflow/views.py +++ b/src/workflow/views.py @@ -8,13 +8,12 @@ ############################################################################## -from django.http import HttpResponse, JsonResponse, HttpResponseRedirect +from django.http import HttpResponse from django.shortcuts import render import uuid from workflow.workflow_manager import ManagerTracker, SessionManager -from booking.models import Booking import logging logger = logging.getLogger(__name__) |