Path: blob/develop/tests/functional/docs/test_help_output.py
1567 views
#!/usr/bin/env python1# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.2#3# Licensed under the Apache License, Version 2.0 (the "License"). You4# may not use this file except in compliance with the License. A copy of5# the License is located at6#7# http://aws.amazon.com/apache2.0/8#9# or in the "license" file accompanying this file. This file is10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF11# ANY KIND, either express or implied. See the License for the specific12# language governing permissions and limitations under the License.13"""Test help output for the AWS CLI.1415The purpose of these docs is to test that the generated output looks how16we expect.1718It's intended to be as end to end as possible, but instead of looking19at the man output, we look one step before at the generated rst output20(it's easier to verify).2122"""23from awscli.testutils import BaseAWSHelpOutputTest24from awscli.testutils import FileCreator25from awscli.testutils import mock26from awscli.testutils import aws27from awscli.compat import StringIO2829from awscli.alias import AliasLoader303132class TestHelpOutput(BaseAWSHelpOutputTest):33def test_output(self):34self.driver.main(['help'])35# Check for the reference label.36self.assert_contains('.. _cli:aws:')37self.assert_contains('***\naws\n***')38self.assert_contains(39'The AWS Command Line Interface is a unified tool '40'to manage your AWS services.')41self.assert_contains('Use *aws help topics* to view')42# Verify we see the docs for top level params, so pick43# a few representative types of params.44self.assert_contains('``--endpoint-url``')45# Boolean type46self.assert_contains('``--no-paginate``')47# Arg with choices48self.assert_contains('``--color``')49self.assert_contains('* on')50self.assert_contains('* off')51self.assert_contains('* auto')52# Then we should see the services.53self.assert_contains('* ec2')54self.assert_contains('* s3api')55self.assert_contains('* sts')56# Make sure it its a related item57self.assert_contains('========\nSee Also\n========')58self.assert_contains('aws help topics')5960def test_service_help_output(self):61self.driver.main(['ec2', 'help'])62# Check for the reference label.63self.assert_contains('.. _cli:aws ec2:')64# We should see the section title for the service.65self.assert_contains('***\nec2\n***')66# With a description header.67self.assert_contains('===========\nDescription\n===========')68# And we should see the operations listed.69self.assert_contains('* monitor-instances')70self.assert_contains('* run-instances')71self.assert_contains('* describe-instances')7273def test_operation_help_output(self):74self.driver.main(['ec2', 'run-instances', 'help'])75# Check for the reference label.76self.assert_contains('.. _cli:aws ec2 run-instances:')77# Should see the title with the operation name78self.assert_contains('*************\nrun-instances\n*************')79# Should contain part of the help text from the model.80self.assert_contains('Launches the specified number of instances')81self.assert_contains('``--count`` (string)')8283def test_custom_service_help_output(self):84self.driver.main(['s3', 'help'])85self.assert_contains('.. _cli:aws s3:')86self.assert_contains('high-level S3 commands')87self.assert_contains('* cp')8889def test_waiter_does_not_have_global_args(self):90self.driver.main(['ec2', 'wait', 'help'])91self.assert_not_contains('--debug')92self.assert_not_contains('Global Options')9394def test_custom_operation_help_output(self):95self.driver.main(['s3', 'ls', 'help'])96self.assert_contains('.. _cli:aws s3 ls:')97self.assert_contains('List S3 objects')98self.assert_contains('--summarize')99self.assert_contains('--debug')100101def test_topic_list_help_output(self):102self.driver.main(['help', 'topics'])103# Should contain the title104self.assert_contains(105'*******************\nAWS CLI Topic Guide\n*******************'106)107# Should contain the description108self.assert_contains('This is the AWS CLI Topic Guide.')109# Should contain the available topics section110self.assert_contains('Available Topics')111# Assert the general order of topic categories.112self.assert_text_order(113'-------\nGeneral\n-------',114'--\nS3\n--',115starting_from='Available Topics'116)117# Make sure that the topic elements elements show up as well.118self.assert_contains(119'* return-codes: Describes'120)121# Make sure the topic elements are underneath the categories as well122# and they get added to each category they fall beneath123self.assert_text_order(124'-------\nGeneral\n-------',125'* return-codes: Describes',126'--\nS3\n--',127starting_from='-------\nGeneral\n-------'128)129130def test_topic_help_command(self):131self.driver.main(['help', 'return-codes'])132self.assert_contains(133'********************\nAWS CLI Return Codes\n********************'134)135self.assert_contains('These are the following return codes')136137def test_arguments_with_example_json_syntax(self):138self.driver.main(['ec2', 'run-instances', 'help'])139self.assert_contains('``--iam-instance-profile``')140self.assert_contains('JSON Syntax')141self.assert_contains('"Arn": "string"')142self.assert_contains('"Name": "string"')143144def test_arguments_with_example_shorthand_syntax(self):145self.driver.main(['ec2', 'run-instances', 'help'])146self.assert_contains('``--iam-instance-profile``')147self.assert_contains('Shorthand Syntax')148self.assert_contains('Arn=string,Name=string')149150def test_required_args_come_before_optional_args(self):151self.driver.main(['ec2', 'run-instances', 'help'])152# We're asserting that the args in the synopsis section appear153# in this order. They don't have to be in this exact order, but154# each item in the list has to come before the previous arg.155self.assert_text_order(156'--image-id <value>',157'[--key-name <value>]',158'[--security-groups <value>]', starting_from='Synopsis')159160def test_service_operation_order(self):161self.driver.main(['ec2', 'help'])162self.assert_text_order(163'activate-license',164'allocate-address',165'assign-private-ip-addresses', starting_from='Available Commands')166167def test_top_level_args_order(self):168self.driver.main(['help'])169self.assert_text_order(170'autoscaling\n', 'cloudformation\n', 'elb\n', 'swf\n',171starting_from='Available Services')172173def test_examples_in_operation_help(self):174self.driver.main(['ec2', 'run-instances', 'help'])175self.assert_contains('========\nExamples\n========')176177def test_add_help_for_dryrun(self):178self.driver.main(['ec2', 'run-instances', 'help'])179self.assert_contains('DryRunOperation')180self.assert_contains('UnauthorizedOperation')181182def test_elb_help_output(self):183self.driver.main(['elb', 'help'])184# We should *not* have any invalid links like185# .. _`:186self.assert_not_contains('.. _`:')187188def test_shorthand_flattens_list_of_single_member_structures(self):189self.driver.main(['elb', 'remove-tags', 'help'])190self.assert_contains("--tags Key1 Key2 Key3")191192def test_deprecated_operations_not_documented(self):193self.driver.main(['s3api', 'help'])194self.assert_not_contains('get-bucket-lifecycle\n')195self.assert_not_contains('put-bucket-lifecycle\n')196self.assert_not_contains('get-bucket-notification\n')197self.assert_not_contains('put-bucket-notification\n')198199200class TestRemoveDeprecatedCommands(BaseAWSHelpOutputTest):201def assert_command_does_not_exist(self, service, command):202# Basically try to get the help output for the removed203# command verify that we get a SystemExit exception204# and that we get something in stderr that says that205# we made an invalid choice (because the operation is removed).206stderr = StringIO()207with mock.patch('sys.stderr', stderr):208with self.assertRaises(SystemExit):209self.driver.main([service, command, 'help'])210# We should see an error message complaining about211# an invalid choice because the operation has been removed.212self.assertIn('argument operation: Invalid choice', stderr.getvalue())213214def test_ses_deprecated_commands(self):215self.driver.main(['ses', 'help'])216self.assert_not_contains('list-verified-email-addresses')217self.assert_not_contains('delete-verified-email-address')218self.assert_not_contains('verify-email-address')219220self.assert_command_does_not_exist(221'ses', 'list-verified-email-addresses')222self.assert_command_does_not_exist(223'ses', 'delete-verified-email-address')224self.assert_command_does_not_exist(225'ses', 'verify-email-address')226227def test_ec2_import_export(self):228self.driver.main(['ec2', 'help'])229self.assert_not_contains('import-instance')230self.assert_not_contains('import-volume')231self.assert_command_does_not_exist(232'ec2', 'import-instance')233self.assert_command_does_not_exist(234'ec2', 'import-volume')235236def test_boolean_param_documented(self):237self.driver.main(['autoscaling',238'terminate-instance-in-auto-scaling-group', 'help'])239self.assert_contains(240('``--should-decrement-desired-capacity`` | '241'``--no-should-decrement-desired-capacity`` (boolean)'))242243def test_streaming_output_arg(self):244self.driver.main(['s3api', 'get-object', 'help'])245self.assert_not_contains('``--outfile``')246self.assert_contains('``outfile`` (string)')247248def test_rds_add_arg_help_has_correct_command_name(self):249self.driver.main(['rds', 'add-option-to-option-group', 'help'])250self.assert_contains('add-option-to-option-group')251252def test_rds_remove_arg_help_has_correct_command_name(self):253self.driver.main(['rds', 'remove-option-from-option-group', 'help'])254self.assert_contains('remove-option-from-option-group')255256def test_modify_operation_not_in_help(self):257self.driver.main(['rds', 'help'])258# This was split into add/remove commands. The modify259# command should not be available.260self.assert_not_contains('modify-option-group')261262263class TestPagingParamDocs(BaseAWSHelpOutputTest):264def test_starting_token_injected(self):265self.driver.main(['s3api', 'list-objects', 'help'])266self.assert_contains('``--starting-token``')267268def test_max_items_injected(self):269self.driver.main(['s3api', 'list-objects', 'help'])270self.assert_contains('``--max-items``')271272def test_builtin_paging_params_removed(self):273self.driver.main(['s3api', 'list-objects', 'help'])274self.assert_not_contains('``--next-token``')275self.assert_not_contains('``--max-keys``')276277def test_paging_documentation_added(self):278self.driver.main(['s3api', 'list-objects', 'help'])279self.assert_contains('``list-objects`` is a paginated operation')280self.assert_contains('When using ``--output text`` and the')281self.assert_contains('following query expressions: ')282283284class TestMergeBooleanGroupArgs(BaseAWSHelpOutputTest):285def test_merge_bool_args(self):286# Boolean args need to be group together so rather than287# --foo foo docs288# --no-foo foo docs again289#290# We instead have:291# --foo | --no-foo foo docs292self.driver.main(['ec2', 'run-instances', 'help'])293self.assert_contains('``--dry-run`` | ``--no-dry-run``')294295def test_top_level_bools(self):296# structure(scalar) of a single value of Value whose value is297# a boolean is pulled into a top level arg.298self.driver.main(['ec2', 'modify-instance-attribute', 'help'])299self.assert_contains('``--ebs-optimized`` | ``--no-ebs-optimized``')300301def test_top_level_bool_has_no_example(self):302# Normally a structure(bool) param would have an example303# of {"Value": true|false}", but when we pull the arg up into304# a top level bool, we should not generate an example.305self.driver.main(['ec2', 'modify-instance-attribute', 'help'])306self.assert_not_contains('"Value": true|false')307308309class TestStructureScalarHasNoExamples(BaseAWSHelpOutputTest):310def test_no_examples_for_structure_single_scalar(self):311self.driver.main(['ec2', 'modify-instance-attribute', 'help'])312self.assert_not_contains('"Value": "string"')313self.assert_not_contains('Value=string')314315def test_example_for_single_structure_not_named_value(self):316# Verify that if a structure does match our special case317# (single element named "Value"), then we still document318# the example syntax.319self.driver.main(['s3api', 'create-bucket', 'help'])320self.assert_contains('LocationConstraint=string')321# Also should see the JSON syntax in the help output.322self.assert_contains('"LocationConstraint": ')323324325class TestJSONListScalarDocs(BaseAWSHelpOutputTest):326def test_space_separated_list_docs(self):327# A list of scalar type can be specified as JSON:328# JSON Syntax:329#330# ["string", ...]331# But at the same time you can always replace that with332# a space separated list. Therefore we want to document333# the space separated list version and not the JSON list334# version.335self.driver.main(['ec2', 'terminate-instances', 'help'])336self.assert_not_contains('["string", ...]')337self.assert_contains('"string" "string"')338339340class TestParamRename(BaseAWSHelpOutputTest):341def test_create_image_renames(self):342# We're just cherry picking this particular operation to verify343# that the rename arg customizations are working.344self.driver.main(['ec2', 'create-image', 'help'])345self.assert_not_contains('no-no-reboot')346self.assert_contains('--reboot')347348class TestCustomCommandDocsFromFile(BaseAWSHelpOutputTest):349def test_description_from_rst_file(self):350# The description for the configure command351# is in _description.rst. We're verifying that we352# can read those contents properly.353self.driver.main(['configure', 'help'])354# These are a few options that are documented in the help output.355self.assert_contains('metadata_service_timeout')356self.assert_contains('metadata_service_num_attempts')357self.assert_contains('aws_access_key_id')358359class TestEnumDocsArentDuplicated(BaseAWSHelpOutputTest):360def test_enum_docs_arent_duplicated(self):361# Test for: https://github.com/aws/aws-cli/issues/609362# What's happening is if you have a list param that has363# an enum, we document it as:364# a|b|c|d a|b|c|d365# Except we show all of the possible enum params twice.366# Each enum param should only occur once. The ideal documentation367# should be:368#369# string1 string2370#371# Where each value is one of:372# value1373# value2374self.driver.main(['cloudformation', 'list-stacks', 'help'])375# "CREATE_IN_PROGRESS" is a enum value, and should only376# appear once in the help output.377contents = self.renderer.rendered_contents378self.assertTrue(contents.count("CREATE_IN_PROGRESS") == 1,379("Enum param was only suppose to be appear once in "380"rendered doc output, appeared: %s" %381contents.count("CREATE_IN_PROGRESS")))382383384class TestParametersCanBeHidden(BaseAWSHelpOutputTest):385def mark_as_undocumented(self, argument_table, **kwargs):386argument_table['starting-sequence-number']._UNDOCUMENTED = True387388def test_hidden_params_are_not_documented(self):389# We're going to demonstrate hiding a parameter.390# --device391self.driver.session.register('building-argument-table',392self.mark_as_undocumented)393self.driver.main(['kinesis', 'get-shard-iterator', 'help'])394self.assert_not_contains('--starting-sequence-number')395396397class TestCanDocumentAsRequired(BaseAWSHelpOutputTest):398def test_can_doc_as_required(self):399# This param is already marked as required, but to be400# explicit this is repeated here to make it more clear.401def doc_as_required(argument_table, **kwargs):402arg = argument_table['volume-arns']403self.driver.session.register('building-argument-table',404doc_as_required)405self.driver.main(['storagegateway', 'describe-cached-iscsi-volumes',406'help'])407self.assert_not_contains('[--volume-arns <value>]')408409410class TestEC2AuthorizeSecurityGroupNotRendered(BaseAWSHelpOutputTest):411def test_deprecated_args_not_documented(self):412self.driver.main(['ec2', 'authorize-security-group-ingress', 'help'])413self.assert_not_contains('--ip-protocol')414self.assert_not_contains('--from-port')415self.assert_not_contains('--to-port')416self.assert_not_contains('--source-security-group-name')417self.assert_not_contains('--source-security-group-owner-id')418419420class TestKMSCreateGrant(BaseAWSHelpOutputTest):421def test_proper_casing(self):422self.driver.main(['kms', 'create-grant', 'help'])423# Ensure that the proper casing is used for this command's docs.424self.assert_not_contains('generate-data-key')425self.assert_contains('GenerateDataKey')426427428class TestRoute53CreateHostedZone(BaseAWSHelpOutputTest):429def test_proper_casing(self):430self.driver.main(['route53', 'create-hosted-zone', 'help'])431# Ensure that the proper casing is used for this command's docs.432self.assert_contains(433'do **not** include ``PrivateZone`` in this input structure')434435436class TestIotData(BaseAWSHelpOutputTest):437def test_service_help_command_has_note(self):438self.driver.main(['iot-data', 'help'])439# Ensure the note is in help page.440self.assert_contains(441'The default endpoints (intended for testing purposes only) can be found at '442'https://docs.aws.amazon.com/general/latest/gr/iot-core.html#iot-core-data-plane-endpoints')443444def test_operation_help_command_has_note(self):445self.driver.main(['iot-data', 'get-thing-shadow', 'help'])446# Ensure the note is in help page.447self.assert_contains(448'The default endpoints (intended for testing purposes only) can be found at '449'https://docs.aws.amazon.com/general/latest/gr/iot-core.html#iot-core-data-plane-endpoints')450451452class TestSMSVoice(BaseAWSHelpOutputTest):453def test_service_help_not_listed(self):454self.driver.main(['help'])455# Ensure the hidden service is not in the help listing.456self.assert_not_contains('* sms-voice')457458459class TestAliases(BaseAWSHelpOutputTest):460def setUp(self):461super(TestAliases, self).setUp()462self.files = FileCreator()463self.alias_file = self.files.create_file('alias', '[toplevel]\n')464self.driver.alias_loader = AliasLoader(self.alias_file)465466def tearDown(self):467super(TestAliases, self).tearDown()468self.files.remove_all()469470def add_alias(self, alias_name, alias_value):471with open(self.alias_file, 'a+') as f:472f.write('%s = %s\n' % (alias_name, alias_value))473474def test_alias_not_in_main_help(self):475self.add_alias('my-alias', 'ec2 describe-regions')476self.driver.main(['help'])477self.assert_not_contains('my-alias')478479480class TestStreamingOutputHelp(BaseAWSHelpOutputTest):481def test_service_help_command_has_note(self):482self.driver.main(['s3api', 'get-object', 'help'])483self.assert_not_contains('outfile <value>')484self.assert_contains('<outfile>')485486487# Use this test class for "help" cases that require the default renderer488# (i.e. renderer from get_render()) instead of a mocked version.489class TestHelpOutputDefaultRenderer:490def test_line_lengths_do_not_break_create_launch_template_version_cmd(self):491result = aws('ec2 create-launch-template-version help')492assert 'exceeds the line-length-limit' not in result.stderr493494495