Centralize and standardize fake test object resources

Before this, we'd create different fake resources in different ways
for test purposes. This has now been fairly standardized across
the unit tests.
There has also been some coverage improvement as a result of noticing
some tests that had been missing.

Change-Id: I504f4abb0f70345a68acc829ef95bf494939295e
This commit is contained in:
David Moreau-Simard 2017-02-11 10:51:40 -05:00 committed by David Moreau Simard
parent 5061b967f3
commit e8791f42ee
12 changed files with 440 additions and 246 deletions

0
ara/tests/__init__.py Normal file
View File

View File

View File

@ -1,4 +1,3 @@
import random
import flask
import unittest
@ -6,6 +5,8 @@ import ara.webapp as w
import ara.models as m
from ara.models import db
from ara.tests.unit import fakes
class TestAra(unittest.TestCase):
'''Common setup/teardown for ARA tests'''
@ -31,122 +32,156 @@ class TestAra(unittest.TestCase):
def ansible_run(complete=True, failed=False, gather_facts=True,
ara_record=False):
'''Simulate a simple Ansible run by creating the
expected database objects. This roughly approximates the
following playbook:
"""
Simulates an Ansible run by creating the expected database objects.
This roughly approximates the following playbook:
- hosts: host-<int>
gather_facts: true
---
- name: ARA unit tests
hosts: host-<random>
gather_facts: yes
tasks:
- test-action:
when: not ara_record
- ara_record:
- name: Fake task
include: some/path/main.yml
- name: Record something
ara_record:
key: 'test key'
value: 'test value'
when: ara_record
Where `<int>` is a random integer generated each time this
function is called.
- name: Fail something
fail:
when: failed
Set the `complete` parameter to `False` to simulate an
aborted Ansible run.
Set the `failed` parameter to `True` to simulate a
failed Ansible run.
Set the `gathered_facts` parameter to `False` to simulate a run with no
Where '<random>' is a random integer generated each time.
Set the 'complete' parameter to 'False' to simulate an aborted Ansible run.
Set the 'failed' parameter to 'True' to simulate a failed Ansible run.
Set the 'gathered_facts' parameter to 'False' to simulate a run with no
facts gathered.
Set the `ara_record` parameter to `True` to simulate a run with an
Set the 'ara_record' parameter to 'True' to simulate a run with an
ara_record task.
'''
playbook_string = """
- name: ARA unit tests
hosts: localhost
gather_facts: no
tasks:
- debug:
msg: 'unit tests'
"""
playbook_content = m.FileContent(content=playbook_string)
playbook = fakes.Playbook(complete=complete, path='playbook.yml').model
pb_file = fakes.File(playbook=playbook,
is_playbook=True,
path=playbook.path).model
pb_content = fakes.FileContent(content=fakes.FAKE_PLAYBOOK_CONTENT).model
playbook = m.Playbook(path='testing.yml')
playbook_file = m.File(path=playbook.path,
playbook=playbook,
content=playbook_content,
is_playbook=True)
play = fakes.Play(playbook=playbook).model
host = fakes.Host(playbook=playbook).model
play = m.Play(playbook=playbook, name='test play')
host = m.Host(name='host-%04d' % random.randint(0, 9999),
playbook=playbook)
tasks = []
task_results = []
if ara_record:
task = m.Task(play=play, playbook=playbook, action='ara_record')
msg = 'Data recorded in ARA for this playbook.'
else:
task = m.Task(play=play, playbook=playbook, action='test-action')
msg = 'This is a test'
task_string = """
- debug:
msg: 'task'
"""
task_content = m.FileContent(content=task_string)
task_file = m.File(path='main.yml',
playbook=playbook,
content=task_content)
task.lineno = '1'
task.file = task_file
task.file_id = task_file.id
task_file = fakes.File(playbook=playbook,
is_playbook=False,
path='some/path/main.yml').model
task_content = fakes.FileContent(content=fakes.FAKE_TASK_CONTENT).model
task = fakes.Task(play=play,
playbook=playbook,
action='fake_action',
file=task_file,
file_id=task_file.id).model
tasks.append(task)
task_result = fakes.TaskResult(task=task,
host=host,
status='ok',
changed=True,
result='fake action result').model
task_results.append(task_result)
result = m.TaskResult(task=task, status='ok', host=host, result=msg)
task_skipped = m.Task(play=play, playbook=playbook, action='foo')
result_skipped = m.TaskResult(task=task_skipped, status='skipped',
host=host, result='Conditional check failed')
task_failed = m.Task(play=play, playbook=playbook, action='bar')
result_failed = m.TaskResult(task=task_failed, status='failed', host=host,
result='Failed to do thing')
record_task = fakes.Task(play=play,
playbook=playbook,
action='ara_record').model
tasks.append(record_task)
ctx = dict(
playbook=playbook,
pb_file=pb_file,
pb_content=pb_content,
play=play,
host=host,
task=task,
result=result,
host=host)
task_file=task_file,
task_content=task_content,
result=task_result,
)
items = [playbook, pb_file, pb_content,
task_file, task_content, play, host]
skipped = False
if ara_record:
msg = 'Data recorded in ARA for this playbook.'
record_result = fakes.TaskResult(task=record_task,
host=host,
status='ok',
changed=True,
result=msg).model
data = fakes.Data(playbook=playbook).model
ctx['data'] = data
items.append(data)
else:
skipped = True
msg = 'Conditional check failed'
record_result = fakes.TaskResult(task=record_task,
host=host,
status='skipped',
changed=False,
skipped=True,
result=msg).model
task_results.append(record_result)
failed_task = fakes.Task(play=play,
playbook=playbook,
action='fail').model
tasks.append(failed_task)
if failed:
msg = 'FAILED!'
failed_result = fakes.TaskResult(task=failed_task,
host=host,
status='failed',
changed=False,
failed=True,
result=msg).model
else:
skipped = True
msg = 'Conditional check failed'
failed_result = fakes.TaskResult(task=failed_task,
host=host,
status='skipped',
changed=False,
skipped=True,
result=msg).model
task_results.append(failed_result)
if gather_facts:
facts = m.HostFacts(host=host, values='{"fact": "value"}')
facts = fakes.HostFacts(host=host).model
ctx['facts'] = facts
items.append(facts)
if ara_record:
data = m.Data(playbook=playbook, key='test key', value='test value')
ctx['data'] = data
for obj in ctx.values():
if hasattr(obj, 'start'):
obj.start()
db.session.add(obj)
extra_objects = [playbook_file, playbook_content, task_file, task_content,
task_skipped, result_skipped]
if failed:
extra_objects.append(task_failed)
extra_objects.append(result_failed)
for obj in extra_objects:
db.session.add(obj)
db.session.commit()
for item in items + tasks + task_results:
if hasattr(item, 'start'):
item.start()
if complete:
stats = m.Stats(playbook=playbook, host=host, ok=1, skipped=1,
failed=int(failed))
stats = fakes.Stats(playbook=playbook,
host=host,
ok=1,
skipped=int(skipped),
failed=int(failed)).model
ctx['stats'] = stats
db.session.add(stats)
ctx['playbook'].complete = True
items.append(stats)
for obj in ctx.values():
if hasattr(obj, 'stop'):
obj.stop()
for item in items + tasks + task_results:
if hasattr(item, 'stop'):
item.stop()
for item in items + tasks + task_results:
db.session.add(item)
db.session.commit()
return ctx

