import pytest import json from puppetboard import app from pypuppetdb.types import Node from puppetboard import default_settings from bs4 import BeautifulSoup class MockDbQuery(object): def __init__(self, responses): self.responses = responses def get(self, method, **kws): resp = None if method in self.responses: resp = self.responses[method].pop(0) if 'validate' in resp: checks = resp['validate']['checks'] resp = resp['validate']['data'] for check in checks: assert check in kws expected_value = checks[check] assert expected_value == kws[check] return resp @pytest.fixture def mock_puppetdb_environments(mocker): environemnts = [ {'name': 'production'}, {'name': 'staging'} ] return mocker.patch.object(app.puppetdb, 'environments', return_value=environemnts) @pytest.fixture def mock_puppetdb_default_nodes(mocker): node_list = [ Node('_', 'node-unreported', report_timestamp='2013-08-01T09:57:00.000Z', latest_report_hash='1234567', catalog_timestamp='2013-08-01T09:57:00.000Z', facts_timestamp='2013-08-01T09:57:00.000Z', status='unreported'), Node('_', 'node-changed', report_timestamp='2013-08-01T09:57:00.000Z', latest_report_hash='1234567', catalog_timestamp='2013-08-01T09:57:00.000Z', facts_timestamp='2013-08-01T09:57:00.000Z', status='changed'), Node('_', 'node-failed', report_timestamp='2013-08-01T09:57:00.000Z', latest_report_hash='1234567', catalog_timestamp='2013-08-01T09:57:00.000Z', facts_timestamp='2013-08-01T09:57:00.000Z', status='failed'), Node('_', 'node-noop', report_timestamp='2013-08-01T09:57:00.000Z', latest_report_hash='1234567', catalog_timestamp='2013-08-01T09:57:00.000Z', facts_timestamp='2013-08-01T09:57:00.000Z', status='noop'), Node('_', 'node-unchanged', report_timestamp='2013-08-01T09:57:00.000Z', latest_report_hash='1234567', catalog_timestamp='2013-08-01T09:57:00.000Z', facts_timestamp='2013-08-01T09:57:00.000Z', status='unchanged'), Node('_', 'node-skipped', report_timestamp='2013-08-01T09:57:00.000Z', latest_report_hash='1234567', catalog_timestamp='2013-08-01T09:57:00.000Z', facts_timestamp='2013-08-01T09:57:00.000Z', status='skipped') ] return mocker.patch.object(app.puppetdb, 'nodes', return_value=iter(node_list)) @pytest.fixture def client(): client = app.app.test_client() return client def test_first_test(): assert app is not None, ("%s" % reg.app) def test_no_env(client, mock_puppetdb_environments): rv = client.get('/nonexsistenv/') assert rv.status_code == 404 def test_get_index(client, mocker, mock_puppetdb_environments, mock_puppetdb_default_nodes): query_data = { 'nodes': [[{'count': 10}]], 'resources': [[{'count': 40}]], } dbquery = MockDbQuery(query_data) mocker.patch.object(app.puppetdb, '_query', side_effect=dbquery.get) rv = client.get('/') soup = BeautifulSoup(rv.data, 'html.parser') assert soup.title.contents[0] == 'Puppetboard' assert rv.status_code == 200 def test_index_all(client, mocker, mock_puppetdb_environments, mock_puppetdb_default_nodes): base_str = 'puppetlabs.puppetdb.population:' query_data = { 'mbean': [ { 'validate': { 'data': {'Value': '50'}, 'checks': { 'path': '%sname=num-nodes' % base_str } } }, { 'validate': { 'data': {'Value': '60'}, 'checks': { 'path': '%sname=num-resources' % base_str } } }, { 'validate': { 'data': {'Value': 60.3}, 'checks': { 'path': '%sname=avg-resources-per-node' % base_str } } } ] } dbquery = MockDbQuery(query_data) mocker.patch.object(app.puppetdb, '_query', side_effect=dbquery.get) rv = client.get('/%2A/') soup = BeautifulSoup(rv.data, 'html.parser') assert soup.title.contents[0] == 'Puppetboard' vals = soup.find_all('h1', {"class": "ui header darkblue no-margin-bottom"}) assert len(vals) == 3 assert vals[0].string == '50' assert vals[1].string == '60' assert vals[2].string == ' 60' assert rv.status_code == 200 def test_index_division_by_zero(client, mocker): mock_puppetdb_environments(mocker) mock_puppetdb_default_nodes(mocker) query_data = { 'nodes': [[{'count': 0}]], 'resources': [[{'count': 40}]], } dbquery = MockDbQuery(query_data) mocker.patch.object(app.puppetdb, '_query', side_effect=dbquery.get) rv = client.get('/') assert rv.status_code == 200 soup = BeautifulSoup(rv.data, 'html.parser') assert soup.title.contents[0] == 'Puppetboard' vals = soup.find_all('h1', {"class": "ui header darkblue no-margin-bottom"}) assert len(vals) == 3 assert vals[2].string == '0' def test_offline_mode(client, mocker): app.app.config['OFFLINE_MODE'] = True mock_puppetdb_environments(mocker) mock_puppetdb_default_nodes(mocker) query_data = { 'nodes': [[{'count': 10}]], 'resources': [[{'count': 40}]], } dbquery = MockDbQuery(query_data) mocker.patch.object(app.puppetdb, '_query', side_effect=dbquery.get) rv = client.get('/') soup = BeautifulSoup(rv.data, 'html.parser') assert soup.title.contents[0] == 'Puppetboard' for link in soup.find_all('link'): assert "//" not in link['href'] for script in soup.find_all('script'): if "src" in script.attrs: assert "//" not in script['src'] assert rv.status_code == 200 def test_default_node_view(client, mocker, mock_puppetdb_environments, mock_puppetdb_default_nodes): rv = client.get('/nodes') soup = BeautifulSoup(rv.data, 'html.parser') assert soup.title.contents[0] == 'Puppetboard' for label in ['failed', 'changed', 'unreported', 'noop']: vals = soup.find_all('a', {"class": "ui %s label status" % label}) assert len(vals) == 1 assert 'node-%s' % (label) in vals[0].attrs['href'] assert rv.status_code == 200 def test_radiator_view(client, mocker, mock_puppetdb_environments, mock_puppetdb_default_nodes): query_data = { 'nodes': [[{'count': 10}]], 'resources': [[{'count': 40}]], } dbquery = MockDbQuery(query_data) mocker.patch.object(app.puppetdb, '_query', side_effect=dbquery.get) rv = client.get('/radiator') assert rv.status_code == 200 soup = BeautifulSoup(rv.data, 'html.parser') assert soup.title.contents[0] == 'Puppetboard' assert soup.h1 != 'Not Found' total = soup.find(class_='total') assert '10' in total.text def test_radiator_view_json(client, mocker, mock_puppetdb_environments, mock_puppetdb_default_nodes): query_data = { 'nodes': [[{'count': 10}]], 'resources': [[{'count': 40}]], } dbquery = MockDbQuery(query_data) mocker.patch.object(app.puppetdb, '_query', side_effect=dbquery.get) rv = client.get('/radiator', headers={'Accept': 'application/json'}) assert rv.status_code == 200 json_data = json.loads(rv.data.decode('utf-8')) assert json_data['unreported'] == 1 assert json_data['noop'] == 1 assert json_data['failed'] == 1 assert json_data['changed'] == 1 assert json_data['skipped'] == 1 assert json_data['unchanged'] == 1 def test_radiator_view_bad_env(client, mocker): mock_puppetdb_environments(mocker) mock_puppetdb_default_nodes(mocker) query_data = { 'nodes': [[{'count': 10}]], 'resources': [[{'count': 40}]], } dbquery = MockDbQuery(query_data) mocker.patch.object(app.puppetdb, '_query', side_effect=dbquery.get) rv = client.get('/nothere/radiator') assert rv.status_code == 404 soup = BeautifulSoup(rv.data, 'html.parser') assert soup.title.contents[0] == 'Puppetboard' assert soup.h1.text == 'Not Found' def test_radiator_view_division_by_zero(client, mocker): mock_puppetdb_environments(mocker) mock_puppetdb_default_nodes(mocker) query_data = { 'nodes': [[{'count': 0}]], 'resources': [[{'count': 40}]], } dbquery = MockDbQuery(query_data) mocker.patch.object(app.puppetdb, '_query', side_effect=dbquery.get) rv = client.get('/radiator') assert rv.status_code == 200 soup = BeautifulSoup(rv.data, 'html.parser') assert soup.title.contents[0] == 'Puppetboard' total = soup.find(class_='total') assert '0' in total.text