diff --git a/test/test.py b/test/test.py index f16b46c..aa04fcb 100644 --- a/test/test.py +++ b/test/test.py @@ -24,18 +24,19 @@ base_config = { ] } -def run_server(sf): - def server_thread(sf): - sf.run(listen_port=8002) - thread.start_new_thread(server_thread, (sf, )) - # Wait until the webserver is ready - while True: - time.sleep(0.1) - if sf.running: - break - class FormConfigTestCase(unittest.TestCase): + """ + Test the proper low-level handling of form configurations such as loading, + callbacks, etc. + """ + @classmethod + def tearDownClass(cls): + if os.path.exists('tmp_stdout'): + os.unlink('tmp_stdout') + if os.path.exists('tmp_stderr'): + os.unlink('tmp_stderr') + def testMissing(self): """Missing script callbacks should raise an OSError""" self.assertRaises(OSError, scriptform.ScriptForm, 'test_formconfig_missingscript.json') @@ -51,6 +52,7 @@ class FormConfigTestCase(unittest.TestCase): self.assertTrue(fc.get_visible_forms() == []) def testCallbackStore(self): + """Test a callback that returns output in strings""" sf = scriptform.ScriptForm('test_formconfig_callback.json') fc = sf.get_form_config() res = fc.callback('test_store', {}) @@ -58,17 +60,104 @@ class FormConfigTestCase(unittest.TestCase): self.assertTrue('stdout' in res['stdout']) self.assertTrue('stderr' in res['stderr']) - #def testCallbackRaw(self): - # sf = scriptform.ScriptForm('test_formconfig_callback.json') - # fc = sf.get_form_config() - # stdout = StringIO.StringIO() - # stderr = StringIO.StringIO() - # res = fc.callback('test_raw', {}, stdout, stderr) - # stdout.seek(0) - # stderr.seek(0) - # self.assertTrue(res['exitcode'] == 33) - # print stdout.read() - # self.assertTrue('stdout' in stdout.read()) + def testCallbackRaw(self): + """Test a callback that returns raw output""" + sf = scriptform.ScriptForm('test_formconfig_callback.json') + fc = sf.get_form_config() + stdout = file('tmp_stdout', 'w+') # can't use StringIO + stderr = file('tmp_stderr', 'w+') + exitcode = fc.callback('test_raw', {}, stdout, stderr) + stdout.seek(0) + stderr.seek(0) + self.assertTrue(exitcode == 33) + self.assertTrue('stdout' in stdout.read()) + +class FormDefinitionTest(unittest.TestCase): + """ + Form Definition tests. Mostly directly testing if validations work. + """ + def setUp(self): + self.sf = scriptform.ScriptForm('test_formdefinition_validate.json') + self.fc = self.sf.get_form_config() + + def testValidateString(self): + fd = self.fc.get_form_def('test_val_string') + + form_values = {"val_string": "123"} + errors, values = fd.validate(form_values) + self.assertTrue('val_string' in errors) + self.assertTrue('Minimum' in errors['val_string'][0]) + + form_values = {"val_string": "1234567"} + errors, values = fd.validate(form_values) + self.assertTrue('val_string' in errors) + self.assertTrue('Maximum' in errors['val_string'][0]) + + def testValidateString(self): + fd = self.fc.get_form_def('test_val_integer') + + form_values = {"val_integer": 3} + errors, values = fd.validate(form_values) + self.assertTrue('val_integer' in errors) + self.assertTrue('Minimum' in errors['val_integer'][0]) + + form_values = {"val_integer": 7} + errors, values = fd.validate(form_values) + self.assertTrue('val_integer' in errors) + self.assertTrue('Maximum' in errors['val_integer'][0]) + + def testValidateFloat(self): + fd = self.fc.get_form_def('test_val_float') + + form_values = {"val_float": 2.05} + errors, values = fd.validate(form_values) + self.assertTrue('val_float' in errors) + self.assertTrue('Minimum' in errors['val_float'][0]) + + form_values = {"val_float": 2.31} + errors, values = fd.validate(form_values) + self.assertTrue('val_float' in errors) + self.assertTrue('Maximum' in errors['val_float'][0]) + + +class WebAppTest(unittest.TestCase): + """ + Test the web app by actually running the server and making web calls to it. + """ + @classmethod + def setUpClass(cls): + def server_thread(sf): + sf.run(listen_port=8002) + cls.sf = scriptform.ScriptForm('test_webapp.json') + thread.start_new_thread(server_thread, (cls.sf, )) + # Wait until the webserver is ready + while True: + time.sleep(0.1) + if cls.sf.running: + break + + @classmethod + def tearDownClass(cls): + cls.sf.shutdown() + + def testHidden(self): + """Hidden forms shouldn't appear in the output""" + r = requests.get('http://localhost:8002/') + self.assertNotIn('Hidden form', r.text) + + def testShown(self): + """Non-hidden forms should appear in the output""" + r = requests.get('http://localhost:8002/') + self.assertIn('Output escaped', r.text) + + def testOutputEscaped(self): + """Form with 'escaped' output should have HTML entities escaped""" + data = { + "form_name": 'output_escaped', + "string": '' + } + r = requests.post('http://localhost:8000/submit', data) + self.assertIn('string=<foo>', r.text) if __name__ == '__main__': diff --git a/test/test_formdefinition_validate.json b/test/test_formdefinition_validate.json new file mode 100644 index 0000000..f2b3c8c --- /dev/null +++ b/test/test_formdefinition_validate.json @@ -0,0 +1,47 @@ +{ + "title": "test", + "forms": [ + { + "name": "test_val_string", + "title": "title", + "description": "description", + "script": "test.sh", + "fields": [ + { + "name": "val_string", + "type": "string", + "minlen": 4, + "maxlen": 6 + } + ] + }, + { + "name": "test_val_integer", + "title": "title", + "description": "description", + "script": "test.sh", + "fields": [ + { + "name": "val_integer", + "type": "integer", + "min": 4, + "max": 6 + } + ] + }, + { + "name": "test_val_float", + "title": "title", + "description": "description", + "script": "test.sh", + "fields": [ + { + "name": "val_float", + "type": "float", + "min": 2.1, + "max": 2.3 + } + ] + } + ] +} diff --git a/test/test_webapp.json b/test/test_webapp.json new file mode 100644 index 0000000..265040d --- /dev/null +++ b/test/test_webapp.json @@ -0,0 +1,103 @@ +{ + "title": "Webapp test", + "forms": [ + { + "name": "hidden", + "title": "Hidden form", + "description": "Hidden form", + "script": "test.sh", + "hidden": "true", + "fields": [] + }, + { + "name": "output_escaped", + "title": "Output escaped", + "description": "Output escaped", + "script": "test.sh", + "output": "escaped", + "fields": [ + { + "name": "string", + "title": "This string should be escaped in the output", + "type": "string" + } + ] + }, + { + "name": "validate", + "title": "Validated form", + "description": "This form is heavily validated", + "submit_title": "Validate it", + "script": "test.sh", + "fields": [ + { + "name": "string", + "title": "A string between 5 and 7 characters.", + "type": "string", + "required": true, + "minlen": 5, + "maxlen": 7 + }, + { + "name": "integer", + "title": "An integer between 10 and 20", + "type": "integer", + "required": true, + "min": 10, + "max": 20 + }, + { + "name": "float", + "title": "A real number between 0.5 and 1", + "type": "float", + "required": true, + "min": 0.5, + "max": 1.0 + }, + { + "name": "date", + "title": "A date", + "type": "date", + "required": true, + "min": "2015-01-01", + "max": "2015-02-01" + }, + { + "name": "radio", + "title": "A radio", + "type": "radio", + "required": true, + "options": [ + ["One", "one"], + ["Two", "two"], + ["Three", "three"] + ] + }, + { + "name": "text", + "title": "A text input field", + "type": "text", + "required": true, + "rows": 2, + "cols": 50, + "maxlen": 100, + "minlen": 10 + }, + { + "name": "password", + "title": "A password input field", + "type": "password", + "required": true, + "minlen": 5 + }, + { + "name": "file", + "title": "A file upload field", + "type": "file", + "required": true, + "extensions": ["csv"] + } + ] + } + ] +}