ironic-python-agent/ironic_python_agent/tests/functional/test_commands.py

83 lines
3.6 KiB
Python

# Copyright 2015 Rackspace, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from ironic_python_agent.tests.functional import base
class TestCommands(base.FunctionalBase):
"""Tests the commands API.
These tests are structured monolithically as one test with multiple steps
to preserve ordering and ensure IPA state remains consistent across
different test runs.
"""
node = {'uuid': '1', 'properties': {}, 'instance_info': {}}
def step_1_get_empty_commands(self):
response = self.request('get', 'commands')
self.assertEqual({'commands': []}, response)
def step_2_run_command(self):
# NOTE(mariojv): get_clean_steps always returns the default
# HardwareManager clean steps if there's not a more specific HWM. So,
# this command succeeds even with an empty node and port. This test's
# success is required for steps 3 and 4 to succeed.
command = {'name': 'clean.get_clean_steps',
'params': {'node': self.node, 'ports': {}}}
response = self.request('post', 'commands', json=command,
headers={'Content-Type': 'application/json'})
self.assertIsNone(response['command_error'])
def step_3_get_commands(self):
# This test relies on step 2 to succeed since step 2 runs the command
# we're checking for
response = self.request('get', 'commands')
self.assertEqual(1, len(response['commands']))
self.assertEqual(
'get_clean_steps', response['commands'][0]['command_name'])
def step_4_get_command_by_id(self):
# First, we have to query the commands API to retrieve the ID. Make
# sure this API call succeeds again, just in case it fails for some
# reason after the last test. This test relies on step 2 to succeed
# since step 2 runs the command we're checking for.
response = self.request('get', 'commands')
command_id = response['commands'][0]['id']
command_from_id = self.request(
'get', 'commands/%s' % command_id)
self.assertEqual('get_clean_steps', command_from_id['command_name'])
def step_5_run_non_existent_command(self):
fake_command = {'name': 'bad_extension.fake_command', 'params': {}}
self.request('post', 'commands', expect_error=404, json=fake_command)
def positive_get_post_command_steps(self):
"""Returns generator with test steps sorted by step number."""
steps_unsorted = [step for step in dir(self)
if step.startswith('step_')]
# The lambda retrieves the step number from the function name and casts
# it to an integer. This is necessary, otherwise a lexicographic sort
# would return ['step_1', 'step_12', 'step_3'] after sorting instead of
# ['step_1', 'step_3', 'step_12'].
steps = sorted(steps_unsorted, key=lambda s: int(s.split('_', 2)[1]))
for name in steps:
yield getattr(self, name)
def test_positive_get_post_commands(self):
for step in self.positive_get_post_command_steps():
step()