From 6c0b5563fbc8b0fbf9ed0aa722302a8f1e74cdf4 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Sun, 11 Sep 2016 10:12:36 +0200 Subject: [PATCH] SONAR-7851 add QProfileChangeDao --- .../ComputeEngineContainerImplTest.java | 2 +- .../sonar/core/util/SequenceUuidFactory.java | 36 +++ .../core/util/SequenceUuidFactoryTest.java | 37 +++ .../src/main/java/org/sonar/db/DaoModule.java | 7 +- .../src/main/java/org/sonar/db/DbClient.java | 7 + .../src/main/java/org/sonar/db/MyBatis.java | 3 +- .../db/qualityprofile/QProfileChangeDao.java | 56 +++++ .../db/qualityprofile/QProfileChangeDto.java | 117 ++++++++++ .../qualityprofile/QProfileChangeMapper.java | 32 +++ .../qualityprofile/QProfileChangeQuery.java | 92 ++++++++ .../sonar/db/version/MigrationStepModule.java | 4 - .../qualityprofile/QProfileChangeMapper.xml | 74 ++++++ .../test/java/org/sonar/db/DaoModuleTest.java | 2 +- .../src/test/java/org/sonar/db/DbTester.java | 2 + .../qualityprofile/QProfileChangeDaoTest.java | 213 ++++++++++++++++++ .../qualityprofile/QProfileChangeDtoTest.java | 45 ++++ .../qualityprofile/QualityProfileTesting.java | 22 ++ .../db/version/MigrationStepModuleTest.java | 2 +- 18 files changed, 743 insertions(+), 10 deletions(-) create mode 100644 sonar-core/src/main/java/org/sonar/core/util/SequenceUuidFactory.java create mode 100644 sonar-core/src/test/java/org/sonar/core/util/SequenceUuidFactoryTest.java create mode 100644 sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeDao.java create mode 100644 sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeDto.java create mode 100644 sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeMapper.java create mode 100644 sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeQuery.java create mode 100644 sonar-db/src/main/resources/org/sonar/db/qualityprofile/QProfileChangeMapper.xml create mode 100644 sonar-db/src/test/java/org/sonar/db/qualityprofile/QProfileChangeDaoTest.java create mode 100644 sonar-db/src/test/java/org/sonar/db/qualityprofile/QProfileChangeDtoTest.java 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 fa04a3ace0f..de430a0516e 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 @@ -106,7 +106,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION + 24 // level 1 - + 49 // content of DaoModule + + 50 // content of DaoModule + 2 // content of EsSearchModule + 61 // content of CorePropertyDefinitions + 1 // content of CePropertyDefinitions diff --git a/sonar-core/src/main/java/org/sonar/core/util/SequenceUuidFactory.java b/sonar-core/src/main/java/org/sonar/core/util/SequenceUuidFactory.java new file mode 100644 index 00000000000..c35f78c71dc --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/util/SequenceUuidFactory.java @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.core.util; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Only for tests. This implementation of {@link UuidFactory} generates + * ids as a sequence of integers ("1", "2", ...). It starts with "1". + */ +public class SequenceUuidFactory implements UuidFactory { + + private final AtomicInteger id = new AtomicInteger(1); + + @Override + public String create() { + return String.valueOf(id.getAndIncrement()); + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/util/SequenceUuidFactoryTest.java b/sonar-core/src/test/java/org/sonar/core/util/SequenceUuidFactoryTest.java new file mode 100644 index 00000000000..2ca09ce5907 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/util/SequenceUuidFactoryTest.java @@ -0,0 +1,37 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.core.util; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class SequenceUuidFactoryTest { + + private SequenceUuidFactory underTest = new SequenceUuidFactory(); + + @Test + public void generate_sequence_of_integer_ids() { + for (int i = 1; i < 10; i++) { + assertThat(underTest.create()).isEqualTo(String.valueOf(i)); + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/DaoModule.java b/sonar-db/src/main/java/org/sonar/db/DaoModule.java index 880090dcdde..630f07632b9 100644 --- a/sonar-db/src/main/java/org/sonar/db/DaoModule.java +++ b/sonar-db/src/main/java/org/sonar/db/DaoModule.java @@ -25,6 +25,7 @@ import org.sonar.core.platform.Module; import org.sonar.db.activity.ActivityDao; import org.sonar.db.ce.CeActivityDao; import org.sonar.db.ce.CeQueueDao; +import org.sonar.db.ce.CeScannerContextDao; import org.sonar.db.ce.CeTaskInputDao; import org.sonar.db.component.ComponentDao; import org.sonar.db.component.ComponentKeyUpdaterDao; @@ -59,9 +60,9 @@ import org.sonar.db.qualitygate.ProjectQgateAssociationDao; import org.sonar.db.qualitygate.QualityGateConditionDao; import org.sonar.db.qualitygate.QualityGateDao; import org.sonar.db.qualityprofile.ActiveRuleDao; +import org.sonar.db.qualityprofile.QProfileChangeDao; import org.sonar.db.qualityprofile.QualityProfileDao; import org.sonar.db.rule.RuleDao; -import org.sonar.db.ce.CeScannerContextDao; import org.sonar.db.source.FileSourceDao; import org.sonar.db.user.AuthorDao; import org.sonar.db.user.AuthorizationDao; @@ -110,6 +111,7 @@ public class DaoModule extends Module { QualityGateDao.class, QualityGateConditionDao.class, QualityProfileDao.class, + QProfileChangeDao.class, CeScannerContextDao.class, RuleDao.class, ActiveRuleDao.class, @@ -122,7 +124,8 @@ public class DaoModule extends Module { UserGroupDao.class, UserTokenDao.class, WidgetDao.class, - WidgetPropertyDao.class).build(); + WidgetPropertyDao.class + ).build(); @Override protected void configureModule() { diff --git a/sonar-db/src/main/java/org/sonar/db/DbClient.java b/sonar-db/src/main/java/org/sonar/db/DbClient.java index 6a6f4d182ca..ceabd8c62ae 100644 --- a/sonar-db/src/main/java/org/sonar/db/DbClient.java +++ b/sonar-db/src/main/java/org/sonar/db/DbClient.java @@ -60,6 +60,7 @@ import org.sonar.db.qualitygate.ProjectQgateAssociationDao; import org.sonar.db.qualitygate.QualityGateConditionDao; import org.sonar.db.qualitygate.QualityGateDao; import org.sonar.db.qualityprofile.ActiveRuleDao; +import org.sonar.db.qualityprofile.QProfileChangeDao; import org.sonar.db.qualityprofile.QualityProfileDao; import org.sonar.db.rule.RuleDao; import org.sonar.db.source.FileSourceDao; @@ -125,6 +126,7 @@ public class DbClient { private final GroupDao groupDao; private final RuleDao ruleDao; private final ActiveRuleDao activeRuleDao; + private final QProfileChangeDao qProfileChangeDao; public DbClient(Database database, MyBatis myBatis, Dao... daos) { this.database = database; @@ -183,6 +185,7 @@ public class DbClient { groupDao = getDao(map, GroupDao.class); ruleDao = getDao(map, RuleDao.class); activeRuleDao = getDao(map, ActiveRuleDao.class); + qProfileChangeDao = getDao(map, QProfileChangeDao.class); } public DbSession openSession(boolean batch) { @@ -393,6 +396,10 @@ public class DbClient { return activeRuleDao; } + public QProfileChangeDao qProfileChangeDao() { + return qProfileChangeDao; + } + protected K getDao(Map map, Class clazz) { return (K) map.get(clazz); } diff --git a/sonar-db/src/main/java/org/sonar/db/MyBatis.java b/sonar-db/src/main/java/org/sonar/db/MyBatis.java index e2d37996d3c..ca25a31117a 100644 --- a/sonar-db/src/main/java/org/sonar/db/MyBatis.java +++ b/sonar-db/src/main/java/org/sonar/db/MyBatis.java @@ -108,6 +108,7 @@ import org.sonar.db.qualitygate.QualityGateMapper; import org.sonar.db.qualityprofile.ActiveRuleDto; import org.sonar.db.qualityprofile.ActiveRuleMapper; import org.sonar.db.qualityprofile.ActiveRuleParamDto; +import org.sonar.db.qualityprofile.QProfileChangeMapper; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.db.qualityprofile.QualityProfileMapper; import org.sonar.db.rule.RuleDto; @@ -236,7 +237,7 @@ public class MyBatis { UserMapper.class, GroupMapper.class, UserGroupMapper.class, UserTokenMapper.class, FileSourceMapper.class, NotificationQueueMapper.class, - GroupMembershipMapper.class, QualityProfileMapper.class, ActiveRuleMapper.class, + GroupMembershipMapper.class, QualityProfileMapper.class, ActiveRuleMapper.class, QProfileChangeMapper.class, MeasureMapper.class, MetricMapper.class, CustomMeasureMapper.class, QualityGateMapper.class, QualityGateConditionMapper.class, ComponentMapper.class, SnapshotMapper.class, ProjectQgateAssociationMapper.class, EventMapper.class, CeQueueMapper.class, CeActivityMapper.class, CeTaskInputMapper.class, CeScannerContextMapper.class, diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeDao.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeDao.java new file mode 100644 index 00000000000..41c6474d4e4 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeDao.java @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.qualityprofile; + +import java.util.List; +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.checkState; + +public class QProfileChangeDao implements Dao { + + private final System2 system2; + private final UuidFactory uuidFactory; + + public QProfileChangeDao(System2 system2, UuidFactory uuidFactory) { + this.system2 = system2; + this.uuidFactory = uuidFactory; + } + + public void insert(DbSession dbSession, QProfileChangeDto dto) { + checkState(dto.getKey() == null, "Key of QProfileChangeDto must be set by DAO only. Got %s.", dto.getKey()); + checkState(dto.getCreatedAt() == 0L, "Date of QProfileChangeDto must be set by DAO only. Got %s.", dto.getCreatedAt()); + + dto.setKey(uuidFactory.create()); + dto.setCreatedAt(system2.now()); + dbSession.getMapper(QProfileChangeMapper.class).insert(dto); + } + + public List selectByQuery(DbSession dbSession, QProfileChangeQuery query) { + return dbSession.getMapper(QProfileChangeMapper.class).selectByQuery(query); + } + + public int countForProfileKey(DbSession dbSession, String profileKey) { + return dbSession.getMapper(QProfileChangeMapper.class).countForProfileKey(profileKey); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeDto.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeDto.java new file mode 100644 index 00000000000..ab12cb0908f --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeDto.java @@ -0,0 +1,117 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.qualityprofile; + +import java.util.Collections; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.sonar.api.utils.KeyValueFormat; + +public class QProfileChangeDto { + + private String key; + private String profileKey; + // can't be named "type" because it's a reserved word in Oracle + // (used by Mybatis to map DB column with DTO field) + private String changeType; + private String login; + private String data; + private long createdAt; + + public String getKey() { + return key; + } + + public QProfileChangeDto setKey(String s) { + this.key = s; + return this; + } + + public String getProfileKey() { + return profileKey; + } + + public QProfileChangeDto setProfileKey(String s) { + this.profileKey = s; + return this; + } + + public String getChangeType() { + return changeType; + } + + public QProfileChangeDto setChangeType(String s) { + this.changeType = s; + return this; + } + + public long getCreatedAt() { + return createdAt; + } + + public QProfileChangeDto setCreatedAt(long l) { + this.createdAt = l; + return this; + } + + @CheckForNull + public String getLogin() { + return login; + } + + public QProfileChangeDto setLogin(@Nullable String s) { + this.login = s; + return this; + } + + @CheckForNull + public String getData() { + return data; + } + + public Map getDataAsMap() { + if (data == null) { + return Collections.emptyMap(); + } + return KeyValueFormat.parse(data); + } + + public QProfileChangeDto setData(@Nullable String csv) { + this.data = csv; + return this; + } + + public QProfileChangeDto setData(@Nullable Map m) { + if (m == null || m.isEmpty()) { + this.data = null; + } else { + this.data = KeyValueFormat.format(m); + } + return this; + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeMapper.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeMapper.java new file mode 100644 index 00000000000..a50c9b8cbf1 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeMapper.java @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.qualityprofile; + +import java.util.List; +import org.apache.ibatis.annotations.Param; + +public interface QProfileChangeMapper { + + void insert(QProfileChangeDto dto); + + List selectByQuery(@Param("query") QProfileChangeQuery query); + + int countForProfileKey(@Param("profileKey") String profileKey); +} diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeQuery.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeQuery.java new file mode 100644 index 00000000000..334dbaab240 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QProfileChangeQuery.java @@ -0,0 +1,92 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.qualityprofile; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + +import static java.util.Objects.requireNonNull; + +public class QProfileChangeQuery { + + private final String profileKey; + private Long fromIncluded; + private Long toExcluded; + private int offset = 0; + private int limit = 100; + + public QProfileChangeQuery(String profileKey) { + this.profileKey = requireNonNull(profileKey); + } + + public String getProfileKey() { + return profileKey; + } + + @CheckForNull + public Long getFromIncluded() { + return fromIncluded; + } + + public void setFromIncluded(@Nullable Long l) { + this.fromIncluded = l; + } + + @CheckForNull + public Long getToExcluded() { + return toExcluded; + } + + public void setToExcluded(@Nullable Long l) { + this.toExcluded = l; + } + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public int getLimit() { + return limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public void setPage(int page, int pageSize) { + offset = (page -1) * pageSize; + limit = pageSize; + } + + public int getTotal() { + return offset + limit; + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java index 9bd60db038d..3d97895f8ac 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java +++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java @@ -322,13 +322,9 @@ public class MigrationStepModule extends Module { ShrinkModuleUuidPathOfProjects.class, AddBUuidPathToProjects.class, AddErrorColumnsToCeActivity.class, -<<<<<<< HEAD PopulateTableProperties2.class, - RemoveViewsDefinitionFromProperties.class); -======= RemoveViewsDefinitionFromProperties.class, CopyActivitiesToQprofileChanges.class ); ->>>>>>> SONAR-7851 copy activities to qprofile_changes } } diff --git a/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QProfileChangeMapper.xml b/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QProfileChangeMapper.xml new file mode 100644 index 00000000000..d2a3b3b6490 --- /dev/null +++ b/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QProfileChangeMapper.xml @@ -0,0 +1,74 @@ + + + + + + + insert into qprofile_changes + ( + kee, + qprofile_key, + created_at, + user_login, + change_type, + data + ) values ( + #{key,jdbcType=VARCHAR}, + #{profileKey,jdbcType=VARCHAR}, + #{createdAt,jdbcType=BIGINT}, + #{login,jdbcType=VARCHAR}, + #{changeType,jdbcType=VARCHAR}, + #{data,jdbcType=VARCHAR} + ) + + + + + + + + + + + + kee as "key", + qprofile_key as profileKey, + created_at as createdAt, + user_login as login, + change_type as changeType, + data + + + + select + from qprofile_changes + where qprofile_key = #{query.profileKey} + + and created_at >= #{query.fromIncluded} + + + and created_at < #{query.toExcluded} + + order by created_at desc + + + diff --git a/sonar-db/src/test/java/org/sonar/db/DaoModuleTest.java b/sonar-db/src/test/java/org/sonar/db/DaoModuleTest.java index 8e8d42a1996..8e7a117b0eb 100644 --- a/sonar-db/src/test/java/org/sonar/db/DaoModuleTest.java +++ b/sonar-db/src/test/java/org/sonar/db/DaoModuleTest.java @@ -29,6 +29,6 @@ public class DaoModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new DaoModule().configure(container); - assertThat(container.size()).isEqualTo(2 + 49); + assertThat(container.size()).isEqualTo(2 + 50); } } diff --git a/sonar-db/src/test/java/org/sonar/db/DbTester.java b/sonar-db/src/test/java/org/sonar/db/DbTester.java index b2b6f08716d..f56843dd329 100644 --- a/sonar-db/src/test/java/org/sonar/db/DbTester.java +++ b/sonar-db/src/test/java/org/sonar/db/DbTester.java @@ -57,6 +57,7 @@ import org.junit.rules.ExternalResource; import org.picocontainer.containers.TransientPicoContainer; import org.sonar.api.utils.System2; import org.sonar.api.utils.log.Loggers; +import org.sonar.core.util.SequenceUuidFactory; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Lists.newArrayList; @@ -123,6 +124,7 @@ public class DbTester extends ExternalResource { TransientPicoContainer ioc = new TransientPicoContainer(); ioc.addComponent(db.getMyBatis()); ioc.addComponent(system2); + ioc.addComponent(new SequenceUuidFactory()); for (Class daoClass : DaoModule.classes()) { ioc.addComponent(daoClass); } diff --git a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QProfileChangeDaoTest.java b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QProfileChangeDaoTest.java new file mode 100644 index 00000000000..80fb2ce73d4 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QProfileChangeDaoTest.java @@ -0,0 +1,213 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.qualityprofile; + +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +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.AssertionsForInterfaceTypes.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class QProfileChangeDaoTest { + + private static final long A_DATE = 1_500_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 QProfileChangeDao underTest = new QProfileChangeDao(system2, uuidFactory); + + @Test + public void test_insert_without_null_fields() { + when(system2.now()).thenReturn(A_DATE); + when(uuidFactory.create()).thenReturn("C1"); + + String profileKey = "P1"; + String login = "marcel"; + String type = "ACTIVATED"; + String data = "some_data"; + insertChange(profileKey, type, login, data); + + Map row = selectChangeByKey("C1"); + assertThat(row.get("qprofileKey")).isEqualTo(profileKey); + assertThat(row.get("createdAt")).isEqualTo(A_DATE); + assertThat(row.get("login")).isEqualTo(login); + assertThat(row.get("changeType")).isEqualTo(type); + assertThat(row.get("data")).isEqualTo(data); + } + + /** + * user_login and data can be null + */ + @Test + public void test_insert_with_nullable_fields() { + when(system2.now()).thenReturn(A_DATE); + when(uuidFactory.create()).thenReturn("C1"); + + insertChange("P1", "ACTIVATED", null, null); + + Map row = selectChangeByKey("C1"); + assertThat(row.get("qprofileKey")).isEqualTo("P1"); + assertThat(row.get("createdAt")).isEqualTo(A_DATE); + assertThat(row.get("changeType")).isEqualTo("ACTIVATED"); + assertThat(row.get("login")).isNull(); + assertThat(row.get("data")).isNull(); + } + + @Test + public void insert_throws_ISE_if_key_is_already_set() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Key of QProfileChangeDto must be set by DAO only. Got C1."); + + underTest.insert(dbSession, new QProfileChangeDto().setKey("C1")); + } + + @Test + public void insert_throws_ISE_if_date_is_already_set() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Date of QProfileChangeDto must be set by DAO only. Got 123."); + + underTest.insert(dbSession, new QProfileChangeDto().setCreatedAt(123L)); + } + + @Test + public void selectByQuery_returns_empty_list_if_no_profile_changes() { + List changes = underTest.selectByQuery(dbSession, new QProfileChangeQuery("P1")); + assertThat(changes).isEmpty(); + } + + @Test + public void selectByQuery_returns_changes_ordered_by_descending_date() { + when(system2.now()).thenReturn(A_DATE, A_DATE + 1, A_DATE + 2); + when(uuidFactory.create()).thenReturn("C1", "C2", "C3"); + + // profile P1 + insertChange("P1", "ACTIVATED", null, null);// key: C1 + insertChange("P1", "ACTIVATED", null, null);// key: C2 + // profile P2: C3 + insertChange("P2", "ACTIVATED", null, null);// key: C3 + + List changes = underTest.selectByQuery(dbSession, new QProfileChangeQuery("P1")); + assertThat(changes).extracting(QProfileChangeDto::getKey).containsExactly("C2", "C1"); + } + + @Test + public void selectByQuery_supports_pagination_of_changes() { + when(system2.now()).thenReturn(A_DATE, A_DATE + 10, A_DATE + 20, A_DATE + 30); + when(uuidFactory.create()).thenReturn("C1", "C2", "C3", "C4"); + insertChange("P1", "ACTIVATED", null, null);// key: C1 + insertChange("P1", "ACTIVATED", null, null);// key: C2 + insertChange("P1", "ACTIVATED", null, null);// key: C3 + insertChange("P1", "ACTIVATED", null, null);// key: C4 + + QProfileChangeQuery query = new QProfileChangeQuery("P1"); + query.setOffset(2); + query.setLimit(1); + List changes = underTest.selectByQuery(dbSession, query); + assertThat(changes).extracting(QProfileChangeDto::getKey).containsExactly("C2"); + } + + @Test + public void selectByQuery_returns_changes_after_given_date() { + when(system2.now()).thenReturn(A_DATE, A_DATE + 10, A_DATE + 20); + when(uuidFactory.create()).thenReturn("C1", "C2", "C3", "C4"); + insertChange("P1", "ACTIVATED", null, null);// key: C1 + insertChange("P1", "ACTIVATED", null, null);// key: C2 + insertChange("P1", "ACTIVATED", null, null);// key: C3 + + QProfileChangeQuery query = new QProfileChangeQuery("P1"); + query.setFromIncluded(A_DATE + 10); + List changes = underTest.selectByQuery(dbSession, query); + assertThat(changes).extracting(QProfileChangeDto::getKey).containsExactly("C3", "C2"); + } + + @Test + public void selectByQuery_returns_changes_before_given_date() { + when(system2.now()).thenReturn(A_DATE, A_DATE + 10, A_DATE + 20); + when(uuidFactory.create()).thenReturn("C1", "C2", "C3", "C4"); + insertChange("P1", "ACTIVATED", null, null);// key: C1 + insertChange("P1", "ACTIVATED", null, null);// key: C2 + insertChange("P1", "ACTIVATED", null, null);// key: C3 + + QProfileChangeQuery query = new QProfileChangeQuery("P1"); + query.setToExcluded(A_DATE + 12); + List changes = underTest.selectByQuery(dbSession, query); + assertThat(changes).extracting(QProfileChangeDto::getKey).containsExactly("C2", "C1"); + } + + @Test + public void selectByQuery_returns_changes_in_a_range_of_dates() { + when(system2.now()).thenReturn(A_DATE, A_DATE + 10, A_DATE + 20, A_DATE + 30); + when(uuidFactory.create()).thenReturn("C1", "C2", "C3", "C4"); + insertChange("P1", "ACTIVATED", null, null);// key: C1 + insertChange("P1", "ACTIVATED", null, null);// key: C2 + insertChange("P1", "ACTIVATED", null, null);// key: C3 + insertChange("P1", "ACTIVATED", null, null);// key: C4 + + QProfileChangeQuery query = new QProfileChangeQuery("P1"); + query.setFromIncluded(A_DATE + 8); + query.setToExcluded(A_DATE + 22); + List changes = underTest.selectByQuery(dbSession, query); + assertThat(changes).extracting(QProfileChangeDto::getKey).containsExactly("C3", "C2"); + } + + @Test + public void test_countForProfileKey() { + when(system2.now()).thenReturn(A_DATE, A_DATE + 10); + when(uuidFactory.create()).thenReturn("C1", "C2"); + + insertChange("P1", "ACTIVATED", null, null);// key: C1 + insertChange("P1", "ACTIVATED", null, null);// key: C2 + + assertThat(underTest.countForProfileKey(dbSession, "P1")).isEqualTo(2); + assertThat(underTest.countForProfileKey(dbSession, "P2")).isEqualTo(0); + } + + private void insertChange(String profileKey, String type, @Nullable String login, @Nullable String data) { + QProfileChangeDto dto = new QProfileChangeDto() + .setProfileKey(profileKey) + .setLogin(login) + .setChangeType(type) + .setData(data); + underTest.insert(dbSession, dto); + } + + private Map selectChangeByKey(String key) { + return dbTester.selectFirst(dbSession, + "select qprofile_key as \"qprofileKey\", created_at as \"createdAt\", user_login as \"login\", change_type as \"changeType\", data as \"data\" from qprofile_changes where kee='" + + key + "'"); + } +} diff --git a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QProfileChangeDtoTest.java b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QProfileChangeDtoTest.java new file mode 100644 index 00000000000..d3912dcd6f6 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QProfileChangeDtoTest.java @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.qualityprofile; + +import com.google.common.collect.ImmutableMap; +import java.util.Collections; +import java.util.Map; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.data.MapEntry.entry; + +public class QProfileChangeDtoTest { + + private QProfileChangeDto underTest = new QProfileChangeDto(); + + @Test + public void convert_data_to_map() { + underTest.setData((Map) null); + assertThat(underTest.getDataAsMap()).isEmpty(); + + underTest.setData(Collections.emptyMap()); + assertThat(underTest.getDataAsMap()).isEmpty(); + + underTest.setData(ImmutableMap.of("k1", "v1", "k2", "v2")); + assertThat(underTest.getDataAsMap()).containsOnly(entry("k1", "v1"), entry("k2", "v2")); + } +} diff --git a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileTesting.java b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileTesting.java index 836b5beca31..0dccace4541 100644 --- a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileTesting.java +++ b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileTesting.java @@ -22,11 +22,15 @@ package org.sonar.db.qualityprofile; import java.util.Date; import org.sonar.core.util.Uuids; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import static java.util.Arrays.stream; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.apache.commons.lang.math.RandomUtils.nextLong; public class QualityProfileTesting { + public static QualityProfileDto newQualityProfileDto() { String uuid = Uuids.createFast(); QualityProfileDto dto = QualityProfileDto.createFor(uuid) @@ -37,4 +41,22 @@ public class QualityProfileTesting { .setUpdatedAt(new Date()); return dto; } + + public static QProfileChangeDto newQProfileChangeDto() { + return new QProfileChangeDto() + .setKey(randomAlphanumeric(40)) + .setProfileKey(randomAlphanumeric(40)) + .setCreatedAt(nextLong()) + .setChangeType("ACTIVATED") + .setLogin(randomAlphanumeric(10)); + } + + public static void insert(DbTester dbTester, QProfileChangeDto... dtos) { + // do not use QProfileChangeDao so that generated fields key and creation date + // can be defined by tests + DbSession dbSession = dbTester.getSession(); + QProfileChangeMapper mapper = dbSession.getMapper(QProfileChangeMapper.class); + stream(dtos).forEach(dto -> mapper.insert(dto)); + dbSession.commit(); + } } diff --git a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java index d7acef90b52..d1dfd506a72 100644 --- a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java +++ b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java @@ -29,6 +29,6 @@ public class MigrationStepModuleTest { public void verify_count_of_added_MigrationStep_types() { ComponentContainer container = new ComponentContainer(); new MigrationStepModule().configure(container); - assertThat(container.size()).isEqualTo(134); + assertThat(container.size()).isEqualTo(135); } } -- 2.39.5