You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
scriptform/test/test.py

591 lines
21 KiB

10 years ago
import logging
10 years ago
import sys
import unittest
import json
import os
import copy
import threading
10 years ago
import time
import requests
import re
import random
def gen_random_file(fname, size=1024):
with open(fname, 'wb') as fh:
for i in range(size):
fh.write(chr(random.randint(0, 255)).encode('utf-8'))
10 years ago
class FormConfigTestCase(unittest.TestCase):
10 years ago
"""
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 testNoSuchForm(self):
"""Getting non-existing form should raise ValueError"""
sf = scriptform.ScriptForm('test_formconfig_hidden.json')
fc = sf.get_form_config()
self.assertRaises(ValueError, fc.get_form_def, 'nonexisting')
def testMissingScript(self):
10 years ago
"""Missing script callbacks should raise an OSError"""
self.assertRaises(OSError, scriptform.ScriptForm, 'test_formconfig_missingscript.json')
10 years ago
def testNoExec(self):
"""Non-executable script callbacks should raise an FormConfigError"""
from formconfig import FormConfigError
self.assertRaises(FormConfigError, scriptform.ScriptForm, 'test_formconfig_noexec.json')
10 years ago
def testHidden(self):
"""Hidden forms should not show up in the list of forms"""
sf = scriptform.ScriptForm('test_formconfig_hidden.json')
10 years ago
fc = sf.get_form_config()
self.assertTrue(fc.get_visible_forms() == [])
def testCallbackStore(self):
10 years ago
"""Test a callback that returns output in strings"""
sf = scriptform.ScriptForm('test_formconfig_callback.json')
fc = sf.get_form_config()
fd = fc.get_form_def('test_store')
res = runscript.run_script(fd, {}, {})
self.assertEqual(res['exitcode'], 33)
self.assertTrue(b'stdout' in res['stdout'])
self.assertTrue(b'stderr' in res['stderr'])
10 years ago
def testCallbackRaw(self):
"""Test a callback that returns raw output"""
sf = scriptform.ScriptForm('test_formconfig_callback.json')
fc = sf.get_form_config()
fd = fc.get_form_def('test_raw')
stdout = open('tmp_stdout', 'w+') # can't use StringIO
stderr = open('tmp_stderr', 'w+')
exitcode = runscript.run_script(fd, {}, {}, stdout, stderr)
10 years ago
stdout.seek(0)
stderr.seek(0)
self.assertTrue(exitcode == 33)
self.assertTrue('stdout' in stdout.read())
stdout.close()
stderr.close()
10 years ago
def testCallbackMissingParams(self):
"""
"""
sf = scriptform.ScriptForm('test_formconfig_callback.json')
fc = sf.get_form_config()
fd = fc.get_form_def('test_raw')
self.assertRaises(ValueError, runscript.run_script, fd, {}, {})
10 years ago
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()
10 years ago
def testUnknownFieldError(self):
fd = self.fc.get_form_def('test_required')
self.assertRaises(KeyError, fd.get_field_def, 'nosuchfield')
def testRequired(self):
fd = self.fc.get_form_def('test_required')
form_values = {}
errors, values = fd.validate(form_values)
self.assertIn('string', errors)
self.assertIn('required', errors['string'][0])
10 years ago
def testValidateStringMin(self):
fd = self.fc.get_form_def('test_val_string')
10 years ago
form_values = {"val_string": "123"}
errors, values = fd.validate(form_values)
self.assertIn('val_string', errors)
self.assertIn('Minimum', errors['val_string'][0])
10 years ago
def testValidateStringMax(self):
fd = self.fc.get_form_def('test_val_string')
10 years ago
form_values = {"val_string": "1234567"}
errors, values = fd.validate(form_values)
self.assertIn('val_string', errors)
self.assertIn('Maximum', errors['val_string'][0])
10 years ago
def testValidateStringValue(self):
fd = self.fc.get_form_def('test_val_string')
form_values = {"val_string": "1234"}
errors, values = fd.validate(form_values)
self.assertNotIn('val_string', errors)
self.assertEqual(values['val_string'], "1234")
def testValidateIntegerInvalid(self):
10 years ago
fd = self.fc.get_form_def('test_val_integer')
form_values = {"val_integer": 'three'}
errors, values = fd.validate(form_values)
self.assertIn('val_integer', errors)
self.assertIn('Must be a', errors['val_integer'][0])
10 years ago
def testValidateIntegerMin(self):
fd = self.fc.get_form_def('test_val_integer')
10 years ago
form_values = {"val_integer": 3}
errors, values = fd.validate(form_values)
self.assertIn('val_integer', errors)
self.assertIn('Minimum', errors['val_integer'][0])
10 years ago
def testValidateIntegerMax(self):
fd = self.fc.get_form_def('test_val_integer')
10 years ago
form_values = {"val_integer": 7}
errors, values = fd.validate(form_values)
self.assertIn('val_integer', errors)
self.assertIn('Maximum', errors['val_integer'][0])
10 years ago
def testValidateIntegerValue(self):
fd = self.fc.get_form_def('test_val_integer')
form_values = {"val_integer": 6}
errors, values = fd.validate(form_values)
self.assertNotIn('val_integer', errors)
self.assertEqual(values['val_integer'], 6)
def testValidateFloatInvalid(self):
10 years ago
fd = self.fc.get_form_def('test_val_float')
form_values = {"val_float": 'four'}
errors, values = fd.validate(form_values)
self.assertTrue('val_float' in errors)
self.assertTrue('Must be a' in errors['val_float'][0])
10 years ago
def testValidateFloatMin(self):
fd = self.fc.get_form_def('test_val_float')
10 years ago
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])
def testValidateFloatMax(self):
fd = self.fc.get_form_def('test_val_float')
10 years ago
form_values = {"val_float": 2.31}
errors, values = fd.validate(form_values)
self.assertIn('val_float', errors)
self.assertIn('Maximum', errors['val_float'][0])
10 years ago
def testValidateFloatValue(self):
fd = self.fc.get_form_def('test_val_float')
form_values = {"val_float": 2.29}
errors, values = fd.validate(form_values)
self.assertNotIn('val_float', errors)
self.assertEqual(values['val_float'], 2.29)
10 years ago
def testValidateDateInvalid(self):
fd = self.fc.get_form_def('test_val_date')
form_values = {"val_date": '2015-001'}
errors, values = fd.validate(form_values)
self.assertIn('val_date', errors)
self.assertIn('Invalid date', errors['val_date'][0])
def testValidateDateMin(self):
fd = self.fc.get_form_def('test_val_date')
form_values = {"val_date": '2015-03-01'}
errors, values = fd.validate(form_values)
self.assertIn('val_date', errors)
self.assertIn('Minimum', errors['val_date'][0])
def testValidateDateMax(self):
fd = self.fc.get_form_def('test_val_date')
form_values = {"val_date": '2015-03-06'}
errors, values = fd.validate(form_values)
self.assertIn('val_date', errors)
self.assertIn('Maximum', errors['val_date'][0])
10 years ago
10 years ago
def testValidateDateValue(self):
import datetime
fd = self.fc.get_form_def('test_val_date')
form_values = {"val_date": '2015-03-03'}
errors, values = fd.validate(form_values)
self.assertNotIn('val_date', errors)
self.assertEqual(values['val_date'], datetime.date(2015, 3, 3))
10 years ago
def testValidateSelectValue(self):
fd = self.fc.get_form_def('test_val_select')
form_values = {"val_select": 'option_a'}
errors, values = fd.validate(form_values)
self.assertNotIn('val_select', errors)
self.assertEqual(values['val_select'], 'option_a')
def testValidateSelectInvalid(self):
fd = self.fc.get_form_def('test_val_select')
form_values = {"val_select": 'option_c'}
errors, values = fd.validate(form_values)
self.assertIn('val_select', errors)
self.assertIn('Invalid value', errors['val_select'][0])
def testValidateCheckbox(self):
fd = self.fc.get_form_def('test_val_checkbox')
form_values = {"val_checkbox": 'on'}
errors, values = fd.validate(form_values)
self.assertNotIn('val_checkbox', errors)
self.assertEqual(values['val_checkbox'], 'on')
def testValidateCheckboxDefaultOn(self):
fd = self.fc.get_form_def('test_val_checkbox_on')
form_values = {"val_checkbox_on": 'off'}
errors, values = fd.validate(form_values)
self.assertNotIn('val_checkbox_on', errors)
self.assertEqual(values['val_checkbox_on'], 'off')
def testValidateCheckboxInvalid(self):
fd = self.fc.get_form_def('test_val_checkbox')
form_values = {"val_checkbox": 'true'}
errors, values = fd.validate(form_values)
self.assertIn('val_checkbox', errors)
self.assertIn('Invalid value', errors['val_checkbox'][0])
def testValidateTextMin(self):
fd = self.fc.get_form_def('test_val_text')
form_values = {"val_text": '1234'}
errors, values = fd.validate(form_values)
self.assertIn('val_text', errors)
self.assertIn('Minimum', errors['val_text'][0])
def testValidateTextMax(self):
fd = self.fc.get_form_def('test_val_text')
form_values = {"val_text": '12345678901'}
errors, values = fd.validate(form_values)
self.assertIn('val_text', errors)
self.assertIn('Maximum', errors['val_text'][0])
def testValidateFileMissingFile(self):
fd = self.fc.get_form_def('test_val_file')
form_values = {}
errors, values = fd.validate(form_values)
self.assertIn('val_file', errors)
self.assertIn('required', errors['val_file'][0])
def testValidateFileMissingFileName(self):
fd = self.fc.get_form_def('test_val_file')
form_values = {'val_file': 'foo'}
self.assertRaises(KeyError, fd.validate, form_values)
class FormDefinitionFieldMissingProperty(unittest.TestCase):
"""
"""
def testMissing(self):
self.assertRaises(KeyError, scriptform.ScriptForm, 'test_formdefinition_missing_title.json')
10 years ago
class WebAppTest(unittest.TestCase):
"""
Test the web app by actually running the server and making web calls to it.
"""
@classmethod
def setUpClass(cls):
cls.auth_admin = requests.auth.HTTPBasicAuth('admin', 'admin')
cls.auth_user = requests.auth.HTTPBasicAuth('user', 'user')
# Run the server in a thread, so we can execute the tests in the main
# program.
10 years ago
def server_thread(sf):
sf.run(listen_port=8002)
cls.sf = scriptform.ScriptForm('test_webapp.json')
thread = threading.Thread(target=server_thread, args=(cls.sf,))
thread.start()
10 years ago
while True:
time.sleep(0.1)
if cls.sf.running is True:
10 years ago
break
@classmethod
def tearDownClass(cls):
# Shut down the webserver and wait until it has shut down.
10 years ago
cls.sf.shutdown()
while True:
time.sleep(0.1)
if cls.sf.running is False:
break
10 years ago
def testError404(self):
r = requests.get('http://localhost:8002/nosuchurl')
self.assertEqual(r.status_code, 404)
self.assertIn('Not found', r.text)
def testError401(self):
r = requests.get('http://localhost:8002/')
self.assertEqual(r.status_code, 401)
10 years ago
def testAuthFormNoAuthGet(self):
r = requests.get('http://localhost:8002/form?form_name=admin_only')
self.assertEqual(r.status_code, 401)
10 years ago
def testAuthFormNoAuthPost(self):
data = {"form_name": 'admin_only'}
r = requests.post('http://localhost:8002/submit', data)
self.assertEqual(r.status_code, 401)
10 years ago
def testAuthFormUnauthorizedGet(self):
r = requests.get('http://localhost:8002/form?form_name=admin_only', auth=self.auth_user)
10 years ago
self.assertEqual(r.status_code, 403)
10 years ago
def testAuthFormUnauthorizedPost(self):
10 years ago
data = {"form_name": 'admin_only'}
r = requests.post('http://localhost:8002/submit', data, auth=self.auth_user)
10 years ago
self.assertEqual(r.status_code, 403)
10 years ago
10 years ago
def testHidden(self):
"""Hidden forms shouldn't appear in the output"""
r = requests.get('http://localhost:8002/', auth=self.auth_user)
10 years ago
self.assertNotIn('Hidden form', r.text)
def testShown(self):
"""Non-hidden forms should appear in the output"""
r = requests.get('http://localhost:8002/', auth=self.auth_user)
10 years ago
self.assertIn('Output escaped', r.text)
def testRender(self):
r = requests.get('http://localhost:8002/form?form_name=validate', auth=self.auth_user)
self.assertIn('Validated form', r.text)
self.assertIn('This form is heavily validated', r.text)
self.assertIn('name="string"', r.text)
def testValidateCorrectData(self):
data = {
"form_name": 'validate',
"string": "12345",
"integer": "12",
"float": "0.6",
"date": "2015-01-02",
"text": "1234567890",
"password": "12345",
"radio": "One",
"checkbox": "on",
"select": "option_a",
}
gen_random_file('data.csv')
with open('data.csv', 'rb') as fh:
files = {'file': fh}
r = requests.post("http://localhost:8002/submit", data=data, files=files, auth=self.auth_user)
self.assertIn('string=12345', r.text)
self.assertIn('integer=12', r.text)
self.assertIn('float=0.6', r.text)
self.assertIn('date=2015-01-02', r.text)
self.assertIn('text=1234567890', r.text)
self.assertIn('password=12345', r.text)
self.assertIn('radio=One', r.text)
self.assertIn('checkbox=on', r.text)
self.assertIn('select=option_a', r.text)
os.unlink('data.csv')
def testValidateIncorrectData(self):
data = {
"form_name": 'validate',
"string": "12345678",
"integer": "9",
"float": "1.1",
"date": "2015-02-02",
"radio": "Ten",
"text": "123456789",
"password": "1234",
"checkbox": "invalidvalue",
"select": "invalidvalue",
}
gen_random_file('data.txt')
with open('data.txt', 'rb') as fh:
files = {'file': fh}
r = requests.post("http://localhost:8002/submit", data=data, files=files, auth=self.auth_user)
self.assertIn('Maximum length is 7', r.text)
self.assertIn('Minimum value is 10', r.text)
self.assertIn('Maximum value is 1.0', r.text)
self.assertIn('Maximum value is 2015-02-01', r.text)
self.assertIn('Invalid value for radio button: Ten', r.text)
self.assertIn('Minimum length is 10', r.text)
self.assertIn('Minimum length is 5', r.text)
self.assertIn('Only file types allowed: csv', r.text)
self.assertIn('Invalid value for radio button', r.text)
self.assertIn('Invalid value for dropdown', r.text)
os.unlink('data.txt')
def testValidateRefill(self):
"""
Ensure that field values are properly repopulated if there were any
errors in validation.
"""
data = {
"form_name": 'validate',
"string": "123",
"integer": "12",
"float": "0.6",
"date": "2015-01-02",
"text": "1234567890",
"password": "12345",
"radio": "One",
"checkbox": "on",
"select": "option_b",
}
gen_random_file('data.txt')
with open ('data.txt', 'rb') as fh:
files = {'file': fh}
r = requests.post("http://localhost:8002/submit", data=data, files=files, auth=self.auth_user)
self.assertIn('value="123"', r.text)
self.assertIn('value="12"', r.text)
self.assertIn('value="0.6"', r.text)
self.assertIn('value="2015-01-02"', r.text)
self.assertIn('>1234567890<', r.text)
self.assertIn('value="12345"', r.text)
self.assertIn('value="on"', r.text)
self.assertIn('selected>Option B', r.text)
os.unlink('data.txt')
10 years ago
def testOutputEscaped(self):
"""Form with 'escaped' output should have HTML entities escaped"""
data = {
"form_name": 'output_escaped',
"string": '<foo>'
}
r = requests.post('http://localhost:8002/submit', data, auth=self.auth_user)
10 years ago
self.assertIn('string=&lt;foo&gt;', r.text)
def testOutputRaw(self):
data = {
"form_name": 'output_raw',
"string": '<foo>'
}
r = requests.post('http://localhost:8002/submit', data, auth=self.auth_user)
self.assertIn('string=<foo>', r.text)
def testOutputHTML(self):
data = {
"form_name": 'output_html',
"string": '<foo>'
}
r = requests.post('http://localhost:8002/submit', data, auth=self.auth_user)
self.assertIn('string=<foo>', r.text)
10 years ago
def testUpload(self):
gen_random_file('data.raw')
10 years ago
data = {
"form_name": "upload"
}
with open('data.raw', 'rb') as fh:
files = {'file': fh}
r = requests.post("http://localhost:8002/submit", files=files, data=data, auth=self.auth_user)
self.assertIn('SAME', r.text)
os.unlink('data.raw')
10 years ago
def testStaticValid(self):
r = requests.get("http://localhost:8002/static?fname=ssh_server.png", auth=self.auth_user)
self.assertEqual(r.status_code, 200)
f_served = b''
for c in r.iter_content():
f_served += c
with open('static/ssh_server.png', 'rb')as fh:
f_orig = fh.read()
self.assertEqual(f_orig, f_served)
def testStaticInvalidFilename(self):
r = requests.get("http://localhost:8002/static?fname=../../ssh_server.png", auth=self.auth_user)
self.assertEqual(r.status_code, 403)
def testStaticInvalidNotFound(self):
r = requests.get("http://localhost:8002/static?fname=nosuchfile.png", auth=self.auth_user)
self.assertEqual(r.status_code, 404)
def testHiddenField(self):
r = requests.get('http://localhost:8002/form?form_name=hidden_field', auth=self.auth_user)
self.assertIn('class="hidden"', r.text)
def testCallbackFail(self):
data = {
"form_name": "callback_fail"
}
r = requests.post("http://localhost:8002/submit", data=data, auth=self.auth_user)
self.assertIn('<span class="error">stderr output\n</span>', r.text)
10 years ago
class WebAppSingleTest(unittest.TestCase):
"""
Test that Scriptform doesn't show us a list of forms, but directly shows us
the form is there's only one.
"""
@classmethod
def setUpClass(cls):
# Run the server in a thread, so we can execute the tests in the main
# program.
def server_thread(sf):
sf.run(listen_port=8002)
cls.sf = scriptform.ScriptForm('test_webapp_singleform.json')
thread = threading.Thread(target=server_thread, args=(cls.sf,))
thread.start()
while True:
time.sleep(0.1)
if cls.sf.running is True:
break
@classmethod
def tearDownClass(cls):
cls.sf.shutdown()
while True:
time.sleep(0.1)
if not cls.sf.running:
break
def testSingleForm(self):
"""
Ensure that Scriptform directly shows the form if there is only one.
"""
r = requests.get("http://localhost:8002/")
self.assertIn('only_form', r.text)
def testStaticDisabled(self):
"""
"""
r = requests.get("http://localhost:8002/static?fname=nosuchfile.png")
self.assertEqual(r.status_code, 501)
10 years ago
if __name__ == '__main__':
10 years ago
logging.basicConfig(level=logging.FATAL,
format='%(asctime)s:%(name)s:%(levelname)s:%(message)s',
filename='test.log',
filemode='a')
import coverage
cov = coverage.coverage(omit=['*test*', 'main', '*/lib/python*'])
cov.start()
sys.path.insert(0, '../src')
import scriptform
import runscript
unittest.main(exit=True)
cov.stop()
cov.save()
print(cov.report())
try:
print(cov.html_report())
except coverage.misc.CoverageException as err:
if "Couldn't find static file 'jquery.hotkeys.js'" in err.message:
pass
else:
raise