def test_command_index_positional_argument(self):
        from azure.cli.core._session import INDEX
        from azure.cli.core import CommandIndex

        cli = DummyCli()
        loader = cli.commands_loader
        index = CommandIndex()
        index.invalidate()

        # Test command index is built for command with positional argument
        cmd_tbl = loader.load_command_table(
            ["extra", "extra", "positional_argument"])
        self.assertDictEqual(INDEX[CommandIndex._COMMAND_INDEX],
                             self.expected_command_index)
        self.assertEqual(list(cmd_tbl), [
            'hello mod-only', 'hello overridden', 'extra final',
            'hello ext-only'
        ])

        # Test command index is used by command with positional argument
        cmd_tbl = loader.load_command_table(
            ["hello", "mod-only", "positional_argument"])
        self.assertDictEqual(INDEX[CommandIndex._COMMAND_INDEX],
                             self.expected_command_index)
        self.assertEqual(
            list(cmd_tbl),
            ['hello mod-only', 'hello overridden', 'hello ext-only'])

        # Test command index is used by command with positional argument
        cmd_tbl = loader.load_command_table(
            ["extra", "final", "positional_argument2"])
        self.assertDictEqual(INDEX[CommandIndex._COMMAND_INDEX],
                             self.expected_command_index)
        self.assertEqual(list(cmd_tbl), ['extra final'])
    def test_command_index_always_loaded_extensions(self):

        cli = DummyCli()
        loader = cli.commands_loader

        from azure.cli.core import CommandIndex
        index = CommandIndex()
        index.invalidate()

        # Test azext_always_loaded is loaded when command index is rebuilt
        with mock.patch('azure.cli.core.ALWAYS_LOADED_EXTENSIONS', ['azext_always_loaded']):
            loader.load_command_table(["hello", "mod-only"])
            self.assertEqual(TestCommandRegistration.test_hook, "FAKE_HANDLER")

        TestCommandRegistration.test_hook = []

        # Test azext_always_loaded is loaded when command index is used
        with mock.patch('azure.cli.core.ALWAYS_LOADED_EXTENSIONS', ['azext_always_loaded']):
            loader.load_command_table(["hello", "mod-only"])
            self.assertEqual(TestCommandRegistration.test_hook, "FAKE_HANDLER")
예제 #3
0
    def test_command_index(self):

        from azure.cli.core._session import INDEX
        from azure.cli.core import CommandIndex, __version__

        cli = DummyCli()
        loader = cli.commands_loader
        command_index = CommandIndex(cli)

        expected_command_index = {
            'hello': [
                'azure.cli.command_modules.hello', 'azext_hello2',
                'azext_hello1'
            ],
            'extra': ['azure.cli.command_modules.extra']
        }
        expected_command_table = [
            'hello mod-only', 'hello overridden', 'extra unused',
            'hello ext-only'
        ]

        def _set_index(dict_):
            INDEX[CommandIndex._COMMAND_INDEX] = dict_

        def _check_index():
            self.assertEqual(INDEX[CommandIndex._COMMAND_INDEX_VERSION],
                             __version__)
            self.assertEqual(INDEX[CommandIndex._COMMAND_INDEX_CLOUD_PROFILE],
                             cli.cloud.profile)
            self.assertDictEqual(INDEX[CommandIndex._COMMAND_INDEX],
                                 expected_command_index)

        # Clear the command index
        _set_index({})
        self.assertFalse(INDEX[CommandIndex._COMMAND_INDEX])
        loader.load_command_table(None)
        # Test command index is built for None args
        _check_index()

        # Test command index is built when `args` is provided
        _set_index({})
        loader.load_command_table(["hello", "mod-only"])
        _check_index()

        # Test rebuild command index if no module found
        _set_index({"network": ["azure.cli.command_modules.network"]})
        loader.load_command_table(["hello", "mod-only"])
        _check_index()

        with mock.patch("azure.cli.core.__version__",
                        "2.7.0"), mock.patch.object(cli.cloud, "profile",
                                                    "2019-03-01-hybrid"):

            def update_and_check_index():
                loader.load_command_table(["hello", "mod-only"])
                self.assertEqual(INDEX[CommandIndex._COMMAND_INDEX_VERSION],
                                 "2.7.0")
                self.assertEqual(
                    INDEX[CommandIndex._COMMAND_INDEX_CLOUD_PROFILE],
                    "2019-03-01-hybrid")
                self.assertDictEqual(INDEX[CommandIndex._COMMAND_INDEX],
                                     expected_command_index)

            # Test rebuild command index if version is not present
            del INDEX[CommandIndex._COMMAND_INDEX_VERSION]
            del INDEX[CommandIndex._COMMAND_INDEX]
            update_and_check_index()

            # Test rebuild command index if version is not valid
            INDEX[CommandIndex._COMMAND_INDEX_VERSION] = ""
            _set_index({})
            update_and_check_index()

            # Test rebuild command index if version is outdated
            INDEX[CommandIndex._COMMAND_INDEX_VERSION] = "2.6.0"
            _set_index({})
            update_and_check_index()

            # Test rebuild command index if profile is outdated
            INDEX[CommandIndex.
                  _COMMAND_INDEX_CLOUD_PROFILE] = "2017-03-09-profile"
            _set_index({})
            update_and_check_index()

        # Test rebuild command index if modules are found but outdated
        # This only happens in dev environment. For users, the version check logic prevents it
        _set_index({"hello": ["azure.cli.command_modules.extra"]})
        loader.load_command_table(["hello", "mod-only"])
        _check_index()

        # Test irrelevant commands are not loaded
        _set_index(expected_command_index)
        cmd_tbl = loader.load_command_table(["hello", "mod-only"])
        self.assertEqual(
            ['hello mod-only', 'hello overridden', 'hello ext-only'],
            list(cmd_tbl.keys()))

        # Full scenario test 1: Installing an extension 'azext_hello1' that extends 'hello' group
        outdated_command_index = {
            'hello': ['azure.cli.command_modules.hello'],
            'extra': ['azure.cli.command_modules.extra']
        }
        _set_index(outdated_command_index)

        # Command for an outdated group
        cmd_tbl = loader.load_command_table(["hello", "-h"])
        # Index is not updated, and only built-in commands are loaded
        _set_index(outdated_command_index)
        self.assertListEqual(list(cmd_tbl),
                             ['hello mod-only', 'hello overridden'])

        # Command index is explicitly invalidated by azure.cli.core.extension.operations.add_extension
        command_index.invalidate()

        cmd_tbl = loader.load_command_table(["hello", "-h"])
        # Index is updated, and new commands are loaded
        _check_index()
        self.assertListEqual(list(cmd_tbl), expected_command_table)

        # Full scenario test 2: Installing extension 'azext_hello2' that overrides existing command 'hello overridden'
        outdated_command_index = {
            'hello': ['azure.cli.command_modules.hello', 'azext_hello1'],
            'extra': ['azure.cli.command_modules.extra']
        }
        _set_index(outdated_command_index)
        # Command for an overridden command
        cmd_tbl = loader.load_command_table(["hello", "overridden"])
        # Index is not updated
        self.assertEqual(INDEX[CommandIndex._COMMAND_INDEX],
                         outdated_command_index)
        # With the old command index, 'hello overridden' is loaded from the build-in module
        hello_overridden_cmd = cmd_tbl['hello overridden']
        self.assertEqual(hello_overridden_cmd.command_source, 'hello')
        self.assertListEqual(
            list(cmd_tbl),
            ['hello mod-only', 'hello overridden', 'hello ext-only'])

        # Command index is explicitly invalidated by azure.cli.core.extension.operations.add_extension
        command_index.invalidate()

        # Command index is updated, and 'hello overridden' is loaded from the new extension
        cmd_tbl = loader.load_command_table(["hello", "overridden"])
        hello_overridden_cmd = cmd_tbl['hello overridden']
        self.assertTrue(
            isinstance(hello_overridden_cmd.command_source,
                       ExtensionCommandSource))
        _check_index()
        self.assertListEqual(list(cmd_tbl), expected_command_table)

        # Call again with the new command index. Irrelevant commands are not loaded
        cmd_tbl = loader.load_command_table(["hello", "overridden"])
        hello_overridden_cmd = cmd_tbl['hello overridden']
        self.assertTrue(
            isinstance(hello_overridden_cmd.command_source,
                       ExtensionCommandSource))
        _check_index()
        self.assertListEqual(
            list(cmd_tbl),
            ['hello mod-only', 'hello overridden', 'hello ext-only'])

        del INDEX[CommandIndex._COMMAND_INDEX_VERSION]
        del INDEX[CommandIndex._COMMAND_INDEX_CLOUD_PROFILE]
        del INDEX[CommandIndex._COMMAND_INDEX]