<commit_before># Copyright 2015-2016 Yelp 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.
import datetime
from collections import defaultdict
from typing import Any
from typing import Dict
from typing import Mapping
from typing import Set

import mock
import pytest
from mock import ANY
from mock import MagicMock
from mock import Mock
from mock import patch

import paasta_tools.paastaapi.models as paastamodels
from paasta_tools import marathon_tools
from paasta_tools import utils
from paasta_tools.cli.cmds import status
from paasta_tools.cli.cmds.status import apply_args_filters
from paasta_tools.cli.cmds.status import build_smartstack_backends_table
from paasta_tools.cli.cmds.status import create_autoscaling_info_table
from paasta_tools.cli.cmds.status import create_mesos_non_running_tasks_table
from paasta_tools.cli.cmds.status import create_mesos_running_tasks_table
from paasta_tools.cli.cmds.status import desired_state_human
from paasta_tools.cli.cmds.status import format_kubernetes_pod_table
from paasta_tools.cli.cmds.status import format_kubernetes_replicaset_table
from paasta_tools.cli.cmds.status import format_marathon_task_table
from paasta_tools.cli.cmds.status import get_instance_state
from paasta_tools.cli.cmds.status import get_smartstack_status_human
from paasta_tools.cli.cmds.status import get_versions_table
from paasta_tools.cli.cmds.status import haproxy_backend_report
from paasta_tools.cli.cmds.status import marathon_app_status_human
from paasta_tools.cli.cmds.status import marathon_mesos_status_human
from paasta_tools.cli.cmds.status import marathon_mesos_status_summary
from paasta_tools.cli.cmds.status import missing_deployments_message
from paasta_tools.cli.cmds.status import paasta_status
from paasta_tools.cli.cmds.status import paasta_status_on_api_endpoint
from paasta_tools.cli.cmds.status import print_kafka_status
from paasta_tools.cli.cmds.status import print_kubernetes_status
from paasta_tools.cli.cmds.status import print_kubernetes_status_v2
from paasta_tools.cli.cmds.status import print_marathon_status
from paasta_tools.cli.cmds.status import report_invalid_whitelist_values
from paasta_tools.cli.utils import NoSuchService
from paasta_tools.cli.utils import PaastaColors
from paasta_tools.paastaapi import ApiException
from paasta_tools.utils import remove_ansi_escape_sequences
from tests.conftest import Struct


def make_fake_instance_conf(
    cluster, service, instance, deploy_group=None, team=None, registrations=()
):
    conf = MagicMock()
    conf.get_cluster.return_value = cluster
    conf.get_service.return_value = service
    conf.get_instance.return_value = instance
    conf.get_deploy_group.return_value = deploy_group
    conf.get_team.return_value = team
    conf.get_registrations.return_value = registrations if registrations else []
    if registrations is None:
        del (
            conf.get_registrations
        )  # http://www.voidspace.org.uk/python/mock/mock.html#deleting-attributes
    return conf


@patch("paasta_tools.cli.utils.validate_service_name", autospec=True)
def test_figure_out_service_name_not_found(mock_validate_service_name, capfd):
    # paasta_status with invalid -s service_name arg results in error
    mock_validate_service_name.side_effect = NoSuchService(None)
    parsed_args = Mock()
    parsed_args.service = "fake_service"

    expected_output = "%s\n" % NoSuchService.GUESS_ERROR_MSG

    # Fail if exit(1) does not get called
    with pytest.raises(SystemExit) as sys_exit:
        status.figure_out_service_name(parsed_args)

    output, _ = capfd.readouterr()
    assert sys_exit.value.code == 1
    assert output == expected_output


