# Copyright 2017 AT&T Intellectual Property. All other rights reserved. # # 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 unittest import mock import responses from shipyard_client.api_client.base_client import BaseClient from shipyard_client.cli.describe.actions import DescribeAction from shipyard_client.cli.describe.actions import DescribeStep from shipyard_client.cli.describe.actions import DescribeValidation from shipyard_client.cli.describe.actions import DescribeWorkflow from tests.unit.cli import stubs GET_ACTION_API_RESP = """ { "name": "deploy_site", "dag_execution_date": "2017-09-24T19:05:49", "validations": [], "id": "01BTTMFVDKZFRJM80FGD7J1AKN", "dag_id": "deploy_site", "command_audit": [ { "id": "01BTTMG16R9H3Z4JVQNBMRV1MZ", "action_id": "01BTTMFVDKZFRJM80FGD7J1AKN", "datetime": "2017-09-24 19:05:49.530223+00:00", "user": "shipyard", "command": "invoke" } ], "user": "shipyard", "context_marker": "629f2ea2-c59d-46b9-8641-7367a91a7016", "datetime": "2017-09-24 19:05:43.603591+00:00", "dag_status": "failed", "parameters": {}, "steps": [ { "id": "action_xcom", "url": "/actions/01BTTMFVDKZFRJM80FGD7J1AKN/steps/action_xcom", "index": 1, "state": "success", "notes": [ { "assoc_id": "step/01BTTMFVDKZFRJM80FGD7J1AKN/action_xcom", "subject": "action_xcom", "sub_type": "step metadata", "note_val": "This is a note for the action_xcom", "verbosity": 1, "note_id": "ABCDEFGHIJKLMNOPQRSTUVWXY0", "note_timestamp": "2018-10-08 14:23:53.346534", "resolved_url_value": null }, { "assoc_id": "step/01BTTMFVDKZFRJM80FGD7J1AKN/action_xcom", "subject": "action_xcom", "sub_type": "step metadata", "note_val": "action_xcom really worked", "verbosity": 1, "note_id": "ABCDEFGHIJKLMNOPQRSTUVWXY1", "note_timestamp": "2018-10-08 14:23:53.346534", "resolved_url_value": null } ] }, { "id": "part2", "url": "/actions/01BTTMFVDKZFRJM80FGD7J1AKN/steps/part2", "index": 2, "state": "success", "notes": [] }, { "id": "part3", "url": "/actions/01BTTMFVDKZFRJM80FGD7J1AKN/steps/part3", "index": 3, "state": "success", "notes": [ { "assoc_id": "step/01BTTMFVDKZFRJM80FGD7J1AKN/part3", "subject": "part3", "sub_type": "step metadata", "note_val": "This is a note for the part3", "verbosity": 1, "note_id": "ABCDEFGHIJKLMNOPQRSTUVWXY2", "note_timestamp": "2018-10-08 14:23:53.346534", "resolved_url_value": null } ] } ], "action_lifecycle": "Failed", "notes": [ { "assoc_id": "action/01BTTMFVDKZFRJM80FGD7J1AKN", "subject": "01BTTMFVDKZFRJM80FGD7J1AKN", "sub_type": "action metadata", "note_val": "This is a note for some action", "verbosity": 1, "note_id": "ABCDEFGHIJKLMNOPQRSTUVWXYA", "note_timestamp": "2018-10-08 14:23:53.346534", "resolved_url_value": "Your lucky numbers are 1, 3, 5, and Q" } ] } """ @responses.activate @mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest') @mock.patch.object(BaseClient, 'get_token', lambda x: 'abc') def test_describe_action(*args): responses.add(responses.GET, 'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN', body=GET_ACTION_API_RESP, status=200) response = DescribeAction( stubs.StubCliContext(), '01BTTMFVDKZFRJM80FGD7J1AKN' ).invoke_and_return_resp() assert 'action/01BTTMFVDKZFRJM80FGD7J1AKN' in response assert 'step/01BTTMFVDKZFRJM80FGD7J1AKN/action_xcom' in response assert 'Steps' in response assert 'Commands' in response assert 'Validations:' in response assert 'This is a note for the part3' in response assert '>>> Your lucky numbers are 1, 3, 5, and Q' @responses.activate @mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest') @mock.patch.object(BaseClient, 'get_token', lambda x: 'abc') def test_describe_action_not_found(*args): api_resp = stubs.gen_err_resp(message='Not Found', sub_error_count=0, sub_info_count=0, reason='It does not exist', code=404) responses.add(responses.GET, 'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN', body=api_resp, status=404) response = DescribeAction( stubs.StubCliContext(), '01BTTMFVDKZFRJM80FGD7J1AKN' ).invoke_and_return_resp() assert 'Error: Not Found' in response assert 'Reason: It does not exist' in response GET_STEP_API_RESP = """ { "end_date": "2017-09-24 19:05:59.446213", "duration": 0.165181, "queued_dttm": "2017-09-24 19:05:52.993983", "operator": "PythonOperator", "try_number": 1, "task_id": "preflight", "state": "success", "execution_date": "2017-09-24 19:05:49", "dag_id": "deploy_site", "index": 1, "start_date": "2017-09-24 19:05:59.281032", "notes": [ { "assoc_id": "step/01BTTMFVDKZFRJM80FGD7J1AKN/preflight", "subject": "preflight", "sub_type": "step metadata", "note_val": "This is a note for the preflight", "verbosity": 1, "note_id": "ABCDEFGHIJKLMNOPQRSTUVWXY3", "note_timestamp": "2018-10-08 14:23:53.346534", "resolved_url_value": null }, { "assoc_id": "step/01BTTMFVDKZFRJM80FGD7J1AKN/preflight", "subject": "preflight", "sub_type": "step metadata", "note_val": "preflight really worked", "verbosity": 1, "note_id": "ABCDEFGHIJKLMNOPQRSTUVWXY4", "note_timestamp": "2018-10-08 14:23:53.346534", "resolved_url_value": null } ] } """ @responses.activate @mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest') @mock.patch.object(BaseClient, 'get_token', lambda x: 'abc') def test_describe_step(*args): responses.add( responses.GET, 'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN/steps/preflight', body=GET_STEP_API_RESP, status=200) response = DescribeStep(stubs.StubCliContext(), '01BTTMFVDKZFRJM80FGD7J1AKN', 'preflight').invoke_and_return_resp() assert 'step/01BTTMFVDKZFRJM80FGD7J1AKN/preflight' in response assert 'preflight really worked' in response assert 'This is a note for the preflight' in response @responses.activate @mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest') @mock.patch.object(BaseClient, 'get_token', lambda x: 'abc') def test_describe_step_not_found(*args): api_resp = stubs.gen_err_resp(message='Not Found', sub_error_count=0, sub_info_count=0, reason='It does not exist', code=404) responses.add( responses.GET, 'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN/steps/preflight', body=api_resp, status=404) response = DescribeStep(stubs.StubCliContext(), '01BTTMFVDKZFRJM80FGD7J1AKN', 'preflight').invoke_and_return_resp() assert 'Error: Not Found' in response assert 'Reason: It does not exist' in response GET_VALIDATION_API_RESP = """ { "validation_name": "validation_1", "action_id": "01BTTMFVDKZFRJM80FGD7J1AKN", "id": "02AURNEWAAAESKN99EBF8J2BHD", "details": "Validations failed for field 'abc'" } """ @responses.activate @mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest') @mock.patch.object(BaseClient, 'get_token', lambda x: 'abc') def test_describe_validation(*args): responses.add( responses.GET, 'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN/' 'validations/02AURNEWAAAESKN99EBF8J2BHD', body=GET_VALIDATION_API_RESP, status=200) response = DescribeValidation( stubs.StubCliContext(), action_id='01BTTMFVDKZFRJM80FGD7J1AKN', validation_id='02AURNEWAAAESKN99EBF8J2BHD').invoke_and_return_resp() v_str = "validation/01BTTMFVDKZFRJM80FGD7J1AKN/02AURNEWAAAESKN99EBF8J2BHD" assert v_str in response assert "Validations failed for field 'abc'" in response @responses.activate @mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest') @mock.patch.object(BaseClient, 'get_token', lambda x: 'abc') def test_describe_validation_not_found(*args): api_resp = stubs.gen_err_resp(message='Not Found', sub_error_count=0, sub_info_count=0, reason='It does not exist', code=404) responses.add( responses.GET, 'http://shiptest/actions/01BTTMFVDKZFRJM80FGD7J1AKN/' 'validations/02AURNEWAAAESKN99EBF8J2BHD', body=api_resp, status=404) response = DescribeValidation( stubs.StubCliContext(), action_id='01BTTMFVDKZFRJM80FGD7J1AKN', validation_id='02AURNEWAAAESKN99EBF8J2BHD').invoke_and_return_resp() assert 'Error: Not Found' in response assert 'Reason: It does not exist' in response WF_API_RESP = """ { "execution_date": "2017-10-09 21:19:03", "end_date": null, "workflow_id": "deploy_site__2017-10-09T21:19:03.000000", "start_date": "2017-10-09 21:19:03.361522", "external_trigger": true, "steps": [ { "end_date": "2017-10-09 21:19:14.916220", "task_id": "action_xcom", "start_date": "2017-10-09 21:19:14.798053", "duration": 0.118167, "queued_dttm": "2017-10-09 21:19:08.432582", "try_number": 1, "state": "success", "operator": "PythonOperator", "dag_id": "deploy_site", "execution_date": "2017-10-09 21:19:03" }, { "end_date": "2017-10-09 21:19:25.283785", "task_id": "dag_concurrency_check", "start_date": "2017-10-09 21:19:25.181492", "duration": 0.102293, "queued_dttm": "2017-10-09 21:19:19.283132", "try_number": 1, "state": "success", "operator": "ConcurrencyCheckOperator", "dag_id": "deploy_site", "execution_date": "2017-10-09 21:19:03" }, { "end_date": "2017-10-09 21:20:05.394677", "task_id": "preflight", "start_date": "2017-10-09 21:19:34.994775", "duration": 30.399902, "queued_dttm": "2017-10-09 21:19:28.449848", "try_number": 1, "state": "failed", "operator": "SubDagOperator", "dag_id": "deploy_site", "execution_date": "2017-10-09 21:19:03" } ], "dag_id": "deploy_site", "state": "failed", "run_id": "manual__2017-10-09T21:19:03", "sub_dags": [ { "execution_date": "2017-10-09 21:19:03", "end_date": null, "workflow_id": "deploy_site.preflight__2017-10-09T21:19:03.000000", "start_date": "2017-10-09 21:19:35.082479", "external_trigger": false, "dag_id": "deploy_site.preflight", "state": "failed", "run_id": "backfill_2017-10-09T21:19:03" }, { "execution_date": "2017-10-09 21:19:03", "end_date": null, "workflow_id": "deploy_site.postflight__2017-10-09T21:19:03.000000", "start_date": "2017-10-09 21:19:35.082479", "external_trigger": false, "dag_id": "deploy_site.postflight", "state": "failed", "run_id": "backfill_2017-10-09T21:19:03" } ] } """ @responses.activate @mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest') @mock.patch.object(BaseClient, 'get_token', lambda x: 'abc') def test_describe_workflow(*args): responses.add( responses.GET, 'http://shiptest/workflows/deploy_site__2017-10-09T21:19:03.000000', body=WF_API_RESP, status=200) response = DescribeWorkflow( stubs.StubCliContext(), 'deploy_site__2017-10-09T21:19:03.000000' ).invoke_and_return_resp() assert 'deploy_site__2017-10-09T21:19:03.000000' in response assert 'deploy_site.preflight__2017-10-09T21:19:03.000000' in response assert 'deploy_site.postflight__2017-10-09T21:19:03.000000' in response assert 'dag_concurrency_check' in response assert 'Subworkflows:' in response @responses.activate @mock.patch.object(BaseClient, 'get_endpoint', lambda x: 'http://shiptest') @mock.patch.object(BaseClient, 'get_token', lambda x: 'abc') def test_describe_workflow_not_found(*args): api_resp = stubs.gen_err_resp(message='Not Found', sub_error_count=0, sub_info_count=0, reason='It does not exist', code=404) responses.add( responses.GET, 'http://shiptest/workflows/deploy_site__2017-10-09T21:19:03.000000', body=api_resp, status=404) response = DescribeWorkflow( stubs.StubCliContext(), 'deploy_site__2017-10-09T21:19:03.000000' ).invoke_and_return_resp() assert 'Error: Not Found' in response assert 'Reason: It does not exist' in response