Commit 323dc259 authored by Aurélien Campéas's avatar Aurélien Campéas
Browse files

tests: have a bunch of them


Resolves #1.
parent 96c944e68f1a
......@@ -4,8 +4,13 @@ from flask import Flask
from rework_ui.blueprint import reworkui
def startapp(host, port, dburi):
engine = create_engine(dburi)
def make_app(engine):
app = Flask('rework')
app.register_blueprint(reworkui(engine))
return app
def startapp(host, port, dburi):
engine = create_engine(dburi)
app = make_app(engine)
app.run(host=host, port=port, threaded=True)
from pathlib import Path
import pytest
from sqlalchemy import create_engine
from pytest_sa_pg import db
import webtest
from rework import schema as reworkschema
from rework import api
from rework_ui import schema as ruischema, app
DATADIR = Path(__file__).parent / 'data'
PORT = 2346
@pytest.fixture(scope='session')
def engine(request):
db.setup_local_pg_cluster(request, DATADIR, PORT)
uri = 'postgresql://localhost:{}/postgres'.format(PORT)
e = create_engine(uri)
reworkschema.reset(e)
reworkschema.init(e)
ruischema.init(e)
api.freeze_operations(e)
return e
# Error-displaying web tester
class WebTester(webtest.TestApp):
def _check_status(self, status, res):
try:
super(WebTester, self)._check_status(status, res)
except:
print(res.errors)
# raise <- default behaviour on 4xx is silly
@pytest.fixture(scope='session')
def client(engine):
yield WebTester(app.make_app(engine))
def pytest_addoption(parser):
parser.addoption('--refresh-refs', action='store_true', default=False,
help='refresh reference outputs')
@pytest.fixture
def refresh(request):
return request.config.getoption('--refresh-refs')
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<br>
<table class="table table-sm table-bordered table-striped table-hover">
<thead class="thead-inverse"><tr>
<th>#</th>
<th>host</th>
<th>name</th>
<th>path</th>
<th>domain</th>
</tr></thead>
<tr>
<td scope="row">2</td>
<td>HOSTNAME</td>
<td>bad_job</td>
<td>test_rui.py</td>
<td>default</td>
</tr>
<tr>
<td scope="row">1</td>
<td>HOSTNAME</td>
<td>good_job</td>
<td>test_rui.py</td>
<td>default</td>
</tr>
</table>
</body></html>
<!DOCTYPE html PUBLIC "-//W<X>C//DTD HTML <X>.<X> Transitional//EN" "http://www.w<X>.org/TR/REC-html<X>/loose.dtd">
<html><body>
<br>
<table class="table table-sm table-bordered table-striped table-hover">
<thead class="thead-inverse"><tr>
<th>#</th>
<th>service</th>
<th>domain</th>
<th>created</th>
<th>user</th>
<th>worker</th>
<th>status</th>
<th>action</th>
</tr></thead>
<tr>
<th scope="row"><X></th>
<td>
<span><a title="show the tasks log (if any)" target="_blank" href="tasklogs/<X>">bad_job</a></span><span> <a title="show the error" target="_blank" href="taskerror/<X>">[traceback]</a></span>
</td>
<td>default</td>
<td><X>-<X>-<X> <X>:<X>:<X></td>
<td>Babar</td>
<td>#<X></td>
<td class="failed" title="Traceback (most recent call last):
/path/to/src/file, line <X>, in run
func(self)
/path/to/src/file, line <X>, in bad_job
raise Exception('I am a little crasher.')
Exception: I am a little crasher.
">failed</td>
<td>
<button type="button" class="btn btn-warning btn-sm" onclick="delete_task(<X>)">delete</button><span> </span><button type="button" class="btn btn-primary btn-sm" onclick="relaunch_task(<X>)">relaunch</button><span> </span><button type="button" class="btn btn-info btn-sm" onclick='download_input(<X>, "xml")'>input</button>
</td>
</tr>
<tr>
<th scope="row"><X></th>
<td><span><a title="show the tasks log (if any)" target="_blank" href="tasklogs/<X>">good_job</a></span></td>
<td>default</td>
<td><X>-<X>-<X> <X>:<X>:<X></td>
<td>Babar</td>
<td>#<X></td>
<td class="done">done</td>
<td>
<button type="button" class="btn btn-warning btn-sm" onclick="delete_task(<X>)">delete</button><span> </span><button type="button" class="btn btn-primary btn-sm" onclick="relaunch_task(<X>)">relaunch</button><span> </span><button type="button" class="btn btn-info btn-sm" onclick='download_input(<X>, "xml")'>input</button><span> </span><button type="button" class="btn btn-success btn-sm" onclick="download_results(<X>)">results</button>
</td>
</tr>
</table>
</body></html>
\ No newline at end of file
<br>
<table class="table table-sm table-bordered table-striped table-hover">
<thead class="thead-inverse"><tr><th>#</th><th>service</th><th>domain</th><th>created</th><th>user</th><th>worker</th><th>status</th><th>action</th></tr></thead>
<tr><th scope="row"><X></th><td><span><a title="show the tasks log (if any)" target="_blank" href="tasklogs/<X>">justdoit</a></span></td><td>uranus</td><td><X>-<X>-<X> <X>:<X>:<X></td><td>Celeste</td><td>#<X></td><td class="done">done</td><td><button type="button" class="btn btn-warning btn-sm" onclick="delete_task(<X>)">delete</button><span> </span><button type="button" class="btn btn-primary btn-sm" onclick="relaunch_task(<X>)">relaunch</button><span> </span><button type="button" class="btn btn-info btn-sm" onclick="download_input(<X>, &quot;xml&quot;)">input</button><span> </span><button type="button" class="btn btn-success btn-sm" onclick="download_results(<X>)">results</button></td></tr>
</table>
\ No newline at end of file
<br>
<table class="table table-sm table-bordered table-striped table-hover">
<thead class="thead-inverse"><tr><th>#</th><th>service</th><th>domain</th><th>created</th><th>user</th><th>worker</th><th>status</th><th>action</th></tr></thead>
<tr><th scope="row"><X></th><td><span><a title="show the tasks log (if any)" target="_blank" href="tasklogs/<X>">good_job</a></span></td><td>default</td><td><X>-<X>-<X> <X>:<X>:<X></td><td>Babar</td><td>#<X></td><td class="done">done</td><td><button type="button" class="btn btn-warning btn-sm" onclick="delete_task(<X>)">delete</button><span> </span><button type="button" class="btn btn-primary btn-sm" onclick="relaunch_task(<X>)">relaunch</button><span> </span><button type="button" class="btn btn-info btn-sm" onclick="download_input(<X>, &quot;xml&quot;)">input</button><span> </span><button type="button" class="btn btn-success btn-sm" onclick="download_results(<X>)">results</button></td></tr>
</table>
\ No newline at end of file
from rework.api import task
@task(domain='uranus')
def justdoit(task):
task.save_output(True)
import re
from pathlib import Path
from lxml import etree
from rework import api
from rework.task import Task
from rework.testutils import workers, scrub
from rework_ui import taskstable
DATADIR = Path(__file__).parent / 'data'
# html editor
def edittag(tag, editor, anstr):
if isinstance(tag, str):
tags = [tag]
else:
tags = tag
tree = etree.fromstring(anstr, parser=etree.HTMLParser())
for tag in tags:
for elt in tree.xpath('//%s' % tag):
editor(elt)
return etree.tostring(tree.getroottree(),
pretty_print=True, method='html')
# test tasks
@api.task
def good_job(task):
task.save_output(b'Well done !', raw=True)
@api.task
def bad_job(task):
raise Exception('I am a little crasher.')
def test_no_job(client):
res = client.get('/job_status/babar')
assert res.status_code == 404
assert 'NO SUCH JOB' in res.text
def test_bad_request(engine, client):
# bad hostid
res = client.put('/new_job/good_job?user={}&hostid={}'.format('Babar', 'fancyhost'),
upload_files=[('input_file', 'input.xml', b'the file', 'text/xml')]
)
assert res.status == '400 BAD REQUEST'
assert b'No operation was found' in res.body
# bad operation
res = client.put('/new_job/fake_job?user={}'.format('Babar'),
upload_files=[('input_file', 'input.xml', b'the file', 'text/xml')]
)
assert res.status == '400 BAD REQUEST'
assert b'No operation was found' in res.body
# bad operation
res = client.put('/new_job/good_job?user={}'.format('Babar'))
assert res.status == '400 BAD REQUEST'
assert b'input file is mandatory' in res.body
def test_task_life_cycle(engine, client, refresh):
with workers(engine):
tasks = []
for user in ('Babar', 'Babar', 'Celeste'):
res = client.put('/new_job/good_job?user={}'.format(user),
upload_files=[('input_file', 'input.xml', b'the file', 'text/xml')]
)
tid = int(res.body)
t1 = Task.byid(engine, tid)
t1.join()
assert t1.raw_output == b'Well done !'
res = client.get('/job_results/{}'.format(t1.tid))
assert res.headers['Content-Type'] == 'application/zip'
assert res.body == b'Well done !'
tasks.append(t1)
res = client.put('/new_job/bad_job?user=Celeste',
upload_files=[('input_file', 'input.xml', b'the file', 'text/xml')]
)
tid = int(res.body)
t2 = Task.byid(engine, tid)
t2.join()
tasks.append(t2)
res = client.get('/job_results/{}'.format(t2.tid))
assert res.headers['Content-Type'] == 'text/plain; charset=utf-8'
assert res.body.startswith(b'Traceback')
assert 'I am a little crasher.' in res.text
results = []
for t in tasks:
results.append(client.get('/delete-task/{}'.format(t.tid)))
assert all(res.body == b'true' for res in results)
res = client.get('/services-table')
ipaddr = re.compile('^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$')
def edit(elt):
if elt.text:
if 'test_rui' in elt.text:
elt.text = Path(elt.text).name
elif ipaddr.match(elt.text):
elt.text = 'HOSTNAME'
if 'value' in elt.attrib and ipaddr.match(elt.attrib['value']):
elt.attrib['value'] = 'HOSTNAME'
return elt
html = edittag(('td', 'input'), edit, res.text)
refpath = DATADIR / 'services.html'
if refresh:
refpath.write_bytes(html)
assert html == refpath.read_bytes()
def test_tasks_table(engine, client, refresh):
with engine.connect() as cn:
cn.execute('delete from rework.task')
with workers(engine):
res = client.get('/tasks-table')
assert res.text == '<p>Table under construction ...</p>'
res = client.get('/tasks-table-hash')
assert res.text == 'no-hash-yet'
taskstable.refresh_tasks_file(engine)
res = client.get('/tasks-table')
assert res.text == (
'<br>\n'
'<table class="table table-sm table-bordered table-striped table-hover">\n'
'<thead class="thead-inverse"><tr><th>#</th><th>service</th><th>domain</th><th>created</th>'
'<th>user</th><th>worker</th><th>status</th><th>action</th></tr></thead>\n</table>'
)
res = client.get('/tasks-table-hash')
assert res.text == 'd751713988987e9331980363e24189ce'
res = client.get('/tasks-table-hash?domain=all')
assert res.text == 'no-hash-yet'
res = client.get('/tasks-table-hash?domain=default')
assert res.text == 'd751713988987e9331980363e24189ce'
t = api.schedule(engine, 'good_job', metadata={'user': 'Babar'})
t.join()
taskstable.refresh_tasks_file(engine)
res = client.get('/tasks-table')
refpath = DATADIR / 'tasks-table.html'
if refresh:
refpath.write_bytes(scrub(res.text).encode('utf-8'))
assert scrub(res.text) == refpath.read_bytes().decode('utf-8')
count = engine.execute('select count(*) from rework.taskstable').scalar()
assert count == 1 # only default domains, 'all' appears with many domains
t = api.schedule(engine, 'bad_job', metadata={'user': 'Babar'})
t.join()
taskstable.refresh_tasks_file(engine)
res = client.get('/tasks-table')
srcpath = re.compile('File "(.*)"')
def edit(elt):
if 'title' in elt.attrib:
elt.attrib['title'] = srcpath.sub('/path/to/src/file', elt.attrib['title'])
return elt
html = edittag('td', edit, res.text).decode('utf-8')
refpath = DATADIR / 'tasks-table-error.html'
if refresh:
refpath.write_bytes(scrub(html).encode('utf-8'))
assert scrub(html) == refpath.read_bytes().decode('utf-8')
# declare an new domain
from . import tasks
api.freeze_operations(engine)
with workers(engine, domain='uranus'):
t = api.schedule(engine, 'justdoit', domain='uranus', metadata={'user': 'Celeste'})
t.join()
taskstable.refresh_tasks_file(engine)
res = client.get('/tasks-table-hash?domain=uranus')
assert res.text == 'ce14320fb847e8e7443ffca102315671'
res = client.get('/tasks-table?domain=uranus')
refpath = DATADIR / 'tasks-table-uranus.html'
if refresh:
refpath.write_bytes(scrub(res.text).encode('utf-8'))
assert scrub(res.text) == refpath.read_bytes().decode('utf-8')
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment