123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- /*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- package org.sonarqube.ws.tester;
-
- import com.google.common.base.Preconditions;
- import com.sonar.orchestrator.Orchestrator;
- import com.sonar.orchestrator.container.Edition;
- import com.sonar.orchestrator.container.Server;
- import com.sonar.orchestrator.junit4.OrchestratorRule;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
- import java.util.List;
- import java.util.function.Consumer;
- import java.util.stream.Collectors;
- import javax.annotation.Nullable;
- import org.junit.jupiter.api.extension.AfterAllCallback;
- import org.junit.jupiter.api.extension.AfterEachCallback;
- import org.junit.jupiter.api.extension.BeforeAllCallback;
- import org.junit.jupiter.api.extension.BeforeEachCallback;
- import org.junit.jupiter.api.extension.ExtensionContext;
- import org.junit.rules.ExternalResource;
- import org.sonarqube.ws.Ce;
- import org.sonarqube.ws.client.HttpConnector;
- import org.sonarqube.ws.client.WsClient;
- import org.sonarqube.ws.client.WsClientFactories;
- import org.sonarqube.ws.client.ce.ActivityRequest;
-
- import static com.sonar.orchestrator.container.Edition.DEVELOPER;
- import static com.sonar.orchestrator.container.Edition.ENTERPRISE;
- import static java.lang.String.format;
- import static java.util.Arrays.stream;
- import static org.sonarqube.ws.client.HttpConnector.DEFAULT_READ_TIMEOUT_MILLISECONDS;
-
- /**
- * This JUnit rule wraps an {@link OrchestratorRule} instance and provides :
- * <ul>
- * <li>clean-up of users between tests</li>
- * <li>clean-up of session when opening a browser (cookies, local storage)</li>
- * <li>quick access to {@link WsClient} instances</li>
- * <li>clean-up of defined settings. Properties that are not defined by a plugin are not reset.</li>
- * <li>helpers to generate users</li>
- * </ul>
- * <p>
- * Recommendation is to define a {@code @Rule} instance. If not possible, then
- * {@code @ClassRule} must be used through a {@link org.junit.rules.RuleChain}
- * around {@link OrchestratorRule}.
- * <p>
- * Not supported:
- * <ul>
- * <li>clean-up global settings</li>
- * <li>clean-up system administrators/roots</li>
- * <li>clean-up the properties that are not defined (no PropertyDefinition)</li>
- * </ul>
- *
- * When used with JUnit5, the tester can be started and stopped in the same pattern as Junit4 for @ClassRule or @Rule using the flag #useJunit5ClassInitialization
- */
- public class Tester extends ExternalResource implements TesterSession, BeforeEachCallback, AfterEachCallback, BeforeAllCallback, AfterAllCallback {
- static final String FORCE_AUTHENTICATION_PROPERTY_NAME = "sonar.forceAuthentication";
-
- private final Orchestrator orchestrator;
-
- private boolean enableForceAuthentication = false;
- private Elasticsearch elasticsearch = null;
-
- // initialized in #before()
- private boolean beforeCalled = false;
- private TesterSession rootSession;
-
- private final int readTimeoutMilliseconds;
-
- /**
- * Defines if the tester is executed at class level or method level in the Junit5 test.
- * If true, the tester will be started and stopped at the class level.
- */
- private boolean classLevel = false;
-
- public Tester(OrchestratorRule orchestrator) {
- this(orchestrator, DEFAULT_READ_TIMEOUT_MILLISECONDS);
- }
-
- public Tester(OrchestratorRule orchestrator, int readTimeoutMilliseconds) {
- this(orchestrator.getOrchestrator(), readTimeoutMilliseconds);
- }
-
- public Tester(Orchestrator orchestrator) {
- this(orchestrator, DEFAULT_READ_TIMEOUT_MILLISECONDS);
- }
-
- public Tester(Orchestrator orchestrator, int readTimeoutMilliseconds) {
- this.orchestrator = orchestrator;
- this.readTimeoutMilliseconds = readTimeoutMilliseconds;
- }
-
- public Tester enableForceAuthentication() {
- verifyNotStarted();
- enableForceAuthentication = true;
- return this;
- }
-
- /**
- * Enable class level initialization for Junit5.
- * Should only be used with Junit5.
- *
- * @return Tester
- */
- public Tester withClassLevel() {
- classLevel = true;
- return this;
- }
-
- @Override
- public void before() {
- verifyNotStarted();
- rootSession = new TesterSessionImpl(orchestrator,
- httpConnectorBuilder -> httpConnectorBuilder.readTimeoutMilliseconds(readTimeoutMilliseconds),
- httpConnectorBuilder -> httpConnectorBuilder.credentials("admin", "admin"));
-
- setForceAuthentication(enableForceAuthentication);
-
- beforeCalled = true;
- }
-
- @Override
- public void after() {
- waitForCeTasksToFinish();
-
- deactivateScim();
-
- users().deleteAll();
- projects().deleteAll();
- settings().deleteAll();
- qGates().deleteAll();
- qProfiles().deleteAll();
- webhooks().deleteAllGlobal();
- almSettings().deleteAll();
- groups().deleteAllGenerated();
-
- Edition edition = orchestrator.getDistribution().getEdition();
- if (edition.equals(ENTERPRISE)) {
- applications().deleteAll();
- views().deleteAll();
- applications().deleteAll();
- } else if (edition.equals(DEVELOPER)) {
- applications().deleteAll();
- }
-
- setForceAuthentication(enableForceAuthentication);
- }
-
- public void deactivateScim() {
- try (Connection connection = orchestrator.getDatabase().openConnection();
- PreparedStatement preparedStatement = connection.prepareStatement("delete from internal_properties where kee = ?")) {
- preparedStatement.setString(1, "sonar.scim.enabled");
- preparedStatement.execute();
- } catch (SQLException e) {
- throw new IllegalStateException(e);
- }
- }
-
- private void setForceAuthentication(boolean enableForceAuthentication) {
- String serverProperty = orchestrator.getDistribution().getServerProperty(FORCE_AUTHENTICATION_PROPERTY_NAME);
- if (serverProperty != null) {
- Preconditions.checkArgument(enableForceAuthentication == Boolean.parseBoolean(serverProperty),
- "This test was expecting to have authentication configured, but server property configuration has mismatched.");
- return;
- }
-
- if (enableForceAuthentication) {
- settings().resetSettings(FORCE_AUTHENTICATION_PROPERTY_NAME);
- } else {
- settings().setGlobalSetting(FORCE_AUTHENTICATION_PROPERTY_NAME, Boolean.toString(false));
- }
- }
-
- private void waitForCeTasksToFinish() {
- // Let's try to wait for 30s for in progress or pending tasks to finish
- int counter = 60;
- while (counter > 0 &&
- wsClient().ce().activity(new ActivityRequest().setStatus(List.of("PENDING", "IN_PROGRESS"))).getTasksCount() != 0) {
- counter--;
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- break;
- }
- }
-
- Ce.ActivityResponse activity = wsClient().ce().activity(new ActivityRequest().setStatus(List.of("PENDING", "IN_PROGRESS")));
- if (activity.getTasksCount() != 0) {
- throw new IllegalStateException(format("Waiting for 30 seconds for tasks to finish but there are still ce tasks : %n %s",
- activity.getTasksList().stream()
- .map(t -> format("analysisId: [%s] type: [%s] componentName: [%s]", t.getAnalysisId(), t.getType(), t.getComponentName()))
- .collect(Collectors.joining("\n"))));
- }
- }
-
- public TesterSession asAnonymous() {
- return as(null, null);
- }
-
- public TesterSession as(String login) {
- return as(login, login);
- }
-
- public TesterSession as(String login, String password) {
- verifyStarted();
- return new TesterSessionImpl(orchestrator, login, password);
- }
-
- public TesterSession withSystemPassCode(String systemPassCode) {
- verifyStarted();
- return new TesterSessionImpl(orchestrator, systemPassCode);
- }
-
- public Elasticsearch elasticsearch() {
- if (elasticsearch != null) {
- return elasticsearch;
- }
- elasticsearch = new Elasticsearch(orchestrator.getServer().getSearchPort());
- return elasticsearch;
- }
-
- private void verifyNotStarted() {
- if (beforeCalled) {
- throw new IllegalStateException("org.sonarqube.ws.tester.Tester should not be already started");
- }
- }
-
- private void verifyStarted() {
- if (!beforeCalled) {
- throw new IllegalStateException("org.sonarqube.ws.tester.Tester is not started yet");
- }
- }
-
- /**
- * Web service client configured with root access
- */
- @Override
- public WsClient wsClient() {
- verifyStarted();
- return rootSession.wsClient();
- }
-
- @Override
- public GroupTester groups() {
- return rootSession.groups();
- }
-
- @Override
- public ProjectTester projects() {
- return rootSession.projects();
- }
-
- @Override
- public QModelTester qModel() {
- return rootSession.qModel();
- }
-
- @Override
- public QProfileTester qProfiles() {
- return rootSession.qProfiles();
- }
-
- @Override
- public UserTester users() {
- return rootSession.users();
- }
-
- @Override
- public SettingTester settings() {
- return rootSession.settings();
- }
-
- @Override
- public NewCodePeriodTester newCodePeriods() {
- return rootSession.newCodePeriods();
- }
-
- @Override
- public QGateTester qGates() {
- return rootSession.qGates();
- }
-
- @Override
- public WebhookTester webhooks() {
- return rootSession.webhooks();
- }
-
- @Override
- public PermissionTester permissions() {
- return rootSession.permissions();
- }
-
- @Override
- public ViewTester views() {
- return rootSession.views();
- }
-
- @Override
- public ApplicationTester applications() {
- return rootSession.applications();
- }
-
- @Override
- public MeasureTester measures() {
- return rootSession.measures();
- }
-
- @Override
- public AlmSettingsTester almSettings() {
- return rootSession.almSettings();
- }
-
- @Override
- public void afterAll(ExtensionContext context) throws Exception {
- if (classLevel) {
- after();
- }
- }
-
- @Override
- public void afterEach(ExtensionContext context) throws Exception {
- if (!classLevel) {
- after();
- }
- }
-
- @Override
- public void beforeAll(ExtensionContext context) throws Exception {
- if (classLevel) {
- before();
- }
- }
-
- @Override
- public void beforeEach(ExtensionContext context) throws Exception {
- if (!classLevel) {
- before();
- }
- }
-
- private static class TesterSessionImpl implements TesterSession {
- private final WsClient client;
-
- private TesterSessionImpl(Orchestrator orchestrator, @Nullable String login, @Nullable String password) {
- Server server = orchestrator.getServer();
- this.client = WsClientFactories.getDefault().newClient(HttpConnector.newBuilder()
- .acceptGzip(true)
- .url(server.getUrl())
- .credentials(login, password)
- .build());
- }
-
- private TesterSessionImpl(Orchestrator orchestrator, @Nullable String systemPassCode) {
- Server server = orchestrator.getServer();
- this.client = WsClientFactories.getDefault().newClient(HttpConnector.newBuilder()
- .acceptGzip(true)
- .systemPassCode(systemPassCode)
- .url(server.getUrl())
- .build());
- }
-
- private TesterSessionImpl(Orchestrator orchestrator, Consumer<HttpConnector.Builder>... httpConnectorPopulators) {
- Server server = orchestrator.getServer();
- HttpConnector.Builder httpConnectorBuilder = HttpConnector.newBuilder()
- .acceptGzip(true)
- .url(server.getUrl());
- stream(httpConnectorPopulators).forEach(populator -> populator.accept(httpConnectorBuilder));
- this.client = WsClientFactories.getDefault().newClient(httpConnectorBuilder.build());
- }
-
- @Override
- public WsClient wsClient() {
- return client;
- }
-
- @Override
- public GroupTester groups() {
- return new GroupTester(this);
- }
-
- @Override
- public ProjectTester projects() {
- return new ProjectTester(this);
- }
-
- @Override
- public QModelTester qModel() {
- return new QModelTester(this);
- }
-
- @Override
- public QProfileTester qProfiles() {
- return new QProfileTester(this);
- }
-
- @Override
- public UserTester users() {
- return new UserTester(this);
- }
-
- @Override
- public SettingTester settings() {
- return new SettingTester(this);
- }
-
- @Override
- public NewCodePeriodTester newCodePeriods() {
- return new NewCodePeriodTester(this);
- }
-
- @Override
- public QGateTester qGates() {
- return new QGateTester(this);
- }
-
- @Override
- public WebhookTester webhooks() {
- return new WebhookTester(this);
- }
-
- @Override
- public PermissionTester permissions() {
- return new PermissionTester(this);
- }
-
- @Override
- public ViewTester views() {
- return new ViewTester(this);
- }
-
- @Override
- public ApplicationTester applications() {
- return new ApplicationTester(this);
- }
-
- @Override
- public MeasureTester measures() {
- return new MeasureTester(this);
- }
-
- @Override
- public AlmSettingsTester almSettings() {
- return new AlmSettingsTester(this);
- }
-
- }
- }
|