@patch("paasta_tools.cli.cmds.status.list_clusters", autospec=True)
@patch("paasta_tools.cli.cmds.status.load_system_paasta_config", autospec=True)
@patch("paasta_tools.cli.utils.validate_service_name", autospec=True)
@patch("paasta_tools.cli.utils.guess_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_services", autospec=True)
def test_status_arg_service_not_found(
    mock_list_services,
    mock_guess_service_name,
    mock_validate_service_name,
    mock_load_system_paasta_config,
    mock_list_clusters,
    capfd,
    system_paasta_config,
):
    # paasta_status with no args and non-service directory results in error
    mock_list_services.return_value = []
    mock_guess_service_name.return_value = "not_a_service"
    error = NoSuchService("fake_service")
    mock_validate_service_name.side_effect = error
    mock_list_clusters.return_value = ["cluster1"]
    mock_load_system_paasta_config.return_value = system_paasta_config
    expected_output = str(error) + "\n"

    args = MagicMock()
    args.service = None
    args.owner = None
    args.clusters = None
    args.instances = None
    args.deploy_group = None
    args.registration = None
    args.service_instance = None

    # Fail if exit(1) does not get called
    with pytest.raises(SystemExit) as sys_exit:
        paasta_status(args)

    output, _ = capfd.readouterr()
    assert sys_exit.value.code == 1
    assert output == expected_output


@patch("paasta_tools.cli.cmds.status.paasta_status_on_api_endpoint", autospec=True)
@patch("paasta_tools.cli.cmds.status.report_invalid_whitelist_values", autospec=True)
def test_report_status_calls_report_invalid_whitelist_values(
    mock_report_invalid_whitelist_values,
    mock_paasta_status_on_api_endpoint,
    system_paasta_config,
):
    service = "fake_service"
    planned_deployments = ["cluster.instance1", "cluster.instance2"]
    actual_deployments: Dict[str, str] = {}
    instance_whitelist: Dict[str, Any] = {}

    status.report_status_for_cluster(
        service=service,
        cluster="cluster",
        deploy_pipeline=planned_deployments,
        actual_deployments=actual_deployments,
        instance_whitelist=instance_whitelist,
        system_paasta_config=system_paasta_config,
    )
    mock_report_invalid_whitelist_values.assert_called_once_with(
        [], ["instance1", "instance2"], "instance"
    )


