From c24345328a3d90bae66586bcf0a411eff7619c2d Mon Sep 17 00:00:00 2001 From: Janos Gyerik Date: Thu, 26 Jul 2018 17:03:55 +0200 Subject: [PATCH] SONAR-11038 Add table: alm_project_mappings (#559) --- .../ComputeEngineContainerImplTest.java | 2 +- .../java/org/sonar/db/version/SqTables.java | 1 + .../org/sonar/db/version/schema-h2.ddl | 15 +- .../src/main/java/org/sonar/db/DaoModule.java | 2 + .../src/main/java/org/sonar/db/DbClient.java | 7 + .../src/main/java/org/sonar/db/MyBatis.java | 2 + .../src/main/java/org/sonar/db/alm/ALM.java | 31 ++ .../org/sonar/db/alm/AlmAppInstallDao.java | 10 - .../sonar/db/alm/AlmProjectMappingsDao.java | 74 ++++ .../db/alm/AlmProjectMappingsMapper.java | 34 ++ .../org/sonar/db/purge/PurgeCommands.java | 7 + .../java/org/sonar/db/purge/PurgeDao.java | 1 + .../java/org/sonar/db/purge/PurgeMapper.java | 2 + .../sonar/db/alm/AlmProjectMappingsMapper.xml | 51 +++ .../org/sonar/db/purge/PurgeMapper.xml | 4 + .../test/java/org/sonar/db/DaoModuleTest.java | 2 +- .../sonar/db/alm/AlmAppInstallDaoTest.java | 8 +- .../db/alm/AlmProjectMappingsDaoTest.java | 327 ++++++++++++++++++ .../java/org/sonar/db/purge/PurgeDaoTest.java | 20 +- .../v73/CreateAlmProjectMappingsTable.java | 120 +++++++ .../db/migration/version/v73/DbVersion73.java | 1 + .../CreateAlmProjectMappingsTableTest.java | 71 ++++ .../empty.sql | 0 23 files changed, 772 insertions(+), 20 deletions(-) create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/alm/ALM.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmProjectMappingsDao.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmProjectMappingsMapper.java create mode 100644 server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmProjectMappingsMapper.xml create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmProjectMappingsDaoTest.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTable.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTableTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTableTest/empty.sql diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index ff8e310bc7f..bc7a95804b5 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -121,7 +121,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION + 27 // level 1 - + 55 // content of DaoModule + + 56 // content of DaoModule + 3 // content of EsModule + 54 // content of CorePropertyDefinitions + 1 // StopFlagContainer diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java index f33d7f5f575..9effb770606 100644 --- a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java +++ b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java @@ -53,6 +53,7 @@ public final class SqTables { "active_rules", "active_rule_parameters", "alm_app_installs", + "alm_project_mappings", "analysis_properties", "ce_activity", "ce_queue", diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl index e62fc5698f8..1a36753f7d7 100644 --- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl +++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl @@ -853,6 +853,20 @@ CREATE TABLE "ALM_APP_INSTALLS" ( CREATE UNIQUE INDEX "ALM_APP_INSTALLS_OWNER" ON "ALM_APP_INSTALLS" ("ALM_ID", "OWNER_ID"); CREATE UNIQUE INDEX "ALM_APP_INSTALLS_INSTALL" ON "ALM_APP_INSTALLS" ("ALM_ID", "INSTALL_ID"); +CREATE TABLE "ALM_PROJECT_MAPPINGS" ( + "UUID" VARCHAR(40) NOT NULL, + "ALM_ID" VARCHAR(40) NOT NULL, + "REPO_ID" VARCHAR(256) NOT NULL, + "PROJECT_UUID" VARCHAR(40) NOT NULL, + "GITHUB_SLUG" VARCHAR(256) NULL, + "URL" VARCHAR(2000) NOT NULL, + "CREATED_AT" BIGINT NOT NULL, + "UPDATED_AT" BIGINT NOT NULL, + CONSTRAINT "PK_ALM_PROJECT_MAPPINGS" PRIMARY KEY ("UUID") +); +CREATE UNIQUE INDEX "ALM_PROJECT_MAPPINGS_ALM_REPO" ON "ALM_PROJECT_MAPPINGS" ("ALM_ID", "REPO_ID"); +CREATE UNIQUE INDEX "ALM_PROJECT_MAPPINGS_PROJECT" ON "ALM_PROJECT_MAPPINGS" ("PROJECT_UUID"); + CREATE TABLE "PROJECT_MAPPINGS" ( "UUID" VARCHAR(40) NOT NULL, "KEY_TYPE" VARCHAR(200) NOT NULL, @@ -863,4 +877,3 @@ CREATE TABLE "PROJECT_MAPPINGS" ( ); CREATE UNIQUE INDEX "KEY_TYPE_KEE" ON "PROJECT_MAPPINGS" ("KEY_TYPE", "KEE"); CREATE INDEX "PROJECT_UUID" ON "PROJECT_MAPPINGS" ("PROJECT_UUID"); - diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java index ff37086215a..b6908ba0cb0 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import org.sonar.core.platform.Module; +import org.sonar.db.alm.AlmProjectMappingsDao; import org.sonar.db.ce.CeActivityDao; import org.sonar.db.ce.CeQueueDao; import org.sonar.db.ce.CeScannerContextDao; @@ -105,6 +106,7 @@ public class DaoModule extends Module { GroupMembershipDao.class, GroupPermissionDao.class, AlmAppInstallDao.class, + AlmProjectMappingsDao.class, InternalPropertiesDao.class, IssueChangeDao.class, IssueDao.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java index 31d0465dcd4..2965acf71db 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java @@ -21,6 +21,7 @@ package org.sonar.db; import java.util.IdentityHashMap; import java.util.Map; +import org.sonar.db.alm.AlmProjectMappingsDao; import org.sonar.db.ce.CeActivityDao; import org.sonar.db.ce.CeQueueDao; import org.sonar.db.ce.CeScannerContextDao; @@ -90,6 +91,7 @@ public class DbClient { private final QualityProfileDao qualityProfileDao; private final PropertiesDao propertiesDao; private final AlmAppInstallDao almAppInstallDao; + private final AlmProjectMappingsDao almProjectMappingsDao; private final InternalPropertiesDao internalPropertiesDao; private final SnapshotDao snapshotDao; private final ComponentDao componentDao; @@ -149,6 +151,7 @@ public class DbClient { map.put(dao.getClass(), dao); } almAppInstallDao = getDao(map, AlmAppInstallDao.class); + almProjectMappingsDao = getDao(map, AlmProjectMappingsDao.class); schemaMigrationDao = getDao(map, SchemaMigrationDao.class); authorizationDao = getDao(map, AuthorizationDao.class); organizationDao = getDao(map, OrganizationDao.class); @@ -217,6 +220,10 @@ public class DbClient { return almAppInstallDao; } + public AlmProjectMappingsDao almProjectMappingsDao() { + return almProjectMappingsDao; + } + public SchemaMigrationDao schemaMigrationDao() { return schemaMigrationDao; } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java index f26fffb5ef8..e6faa27aff4 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java @@ -31,6 +31,7 @@ import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.ibatis.session.TransactionIsolationLevel; import org.sonar.api.Startable; import org.sonar.db.alm.AlmAppInstallMapper; +import org.sonar.db.alm.AlmProjectMappingsMapper; import org.sonar.db.ce.CeActivityMapper; import org.sonar.db.ce.CeQueueMapper; import org.sonar.db.ce.CeScannerContextMapper; @@ -201,6 +202,7 @@ public class MyBatis implements Startable { ActiveRuleMapper.class, AnalysisPropertiesMapper.class, AlmAppInstallMapper.class, + AlmProjectMappingsMapper.class, AuthorizationMapper.class, BranchMapper.class, CeActivityMapper.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ALM.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ALM.java new file mode 100644 index 00000000000..0e5dd4ee2be --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ALM.java @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.sonar.db.alm; + +import java.util.Locale; + +public enum ALM { + BITBUCKETCLOUD, + GITHUB; + + String getId() { + return this.name().toLowerCase(Locale.ENGLISH); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java index 6d360f4f7f5..6deb486a5f4 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java @@ -19,7 +19,6 @@ */ package org.sonar.db.alm; -import java.util.Locale; import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; @@ -36,15 +35,6 @@ import static org.apache.commons.lang.StringUtils.isNotEmpty; */ public class AlmAppInstallDao implements Dao { - public enum ALM { - BITBUCKETCLOUD, - GITHUB; - - String getId() { - return this.name().toLowerCase(Locale.ENGLISH); - } - } - private final System2 system2; private final UuidFactory uuidFactory; diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmProjectMappingsDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmProjectMappingsDao.java new file mode 100644 index 00000000000..992e8f1f2bf --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmProjectMappingsDao.java @@ -0,0 +1,74 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.sonar.db.alm; + +import java.util.Objects; +import javax.annotation.Nullable; +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactory; +import org.sonar.db.Dao; +import org.sonar.db.DbSession; + +import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.commons.lang.StringUtils.isNotEmpty; + +public class AlmProjectMappingsDao implements Dao { + + private final System2 system2; + private final UuidFactory uuidFactory; + + public AlmProjectMappingsDao(System2 system2, UuidFactory uuidFactory) { + this.system2 = system2; + this.uuidFactory = uuidFactory; + } + + public void insertOrUpdate(DbSession dbSession, ALM alm, String repoId, String projectUuid, @Nullable String githubSlug, String url) { + checkAlm(alm); + checkRepoId(repoId); + checkArgument(isNotEmpty(projectUuid), "projectUuid can't be null nor empty"); + checkArgument(isNotEmpty(url), "url can't be null nor empty"); + + AlmProjectMappingsMapper mapper = getMapper(dbSession); + long now = system2.now(); + + if (mapper.update(alm.getId(), repoId, projectUuid, githubSlug, url, now) == 0) { + mapper.insert(uuidFactory.create(), alm.getId(), repoId, projectUuid, githubSlug, url, now); + } + } + + public boolean mappingExists(DbSession dbSession, ALM alm, String repoId) { + checkAlm(alm); + checkRepoId(repoId); + + return getMapper(dbSession).mappingCount(alm.getId(), repoId) == 1; + } + + private static void checkAlm(@Nullable ALM alm) { + Objects.requireNonNull(alm, "alm can't be null"); + } + + private static void checkRepoId(@Nullable String repoId) { + checkArgument(isNotEmpty(repoId), "repoId can't be null nor empty"); + } + + private static AlmProjectMappingsMapper getMapper(DbSession dbSession) { + return dbSession.getMapper(AlmProjectMappingsMapper.class); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmProjectMappingsMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmProjectMappingsMapper.java new file mode 100644 index 00000000000..250f588ddae --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmProjectMappingsMapper.java @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.sonar.db.alm; + +import javax.annotation.Nullable; +import org.apache.ibatis.annotations.Param; + +public interface AlmProjectMappingsMapper { + + int mappingCount(@Param("almId") String almId, @Param("repoId") String repoId); + + void insert(@Param("uuid") String uuid, @Param("almId") String almId, @Param("repoId") String repoId, @Param("projectUuid") String projectUuid, + @Nullable @Param("githubSlug") String githubSlug, @Param("url") String url, @Param("now") long now); + + int update(@Param("almId") String almId, @Param("repoId") String repoId, @Param("projectUuid") String projectUuid, + @Nullable @Param("githubSlug") String githubSlug, @Param("url") String url, @Param("now") long now); +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java index bab365e9f53..ec3fe0d866f 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java @@ -298,6 +298,13 @@ class PurgeCommands { profiler.stop(); } + void deleteAlmProjectMappings(String rootUuid) { + profiler.start("deleteAlmProjectMappings (alm_project_mappings)"); + purgeMapper.deleteAlmProjectMappingsByProjectUuid(rootUuid); + session.commit(); + profiler.stop(); + } + void deleteBranch(String rootUuid) { profiler.start("deleteBranch (project_branches)"); purgeMapper.deleteBranchByUuid(rootUuid); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java index 0c8316a92e8..17060408739 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java @@ -203,6 +203,7 @@ public class PurgeDao implements Dao { commands.deleteCeQueue(rootUuid); commands.deleteWebhookDeliveries(rootUuid); commands.deleteProjectMappings(rootUuid); + commands.deleteAlmProjectMappings(rootUuid); commands.deleteBranch(rootUuid); commands.deleteLiveMeasures(rootUuid); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java index 510377605fa..6d5d5e0d7d6 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java @@ -110,6 +110,8 @@ public interface PurgeMapper { void deleteProjectMappingsByProjectUuid(@Param("projectUuid") String projectUuid); + void deleteAlmProjectMappingsByProjectUuid(@Param("projectUuid") String projectUuid); + void deleteBranchByUuid(@Param("uuid") String uuid); void deleteLiveMeasuresByProjectUuid(@Param("projectUuid") String projectUuid); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmProjectMappingsMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmProjectMappingsMapper.xml new file mode 100644 index 00000000000..0f6be2489e6 --- /dev/null +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmProjectMappingsMapper.xml @@ -0,0 +1,51 @@ + + + + + + + + + INSERT INTO alm_project_mappings + ( + uuid, + alm_id, + repo_id, + project_uuid, + github_slug, + url, + created_at, + updated_at + ) + VALUES ( + #{uuid, jdbcType=VARCHAR}, + #{almId, jdbcType=VARCHAR}, + #{repoId, jdbcType=VARCHAR}, + #{projectUuid, jdbcType=VARCHAR}, + #{githubSlug, jdbcType=VARCHAR}, + #{url, jdbcType=VARCHAR}, + #{now, jdbcType=BIGINT}, + #{now, jdbcType=BIGINT} + ) + + + + update alm_project_mappings set + project_uuid = #{projectUuid, jdbcType=VARCHAR}, + github_slug = #{githubSlug, jdbcType=VARCHAR}, + url = #{url, jdbcType=VARCHAR}, + updated_at = #{now, jdbcType=BIGINT} + where + alm_id = #{almId, jdbcType=VARCHAR} + and repo_id = #{repoId, jdbcType=VARCHAR} + + + diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml index 3216e49cf64..a248eba7024 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml @@ -364,6 +364,10 @@ delete from project_mappings where project_uuid=#{projectUuid,jdbcType=VARCHAR} + + delete from alm_project_mappings where project_uuid=#{projectUuid,jdbcType=VARCHAR} + + delete from project_branches where uuid=#{uuid,jdbcType=VARCHAR} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java index 60d417966bd..aa65e1e5183 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java @@ -30,6 +30,6 @@ public class DaoModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new DaoModule().configure(container); - assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 55); + assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 56); } } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmAppInstallDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmAppInstallDaoTest.java index a4a24259186..aab2669fb06 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmAppInstallDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmAppInstallDaoTest.java @@ -36,7 +36,7 @@ import org.sonar.db.DbTester; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.sonar.db.alm.AlmAppInstallDao.ALM.GITHUB; +import static org.sonar.db.alm.ALM.GITHUB; public class AlmAppInstallDaoTest { @@ -214,17 +214,17 @@ public class AlmAppInstallDaoTest { expectedException.expectMessage("installId can't be null nor empty"); } - private AlmAppInstallAssert assertThatAlmAppInstall(AlmAppInstallDao.ALM alm, String ownerId) { + private AlmAppInstallAssert assertThatAlmAppInstall(ALM alm, String ownerId) { return new AlmAppInstallAssert(dbTester, dbSession, alm, ownerId); } private static class AlmAppInstallAssert extends AbstractAssert { - private AlmAppInstallAssert(DbTester dbTester, DbSession dbSession, AlmAppInstallDao.ALM alm, String ownerId) { + private AlmAppInstallAssert(DbTester dbTester, DbSession dbSession, ALM alm, String ownerId) { super(asAlmAppInstall(dbTester, dbSession, alm, ownerId), AlmAppInstallAssert.class); } - private static AlmAppInstall asAlmAppInstall(DbTester dbTester, DbSession dbSession, AlmAppInstallDao.ALM alm, String ownerId) { + private static AlmAppInstall asAlmAppInstall(DbTester dbTester, DbSession dbSession, ALM alm, String ownerId) { List> rows = dbTester.select( dbSession, "select" + diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmProjectMappingsDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmProjectMappingsDaoTest.java new file mode 100644 index 00000000000..12c4616e662 --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmProjectMappingsDaoTest.java @@ -0,0 +1,327 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.sonar.db.alm; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import javax.annotation.Nullable; +import org.assertj.core.api.AbstractAssert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactory; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.db.alm.ALM.GITHUB; + +public class AlmProjectMappingsDaoTest { + + private static final String A_UUID = "abcde1234"; + private static final String ANOTHER_UUID = "xyz789"; + private static final String EMPTY_STRING = ""; + + private static final String A_REPO = "my_repo"; + private static final String ANOTHER_REPO = "another_repo"; + + private static final String A_GITHUB_SLUG = null; + private static final String ANOTHER_GITHUB_SLUG = "example/foo"; + + private static final String A_URL = "foo url"; + private static final String ANOTHER_URL = "bar url"; + + private static final long DATE = 1_600_000_000_000L; + private static final long DATE_LATER = 1_700_000_000_000L; + + private System2 system2 = mock(System2.class); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public DbTester dbTester = DbTester.create(system2); + + private DbSession dbSession = dbTester.getSession(); + private UuidFactory uuidFactory = mock(UuidFactory.class); + private AlmProjectMappingsDao underTest = new AlmProjectMappingsDao(system2, uuidFactory); + + @Test + public void insert_throws_NPE_if_alm_is_null() { + expectAlmNPE(); + + underTest.insertOrUpdate(dbSession, null, A_REPO, A_UUID, A_GITHUB_SLUG, A_URL); + } + + @Test + public void insert_throws_IAE_if_repo_id_is_null() { + expectRepoIdNullOrEmptyIAE(); + + underTest.insertOrUpdate(dbSession, GITHUB, null, A_UUID, A_GITHUB_SLUG, A_URL); + } + + @Test + public void insert_throws_IAE_if_repo_id_is_empty() { + expectRepoIdNullOrEmptyIAE(); + + underTest.insertOrUpdate(dbSession, GITHUB, EMPTY_STRING, A_UUID, A_GITHUB_SLUG, A_URL); + } + + @Test + public void insert_throws_IAE_if_project_uuid_is_null() { + expectProjectUuidNullOrEmptyIAE(); + + underTest.insertOrUpdate(dbSession, GITHUB, A_REPO, null, A_GITHUB_SLUG, A_URL); + } + + @Test + public void insert_throws_IAE_if_project_uuid_is_empty() { + expectProjectUuidNullOrEmptyIAE(); + + underTest.insertOrUpdate(dbSession, GITHUB, A_REPO, EMPTY_STRING, A_GITHUB_SLUG, A_URL); + } + + @Test + public void insert_throws_IAE_if_url_is_null() { + expectUrlNullOrEmptyIAE(); + + underTest.insertOrUpdate(dbSession, GITHUB, A_REPO, A_UUID, A_GITHUB_SLUG, null); + } + + @Test + public void insert_throws_IAE_if_url_is_empty() { + expectUrlNullOrEmptyIAE(); + + underTest.insertOrUpdate(dbSession, GITHUB, A_REPO, A_UUID, A_GITHUB_SLUG, EMPTY_STRING); + } + + @Test + public void insert() { + when(uuidFactory.create()).thenReturn(A_UUID); + when(system2.now()).thenReturn(DATE); + underTest.insertOrUpdate(dbSession, GITHUB, A_REPO, A_UUID, A_GITHUB_SLUG, A_URL); + + assertThatAlmProjectMapping(GITHUB, A_REPO) + .hasProjectUuid(A_UUID) + .hasGithubSlug(A_GITHUB_SLUG) + .hasUrl(A_URL) + .hasCreatedAt(DATE) + .hasUpdatedAt(DATE); + } + + @Test + public void update() { + when(uuidFactory.create()).thenReturn(A_UUID); + when(system2.now()).thenReturn(DATE); + underTest.insertOrUpdate(dbSession, GITHUB, A_REPO, A_UUID, A_GITHUB_SLUG, A_URL); + + when(system2.now()).thenReturn(DATE_LATER); + underTest.insertOrUpdate(dbSession, GITHUB, A_REPO, ANOTHER_UUID, ANOTHER_GITHUB_SLUG, ANOTHER_URL); + + assertThatAlmProjectMapping(GITHUB, A_REPO) + .hasProjectUuid(ANOTHER_UUID) + .hasGithubSlug(ANOTHER_GITHUB_SLUG) + .hasUrl(ANOTHER_URL) + .hasCreatedAt(DATE) + .hasUpdatedAt(DATE_LATER); + } + + @Test + public void insert_multiple() { + when(system2.now()).thenReturn(DATE); + when(uuidFactory.create()) + .thenReturn(A_UUID) + .thenReturn(ANOTHER_UUID); + underTest.insertOrUpdate(dbSession, GITHUB, A_REPO, A_UUID, A_GITHUB_SLUG, A_URL); + underTest.insertOrUpdate(dbSession, GITHUB, ANOTHER_REPO, ANOTHER_UUID, ANOTHER_GITHUB_SLUG, ANOTHER_URL); + + assertThatAlmProjectMapping(GITHUB, A_REPO) + .hasProjectUuid(A_UUID) + .hasGithubSlug(A_GITHUB_SLUG) + .hasUrl(A_URL) + .hasCreatedAt(DATE) + .hasUpdatedAt(DATE); + + assertThatAlmProjectMapping(GITHUB, ANOTHER_REPO) + .hasProjectUuid(ANOTHER_UUID) + .hasGithubSlug(ANOTHER_GITHUB_SLUG) + .hasUrl(ANOTHER_URL) + .hasCreatedAt(DATE) + .hasUpdatedAt(DATE); + } + + @Test + public void mappingExists_throws_NPE_when_alm_is_null() { + expectAlmNPE(); + + underTest.mappingExists(dbSession, null, A_REPO); + } + + @Test + public void mappingExists_throws_IAE_when_repo_id_is_null() { + expectRepoIdNullOrEmptyIAE(); + + underTest.mappingExists(dbSession, GITHUB, null); + } + + @Test + public void mappingExists_throws_IAE_when_repo_id_is_empty() { + expectRepoIdNullOrEmptyIAE(); + + underTest.mappingExists(dbSession, GITHUB, EMPTY_STRING); + } + + @Test + public void mappingExists_returns_false_when_entry_does_not_exist_in_DB() { + assertThat(underTest.mappingExists(dbSession, GITHUB, A_REPO)).isFalse(); + } + + @Test + public void mappingExists_returns_true_when_entry_exists() { + when(uuidFactory.create()).thenReturn(A_UUID); + underTest.insertOrUpdate(dbSession, GITHUB, A_REPO, A_UUID, A_GITHUB_SLUG, A_URL); + + assertThat(underTest.mappingExists(dbSession, GITHUB, A_REPO)).isTrue(); + } + + private void expectAlmNPE() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("alm can't be null"); + } + + private void expectRepoIdNullOrEmptyIAE() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("repoId can't be null nor empty"); + } + + private void expectProjectUuidNullOrEmptyIAE() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("projectUuid can't be null nor empty"); + } + + private void expectUrlNullOrEmptyIAE() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("url can't be null nor empty"); + } + + private AlmAppInstallAssert assertThatAlmProjectMapping(ALM alm, String repoId) { + return new AlmAppInstallAssert(dbTester, dbSession, alm, repoId); + } + + private static class AlmAppInstallAssert extends AbstractAssert { + + private AlmAppInstallAssert(DbTester dbTester, DbSession dbSession, ALM alm, String repoId) { + super(asAlmProjectMapping(dbTester, dbSession, alm, repoId), AlmAppInstallAssert.class); + } + + private static AlmProjectMapping asAlmProjectMapping(DbTester dbTester, DbSession dbSession, ALM alm, String repoId) { + List> rows = dbTester.select( + dbSession, + "select" + + " project_uuid as \"projectUuid\", github_slug as \"githubSlug\", url as \"url\", " + + " created_at as \"createdAt\", updated_at as \"updatedAt\"" + + " from alm_project_mappings" + + " where alm_id='" + alm.getId() + "' and repo_id='" + repoId + "'"); + if (rows.isEmpty()) { + return null; + } + if (rows.size() > 1) { + throw new IllegalStateException("Unique index violation"); + } + return new AlmProjectMapping( + (String) rows.get(0).get("projectUuid"), + (String) rows.get(0).get("githubSlug"), + (String) rows.get(0).get("url"), + (Long) rows.get(0).get("createdAt"), + (Long) rows.get(0).get("updatedAt")); + } + + public void doesNotExist() { + isNull(); + } + + AlmAppInstallAssert hasProjectUuid(String expected) { + isNotNull(); + + if (!Objects.equals(actual.projectUuid, expected)) { + failWithMessage("Expected ALM Project Mapping to have column PROJECT_UUID to be <%s> but was <%s>", expected, actual.projectUuid); + } + return this; + } + + AlmAppInstallAssert hasGithubSlug(String expected) { + isNotNull(); + + if (!Objects.equals(actual.githubSlug, expected)) { + failWithMessage("Expected ALM Project Mapping to have column GITHUB_SLUG to be <%s> but was <%s>", expected, actual.githubSlug); + } + return this; + } + + AlmAppInstallAssert hasUrl(String expected) { + isNotNull(); + + if (!Objects.equals(actual.url, expected)) { + failWithMessage("Expected ALM Project Mapping to have column URL to be <%s> but was <%s>", expected, actual.url); + } + return this; + } + + AlmAppInstallAssert hasCreatedAt(long expected) { + isNotNull(); + + if (!Objects.equals(actual.createdAt, expected)) { + failWithMessage("Expected ALM Project Mapping to have column CREATED_AT to be <%s> but was <%s>", expected, actual.createdAt); + } + + return this; + } + + AlmAppInstallAssert hasUpdatedAt(long expected) { + isNotNull(); + + if (!Objects.equals(actual.updatedAt, expected)) { + failWithMessage("Expected ALM Project Mapping to have column UPDATED_AT to be <%s> but was <%s>", expected, actual.updatedAt); + } + + return this; + } + + } + + private static final class AlmProjectMapping { + private final String projectUuid; + private final String githubSlug; + private final String url; + private final Long createdAt; + private final Long updatedAt; + + AlmProjectMapping(@Nullable String projectUuid, @Nullable String githubSlug, @Nullable String url, @Nullable Long createdAt, @Nullable Long updatedAt) { + this.projectUuid = projectUuid; + this.githubSlug = githubSlug; + this.url = url; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + } +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java index 4f7ec70247d..36f75d46862 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java @@ -21,7 +21,6 @@ package org.sonar.db.purge; import com.google.common.collect.ImmutableList; import java.io.ByteArrayInputStream; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; @@ -36,7 +35,6 @@ import org.apache.commons.lang.time.DateUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.mockito.ArgumentCaptor; import org.sonar.api.resources.Scopes; import org.sonar.api.utils.System2; import org.sonar.core.util.CloseableIterator; @@ -45,6 +43,7 @@ import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; +import org.sonar.db.alm.ALM; import org.sonar.db.ce.CeActivityDto; import org.sonar.db.ce.CeQueueDto; import org.sonar.db.ce.CeQueueDto.Status; @@ -53,7 +52,6 @@ import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; -import org.sonar.db.component.SnapshotDto; import org.sonar.db.issue.IssueDto; import org.sonar.db.measure.MeasureDto; import org.sonar.db.measure.custom.CustomMeasureDto; @@ -623,6 +621,22 @@ public class PurgeDaoTest { assertThat(dbClient.projectMappingsDao().get(dbSession, "a.key.type", "another.key")).isNotEmpty(); } + @Test + public void deleteProject_deletes_alm_project_mappings() { + ALM alm = ALM.GITHUB; + String repoId = "123"; + String otherRepoId = repoId + "-foo"; + + ComponentDto project = dbTester.components().insertPublicProject(); + dbClient.almProjectMappingsDao().insertOrUpdate(dbSession, alm, repoId, project.uuid(), null, "foo"); + dbClient.almProjectMappingsDao().insertOrUpdate(dbSession, alm, otherRepoId, "D2", null, "bar"); + + underTest.deleteProject(dbSession, project.uuid()); + + assertThat(dbClient.almProjectMappingsDao().mappingExists(dbSession, alm, repoId)).isFalse(); + assertThat(dbClient.almProjectMappingsDao().mappingExists(dbSession, alm, otherRepoId)).isTrue(); + } + @Test public void deleteNonRootComponents_has_no_effect_when_parameter_is_empty() { DbSession dbSession = mock(DbSession.class); diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTable.java new file mode 100644 index 00000000000..6264cef7962 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTable.java @@ -0,0 +1,120 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.sonar.server.platform.db.migration.version.v73; + +import java.sql.Connection; +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.def.BigIntegerColumnDef; +import org.sonar.server.platform.db.migration.def.VarcharColumnDef; +import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; +import org.sonar.server.platform.db.migration.sql.CreateTableBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder; +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.MAX_SIZE; +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE; +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder; + +public class CreateAlmProjectMappingsTable extends DdlChange { + + private static final String TABLE_NAME = "alm_project_mappings"; + + private static final VarcharColumnDef UUID = newVarcharColumnDefBuilder() + .setColumnName("uuid") + .setLimit(UUID_SIZE) + .setIsNullable(false) + .build(); + private static final VarcharColumnDef ALM_ID_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("alm_id") + .setIsNullable(false) + .setLimit(40) + .build(); + private static final VarcharColumnDef REPO_ID_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("repo_id") + .setIsNullable(false) + .setLimit(256) + .build(); + private static final VarcharColumnDef PROJECT_UUID_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("project_uuid") + .setIsNullable(false) + .setLimit(40) + .build(); + private static final VarcharColumnDef GITHUB_SLUG_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("github_slug") + .setIsNullable(true) + .setLimit(256) + .build(); + private static final VarcharColumnDef URL_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("url") + .setIsNullable(false) + .setLimit(2000) + .build(); + private static final BigIntegerColumnDef CREATED_AT_COLUMN = newBigIntegerColumnDefBuilder() + .setColumnName("created_at") + .setIsNullable(false) + .build(); + private static final BigIntegerColumnDef UPDATED_AT_COLUMN = newBigIntegerColumnDefBuilder() + .setColumnName("updated_at") + .setIsNullable(false) + .build(); + + public CreateAlmProjectMappingsTable(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + + if (!tableExists()) { + context.execute(new CreateTableBuilder(getDialect(), TABLE_NAME) + .addPkColumn(UUID) + .addColumn(ALM_ID_COLUMN) + .addColumn(REPO_ID_COLUMN) + .addColumn(PROJECT_UUID_COLUMN) + .addColumn(GITHUB_SLUG_COLUMN) + .addColumn(URL_COLUMN) + .addColumn(CREATED_AT_COLUMN) + .addColumn(UPDATED_AT_COLUMN) + .build()); + + context.execute(new CreateIndexBuilder(getDialect()) + .addColumn(ALM_ID_COLUMN) + .addColumn(REPO_ID_COLUMN) + .setUnique(true) + .setTable(TABLE_NAME) + .setName(TABLE_NAME + "_alm_repo") + .build()); + context.execute(new CreateIndexBuilder(getDialect()) + .addColumn(PROJECT_UUID_COLUMN) + .setUnique(true) + .setTable(TABLE_NAME) + .setName(TABLE_NAME + "_project") + .build()); + } + } + + private boolean tableExists() throws SQLException { + try (Connection connection = getDatabase().getDataSource().getConnection()) { + return DatabaseUtils.tableExists(TABLE_NAME, connection); + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/DbVersion73.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/DbVersion73.java index 6611ef0b301..b996ea12a5c 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/DbVersion73.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/DbVersion73.java @@ -40,6 +40,7 @@ public class DbVersion73 implements DbVersion { .add(2210, "Add 'securityhotspotadmin' permission to templates characteristics already having 'issueadmin'", PopulateHotspotAdminPermissionOnTemplatesCharacteristics.class) .add(2211, "Set SUBSCRIPTION not nullable in ORGANIZATIONS", SetSubscriptionOnOrganizationsNotNullable.class) .add(2212, "Add index on ORGANIZATION_MEMBERS", AddIndexOnOrganizationMembers.class) + .add(2213, "Create table to store alm project mappings", CreateAlmProjectMappingsTable.class) ; } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTableTest.java new file mode 100644 index 00000000000..79557d02099 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTableTest.java @@ -0,0 +1,71 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.sonar.server.platform.db.migration.version.v73; + +import java.sql.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; + +import static java.sql.Types.BIGINT; +import static java.sql.Types.VARCHAR; +import static org.assertj.core.api.Assertions.assertThat; + +public class CreateAlmProjectMappingsTableTest { + + private static final String TABLE = "alm_project_mappings"; + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(CreateAlmProjectMappingsTableTest.class, "empty.sql"); + + private CreateAlmProjectMappingsTable underTest = new CreateAlmProjectMappingsTable(db.database()); + + @Test + public void creates_table_on_empty_db() throws SQLException { + underTest.execute(); + + checkTable(); + } + + @Test + public void migration_is_reentrant() throws SQLException { + underTest.execute(); + underTest.execute(); + + checkTable(); + } + + private void checkTable() { + assertThat(db.countRowsOfTable(TABLE)).isEqualTo(0); + + db.assertColumnDefinition(TABLE, "uuid", VARCHAR, 40, false); + db.assertPrimaryKey(TABLE, "pk_" + TABLE, "uuid"); + db.assertColumnDefinition(TABLE, "alm_id", VARCHAR, 40, false); + db.assertColumnDefinition(TABLE, "repo_id", VARCHAR, 256, false); + db.assertColumnDefinition(TABLE, "project_uuid", VARCHAR, 40, false); + db.assertColumnDefinition(TABLE, "github_slug", VARCHAR, 256, true); + db.assertColumnDefinition(TABLE, "url", VARCHAR, 2000, false); + db.assertColumnDefinition(TABLE, "created_at", BIGINT, null, false); + db.assertColumnDefinition(TABLE, "updated_at", BIGINT, null, false); + + db.assertUniqueIndex(TABLE, TABLE + "_alm_repo", "alm_id", "repo_id"); + db.assertUniqueIndex(TABLE, TABLE + "_project", "project_uuid"); + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTableTest/empty.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v73/CreateAlmProjectMappingsTableTest/empty.sql new file mode 100644 index 00000000000..e69de29bb2d -- 2.39.5