]> source.dussan.org Git - sonarqube.git/blob
41c36debff36c52d457cc248d23dbd11d439ce3d
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.server.qualitygate;
21
22 import org.junit.After;
23 import org.junit.Before;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.slf4j.event.Level;
27 import org.sonar.api.config.internal.MapSettings;
28 import org.sonar.api.measures.CoreMetrics;
29 import org.sonar.api.measures.Metric;
30 import org.sonar.api.testfixtures.log.LogTester;
31 import org.sonar.api.utils.System2;
32 import org.sonar.api.utils.log.LoggerLevel;
33 import org.sonar.db.DbTester;
34 import org.sonar.db.component.ProjectData;
35 import org.sonar.db.metric.MetricDto;
36 import org.sonar.server.es.EsTester;
37 import org.sonar.server.measure.index.ProjectMeasuresIndex;
38 import org.sonar.server.measure.index.ProjectMeasuresIndexer;
39 import org.sonar.server.permission.index.PermissionIndexerTester;
40 import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
41 import org.sonar.server.util.GlobalLockManager;
42 import org.sonar.server.util.GlobalLockManagerImpl;
43
44 import static org.assertj.core.api.Assertions.assertThat;
45 import static org.mockito.ArgumentMatchers.any;
46 import static org.mockito.ArgumentMatchers.anyInt;
47 import static org.mockito.Mockito.mock;
48 import static org.mockito.Mockito.when;
49 import static org.sonar.api.measures.Metric.Level.WARN;
50 import static org.sonar.db.measure.MeasureTesting.newLiveMeasure;
51 import static org.sonar.server.qualitygate.ProjectsInWarningDaemon.PROJECTS_IN_WARNING_INTERNAL_PROPERTY;
52
53 public class ProjectsInWarningDaemonTest {
54
55   @Rule
56   public DbTester db = DbTester.create();
57   @Rule
58   public EsTester es = EsTester.create();
59   @Rule
60   public LogTester logger = new LogTester().setLevel(LoggerLevel.DEBUG);
61
62   private final PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, new ProjectMeasuresIndexer(db.getDbClient(), es.client()));
63   private final ProjectMeasuresIndexer projectMeasuresIndexer = new ProjectMeasuresIndexer(db.getDbClient(), es.client());
64   private final ProjectMeasuresIndex projectMeasuresIndex = new ProjectMeasuresIndex(es.client(), new WebAuthorizationTypeSupport(null), System2.INSTANCE);
65
66   private final MapSettings settings = new MapSettings();
67   private final GlobalLockManager lockManager = mock(GlobalLockManagerImpl.class);
68   private final ProjectsInWarning projectsInWarning = new ProjectsInWarning();
69
70   private final ProjectsInWarningDaemon underTest = new ProjectsInWarningDaemon(db.getDbClient(), projectMeasuresIndex, settings.asConfig(), lockManager, projectsInWarning);
71
72   @Before
73   public void setUp() {
74     settings.setProperty("sonar.projectsInWarning.frequencyInMilliseconds", "100");
75   }
76
77   @After
78   public void tearDown() {
79     underTest.stop();
80   }
81
82   @Test
83   public void store_projects_in_warning() throws InterruptedException {
84     allowLockToBeAcquired();
85     MetricDto qualityGateStatus = insertQualityGateStatusMetric();
86     insertProjectInWarning(qualityGateStatus);
87     insertProjectInWarning(qualityGateStatus);
88     // Setting does not exist
89     assertThat(db.getDbClient().internalPropertiesDao().selectByKey(db.getSession(), PROJECTS_IN_WARNING_INTERNAL_PROPERTY)).isEmpty();
90
91     underTest.notifyStart();
92
93     assertProjectsInWarningValue(2L);
94     assertThat(logger.logs(Level.INFO)).contains("Counting number of projects in warning is enabled.");
95   }
96
97   @Test
98   public void update_projects_in_warning_when_new_project_in_warning() throws InterruptedException {
99     allowLockToBeAcquired();
100     MetricDto qualityGateStatus = insertQualityGateStatusMetric();
101     insertProjectInWarning(qualityGateStatus);
102     insertProjectInWarning(qualityGateStatus);
103     // Setting does not exist
104     assertThat(db.getDbClient().internalPropertiesDao().selectByKey(db.getSession(), PROJECTS_IN_WARNING_INTERNAL_PROPERTY)).isEmpty();
105
106     underTest.notifyStart();
107     // Add a project in warning after the start in order to let the thread do his job
108     insertProjectInWarning(qualityGateStatus);
109
110     assertProjectsInWarningValue(3L);
111     assertThat(logger.logs(Level.INFO)).contains("Counting number of projects in warning is enabled.");
112   }
113
114   @Test
115   public void stop_thread_when_number_of_projects_in_warning_reach_zero() throws InterruptedException {
116     allowLockToBeAcquired();
117     MetricDto qualityGateStatus = insertQualityGateStatusMetric();
118     ProjectData project = insertProjectInWarning(qualityGateStatus);
119
120     underTest.notifyStart();
121     assertProjectsInWarningValue(1L);
122     // Set quality gate status of the project to OK => No more projects in warning
123     db.getDbClient().liveMeasureDao().insertOrUpdate(db.getSession(),
124       newLiveMeasure(project.getMainBranchComponent(), qualityGateStatus).setData(Metric.Level.OK.name()).setValue(null));
125     db.commit();
126     projectMeasuresIndexer.indexOnAnalysis(project.mainBranchUuid());
127
128     assertProjectsInWarningValue(0L);
129     assertThat(logger.logs(Level.INFO))
130       .contains(
131         "Counting number of projects in warning is enabled.",
132         "Counting number of projects in warning will be disabled as there are no more projects in warning.");
133   }
134
135   @Test
136   public void update_internal_properties_when_already_exits_and_projects_in_warnings_more_than_zero() throws InterruptedException {
137     allowLockToBeAcquired();
138     MetricDto qualityGateStatus = insertQualityGateStatusMetric();
139     insertProjectInWarning(qualityGateStatus);
140     insertProjectInWarning(qualityGateStatus);
141     // Setting contains 10, it should be updated with new value
142     db.getDbClient().internalPropertiesDao().save(db.getSession(), PROJECTS_IN_WARNING_INTERNAL_PROPERTY, "10");
143     db.commit();
144
145     underTest.notifyStart();
146
147     assertProjectsInWarningValue(2L);
148     assertThat(logger.logs(Level.INFO)).contains("Counting number of projects in warning is enabled.");
149   }
150
151   @Test
152   public void store_zero_projects_in_warning_when_no_projects() throws InterruptedException {
153     allowLockToBeAcquired();
154     assertThat(db.getDbClient().internalPropertiesDao().selectByKey(db.getSession(), PROJECTS_IN_WARNING_INTERNAL_PROPERTY)).isEmpty();
155
156     underTest.notifyStart();
157
158     assertProjectsInWarningValue(0L);
159     assertThat(logger.logs(Level.INFO)).contains("Counting number of projects in warning is enabled.");
160   }
161
162   @Test
163   public void do_not_compute_projects_in_warning_when_internal_property_is_zero() throws InterruptedException {
164     allowLockToBeAcquired();
165     MetricDto qualityGateStatus = insertQualityGateStatusMetric();
166     insertProjectInWarning(qualityGateStatus);
167     // Setting contains 0, even if there are projects in warning it will stay 0 (as it's not possible to have new projects in warning)
168     db.getDbClient().internalPropertiesDao().save(db.getSession(), PROJECTS_IN_WARNING_INTERNAL_PROPERTY, "0");
169     db.commit();
170
171     underTest.notifyStart();
172
173     assertProjectsInWarningValue(0L);
174     assertThat(logger.logs(Level.INFO)).contains("Counting number of projects in warning is not started as there are no projects in this situation.");
175   }
176
177   @Test
178   public void do_not_store_projects_in_warning_in_db_when_cannot_acquire_lock() throws InterruptedException {
179     when(lockManager.tryLock(any(), anyInt())).thenReturn(false);
180     MetricDto qualityGateStatus = insertQualityGateStatusMetric();
181     insertProjectInWarning(qualityGateStatus);
182
183     underTest.notifyStart();
184
185     waitForValueToBeComputed(1L);
186     assertThat(projectsInWarning.count()).isOne();
187     assertThat(countNumberOfProjectsInWarning()).isZero();
188   }
189
190   private void waitForValueToBeComputed(long expectedValue) throws InterruptedException {
191     for (int i = 0; i < 1000; i++) {
192       if (projectsInWarning.isInitialized() && projectsInWarning.count() == expectedValue) {
193         break;
194       }
195       Thread.sleep(100);
196     }
197   }
198
199   private void assertProjectsInWarningValue(long expectedValue) throws InterruptedException {
200     waitForValueToBeComputed(expectedValue);
201     assertThat(projectsInWarning.count()).isEqualTo(expectedValue);
202     assertThat(countNumberOfProjectsInWarning()).isEqualTo(expectedValue);
203   }
204
205   private long countNumberOfProjectsInWarning() {
206     return db.getDbClient().internalPropertiesDao().selectByKey(db.getSession(), PROJECTS_IN_WARNING_INTERNAL_PROPERTY)
207       .map(Long::valueOf)
208       .orElse(0L);
209   }
210
211   private ProjectData insertProjectInWarning(MetricDto qualityGateStatus) {
212     ProjectData project = db.components().insertPrivateProject();
213     db.measures().insertLiveMeasure(project, qualityGateStatus, lm -> lm.setData(WARN.name()).setValue(null));
214     authorizationIndexerTester.allowOnlyAnyone(project.getProjectDto());
215     projectMeasuresIndexer.indexOnAnalysis(project.mainBranchUuid());
216     return project;
217   }
218
219   private MetricDto insertQualityGateStatusMetric() {
220     return db.measures().insertMetric(m -> m.setKey(CoreMetrics.ALERT_STATUS_KEY).setValueType(Metric.ValueType.LEVEL.name()));
221   }
222
223   private void allowLockToBeAcquired() {
224     when(lockManager.tryLock(any(), anyInt())).thenReturn(true);
225   }
226
227 }