306:edefddec933d
Anton Shestakov <av6@dwimlabs.net>, Sun, 24 Sep 2017 12:25:05 +0800
viewer: use subqueries to get data on index page Before, there were bare columns in the aggregate query, their values were undefined (but it somehow worked), as SQLite docs say. Good news is that now this bigger query uses (project_id, ordinal) index and is really fast.

next change 322:38b1d78b2242
previous change 303:cfaaf0b0ec71

tests/test_viewer.py

Permissions: -rw-r--r--

Other formats: Feeds:
from datetime import datetime
from xml.etree import ElementTree
from pytest import raises
from tornado.escape import json_encode
from tornado.testing import AsyncHTTPTestCase
from tornado.web import HTTPError
from candolint.handlers import get_project_or_404
from candolint.models import database, Project, Change, Check
from viewer import CandolintViewer
XMLNS = {
'atom': 'http://www.w3.org/2005/Atom',
'svg': 'http://www.w3.org/2000/svg'
}
def setup_module():
with database.transaction():
project = Project.create(
url='https://example.com/alice/test-viewer',
domain='example.com',
user='alice',
name='test-viewer')
change = Change.create(
rev=42,
node='92cfceb39d57d914ed8b14d0e37643de0797ae56',
branch='default',
date='2016-07-19 22:23 +0800',
author='alice',
message='component: do a thing',
project=project)
Check.create(
ordinal=Check.get_next_ordinal(project),
errors=0,
warnings=0,
lines='[]',
success=True,
started=datetime.fromtimestamp(0),
finished=datetime.fromtimestamp(0),
change=change,
project=project)
lines = [{
'text': '# C&O job started: 2016-08-20T02:38:06+00:00',
'cls': 'meta'
}, {
'text': '# C&O task: setup',
'task': 'setup',
'cls': 'task'
}, {
'text': '$ ../venv2/bin/flake8 --version',
'task': 'setup'
}, {
'text': '3.0.3',
'task': 'setup'
}, {
'text': '# C&O task: checks',
'task': 'checks',
'cls': 'task'
}, {
'line_number': 20,
'task': 'checks',
'link_start': 0,
'text': 'test.py:20:80: E501 line too long (82 > 79 characters)',
'filename': 'test.py',
'link_end': 10,
'cls': 'warning'
}, {
'text': '# C&O job finished: 2016-08-20T02:38:53+00:00',
'cls': 'meta'
}]
Check.create(
ordinal=Check.get_next_ordinal(project),
errors=1,
warnings=1,
lines=json_encode(lines),
success=True,
started=datetime(2016, 8, 20, 2, 38, 6),
finished=datetime(2016, 8, 20, 2, 38, 53),
change=change,
project=project)
def test_get_project_or_404():
with raises(HTTPError) as error:
get_project_or_404('butt.cloud', 'cyber', 'wizard-attack')
assert error.value.status_code == 404
project = get_project_or_404('example.com', 'alice', 'test-viewer')
assert project.id is not None
assert project.url == 'https://example.com/alice/test-viewer'
class ViewerTestCase(AsyncHTTPTestCase):
def get_app(self):
return CandolintViewer()
def test_index(self):
response = self.fetch('/')
assert response.code == 200
assert 'online linter' in response.body
assert 'test-viewer' in response.body
assert '1 error' in response.body
assert '1 warning' in response.body
assert '<a href="#">default</a>' in response.body
assert '2016-08-20T02:38:53Z' in response.body
def test_404(self):
response = self.fetch('/nobodyhere')
assert response.code == 404
assert 'online linter' in response.body
def test_project(self):
response = self.fetch('/butt.cloud/cyber/wizard-attack')
assert response.code == 404
assert 'online linter' in response.body
response = self.fetch('/example.com/alice/test-viewer')
assert response.code == 200
assert 'Clone URL' in response.body
assert 'status.svg' in response.body
assert '.. image:: http' in response.body
assert '1 error' in response.body
assert '1 warning' in response.body
assert '<a href="#">default</a>' in response.body
assert ('<link rel="alternate"'
' href="/example.com/alice/test-viewer/atom"'
' type="application/atom+xml"'
' title="test-viewer feed">') in response.body
def test_atom(self):
response = self.fetch('/example.com/alice/test-viewer/atom')
assert response.code == 200
assert 'atom+xml' in response.headers['Content-Type']
root = ElementTree.fromstring(response.body)
assert root.tag == '{%s}%s' % (XMLNS['atom'], 'feed')
title = root.find('./atom:entry/atom:title', XMLNS)
assert title is not None
assert title.text.startswith('Check #')
assert title.text.endswith(' 1 error, 1 warning')
updates = [el.text for el in root.findall('.//atom:updated', XMLNS)]
assert len(updates) == min(Check.select().count(), 20) + 1
assert updates[0] == updates[1] == '2016-08-20T02:38:53Z'
def test_check(self):
response = self.fetch('/butt.cloud/cyber/wizard-attack/latest')
assert response.code == 404
assert 'online linter' in response.body
response = self.fetch('/example.com/alice/test-viewer/latest')
assert response.code == 200
assert '1 error' in response.body
assert '1 warning' in response.body
assert '<a href="#">default</a>' in response.body
assert '<a href="#" class="filelink">test.py:20</a>' in response.body
assert ('<time datetime="2016-08-20T02:38:53Z"'
' title="2016-08-20 02:38:53 UTC">'
'2016-08-20 02:38:53 UTC</time>') in response.body
assert ('<time datetime="2016-07-19T22:23+0800"'
' title="2016-07-19 22:23 +0800">'
'2016-07-19 22:23 +0800</time>') in response.body
response = self.fetch('/example.com/alice/test-viewer/latest/raw')
assert response.code == 200
assert response.headers['Content-Type'] == 'text/plain; charset=utf-8'
assert 'test.py' in response.body
def test_compare(self):
base_url = '/example.com/alice/test-viewer/latest'
response = self.fetch(base_url + '/compare/latest')
assert response.code == 200
assert 'same commit' in response.body
assert 'Diff is empty' in response.body
response = self.fetch(base_url + '/compare/1')
assert response.code == 200
assert 'same commit' in response.body
assert 'E501' in response.body
def test_status(self):
response = self.fetch('/butt.cloud/cyber/wizard-attack/status.svg')
assert response.code == 404
assert 'online linter' in response.body
response = self.fetch('/example.com/alice/test-viewer/status.svg')
assert response.code == 200
root = ElementTree.fromstring(response.body)
assert root.tag == '{%s}%s' % (XMLNS['svg'], 'svg')
assert root.attrib.get('height') == '20'
text = [el.text for el in root.findall('./svg:g/svg:text', XMLNS)]
assert text[::2] == ['lint', '1 error', '1 warning']
def test_dot(self):
response = self.fetch('/example.com/alice/test-viewer/latest/dot.svg')
assert response.code == 200
root = ElementTree.fromstring(response.body)
assert root.tag == '{%s}%s' % (XMLNS['svg'], 'svg')
assert root.attrib.get('height') == '12'
circles = root.findall('./svg:circle', XMLNS)
assert len(circles) == 1
assert circles[0].attrib.get('fill').startswith('#')