public Result evaluateTransition(Node node, ClusterState clusterState, SetUnitStateRequest.Condition condition,
                                     NodeState oldWantedState, NodeState newWantedState) {
        if (condition == FORCE)
            return allow();

        if (inMoratorium)
            return disallow("Master cluster controller is bootstrapping and in moratorium");

        if (condition != SAFE)
            return disallow("Condition not implemented: " + condition.name());

        if (node.getType() != STORAGE)
            return disallow("Safe-set of node state is only supported for storage nodes! " +
                            "Requested node type: " + node.getType().toString());

        StorageNodeInfo nodeInfo = clusterInfo.getStorageNodeInfo(node.getIndex());
        if (nodeInfo == null)
            return disallow("Unknown node " + node);

        if (noChanges(oldWantedState, newWantedState))
            return alreadySet();

        return switch (newWantedState.getState()) {
            case UP -> canSetStateUp(nodeInfo, oldWantedState);
            case MAINTENANCE -> canSetStateMaintenanceTemporarily(nodeInfo, clusterState, newWantedState.getDescription());
            case DOWN -> canSetStateDownPermanently(nodeInfo, clusterState, newWantedState.getDescription());
            default -> disallow("Destination node state unsupported in safe mode: " + newWantedState);
        };
    }
@Test
    void testMaintenanceAllowedFor2Of4Groups() {
        int maxNumberOfGroupsAllowedToBeDown = 2;
        // 4 groups with 1 node in each group
        var cluster = createCluster(4, 4, maxNumberOfGroupsAllowedToBeDown);
        setAllNodesUp(cluster, HostInfo.createHostInfo(createDistributorHostInfo(4, 5, 6)));
        var nodeStateChangeChecker = createChangeChecker(cluster);

        // All nodes up, set a storage node in group 0 to maintenance
        {
            int nodeIndex = 0;
            checkSettingToMaintenanceIsAllowed(nodeIndex, nodeStateChangeChecker, defaultAllUpClusterState());
            setStorageNodeWantedStateToMaintenance(cluster, nodeIndex);
        }

        // Node in group 0 in maintenance, set storage node in group 1 to maintenance
        {
            ClusterState clusterState = clusterState(String.format("version:%d distributor:4 .0.s:d storage:4 .0.s:m", currentClusterStateVersion));
            int nodeIndex = 1;
            checkSettingToMaintenanceIsAllowed(nodeIndex, nodeStateChangeChecker, clusterState);
            setStorageNodeWantedStateToMaintenance(cluster, nodeIndex);
        }

        // Nodes in group 0 and 1 in maintenance, try to set storage node in group 2 to maintenance while storage node 2 is down, should fail
        {
            ClusterState clusterState = clusterState(String.format("version:%d distributor:4 storage:4 .0.s:m .1.s:m .2.s:d", currentClusterStateVersion));
            int nodeIndex = 2;
            cluster.clusterInfo().getStorageNodeInfo(nodeIndex).setReportedState(new NodeState(STORAGE, DOWN), 0);
            Node node = new Node(STORAGE, nodeIndex);
            Result result = nodeStateChangeChecker.evaluateTransition(node, clusterState, SAFE, UP_NODE_STATE, MAINTENANCE_NODE_STATE);
            assertFalse(result.allowed(), result.toString());
            assertFalse(result.isAlreadySet());
            assertEquals("At most 2 groups can have wanted state: [0, 1, 2]", result.reason());
        }

        // Nodes in group 0 and 1 in maintenance, try to set storage node in group 2 to maintenance, should fail
        {
            ClusterState clusterState = clusterState(String.format("version:%d distributor:4 storage:4 .0.s:m .1.s:m", currentClusterStateVersion));
            int nodeIndex = 2;
            Node node = new Node(STORAGE, nodeIndex);
            Result result = nodeStateChangeChecker.evaluateTransition(node, clusterState, SAFE, UP_NODE_STATE, MAINTENANCE_NODE_STATE);
            assertFalse(result.allowed(), result.toString());
            assertFalse(result.isAlreadySet());
            assertEquals("At most 2 groups can have wanted state: [0, 1]", result.reason());
        }

    }