3 * Copyright (C) 2009-2024 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.common.health;
22 import java.util.Arrays;
23 import java.util.Random;
25 import java.util.stream.IntStream;
26 import java.util.stream.Stream;
27 import org.junit.Test;
28 import org.sonar.process.cluster.health.NodeDetails;
29 import org.sonar.process.cluster.health.NodeHealth;
30 import org.sonar.server.health.Health;
32 import static java.util.stream.Collectors.toSet;
33 import static java.util.stream.Stream.of;
34 import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
35 import static org.sonar.process.cluster.health.NodeHealth.Status.GREEN;
36 import static org.sonar.process.cluster.health.NodeHealth.Status.RED;
37 import static org.sonar.process.cluster.health.NodeHealth.Status.YELLOW;
38 import static org.sonar.server.common.health.HealthAssert.assertThat;
40 public class AppNodeClusterCheckTest {
41 private final Random random = new Random();
43 private AppNodeClusterCheck underTest = new AppNodeClusterCheck();
46 public void status_RED_when_no_application_node() {
47 Set<NodeHealth> nodeHealths = nodeHealths().collect(toSet());
49 Health check = underTest.check(nodeHealths);
52 .forInput(nodeHealths)
53 .hasStatus(Health.Status.RED)
54 .andCauses("No application node");
58 public void status_RED_when_single_RED_application_node() {
59 Set<NodeHealth> nodeHealths = nodeHealths(RED).collect(toSet());
61 Health check = underTest.check(nodeHealths);
64 .forInput(nodeHealths)
65 .hasStatus(Health.Status.RED)
66 .andCauses("Status of all application nodes is RED",
67 "There should be at least two application nodes");
71 public void status_YELLOW_when_single_YELLOW_application_node() {
72 Set<NodeHealth> nodeHealths = nodeHealths(YELLOW).collect(toSet());
74 Health check = underTest.check(nodeHealths);
77 .forInput(nodeHealths)
78 .hasStatus(Health.Status.YELLOW)
80 "Status of all application nodes is YELLOW",
81 "There should be at least two application nodes");
85 public void status_YELLOW_when_single_GREEN_application_node() {
86 Set<NodeHealth> nodeHealths = nodeHealths(GREEN).collect(toSet());
88 Health check = underTest.check(nodeHealths);
91 .forInput(nodeHealths)
92 .hasStatus(Health.Status.YELLOW)
93 .andCauses("There should be at least two application nodes");
97 public void status_RED_when_two_RED_application_nodes() {
98 Set<NodeHealth> nodeHealths = nodeHealths(RED, RED).collect(toSet());
100 Health check = underTest.check(nodeHealths);
103 .forInput(nodeHealths)
104 .hasStatus(Health.Status.RED)
105 .andCauses("Status of all application nodes is RED");
109 public void status_YELLOW_when_two_YELLOW_application_nodes() {
110 Set<NodeHealth> nodeHealths = nodeHealths(YELLOW, YELLOW).collect(toSet());
112 Health check = underTest.check(nodeHealths);
115 .forInput(nodeHealths)
116 .hasStatus(Health.Status.YELLOW)
117 .andCauses("Status of all application nodes is YELLOW");
121 public void status_YELLOW_when_one_RED_node_and_one_YELLOW_application_node() {
122 Set<NodeHealth> nodeHealths = nodeHealths(RED, YELLOW).collect(toSet());
124 Health check = underTest.check(nodeHealths);
127 .forInput(nodeHealths)
128 .hasStatus(Health.Status.YELLOW)
130 "At least one application node is RED",
131 "At least one application node is YELLOW");
135 public void status_YELLOW_when_one_RED_node_and_one_GREEN_application_node() {
136 Set<NodeHealth> nodeHealths = nodeHealths(RED, GREEN).collect(toSet());
138 Health check = underTest.check(nodeHealths);
141 .forInput(nodeHealths)
142 .hasStatus(Health.Status.YELLOW)
143 .andCauses("At least one application node is RED");
147 public void status_YELLOW_when_one_YELLOW_node_and_one_GREEN_application_node() {
148 Set<NodeHealth> nodeHealths = nodeHealths(YELLOW, GREEN).collect(toSet());
150 Health check = underTest.check(nodeHealths);
153 .forInput(nodeHealths)
154 .hasStatus(Health.Status.YELLOW)
155 .andCauses("At least one application node is YELLOW");
159 public void status_GREEN_when_two_GREEN_application_node() {
160 Set<NodeHealth> nodeHealths = nodeHealths(GREEN, GREEN).collect(toSet());
162 Health check = underTest.check(nodeHealths);
165 .forInput(nodeHealths)
166 .hasStatus(Health.Status.GREEN)
171 public void status_GREEN_when_two_GREEN_application_node_and_any_number_of_other_is_GREEN() {
172 Set<NodeHealth> nodeHealths = of(
173 // at least 1 extra GREEN
174 of(appNodeHealth(GREEN)),
176 randomNumberOfAppNodeHealthOfAnyStatus(GREEN),
178 nodeHealths(GREEN, GREEN))
182 Health check = underTest.check(nodeHealths);
185 .forInput(nodeHealths)
186 .hasStatus(Health.Status.GREEN)
191 public void status_YELLOW_when_two_GREEN_application_node_and_any_number_of_other_is_YELLOW_or_GREEN() {
192 Set<NodeHealth> nodeHealths = of(
194 of(appNodeHealth(YELLOW)),
195 // 0 to 10 YELLOW/GREEN
196 randomNumberOfAppNodeHealthOfAnyStatus(GREEN, YELLOW),
198 nodeHealths(GREEN, GREEN))
201 Health check = underTest.check(nodeHealths);
204 .forInput(nodeHealths)
205 .hasStatus(Health.Status.YELLOW)
206 .andCauses("At least one application node is YELLOW");
210 public void status_YELLOW_when_two_GREEN_application_node_and_any_number_of_other_is_RED_or_GREEN() {
211 Set<NodeHealth> nodeHealths = of(
213 of(appNodeHealth(RED)),
215 randomNumberOfAppNodeHealthOfAnyStatus(GREEN, RED),
217 nodeHealths(GREEN, GREEN))
220 Health check = underTest.check(nodeHealths);
223 .forInput(nodeHealths)
224 .hasStatus(Health.Status.YELLOW)
225 .andCauses("At least one application node is RED");
229 public void status_YELLOW_when_two_GREEN_application_node_and_any_number_of_other_is_either_RED_or_YELLOW() {
230 Set<NodeHealth> nodeHealths = of(
232 of(appNodeHealth(RED)),
234 of(appNodeHealth(YELLOW)),
235 // 0 to 10 RED/YELLOW/GREEN
236 randomNumberOfAppNodeHealthOfAnyStatus(RED, YELLOW, GREEN),
238 nodeHealths(GREEN, GREEN))
242 Health check = underTest.check(nodeHealths);
245 .forInput(nodeHealths)
246 .hasStatus(Health.Status.YELLOW)
248 "At least one application node is YELLOW",
249 "At least one application node is RED");
253 * Between 0 and 10 NodeHealth of Application node with any of the specified statuses.
255 private Stream<NodeHealth> randomNumberOfAppNodeHealthOfAnyStatus(NodeHealth.Status... randomStatuses) {
256 return IntStream.range(0, random.nextInt(10))
257 .mapToObj(i -> appNodeHealth(randomStatuses[random.nextInt(randomStatuses.length)]));
260 private Stream<NodeHealth> nodeHealths(NodeHealth.Status... appNodeStatuses) {
262 // random number of Search nodes with random status
263 IntStream.range(0, random.nextInt(3))
264 .mapToObj(i -> appNodeHealth(NodeDetails.Type.SEARCH, NodeHealth.Status.values()[random.nextInt(NodeHealth.Status.values().length)])),
265 Arrays.stream(appNodeStatuses).map(this::appNodeHealth))
269 private NodeHealth appNodeHealth(NodeHealth.Status status) {
270 return appNodeHealth(NodeDetails.Type.APPLICATION, status);
273 private NodeHealth appNodeHealth(NodeDetails.Type type, NodeHealth.Status status) {
274 return NodeHealth.newNodeHealthBuilder()
276 .setDetails(NodeDetails.newNodeDetailsBuilder()
278 .setHost(randomAlphanumeric(32))
279 .setName(randomAlphanumeric(32))
280 .setPort(1 + random.nextInt(88))
281 .setStartedAt(1 + random.nextInt(54))