236
ara/tests/unit/fakes.py Normal file
View File

@ -0,0 +1,236 @@
import random
import ara.models as m
from mock import Mock
FAKE_PLAYBOOK_CONTENT = """---
- name: ARA unit tests
hosts: localhost
gather_facts: yes
tasks:
- debug:
msg: 'Unit tests, yay!'
"""
FAKE_TASK_CONTENT = """---
- debug:
msg: 'task'
"""
DEFAULT_CONTENT = """---
# YAML should be here"""
class Data(object):
def __init__(self, playbook=None, key='test key', value='test value'):
if playbook is None:
playbook = Playbook().model
self.playbook = playbook
self.key = key
self.value = value
@property
def model(self):
return m.Data(playbook=self.playbook,
key=self.key,
value=self.value)
class File(object):
def __init__(self, is_playbook=False, path='main.yml', playbook=None):
self.is_playbook = is_playbook
self.path = path
if playbook is None:
playbook = Playbook(path=self.path).model
self.playbook = playbook
@property
def model(self):
return m.File(is_playbook=self.is_playbook,
path=self.path,
playbook=self.playbook)
class FileContent(object):
def __init__(self, content=DEFAULT_CONTENT):
self.content = content
@property
def model(self):
return m.FileContent(content=self.content)
class Host(object):
def __init__(self, name=None, playbook=None):
if name is None:
name = 'host-%04d' % random.randint(0, 9999)
self.name = name
if playbook is None:
playbook = Playbook().model
self.playbook = playbook
@property
def model(self):
return m.Host(name=self.name,
playbook=self.playbook)
class HostFacts(object):
def __init__(self, host=None, values=None):
if host is None:
host = Host().model
self.host = host
if values is None:
values = '{"fact": "value"}'
self.values = values
@property
def model(self):
return m.HostFacts(host=self.host,
values=self.values)
class Playbook(object):
def __init__(self, complete=True, path='playbook.yml'):
self.complete = complete
self.path = path
# Callback specific parameter
self._file_name = path
@property
def model(self):
return m.Playbook(complete=self.complete,
path=self.path)
class Play(object):
def __init__(self, name='ARA unit tests', playbook=None):
self.name = name
if playbook is None:
playbook = Playbook().model
self.playbook = playbook
@property
def model(self):
return m.Play(name=self.name,
playbook=self.playbook)
class Task(object):
def __init__(self, action='fake_action', lineno=1, name='Fake action',
playbook=None, play=None, file=None, file_id=None, path=None):
self.action = action
self.lineno = lineno
self.name = name
if playbook is None:
playbook = Playbook().model
self.playbook = playbook
if play is None:
play = Play(playbook=self.playbook).model
self.play = play
self.file = file
self.file_id = file_id
# Callback specific parameter
if path is None:
path = playbook.path
self.path = '%s:%d' % (path, self.lineno)
def get_path(self):
""" Callback specific method """
return self.path
@property
def model(self):
return m.Task(action=self.action,
lineno=self.lineno,
name=self.name,
playbook=self.playbook,
play=self.play,
file=self.file,
file_id=self.file_id)
class TaskResult(object):
def __init__(self, task=None, host=None, status='ok', ignore_errors=False,
changed=True, failed=False, skipped=False, unreachable=False,
result='Task result <here>'):
assert status in ['ok', 'failed', 'skipped', 'unreachable']
if task is None:
task = Task().model
self.task = task
if host is None:
host = Host(playbook=self.task.playbook)
self.host = host
self.status = status
self.ignore_errors = ignore_errors
self.changed = changed
self.failed = failed
self.skipped = skipped,
self.unreachable = unreachable
self.result = result
# Callback specific parameters
self._host = Mock()
self._host.name = self.host
self._result = {
'changed': self.changed,
'failed': self.failed,
'skipped': self.skipped,
'unreachable': self.unreachable
}
@property
def model(self):
return m.TaskResult(task=self.task,
host=self.host,
status=self.status,
ignore_errors=self.ignore_errors,
changed=self.changed,
failed=self.failed,
skipped=self.skipped,
unreachable=self.unreachable,
result=self.result)
class Stats(object):
def __init__(self, playbook=None, host=None, changed=1, failed=0, ok=1,
skipped=1, unreachable=0, processed=None):
if playbook is None:
playbook = Playbook().model
self.playbook = playbook
if host is None:
host = Host(playbook=self.playbook).model
self.host = host
self.changed = changed
self.failed = failed
self.ok = ok
self.skipped = skipped
self.unreachable = unreachable
# Callback specific parameter
if processed is not None:
self.processed = processed
def summarize(self, name):
""" Callback specific method """
return {
'failures': self.processed[name]['failed'],
'ok': self.processed[name]['ok'],
'changed': self.processed[name]['changed'],
'skipped': self.processed[name]['skipped'],
'unreachable': self.processed[name]['unreachable'],
}
@property
def model(self):
return m.Stats(playbook=self.playbook,
host=self.host,
changed=self.changed,
failed=self.failed,
ok=self.ok,
skipped=self.skipped,
unreachable=self.unreachable)

