@ -13,9 +13,142 @@
# limitations under the License.
""" Tests for the action_helper.py module """
from shipyard_airflow . control . helpers import action_helper
from unittest . mock import patch
import yaml
from shipyard_airflow . control . helpers import action_helper
from shipyard_airflow . control . helpers . design_reference_helper import (
DesignRefHelper
)
def get_repeated_steps ( ) :
""" Returns a list of fake step dictionaries with repeated steps (tries)
For use in testing getting the latest of a step
Currently , for tests that use this , the only thing that matters is the
task ID and the try number . If this function gets used by more / future tests
more data may need to be added
task_A tries : 1 , 2 , 3
task_B tries : 1
task_C tries : 1 , 2
task_D tries : 1
task_E tries : 1
: returns : A list of fake ( and incomplete ) step dictionaries , some of which
are repeated across multiple tries
: rtype : list
"""
return [
{
' task_id ' : ' task_A ' ,
' try_number ' : 1
} ,
{
' task_id ' : ' task_A ' ,
' try_number ' : 2
} ,
{
' task_id ' : ' task_A ' ,
' try_number ' : 3
} ,
{
' task_id ' : ' task_B ' ,
' try_number ' : 1
} ,
{
' task_id ' : ' task_C ' ,
' try_number ' : 2
} ,
{
' task_id ' : ' task_C ' ,
' try_number ' : 1
} ,
{
' task_id ' : ' task_D ' ,
' try_number ' : 1
} ,
{
' task_id ' : ' task_E ' ,
' try_number ' : 1
}
]
def get_fake_latest_step_dict_failed ( ) :
""" Make a fake dictionary of " latest " steps that represent a failed dag
The only key required by the tests calling this function is " state " , so
the steps contained in the returned dict are incomplete
: returns : A dictionary of " latest " steps that represent a failed dag
: rtype : dict
"""
return {
' armada_build ' : { ' state ' : ' failed ' } ,
' arbitrary_step ' : { ' state ' : ' success ' } ,
' another_arbitrary_step ' : { ' state ' : ' running ' } ,
' upgrade_airflow ' : { ' state ' : ' success ' } ,
' concurrency_check ' : { ' state ' : ' success ' }
}
def get_fake_latest_step_dict_running ( ) :
""" Make a fake dictionary of " latest " steps that represent a running dag
The only key required by the tests calling this function is " state " , so
the steps contained in the returned dict are incomplete
: returns : A dictionary of " latest " steps that represent a running dag
: rtype : dict
"""
return {
' armada_build ' : { ' state ' : ' queued ' } ,
' arbitrary_step ' : { ' state ' : ' success ' } ,
' another_arbitrary_step ' : { ' state ' : ' running ' } ,
' upgrade_airflow ' : { ' state ' : ' running ' } ,
' concurrency_check ' : { ' state ' : ' success ' }
}
def get_fake_latest_step_dict_successful ( ) :
""" Make a fake dictionary of " latest " steps that represent a successful dag
The only key required by the tests calling this function is " state " , so
the steps contained in the returned dict are incomplete
: returns : A dictionary of " latest " steps that represent a successful dag
: rtype : dict
"""
return {
' armada_build ' : { ' state ' : ' success ' } ,
' arbitrary_step ' : { ' state ' : ' success ' } ,
' another_arbitrary_step ' : { ' state ' : ' success ' } ,
' upgrade_airflow ' : { ' state ' : ' skipped ' } ,
' concurrency_check ' : { ' state ' : ' success ' }
}
def get_fake_latest_step_dict_unknown ( ) :
""" Make a fake dictionary of " latest " steps that represent a dag of unknown
result
The only key required by the tests calling this function is " state " , so
the steps contained in the returned dict are incomplete
: returns : A dictionary of " latest " steps that represent a dag of unknown
result
: rtype : dict
"""
return {
' armada_build ' : { ' state ' : ' success ' } ,
' arbitrary_step ' : { ' state ' : ' what ' } ,
' another_arbitrary_step ' : { ' state ' : ' are ' } ,
' upgrade_airflow ' : { ' state ' : ' these ' } ,
' concurrency_check ' : { ' state ' : ' states? ' }
}
def test_determine_lifecycle ( ) :
dag_statuses = [
@ -168,3 +301,225 @@ def test_get_step():
try_number = 2
step = actions_helper . get_step ( step_id , try_number )
assert ( step [ ' hostname ' ] . startswith ( ' airflow-worker-1 ' ) )
@patch ( ' shipyard_airflow.control.helpers.deckhand_client.DeckhandClient. '
' get_path ' )
@patch ( ' shipyard_airflow.control.helpers.design_reference_helper. '
' DesignRefHelper.get_design_reference_href ' , return_value = ' href ' )
def test_get_deployment_status_no_action_helper_completed_failed ( get_href ,
get_path ) :
action = {
' committed_rev_id ' : ' rev_id ' ,
' context_marker ' : ' markofcontext ' ,
' dag_status ' : ' FAILED ' ,
' id ' : ' action_id ' ,
' timestamp ' : ' my_timestamp ' ,
' user ' : ' cool-person '
}
expected_data = {
' status ' : ' completed ' ,
' results ' : ' failed ' ,
' context-marker ' : action [ ' context_marker ' ] ,
' action ' : action [ ' id ' ] ,
' document_url ' : ' href ' ,
' user ' : action [ ' user ' ] ,
' date ' : action [ ' timestamp ' ]
}
deployment_status = action_helper . get_deployment_status ( action )
assert deployment_status [ ' status ' ] == expected_data [ ' status ' ]
assert deployment_status [ ' results ' ] == expected_data [ ' results ' ]
assert ( deployment_status [ ' context-marker ' ] ==
expected_data [ ' context-marker ' ] )
assert deployment_status [ ' action ' ] == expected_data [ ' action ' ]
assert deployment_status [ ' document_url ' ] == expected_data [ ' document_url ' ]
assert deployment_status [ ' user ' ] == expected_data [ ' user ' ]
assert deployment_status [ ' date ' ] == expected_data [ ' date ' ]
get_href . assert_called_once_with ( action [ ' committed_rev_id ' ] )
assert get_path . called # This means we created a DesignRefHelper object
@patch ( ' shipyard_airflow.control.helpers.deckhand_client.DeckhandClient. '
' get_path ' )
@patch ( ' shipyard_airflow.control.helpers.design_reference_helper. '
' DesignRefHelper.get_design_reference_href ' , return_value = ' href ' )
def test_get_deployment_status_no_action_helper_completed_success ( get_href ,
get_path ) :
action = {
' committed_rev_id ' : ' rev_id ' ,
' context_marker ' : ' markofcontext ' ,
' dag_status ' : ' SUCCESS ' ,
' id ' : ' action_id ' ,
' timestamp ' : ' my_timestamp ' ,
' user ' : ' cool-person '
}
expected_data = {
' status ' : ' completed ' ,
' results ' : ' successful ' ,
' context-marker ' : action [ ' context_marker ' ] ,
' action ' : action [ ' id ' ] ,
' document_url ' : ' href ' ,
' user ' : action [ ' user ' ] ,
' date ' : action [ ' timestamp ' ]
}
deployment_status = action_helper . get_deployment_status ( action )
assert deployment_status [ ' status ' ] == expected_data [ ' status ' ]
assert deployment_status [ ' results ' ] == expected_data [ ' results ' ]
assert ( deployment_status [ ' context-marker ' ] ==
expected_data [ ' context-marker ' ] )
assert deployment_status [ ' action ' ] == expected_data [ ' action ' ]
assert deployment_status [ ' document_url ' ] == expected_data [ ' document_url ' ]
assert deployment_status [ ' user ' ] == expected_data [ ' user ' ]
assert deployment_status [ ' date ' ] == expected_data [ ' date ' ]
get_href . assert_called_once_with ( action [ ' committed_rev_id ' ] )
assert get_path . called # This means we created a DesignRefHelper object
@patch . object ( action_helper . ActionsHelper ,
' get_result_from_dag_steps ' ,
return_value = ' result ' )
@patch ( ' shipyard_airflow.control.helpers.deckhand_client.DeckhandClient. '
' get_path ' )
@patch ( ' shipyard_airflow.control.helpers.design_reference_helper. '
' DesignRefHelper.get_design_reference_href ' , return_value = ' href ' )
def test_get_deployment_status_use_action_helper ( get_href ,
get_path ,
get_result ) :
action = {
' committed_rev_id ' : ' rev_id ' ,
' context_marker ' : ' markofcontext ' ,
' dag_status ' : ' ASDFJKL: ' ,
' id ' : ' action_id ' ,
' timestamp ' : ' my_timestamp ' ,
' user ' : ' cool-person '
}
expected_data = {
' status ' : ' running ' ,
' results ' : ' result ' ,
' context-marker ' : action [ ' context_marker ' ] ,
' action ' : action [ ' id ' ] ,
' document_url ' : ' href ' ,
' user ' : action [ ' user ' ] ,
' date ' : action [ ' timestamp ' ]
}
deployment_status = action_helper . get_deployment_status ( action )
assert deployment_status [ ' status ' ] == expected_data [ ' status ' ]
assert deployment_status [ ' results ' ] == expected_data [ ' results ' ]
assert ( deployment_status [ ' context-marker ' ] ==
expected_data [ ' context-marker ' ] )
assert deployment_status [ ' action ' ] == expected_data [ ' action ' ]
assert deployment_status [ ' document_url ' ] == expected_data [ ' document_url ' ]
assert deployment_status [ ' user ' ] == expected_data [ ' user ' ]
assert deployment_status [ ' date ' ] == expected_data [ ' date ' ]
get_href . assert_called_once_with ( action [ ' committed_rev_id ' ] )
assert get_result . called
assert get_path . called # This means we created a DesignRefHelper object
@patch . object ( action_helper . ActionsHelper ,
' get_result_from_dag_steps ' ,
return_value = ' result ' )
@patch ( ' shipyard_airflow.control.helpers.deckhand_client.DeckhandClient. '
' get_path ' )
@patch ( ' shipyard_airflow.control.helpers.design_reference_helper. '
' DesignRefHelper.get_design_reference_href ' , return_value = ' href ' )
def test_get_deployment_status_use_action_helper_force_completed ( get_href ,
get_path ,
get_result ) :
action = {
' committed_rev_id ' : ' rev_id ' ,
' context_marker ' : ' markofcontext ' ,
' dag_status ' : ' ASDFJKL: ' ,
' id ' : ' action_id ' ,
' timestamp ' : ' my_timestamp ' ,
' user ' : ' cool-person '
}
expected_data = {
' status ' : ' completed ' ,
' results ' : ' result ' ,
' context-marker ' : action [ ' context_marker ' ] ,
' action ' : action [ ' id ' ] ,
' document_url ' : ' href ' ,
' user ' : action [ ' user ' ] ,
' date ' : action [ ' timestamp ' ]
}
deployment_status = action_helper . get_deployment_status ( action , True )
assert deployment_status [ ' status ' ] == expected_data [ ' status ' ]
assert deployment_status [ ' results ' ] == expected_data [ ' results ' ]
assert ( deployment_status [ ' context-marker ' ] ==
expected_data [ ' context-marker ' ] )
assert deployment_status [ ' action ' ] == expected_data [ ' action ' ]
assert deployment_status [ ' document_url ' ] == expected_data [ ' document_url ' ]
assert deployment_status [ ' user ' ] == expected_data [ ' user ' ]
assert deployment_status [ ' date ' ] == expected_data [ ' date ' ]
get_href . assert_called_once_with ( action [ ' committed_rev_id ' ] )
assert get_result . called
assert get_path . called # This means we created a DesignRefHelper object
@patch . object ( action_helper . ActionsHelper , ' _get_action_info ' )
@patch . object ( action_helper . ActionsHelper , ' _get_all_steps ' ,
return_value = get_repeated_steps ( ) )
def test__get_latest_steps ( get_all_steps , get_action_info ) :
helper = action_helper . ActionsHelper ( action_id = ' irrelevant ' )
latest_steps_dict = helper . _get_latest_steps ( )
assert latest_steps_dict [ ' task_A ' ] [ ' try_number ' ] == 3
assert latest_steps_dict [ ' task_B ' ] [ ' try_number ' ] == 1
assert latest_steps_dict [ ' task_C ' ] [ ' try_number ' ] == 2
assert latest_steps_dict [ ' task_D ' ] [ ' try_number ' ] == 1
assert latest_steps_dict [ ' task_E ' ] [ ' try_number ' ] == 1
assert get_all_steps . called
assert get_action_info . called
@patch . object ( action_helper . ActionsHelper , ' _get_action_info ' )
@patch . object ( action_helper . ActionsHelper , ' _get_latest_steps ' ,
return_value = get_fake_latest_step_dict_successful ( ) )
def test_get_result_from_dag_steps_success ( get_latest_steps , get_action_info ) :
helper = action_helper . ActionsHelper ( action_id = ' irrelevant ' )
result = helper . get_result_from_dag_steps ( )
assert result == ' successful '
assert get_latest_steps . called
assert get_action_info . called
@patch . object ( action_helper . ActionsHelper , ' _get_action_info ' )
@patch . object ( action_helper . ActionsHelper , ' _get_latest_steps ' ,
return_value = get_fake_latest_step_dict_failed ( ) )
def test_get_result_from_dag_steps_failed ( get_latest_steps , get_action_info ) :
helper = action_helper . ActionsHelper ( action_id = ' irrelevant ' )
result = helper . get_result_from_dag_steps ( )
assert result == ' failed '
assert get_latest_steps . called
assert get_action_info . called
@patch . object ( action_helper . ActionsHelper , ' _get_action_info ' )
@patch . object ( action_helper . ActionsHelper , ' _get_latest_steps ' ,
return_value = get_fake_latest_step_dict_running ( ) )
def test_get_result_from_dag_steps_running ( get_latest_steps , get_action_info ) :
helper = action_helper . ActionsHelper ( action_id = ' irrelevant ' )
result = helper . get_result_from_dag_steps ( )
assert result == ' pending '
assert get_latest_steps . called
assert get_action_info . called
@patch ( ' logging.Logger.warning ' )
@patch . object ( action_helper . ActionsHelper , ' _get_action_info ' )
@patch . object ( action_helper . ActionsHelper , ' _get_latest_steps ' ,
return_value = get_fake_latest_step_dict_unknown ( ) )
def test_get_result_from_dag_steps_unknown ( get_latest_steps ,
get_action_info ,
log_warning_patch ) :
helper = action_helper . ActionsHelper ( action_id = ' irrelevant ' )
result = helper . get_result_from_dag_steps ( )
assert result == ' unknown '
assert get_latest_steps . called
assert get_action_info . called
# Each critical step that had an unknown state should log a warning:
assert log_warning_patch . call_count == 1