@patch("paasta_tools.cli.cmds.status.get_instance_configs_for_service", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_services", autospec=True)
@patch("paasta_tools.cli.cmds.status.load_system_paasta_config", autospec=True)
@patch("paasta_tools.cli.cmds.status.figure_out_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.get_deploy_info", autospec=True)
@patch("paasta_tools.cli.cmds.status.get_actual_deployments", autospec=True)
@patch("paasta_tools.cli.cmds.status.validate_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_clusters", autospec=True)
def test_status_pending_pipeline_build_message(
    mock_list_clusters,
    mock_validate_service_name,
    mock_get_actual_deployments,
    mock_get_deploy_info,
    mock_figure_out_service_name,
    mock_load_system_paasta_config,
    mock_list_services,
    mock_get_instance_configs_for_service,
    capfd,
    system_paasta_config,
):
    # If deployments.json is missing SERVICE, output the appropriate message
    service = "fake_service"
    mock_list_clusters.return_value = ["cluster"]
    mock_validate_service_name.return_value = None
    mock_figure_out_service_name.return_value = service
    mock_list_services.return_value = [service]
    pipeline = [{"instancename": "cluster.instance"}]
    mock_get_deploy_info.return_value = {"pipeline": pipeline}
    mock_load_system_paasta_config.return_value = system_paasta_config
    mock_instance_config = make_fake_instance_conf("cluster", service, "instancename")
    mock_get_instance_configs_for_service.return_value = [mock_instance_config]

    actual_deployments: Dict[str, str] = {}
    mock_get_actual_deployments.return_value = actual_deployments
    expected_output = missing_deployments_message(service)

    args = MagicMock()
    args.service = service
    args.deploy_group = None
    args.clusters = None
    args.instances = None
    args.owner = None
    args.soa_dir = utils.DEFAULT_SOA_DIR
    args.registration = None
    args.service_instance = None

    paasta_status(args)
    output, _ = capfd.readouterr()
    assert expected_output in output


@patch("paasta_tools.cli.cmds.status.load_deployments_json", autospec=True)
def test_get_actual_deployments(mock_get_deployments,):
    mock_get_deployments.return_value = utils.DeploymentsJsonV1(
        {
            "fake_service:paasta-b_cluster.b_instance": {
                "docker_image": "this_is_a_sha"
            },
            "fake_service:paasta-a_cluster.a_instance": {
                "docker_image": "this_is_a_sha"
            },
        }
    )
    expected = {
        "a_cluster.a_instance": "this_is_a_sha",
        "b_cluster.b_instance": "this_is_a_sha",
    }

    actual = status.get_actual_deployments("fake_service", "/fake/soa/dir")
    assert expected == actual


@patch("paasta_tools.cli.cmds.status.read_deploy", autospec=True)
def test_get_deploy_info_exists(mock_read_deploy):
    expected = "fake deploy yaml"
    mock_read_deploy.return_value = expected
    actual = status.get_deploy_info("fake_service")
    assert expected == actual


@patch("paasta_tools.cli.cmds.status.read_deploy", autospec=True)
def test_get_deploy_info_does_not_exist(mock_read_deploy, capfd):
    mock_read_deploy.return_value = False
    with pytest.raises(SystemExit) as sys_exit:
        status.get_deploy_info("fake_service")
    output, _ = capfd.readouterr()
    assert sys_exit.value.code == 1
    assert output.startswith("Error encountered with")


@patch("paasta_tools.cli.cmds.status.get_instance_configs_for_service", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_services", autospec=True)
@patch("paasta_tools.cli.cmds.status.load_system_paasta_config", autospec=True)
@patch("paasta_tools.cli.cmds.status.figure_out_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.get_actual_deployments", autospec=True)
@patch("paasta_tools.cli.cmds.status.get_planned_deployments", autospec=True)
@patch("paasta_tools.cli.cmds.status.report_status_for_cluster", autospec=True)
@patch("paasta_tools.cli.cmds.status.validate_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_clusters", autospec=True)
def test_status_calls_sergeants(
    mock_list_clusters,
    mock_validate_service_name,
    mock_report_status,
    mock_get_planned_deployments,
    mock_get_actual_deployments,
    mock_figure_out_service_name,
    mock_load_system_paasta_config,
    mock_list_services,
    mock_get_instance_configs_for_service,
    system_paasta_config,
):
    service = "fake_service"
    cluster = "fake_cluster"
    mock_list_clusters.return_value = ["cluster1", "cluster2", "fake_cluster"]
    mock_validate_service_name.return_value = None
    mock_figure_out_service_name.return_value = service
    mock_list_services.return_value = [service]

    mock_instance_config = make_fake_instance_conf(cluster, service, "fi")
    mock_instance_config.get_service.return_value = service
    mock_instance_config.get_cluster.return_value = cluster
    mock_get_instance_configs_for_service.return_value = [mock_instance_config]

    planned_deployments = [
        "cluster1.instance1",
        "cluster1.instance2",
        "cluster2.instance1",
    ]
    mock_get_planned_deployments.return_value = planned_deployments

    actual_deployments = {"fake_service:paasta-cluster.instance": "this_is_a_sha"}
    mock_get_actual_deployments.return_value = actual_deployments
    mock_load_system_paasta_config.return_value = system_paasta_config
    mock_report_status.return_value = 1776, ["dummy", "output"]

    args = MagicMock()
    args.service = service
    args.clusters = None
    args.instances = None
    args.verbose = False
    args.owner = None
    args.deploy_group = None
    args.soa_dir = "/fake/soa/dir"
    args.registration = None
    args.service_instance = None
    args.new = False
    return_value = paasta_status(args)

    assert return_value == 1776

    mock_get_actual_deployments.assert_called_once_with(service, "/fake/soa/dir")
    mock_report_status.assert_called_once_with(
        service=service,
        deploy_pipeline=planned_deployments,
        actual_deployments=actual_deployments,
        cluster=cluster,
        instance_whitelist={"fi": mock_instance_config.__class__},
        system_paasta_config=system_paasta_config,
        verbose=False,
        new=False,
    )


def test_report_invalid_whitelist_values_no_whitelists():
    whitelist: Set[str] = set()
    items = ["cluster1", "cluster2", "cluster3"]
    item_type = "thingy"
    actual = report_invalid_whitelist_values(whitelist, items, item_type)
    assert actual == ""


def test_report_invalid_whitelist_values_with_whitelists():
    whitelist = {"bogus1", "cluster1"}
    items = ["cluster1", "cluster2", "cluster3"]
    item_type = "thingy"
    actual = report_invalid_whitelist_values(whitelist, items, item_type)
    assert "Warning" in actual
    assert item_type in actual
    assert "bogus1" in actual


class StatusArgs:
    def __init__(
        self,
        service,
        soa_dir,
        clusters,
        instances,
        deploy_group,
        owner,
        registration,
        verbose,
        service_instance=None,
        new=False,
    ):
        self.service = service
        self.soa_dir = soa_dir
        self.clusters = clusters
        self.instances = instances
        self.deploy_group = deploy_group
        self.owner = owner
        self.registration = registration
        self.verbose = verbose
        self.service_instance = service_instance
        self.new = new


@patch("paasta_tools.cli.cmds.status.get_instance_configs_for_service", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_services", autospec=True)
@patch("paasta_tools.cli.cmds.status.figure_out_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.validate_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_clusters", autospec=True)
def test_apply_args_filters_clusters_and_instances_clusters_instances_deploy_group(
    mock_list_clusters,<commit_msg>PAASTA-17414 - Adding --old to paasta status test<commit_after># Copyright 2015-2016 Yelp 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.
import datetime
from collections import defaultdict
from typing import Any
from typing import Dict
from typing import Mapping
from typing import Set

import mock
import pytest
from mock import ANY
from mock import MagicMock
from mock import Mock
from mock import patch

import paasta_tools.paastaapi.models as paastamodels
from paasta_tools import marathon_tools
from paasta_tools import utils
from paasta_tools.cli.cmds import status
from paasta_tools.cli.cmds.status import apply_args_filters
from paasta_tools.cli.cmds.status import build_smartstack_backends_table
from paasta_tools.cli.cmds.status import create_autoscaling_info_table
from paasta_tools.cli.cmds.status import create_mesos_non_running_tasks_table
from paasta_tools.cli.cmds.status import create_mesos_running_tasks_table
from paasta_tools.cli.cmds.status import desired_state_human
from paasta_tools.cli.cmds.status import format_kubernetes_pod_table
from paasta_tools.cli.cmds.status import format_kubernetes_replicaset_table
from paasta_tools.cli.cmds.status import format_marathon_task_table
from paasta_tools.cli.cmds.status import get_instance_state
from paasta_tools.cli.cmds.status import get_smartstack_status_human
from paasta_tools.cli.cmds.status import get_versions_table
from paasta_tools.cli.cmds.status import haproxy_backend_report
from paasta_tools.cli.cmds.status import marathon_app_status_human
from paasta_tools.cli.cmds.status import marathon_mesos_status_human
from paasta_tools.cli.cmds.status import marathon_mesos_status_summary
from paasta_tools.cli.cmds.status import missing_deployments_message
from paasta_tools.cli.cmds.status import paasta_status
from paasta_tools.cli.cmds.status import paasta_status_on_api_endpoint
from paasta_tools.cli.cmds.status import print_kafka_status
from paasta_tools.cli.cmds.status import print_kubernetes_status
from paasta_tools.cli.cmds.status import print_kubernetes_status_v2
from paasta_tools.cli.cmds.status import print_marathon_status
from paasta_tools.cli.cmds.status import report_invalid_whitelist_values
from paasta_tools.cli.utils import NoSuchService
from paasta_tools.cli.utils import PaastaColors
from paasta_tools.paastaapi import ApiException
from paasta_tools.utils import remove_ansi_escape_sequences
from tests.conftest import Struct


def make_fake_instance_conf(
    cluster, service, instance, deploy_group=None, team=None, registrations=()
):
    conf = MagicMock()
    conf.get_cluster.return_value = cluster
    conf.get_service.return_value = service
    conf.get_instance.return_value = instance
    conf.get_deploy_group.return_value = deploy_group
    conf.get_team.return_value = team
    conf.get_registrations.return_value = registrations if registrations else []
    if registrations is None:
        del (
            conf.get_registrations
        )  # http://www.voidspace.org.uk/python/mock/mock.html#deleting-attributes
    return conf


@patch("paasta_tools.cli.utils.validate_service_name", autospec=True)
def test_figure_out_service_name_not_found(mock_validate_service_name, capfd):
    # paasta_status with invalid -s service_name arg results in error
    mock_validate_service_name.side_effect = NoSuchService(None)
    parsed_args = Mock()
    parsed_args.service = "fake_service"

    expected_output = "%s\n" % NoSuchService.GUESS_ERROR_MSG

    # Fail if exit(1) does not get called
    with pytest.raises(SystemExit) as sys_exit:
        status.figure_out_service_name(parsed_args)

    output, _ = capfd.readouterr()
    assert sys_exit.value.code == 1
    assert output == expected_output


@patch("paasta_tools.cli.cmds.status.list_clusters", autospec=True)
@patch("paasta_tools.cli.cmds.status.load_system_paasta_config", autospec=True)
@patch("paasta_tools.cli.utils.validate_service_name", autospec=True)
@patch("paasta_tools.cli.utils.guess_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_services", autospec=True)
def test_status_arg_service_not_found(
    mock_list_services,
    mock_guess_service_name,
    mock_validate_service_name,
    mock_load_system_paasta_config,
    mock_list_clusters,
    capfd,
    system_paasta_config,
):
    # paasta_status with no args and non-service directory results in error
    mock_list_services.return_value = []
    mock_guess_service_name.return_value = "not_a_service"
    error = NoSuchService("fake_service")
    mock_validate_service_name.side_effect = error
    mock_list_clusters.return_value = ["cluster1"]
    mock_load_system_paasta_config.return_value = system_paasta_config
    expected_output = str(error) + "\n"

    args = MagicMock()
    args.service = None
    args.owner = None
    args.clusters = None
    args.instances = None
    args.deploy_group = None
    args.registration = None
    args.service_instance = None

    # Fail if exit(1) does not get called
    with pytest.raises(SystemExit) as sys_exit:
        paasta_status(args)

    output, _ = capfd.readouterr()
    assert sys_exit.value.code == 1
    assert output == expected_output


@patch("paasta_tools.cli.cmds.status.paasta_status_on_api_endpoint", autospec=True)
@patch("paasta_tools.cli.cmds.status.report_invalid_whitelist_values", autospec=True)
def test_report_status_calls_report_invalid_whitelist_values(
    mock_report_invalid_whitelist_values,
    mock_paasta_status_on_api_endpoint,
    system_paasta_config,
):
    service = "fake_service"
    planned_deployments = ["cluster.instance1", "cluster.instance2"]
    actual_deployments: Dict[str, str] = {}
    instance_whitelist: Dict[str, Any] = {}

    status.report_status_for_cluster(
        service=service,
        cluster="cluster",
        deploy_pipeline=planned_deployments,
        actual_deployments=actual_deployments,
        instance_whitelist=instance_whitelist,
        system_paasta_config=system_paasta_config,
    )
    mock_report_invalid_whitelist_values.assert_called_once_with(
        [], ["instance1", "instance2"], "instance"
    )


@patch("paasta_tools.cli.cmds.status.get_instance_configs_for_service", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_services", autospec=True)
@patch("paasta_tools.cli.cmds.status.load_system_paasta_config", autospec=True)
@patch("paasta_tools.cli.cmds.status.figure_out_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.get_deploy_info", autospec=True)
@patch("paasta_tools.cli.cmds.status.get_actual_deployments", autospec=True)
@patch("paasta_tools.cli.cmds.status.validate_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_clusters", autospec=True)
def test_status_pending_pipeline_build_message(
    mock_list_clusters,
    mock_validate_service_name,
    mock_get_actual_deployments,
    mock_get_deploy_info,
    mock_figure_out_service_name,
    mock_load_system_paasta_config,
    mock_list_services,
    mock_get_instance_configs_for_service,
    capfd,
    system_paasta_config,
):
    # If deployments.json is missing SERVICE, output the appropriate message
    service = "fake_service"
    mock_list_clusters.return_value = ["cluster"]
    mock_validate_service_name.return_value = None
    mock_figure_out_service_name.return_value = service
    mock_list_services.return_value = [service]
    pipeline = [{"instancename": "cluster.instance"}]
    mock_get_deploy_info.return_value = {"pipeline": pipeline}
    mock_load_system_paasta_config.return_value = system_paasta_config
    mock_instance_config = make_fake_instance_conf("cluster", service, "instancename")
    mock_get_instance_configs_for_service.return_value = [mock_instance_config]

    actual_deployments: Dict[str, str] = {}
    mock_get_actual_deployments.return_value = actual_deployments
    expected_output = missing_deployments_message(service)

    args = MagicMock()
    args.service = service
    args.deploy_group = None
    args.clusters = None
    args.instances = None
    args.owner = None
    args.soa_dir = utils.DEFAULT_SOA_DIR
    args.registration = None
    args.service_instance = None

    paasta_status(args)
    output, _ = capfd.readouterr()
    assert expected_output in output


@patch("paasta_tools.cli.cmds.status.load_deployments_json", autospec=True)
def test_get_actual_deployments(mock_get_deployments,):
    mock_get_deployments.return_value = utils.DeploymentsJsonV1(
        {
            "fake_service:paasta-b_cluster.b_instance": {
                "docker_image": "this_is_a_sha"
            },
            "fake_service:paasta-a_cluster.a_instance": {
                "docker_image": "this_is_a_sha"
            },
        }
    )
    expected = {
        "a_cluster.a_instance": "this_is_a_sha",
        "b_cluster.b_instance": "this_is_a_sha",
    }

    actual = status.get_actual_deployments("fake_service", "/fake/soa/dir")
    assert expected == actual


@patch("paasta_tools.cli.cmds.status.read_deploy", autospec=True)
def test_get_deploy_info_exists(mock_read_deploy):
    expected = "fake deploy yaml"
    mock_read_deploy.return_value = expected
    actual = status.get_deploy_info("fake_service")
    assert expected == actual


@patch("paasta_tools.cli.cmds.status.read_deploy", autospec=True)
def test_get_deploy_info_does_not_exist(mock_read_deploy, capfd):
    mock_read_deploy.return_value = False
    with pytest.raises(SystemExit) as sys_exit:
        status.get_deploy_info("fake_service")
    output, _ = capfd.readouterr()
    assert sys_exit.value.code == 1
    assert output.startswith("Error encountered with")


@patch("paasta_tools.cli.cmds.status.get_instance_configs_for_service", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_services", autospec=True)
@patch("paasta_tools.cli.cmds.status.load_system_paasta_config", autospec=True)
@patch("paasta_tools.cli.cmds.status.figure_out_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.get_actual_deployments", autospec=True)
@patch("paasta_tools.cli.cmds.status.get_planned_deployments", autospec=True)
@patch("paasta_tools.cli.cmds.status.report_status_for_cluster", autospec=True)
@patch("paasta_tools.cli.cmds.status.validate_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_clusters", autospec=True)
def test_status_calls_sergeants(
    mock_list_clusters,
    mock_validate_service_name,
    mock_report_status,
    mock_get_planned_deployments,
    mock_get_actual_deployments,
    mock_figure_out_service_name,
    mock_load_system_paasta_config,
    mock_list_services,
    mock_get_instance_configs_for_service,
    system_paasta_config,
):
    service = "fake_service"
    cluster = "fake_cluster"
    mock_list_clusters.return_value = ["cluster1", "cluster2", "fake_cluster"]
    mock_validate_service_name.return_value = None
    mock_figure_out_service_name.return_value = service
    mock_list_services.return_value = [service]

    mock_instance_config = make_fake_instance_conf(cluster, service, "fi")
    mock_instance_config.get_service.return_value = service
    mock_instance_config.get_cluster.return_value = cluster
    mock_get_instance_configs_for_service.return_value = [mock_instance_config]

    planned_deployments = [
        "cluster1.instance1",
        "cluster1.instance2",
        "cluster2.instance1",
    ]
    mock_get_planned_deployments.return_value = planned_deployments

    actual_deployments = {"fake_service:paasta-cluster.instance": "this_is_a_sha"}
    mock_get_actual_deployments.return_value = actual_deployments
    mock_load_system_paasta_config.return_value = system_paasta_config
    mock_report_status.return_value = 1776, ["dummy", "output"]

    args = MagicMock()
    args.service = service
    args.clusters = None
    args.instances = None
    args.verbose = False
    args.owner = None
    args.deploy_group = None
    args.soa_dir = "/fake/soa/dir"
    args.registration = None
    args.service_instance = None
    args.new = False
    return_value = paasta_status(args)

    assert return_value == 1776

    mock_get_actual_deployments.assert_called_once_with(service, "/fake/soa/dir")
    mock_report_status.assert_called_once_with(
        service=service,
        deploy_pipeline=planned_deployments,
        actual_deployments=actual_deployments,
        cluster=cluster,
        instance_whitelist={"fi": mock_instance_config.__class__},
        system_paasta_config=system_paasta_config,
        verbose=False,
        new=False,
    )


def test_report_invalid_whitelist_values_no_whitelists():
    whitelist: Set[str] = set()
    items = ["cluster1", "cluster2", "cluster3"]
    item_type = "thingy"
    actual = report_invalid_whitelist_values(whitelist, items, item_type)
    assert actual == ""


def test_report_invalid_whitelist_values_with_whitelists():
    whitelist = {"bogus1", "cluster1"}
    items = ["cluster1", "cluster2", "cluster3"]
    item_type = "thingy"
    actual = report_invalid_whitelist_values(whitelist, items, item_type)
    assert "Warning" in actual
    assert item_type in actual
    assert "bogus1" in actual


class StatusArgs:
    def __init__(
        self,
        service,
        soa_dir,
        clusters,
        instances,
        deploy_group,
        owner,
        registration,
        verbose,
        service_instance=None,
        new=False,
        old=False,
    ):
        self.service = service
        self.soa_dir = soa_dir
        self.clusters = clusters
        self.instances = instances
        self.deploy_group = deploy_group
        self.owner = owner
        self.registration = registration
        self.verbose = verbose
        self.service_instance = service_instance
        self.new = new
        self.old = old


@patch("paasta_tools.cli.cmds.status.get_instance_configs_for_service", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_services", autospec=True)
@patch("paasta_tools.cli.cmds.status.figure_out_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.validate_service_name", autospec=True)
@patch("paasta_tools.cli.cmds.status.list_clusters", autospec=True)
def test_apply_args_filters_clusters_and_instances_clusters_instances_deploy_group(
    mock_list_clusters,
{'fraction_non_alphanumeric': 0.0831349071365399, 'fraction_numerical': 0.0035036397033811825, 'mean_word_length': 4.761120909269057, 'pattern_counts': {'":': 20, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '751295', 'n_tokens_mistral': 9661, 'n_tokens_neox': 9447, 'n_words': 1604}