View File

@ -1,11 +1,11 @@
import pytest
from common import ansible_run
from common import TestAra
from ara.tests.unit.common import ansible_run
from ara.tests.unit.common import TestAra
class TestApp(TestAra):
'''Tests for the ARA web interface'''
""" Tests for the ARA web interface """
def setUp(self):
super(TestApp, self).setUp()

View File

@ -6,7 +6,7 @@ import ara.plugins.callbacks.log_ara as l
import ara.plugins.actions.ara_record as ara_record
import ara.plugins.actions.ara_read as ara_read
from common import TestAra
from ara.tests.unit.common import TestAra
from mock import Mock, MagicMock

View File

@ -5,7 +5,7 @@ import ara.models as m
import ara.plugins.callbacks.log_ara as l
import ara.plugins.actions.ara_record as ara_record
from common import TestAra
from ara.tests.unit.common import TestAra
from mock import Mock, MagicMock

View File

@ -8,64 +8,12 @@ import ara.models as m
import ara.utils as u
import ara.plugins.callbacks.log_ara as l
from common import TestAra
from mock import Mock
class Playbook(object):
def __init__(self, path):
self._file_name = path
self.path = path
class Play(object):
def __init__(self, name):
self.name = name
class Task(object):
def __init__(self, name, path, lineno=1, action='fakeaction'):
self.name = name
self.action = action
self.path = '%s:%d' % (path, lineno)
def get_path(self):
return self.path
class TaskResult(object):
def __init__(self, task, host, status, changed=False):
assert status in ['ok', 'failed', 'skipped', 'unreachable']
self.task = task
self.status = status
self._host = Mock()
self._host.name = host
self._result = {
'changed': changed,
'failed': status == 'failed',
'skipped': status == 'skipped',
'unreachable': status == 'unreachable',
}
class Stats(object):
def __init__(self, processed):
self.processed = processed
def summarize(self, name):
return {
'failures': self.processed[name]['failed'],
'ok': self.processed[name]['ok'],
'changed': self.processed[name]['changed'],
'skipped': self.processed[name]['skipped'],
'unreachable': self.processed[name]['unreachable'],
}
from ara.tests.unit.common import TestAra
from ara.tests.unit import fakes
class TestCallback(TestAra):
'''Tests for the Ansible callback module'''
""" Tests for the Ansible callback module """
def setUp(self):
super(TestCallback, self).setUp()
@ -78,53 +26,45 @@ class TestCallback(TestAra):
super(TestCallback, self).tearDown()
def ansible_run(self):
'''Simulates an ansible run by creating stub versions of the
""" Simulates an ansible run by creating stub versions of the
information that Ansible passes to the callback, and then
calling the various callback methods.'''
calling the various callback methods. """
self.playbook = self._test_playbook()
self.play = self._test_play()
self.task = self._test_task(self.playbook)
self.playbook = fakes.Playbook(path='/playbook-%s.yml' % self.tag)
self.cb.v2_playbook_on_start(self.playbook)
self.play = fakes.Play(playbook=self.playbook.model)
self.cb.v2_playbook_on_play_start(self.play)
self.task = fakes.Task(name='task-%s' % self.tag,
playbook=self.playbook.model,
path=self.playbook.path)
self.cb.v2_playbook_on_task_start(self.task, False)
self.host_one = fakes.Host(name='host1', playbook=self.playbook.model)
self.host_two = fakes.Host(name='host2', playbook=self.playbook.model)
self.results = [
self._test_result(self.task, 'host1', 'ok', changed=True),
self._test_result(self.task, 'host2', 'failed'),
self._test_result(self.host_one, 'ok', changed=True),
self._test_result(self.host_two, 'failed'),
]
self.stats = self._test_stats()
processed_stats = {
self.host_one.name: defaultdict(int, ok=1, changed=1),
self.host_two.name: defaultdict(int, failed=1)
}
self.stats = fakes.Stats(playbook=self.playbook.model,
processed=processed_stats)
self.cb.v2_playbook_on_stats(self.stats)
def _test_stats(self):
stats = Stats({
'host1': defaultdict(int, ok=1, changed=1),
'host2': defaultdict(int, failed=1),
})
self.cb.v2_playbook_on_stats(stats)
return stats
def _test_result(self, task, host, status='ok', changed=False):
result = TaskResult(task, host, status, changed)
def _test_result(self, host, status='ok', changed=False):
result = fakes.TaskResult(task=self.task.model,
host=host.model.name,
status=status,
changed=changed)
func = getattr(self.cb, 'v2_runner_on_%s' % status)
func(result)
return result
def _test_playbook(self):
path = '/test-playbook-%s.yml' % self.tag
playbook = Playbook(path)
self.cb.v2_playbook_on_start(playbook)
return playbook
def _test_play(self):
name = 'test-play-%s' % self.tag
play = Play(name)
self.cb.v2_playbook_on_play_start(play)
return play
def _test_task(self, playbook):
name = 'test-task-%s' % self.tag
task = Task(name, playbook.path)
self.cb.v2_playbook_on_task_start(task, False)
return task
def test_callback_playbook(self):
r_playbook = m.Playbook.query.first()
self.assertIsNotNone(r_playbook)
@ -158,7 +98,8 @@ class TestCallback(TestAra):
self.assertIsNotNone(r_results)
for res in r_results:
self.assertIn(res.host.name, ['host1', 'host2'])
self.assertIn(res.host.name, [self.host_one.name,
self.host_two.name])
self.assertEqual(res.task.name, self.task.name)
def test_callback_stats(self):
@ -175,7 +116,6 @@ class TestCallback(TestAra):
def test_summary_stats(self):
r_hosts = m.Host.query.all()
summary = u.get_summary_stats(r_hosts, 'host_id')
for host in r_hosts:
for status in ['ok', 'changed', 'failed',
'skipped', 'unreachable']:

View File

@ -13,8 +13,8 @@ import ara.cli.result
import ara.cli.task
import ara.cli.stats
from common import ansible_run
from common import TestAra
from ara.tests.unit.common import ansible_run
from ara.tests.unit.common import TestAra
class TestCLI(TestAra):

View File

@ -1,6 +1,6 @@
import datetime
from common import TestAra
from ara.tests.unit.common import TestAra
class TestFilters(TestAra):

View File

@ -1,62 +1,45 @@
import json
import ara.models as m
from common import TestAra
from ara.tests.unit.common import TestAra
from ara.tests.unit import fakes
class TestModels(TestAra):
'''Basic tests for database models'''
""" Basic tests for database models """
def setUp(self):
super(TestModels, self).setUp()
self.playbook = m.Playbook(path='testing.yml')
self.playbook = fakes.Playbook(path='testing.yml').model
self.file = fakes.File(path=self.playbook.path,
playbook=self.playbook,
is_playbook=True).model
content = fakes.FAKE_PLAYBOOK_CONTENT
self.file_content = fakes.FileContent(content=content).model
self.play = fakes.Play(name='test play',
playbook=self.playbook).model
self.task = fakes.Task(name='test task',
play=self.play,
playbook=self.playbook).model
self.data = fakes.Data(playbook=self.playbook,
key='test key',
value='test value').model
self.host = fakes.Host(name='localhost',
playbook=self.playbook).model
self.host_facts = fakes.HostFacts(host=self.host).model
self.task_result = fakes.TaskResult(task=self.task,
status='ok',
host=self.host).model
self.stats = fakes.Stats(playbook=self.playbook,
host=self.host,
changed=0,
failed=0,
skipped=0,
unreachable=0,
ok=0).model
self.play = m.Play(
name='test play',
playbook=self.playbook,
)
self.task = m.Task(
name='test task',
play=self.play,
playbook=self.playbook,
)
self.data = m.Data(
playbook=self.playbook,
key='test key',
value='test value'
)
self.host = m.Host(
name='localhost',
playbook=self.playbook,
)
self.host_facts = m.HostFacts(
host=self.host,
values=json.dumps('{"fact": "value"}')
)
self.task_result = m.TaskResult(
task=self.task,
status='ok',
host=self.host,
)
self.stats = m.Stats(
playbook=self.playbook,
host=self.host,
changed=0,
failed=0,
skipped=0,
unreachable=0,
ok=0,
)
for obj in [self.playbook, self.play, self.task, self.data,
self.host, self.task_result, self.stats]:
for obj in [self.playbook, self.file, self.file_content, self.play,
self.task, self.data, self.host, self.host_facts,
self.task_result, self.stats]:
m.db.session.add(obj)
m.db.session.commit()

View File

@ -1,12 +1,12 @@
import ara.utils as u
import json
from common import ansible_run
from common import TestAra
from ara.tests.unit.common import ansible_run
from ara.tests.unit.common import TestAra
class TestUtils(TestAra):
'''Tests the utils module'''
""" Tests the utils module """
def setUp(self):
super(TestUtils, self).setUp()
self.env = self.app.jinja_env
@ -28,7 +28,7 @@ class TestUtils(TestAra):
res = u.get_summary_stats([ctx['playbook']], 'playbook_id')
self.assertEqual(1, res[playbook]['ok'])
self.assertEqual(0, res[playbook]['changed'])
self.assertEqual(1, res[playbook]['changed'])
self.assertEqual(0, res[playbook]['failed'])
self.assertEqual(1, res[playbook]['skipped'])
self.assertEqual(0, res[playbook]['unreachable'])
@ -52,7 +52,7 @@ class TestUtils(TestAra):
res = u.get_summary_stats([ctx['playbook']], 'playbook_id')
self.assertEqual(1, res[playbook]['ok'])
self.assertEqual(0, res[playbook]['changed'])
self.assertEqual(1, res[playbook]['changed'])
self.assertEqual(1, res[playbook]['failed'])
self.assertEqual(1, res[playbook]['skipped'])
self.assertEqual(0, res[playbook]['unreachable'])