@@ -0,0 +1,77 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2022 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.rule; | |||
import java.util.Objects; | |||
import static org.apache.commons.lang.StringUtils.isNotEmpty; | |||
import static org.sonar.api.utils.Preconditions.checkArgument; | |||
public class RuleDescriptionSectionContextDto { | |||
static final String KEY_MUST_BE_SET_ERROR = "key must be set"; | |||
static final String DISPLAY_NAME_MUST_BE_SET_ERROR = "displayName must be set"; | |||
private final String key; | |||
private final String displayName; | |||
private RuleDescriptionSectionContextDto(String key, String displayName) { | |||
checkArgument(isNotEmpty(key), KEY_MUST_BE_SET_ERROR); | |||
checkArgument(isNotEmpty(displayName), DISPLAY_NAME_MUST_BE_SET_ERROR); | |||
this.key = key; | |||
this.displayName = displayName; | |||
} | |||
public static RuleDescriptionSectionContextDto of(String key, String displayName) { | |||
return new RuleDescriptionSectionContextDto(key, displayName); | |||
} | |||
public String getKey() { | |||
return key; | |||
} | |||
public String getDisplayName() { | |||
return displayName; | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) { | |||
return true; | |||
} | |||
if (o == null || getClass() != o.getClass()) { | |||
return false; | |||
} | |||
RuleDescriptionSectionContextDto that = (RuleDescriptionSectionContextDto) o; | |||
return getKey().equals(that.getKey()) && getDisplayName().equals(that.getDisplayName()); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return Objects.hash(getKey(), getDisplayName()); | |||
} | |||
@Override | |||
public String toString() { | |||
return "RuleDescriptionSectionContextDto[" + | |||
"key='" + key + '\'' + | |||
", displayName='" + displayName + '\'' + | |||
']'; | |||
} | |||
} |
@@ -20,20 +20,27 @@ | |||
package org.sonar.db.rule; | |||
import java.util.StringJoiner; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import static org.sonar.api.utils.Preconditions.checkArgument; | |||
public class RuleDescriptionSectionDto { | |||
public static final String DEFAULT_KEY = "default"; | |||
private final String uuid; | |||
private final String key; | |||
private final String content; | |||
private String uuid; | |||
private String key; | |||
private String content; | |||
private RuleDescriptionSectionContextDto context; | |||
private RuleDescriptionSectionDto(String uuid, String key, String content) { | |||
private RuleDescriptionSectionDto() { | |||
} | |||
private RuleDescriptionSectionDto(String uuid, String key, String content, @Nullable RuleDescriptionSectionContextDto context) { | |||
this.uuid = uuid; | |||
this.key = key; | |||
this.content = content; | |||
this.context = context; | |||
} | |||
public String getUuid() { | |||
@@ -48,6 +55,11 @@ public class RuleDescriptionSectionDto { | |||
return content; | |||
} | |||
@CheckForNull | |||
public RuleDescriptionSectionContextDto getContext() { | |||
return context; | |||
} | |||
public static RuleDescriptionSectionDto createDefaultRuleDescriptionSection(String uuid, String description) { | |||
return RuleDescriptionSectionDto.builder() | |||
.setDefault() | |||
@@ -70,13 +82,17 @@ public class RuleDescriptionSectionDto { | |||
.add("uuid='" + uuid + "'") | |||
.add("key='" + key + "'") | |||
.add("content='" + content + "'") | |||
.add("context='" + context + "'") | |||
.toString(); | |||
} | |||
public static final class RuleDescriptionSectionDtoBuilder { | |||
private String uuid; | |||
private String key = null; | |||
private String content; | |||
private RuleDescriptionSectionContextDto context; | |||
private RuleDescriptionSectionDtoBuilder() { | |||
} | |||
@@ -103,8 +119,14 @@ public class RuleDescriptionSectionDto { | |||
return this; | |||
} | |||
public RuleDescriptionSectionDtoBuilder context(@Nullable RuleDescriptionSectionContextDto context) { | |||
this.context = context; | |||
return this; | |||
} | |||
public RuleDescriptionSectionDto build() { | |||
return new RuleDescriptionSectionDto(uuid, key, content); | |||
return new RuleDescriptionSectionDto(uuid, key, content, context); | |||
} | |||
} | |||
} |
@@ -19,11 +19,13 @@ | |||
*/ | |||
package org.sonar.db.rule; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.base.Splitter; | |||
import com.google.common.collect.ImmutableSet; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Objects; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
@@ -38,10 +40,13 @@ import org.sonar.api.rules.RuleType; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static java.util.Collections.emptySet; | |||
import static java.util.Optional.ofNullable; | |||
import static org.sonar.db.rule.RuleDescriptionSectionDto.DEFAULT_KEY; | |||
public class RuleDto { | |||
static final String ERROR_MESSAGE_SECTION_ALREADY_EXISTS = "A section with key '%s' and context key '%s' already exists"; | |||
public enum Format { | |||
HTML, MARKDOWN | |||
} | |||
@@ -56,7 +61,7 @@ public class RuleDto { | |||
private String repositoryKey = null; | |||
private String ruleKey = null; | |||
private Set<RuleDescriptionSectionDto> ruleDescriptionSectionDtos = new HashSet<>(); | |||
private final Set<RuleDescriptionSectionDto> ruleDescriptionSectionDtos = new HashSet<>(); | |||
/** | |||
* Description format can be null on external rule, otherwise it should never be null | |||
@@ -198,7 +203,10 @@ public class RuleDto { | |||
@CheckForNull | |||
public RuleDescriptionSectionDto getDefaultRuleDescriptionSection() { | |||
return findExistingSectionWithSameKey(DEFAULT_KEY).orElse(null); | |||
return ruleDescriptionSectionDtos.stream() | |||
.filter(ruleDesc -> ruleDesc.getKey().equals(DEFAULT_KEY)) | |||
.findAny() | |||
.orElse(null); | |||
} | |||
public RuleDto replaceRuleDescriptionSectionDtos(Collection<RuleDescriptionSectionDto> ruleDescriptionSectionDtos) { | |||
@@ -207,26 +215,33 @@ public class RuleDto { | |||
return this; | |||
} | |||
@VisibleForTesting | |||
public RuleDto replaceRuleDescriptionSectionDtos(RuleDescriptionSectionDto ruleDescriptionSectionDto) { | |||
replaceRuleDescriptionSectionDtos(List.of(ruleDescriptionSectionDto)); | |||
return this; | |||
} | |||
public RuleDto addRuleDescriptionSectionDto(RuleDescriptionSectionDto ruleDescriptionSectionDto) { | |||
checkArgument(sectionWithSameKeyShouldNotExist(ruleDescriptionSectionDto), | |||
"A section with key %s already exists", ruleDescriptionSectionDto.getKey()); | |||
checkArgument(!hasDescriptionSectionWithSameKeyAndContext(ruleDescriptionSectionDto), | |||
ERROR_MESSAGE_SECTION_ALREADY_EXISTS, ruleDescriptionSectionDto.getKey(), | |||
Optional.ofNullable(ruleDescriptionSectionDto.getContext()).map(RuleDescriptionSectionContextDto::getKey).orElse(null)); | |||
ruleDescriptionSectionDtos.add(ruleDescriptionSectionDto); | |||
return this; | |||
} | |||
private boolean sectionWithSameKeyShouldNotExist(RuleDescriptionSectionDto ruleDescriptionSectionDto) { | |||
return findExistingSectionWithSameKey(ruleDescriptionSectionDto.getKey()).isEmpty(); | |||
private boolean hasDescriptionSectionWithSameKeyAndContext(RuleDescriptionSectionDto ruleDescriptionSectionDto) { | |||
return ruleDescriptionSectionDtos.stream() | |||
.anyMatch(ruleDesc -> hasSameKeyAndContextKey(ruleDescriptionSectionDto, ruleDesc)); | |||
} | |||
public RuleDto addOrReplaceRuleDescriptionSectionDto(RuleDescriptionSectionDto ruleDescriptionSectionDto) { | |||
Optional<RuleDescriptionSectionDto> existingSectionWithSameKey = findExistingSectionWithSameKey(ruleDescriptionSectionDto.getKey()); | |||
existingSectionWithSameKey.ifPresent(ruleDescriptionSectionDtos::remove); | |||
ruleDescriptionSectionDtos.add(ruleDescriptionSectionDto); | |||
return this; | |||
} | |||
private static boolean hasSameKeyAndContextKey(RuleDescriptionSectionDto ruleDescriptionSectionDto, RuleDescriptionSectionDto other) { | |||
if (!ruleDescriptionSectionDto.getKey().equals(other.getKey())){ | |||
return false; | |||
} | |||
private Optional<RuleDescriptionSectionDto> findExistingSectionWithSameKey(String ruleDescriptionSectionKey) { | |||
return ruleDescriptionSectionDtos.stream().filter(section -> section.getKey().equals(ruleDescriptionSectionKey)).findAny(); | |||
String contextKey = ofNullable(ruleDescriptionSectionDto.getContext()).map(RuleDescriptionSectionContextDto::getKey).orElse(null); | |||
String otherContextKey = ofNullable(other.getContext()).map(RuleDescriptionSectionContextDto::getKey).orElse(null); | |||
return Objects.equals(contextKey, otherContextKey); | |||
} | |||
@CheckForNull |
@@ -50,6 +50,8 @@ | |||
rds.content as "rds_content", | |||
rds.uuid as "rds_uuid", | |||
rds.kee as "rds_kee", | |||
rds.context_key as "rds_contextKey", | |||
rds.context_display_name as "rds_contextDisplayName", | |||
r.uuid as "r_uuid", | |||
<include refid="ruleColumns"/> | |||
@@ -68,7 +70,14 @@ | |||
<collection property="ruleDescriptionSectionDtos" ofType="org.sonar.db.rule.RuleDescriptionSectionDto" autoMapping="true" | |||
columnPrefix="rds_"> | |||
<id property="uuid" column="uuid"/> | |||
<result property="key" column="kee"/> | |||
<association property="context" javaType="org.sonar.db.rule.RuleDescriptionSectionContextDto" autoMapping="false"> | |||
<constructor> | |||
<arg column="contextKey" javaType="String"/> | |||
<arg column="contextDisplayName" javaType="String"/> | |||
</constructor> | |||
</association> | |||
</collection> | |||
</resultMap> | |||
@@ -195,7 +204,8 @@ | |||
<foreach collection="languages" item="language" separator="," open="(" close=")">#{language, jdbcType=VARCHAR}</foreach> | |||
</select> | |||
<select id="selectByLanguage" parameterType="String" resultType="org.sonar.db.rule.RuleDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY"> | |||
<select id="selectByLanguage" parameterType="String" resultType="org.sonar.db.rule.RuleDto" fetchSize="${_scrollFetchSize}" | |||
resultSetType="FORWARD_ONLY"> | |||
select | |||
r.uuid as uuid, | |||
<include refid="ruleColumns"/> | |||
@@ -212,13 +222,17 @@ | |||
uuid, | |||
rule_uuid, | |||
kee, | |||
content | |||
content, | |||
context_key, | |||
context_display_name | |||
) | |||
values ( | |||
#{dto.uuid,jdbcType=VARCHAR}, | |||
#{ruleUuid,jdbcType=VARCHAR}, | |||
#{dto.key,jdbcType=VARCHAR}, | |||
#{dto.content,jdbcType=VARCHAR} | |||
#{dto.content,jdbcType=VARCHAR}, | |||
#{dto.context.key,jdbcType=VARCHAR}, | |||
#{dto.context.displayName,jdbcType=VARCHAR} | |||
) | |||
</insert> | |||
@@ -83,7 +83,6 @@ public class RuleDaoTest { | |||
} | |||
@Test | |||
public void selectByUuid() { | |||
RuleDto ruleDto = db.rules().insert(); | |||
@@ -202,6 +201,7 @@ public class RuleDaoTest { | |||
} | |||
private void assertEquals(RuleDto actual, RuleDto expected) { | |||
assertThat(actual.getUuid()).isEqualTo(expected.getUuid()); | |||
assertThat(actual.getRepositoryKey()).isEqualTo(expected.getRepositoryKey()); | |||
assertThat(actual.getRuleKey()).isEqualTo(expected.getRuleKey()); | |||
@@ -236,7 +236,7 @@ public class RuleDaoTest { | |||
assertThat(actual.getAdHocSeverity()).isEqualTo(expected.getAdHocSeverity()); | |||
assertThat(actual.getAdHocType()).isEqualTo(expected.getAdHocType()); | |||
assertThat(actual.getRuleDescriptionSectionDtos()).usingRecursiveFieldByFieldElementComparator() | |||
.isEqualTo(expected.getRuleDescriptionSectionDtos()); | |||
.containsExactlyInAnyOrderElementsOf(expected.getRuleDescriptionSectionDtos()); | |||
} | |||
@Test | |||
@@ -401,6 +401,35 @@ public class RuleDaoTest { | |||
RuleQuery.create().withKey("S001").withConfigKey("S1").withRepositoryKey("java"))).hasSize(1); | |||
} | |||
@Test | |||
public void insert_rule_with_description_section_context() { | |||
RuleDto rule = db.rules().insert(r -> r | |||
.addRuleDescriptionSectionDto(createDescriptionSectionWithContext("how_to_fix", "spring", "Spring"))); | |||
Optional<RuleDto> ruleDto = underTest.selectByUuid(rule.getUuid(), db.getSession()); | |||
assertEquals(ruleDto.get(), rule); | |||
} | |||
@NotNull | |||
private static RuleDescriptionSectionDto createDescriptionSectionWithContext(String key, String contextKey, String contextDisplayName) { | |||
return RuleDescriptionSectionDto.builder() | |||
.uuid(UuidFactoryFast.getInstance().create()) | |||
.content("content") | |||
.key(key) | |||
.context(RuleDescriptionSectionContextDto.of(contextKey, contextDisplayName)) | |||
.build(); | |||
} | |||
@Test | |||
public void insert_rule_with_different_section_context() { | |||
RuleDto rule = db.rules().insert(r -> r | |||
.addRuleDescriptionSectionDto(createDescriptionSectionWithContext("how_to_fix", "spring", "Spring")) | |||
.addRuleDescriptionSectionDto(createDescriptionSectionWithContext("how_to_fix", "myBatis", "My Batis"))); | |||
Optional<RuleDto> ruleDto = underTest.selectByUuid(rule.getUuid(), db.getSession()); | |||
assertEquals(ruleDto.get(), rule); | |||
} | |||
@Test | |||
public void insert() { | |||
RuleDescriptionSectionDto sectionDto = createDefaultRuleDescriptionSection(); | |||
@@ -552,7 +581,29 @@ public class RuleDaoTest { | |||
.content(randomAlphanumeric(1000)) | |||
.build(); | |||
rule.addOrReplaceRuleDescriptionSectionDto(replacingSection); | |||
rule.replaceRuleDescriptionSectionDtos(replacingSection); | |||
underTest.update(db.getSession(), rule); | |||
db.getSession().commit(); | |||
RuleDto ruleDto = underTest.selectOrFailByKey(db.getSession(), RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey())); | |||
assertThat(ruleDto.getRuleDescriptionSectionDtos()) | |||
.usingRecursiveFieldByFieldElementComparator() | |||
.containsOnly(replacingSection); | |||
} | |||
@Test | |||
public void update_rule_sections_replaces_section_with_context() { | |||
RuleDto rule = db.rules().insert(); | |||
RuleDescriptionSectionDto existingSection = rule.getRuleDescriptionSectionDtos().iterator().next(); | |||
RuleDescriptionSectionDto replacingSection = RuleDescriptionSectionDto.builder() | |||
.uuid(randomAlphanumeric(20)) | |||
.key(existingSection.getKey()) | |||
.content(randomAlphanumeric(1000)) | |||
.build(); | |||
rule.replaceRuleDescriptionSectionDtos(replacingSection); | |||
underTest.update(db.getSession(), rule); | |||
db.getSession().commit(); | |||
@@ -816,6 +867,7 @@ public class RuleDaoTest { | |||
.key("DESC") | |||
.uuid("uuid") | |||
.content("my description") | |||
.context(RuleDescriptionSectionContextDto.of("context key", "context display name")) | |||
.build(); | |||
RuleDto r1 = db.rules().insert(r -> { | |||
r.addRuleDescriptionSectionDto(ruleDescriptionSectionDto); | |||
@@ -838,8 +890,8 @@ public class RuleDaoTest { | |||
assertThat(firstRule.getRuleDescriptionSectionsDtos().stream() | |||
.filter(s -> s.getKey().equals(ruleDescriptionSectionDto.getKey())) | |||
.collect(MoreCollectors.onlyElement())) | |||
.usingRecursiveComparison() | |||
.isEqualTo(ruleDescriptionSectionDto); | |||
.usingRecursiveComparison() | |||
.isEqualTo(ruleDescriptionSectionDto); | |||
assertThat(firstRule.getDescriptionFormat()).isEqualTo(r1.getDescriptionFormat()); | |||
assertThat(firstRule.getSeverity()).isEqualTo(r1.getSeverity()); | |||
assertThat(firstRule.getStatus()).isEqualTo(r1.getStatus()); |
@@ -0,0 +1,97 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2022 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.rule; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | |||
import static org.sonar.db.rule.RuleDescriptionSectionContextDto.DISPLAY_NAME_MUST_BE_SET_ERROR; | |||
import static org.sonar.db.rule.RuleDescriptionSectionContextDto.KEY_MUST_BE_SET_ERROR; | |||
public class RuleDescriptionSectionContextDtoTest { | |||
private static final String CONTEXT_KEY = "key"; | |||
private static final String CONTEXT_DISPLAY_NAME = "displayName"; | |||
@Test | |||
public void check_of_instantiate_object() { | |||
RuleDescriptionSectionContextDto context = RuleDescriptionSectionContextDto.of(CONTEXT_KEY, CONTEXT_DISPLAY_NAME); | |||
assertThat(context).extracting(RuleDescriptionSectionContextDto::getKey, | |||
RuleDescriptionSectionContextDto::getDisplayName).contains(CONTEXT_KEY, CONTEXT_DISPLAY_NAME); | |||
} | |||
@Test | |||
public void check_of_with_key_is_empty() { | |||
assertThatThrownBy(() -> RuleDescriptionSectionContextDto.of("", CONTEXT_DISPLAY_NAME)) | |||
.isInstanceOf(IllegalArgumentException.class) | |||
.hasMessage(KEY_MUST_BE_SET_ERROR); | |||
} | |||
@Test | |||
public void check_of_with_display_name_is_empty() { | |||
assertThatThrownBy(() -> RuleDescriptionSectionContextDto.of(CONTEXT_KEY, "")) | |||
.isInstanceOf(IllegalArgumentException.class) | |||
.hasMessage(DISPLAY_NAME_MUST_BE_SET_ERROR); | |||
} | |||
@Test | |||
public void equals_with_equals_objects_should_return_true() { | |||
RuleDescriptionSectionContextDto context1 = RuleDescriptionSectionContextDto.of(CONTEXT_KEY, CONTEXT_DISPLAY_NAME); | |||
RuleDescriptionSectionContextDto context2 = RuleDescriptionSectionContextDto.of(CONTEXT_KEY, CONTEXT_DISPLAY_NAME); | |||
assertThat(context1).isEqualTo(context2); | |||
} | |||
@Test | |||
public void equals_with_same_objects_should_return_true() { | |||
RuleDescriptionSectionContextDto context1 = RuleDescriptionSectionContextDto.of(CONTEXT_KEY, CONTEXT_DISPLAY_NAME); | |||
assertThat(context1).isEqualTo(context1); | |||
} | |||
@Test | |||
public void equals_with_one_null_objet_should_return_false() { | |||
RuleDescriptionSectionContextDto context1 = RuleDescriptionSectionContextDto.of(CONTEXT_KEY, CONTEXT_DISPLAY_NAME); | |||
assertThat(context1).isNotEqualTo(null); | |||
} | |||
@Test | |||
public void equals_with_different_display_names_should_return_false() { | |||
RuleDescriptionSectionContextDto context1 = RuleDescriptionSectionContextDto.of(CONTEXT_KEY, CONTEXT_DISPLAY_NAME); | |||
RuleDescriptionSectionContextDto context2 = RuleDescriptionSectionContextDto.of(CONTEXT_KEY, CONTEXT_DISPLAY_NAME + "2"); | |||
assertThat(context1).isNotEqualTo(context2); | |||
} | |||
@Test | |||
public void equals_with_different_context_keys_should_return_false() { | |||
RuleDescriptionSectionContextDto context1 = RuleDescriptionSectionContextDto.of(CONTEXT_KEY, CONTEXT_DISPLAY_NAME); | |||
RuleDescriptionSectionContextDto context2 = RuleDescriptionSectionContextDto.of(CONTEXT_KEY + "2", CONTEXT_DISPLAY_NAME); | |||
assertThat(context1).isNotEqualTo(context2); | |||
} | |||
@Test | |||
public void hashcode_with_equals_objects_should_return_same_hash() { | |||
RuleDescriptionSectionContextDto context1 = RuleDescriptionSectionContextDto.of(CONTEXT_KEY, CONTEXT_DISPLAY_NAME); | |||
RuleDescriptionSectionContextDto context2 = RuleDescriptionSectionContextDto.of(CONTEXT_KEY, CONTEXT_DISPLAY_NAME); | |||
assertThat(context1).hasSameHashCodeAs(context2); | |||
} | |||
} |
@@ -28,6 +28,7 @@ public class RuleDescriptionSectionDtoTest { | |||
private static final RuleDescriptionSectionDto SECTION = RuleDescriptionSectionDto.builder() | |||
.key("key") | |||
.uuid("uuid") | |||
.context(RuleDescriptionSectionContextDto.of("key", "displayName")) | |||
.content("desc").build(); | |||
@@ -49,9 +50,9 @@ public class RuleDescriptionSectionDtoTest { | |||
.withMessage("Only one of setDefault and key methods can be called"); | |||
} | |||
@Test | |||
public void testToString() { | |||
Assertions.assertThat(SECTION).hasToString("RuleDescriptionSectionDto[uuid='uuid', key='key', content='desc']"); | |||
Assertions.assertThat(SECTION) | |||
.hasToString("RuleDescriptionSectionDto[uuid='uuid', key='key', content='desc', context='RuleDescriptionSectionContextDto[key='key', displayName='displayName']']"); | |||
} | |||
} |
@@ -23,17 +23,21 @@ import com.google.common.collect.ImmutableSet; | |||
import java.util.Collections; | |||
import java.util.Set; | |||
import java.util.TreeSet; | |||
import org.jetbrains.annotations.NotNull; | |||
import org.junit.Test; | |||
import org.sonar.core.util.Uuids; | |||
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; | |||
import static org.apache.commons.lang.StringUtils.repeat; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | |||
import static org.sonar.db.rule.RuleDto.ERROR_MESSAGE_SECTION_ALREADY_EXISTS; | |||
import static org.sonar.db.rule.RuleTesting.newRule; | |||
public class RuleDtoTest { | |||
public static final String SECTION_KEY = "section key"; | |||
@Test | |||
public void fail_if_key_is_too_long() { | |||
assertThatThrownBy(() -> new RuleDto().setRuleKey(repeat("x", 250))) | |||
@@ -115,4 +119,63 @@ public class RuleDtoTest { | |||
.isNotEqualTo(newRule().setRuleKey(dto.getRuleKey()).setUuid(Uuids.createFast()).hashCode()) | |||
.isNotEqualTo(newRule().setUuid(Uuids.createFast()).hashCode()); | |||
} | |||
@Test | |||
public void add_rule_description_section_same_key_should_throw_error() { | |||
RuleDto dto = new RuleDto(); | |||
RuleDescriptionSectionDto section1 = createSection(SECTION_KEY); | |||
dto.addRuleDescriptionSectionDto(section1); | |||
RuleDescriptionSectionDto section2 = createSection(SECTION_KEY); | |||
assertThatThrownBy(() -> dto.addRuleDescriptionSectionDto(section2)) | |||
.isInstanceOf(IllegalArgumentException.class) | |||
.hasMessage(String.format(ERROR_MESSAGE_SECTION_ALREADY_EXISTS, SECTION_KEY, "null")); | |||
} | |||
@Test | |||
public void add_rule_description_section_with_different_context() { | |||
RuleDto dto = new RuleDto(); | |||
RuleDescriptionSectionDto section1 = createSection(RuleDtoTest.SECTION_KEY, "context key 1", "context display Name 1"); | |||
dto.addRuleDescriptionSectionDto(section1); | |||
RuleDescriptionSectionDto section2 = createSection(RuleDtoTest.SECTION_KEY, "context key 2", "context display Name 2"); | |||
dto.addRuleDescriptionSectionDto(section2); | |||
assertThat(dto.getRuleDescriptionSectionDtos()) | |||
.usingRecursiveFieldByFieldElementComparator() | |||
.containsExactlyInAnyOrder(section1, section2); | |||
} | |||
@Test | |||
public void add_rule_description_section_with_same_section_and_context_should_throw_error() { | |||
RuleDto dto = new RuleDto(); | |||
String contextKey = randomAlphanumeric(50); | |||
String displayName = randomAlphanumeric(50); | |||
RuleDescriptionSectionDto section1 = createSection(SECTION_KEY, contextKey, displayName); | |||
dto.addRuleDescriptionSectionDto(section1); | |||
RuleDescriptionSectionDto section2 = createSection(SECTION_KEY, contextKey, displayName); | |||
assertThatThrownBy(() -> dto.addRuleDescriptionSectionDto(section2)) | |||
.isInstanceOf(IllegalArgumentException.class) | |||
.hasMessage(String.format(ERROR_MESSAGE_SECTION_ALREADY_EXISTS, SECTION_KEY, contextKey)); | |||
} | |||
@NotNull | |||
private static RuleDescriptionSectionDto createSection(String section_key, String contextKey, String contextDisplayName) { | |||
return RuleDescriptionSectionDto.builder() | |||
.key(section_key) | |||
.context(RuleDescriptionSectionContextDto.of(contextKey, contextDisplayName)) | |||
.build(); | |||
} | |||
@NotNull | |||
private static RuleDescriptionSectionDto createSection(String section_key) { | |||
return RuleDescriptionSectionDto.builder() | |||
.key(section_key) | |||
.build(); | |||
} | |||
} |
@@ -216,20 +216,20 @@ public class RuleIndexTest { | |||
// otherwise the generated random values may raise false-positives | |||
RuleDto rule1 = createJavaRule(rule -> rule.setRuleKey("123") | |||
.setName("rule 123") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "My great rule CWE-123 which makes your code 1000 times better!"))); | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "My great rule CWE-123 which makes your code 1000 times better!"))); | |||
RuleDto rule2 = createJavaRule(rule -> rule.setRuleKey("124") | |||
.setName("rule 124") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Another great and shiny rule CWE-124"))); | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "Another great and shiny rule CWE-124"))); | |||
RuleDto rule3 = createJavaRule(rule -> rule.setRuleKey("1000") | |||
.setName("rule 1000") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Another great rule CWE-1000"))); | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "Another great rule CWE-1000"))); | |||
RuleDto rule4 = createJavaRule(rule -> rule.setRuleKey("404") | |||
.setName("rule 404") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), | |||
"<h1>HTML-Geeks</h1><p style=\"color:blue\">special formatting!</p><table><tr><td>inside</td><td>tables</td></tr></table>"))); | |||
RuleDto rule5 = createJavaRule(rule -> rule.setRuleKey("405") | |||
.setName("rule 405") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "internationalization missunderstandings alsdkjfnadklsjfnadkdfnsksdjfn"))); | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "internationalization missunderstandings alsdkjfnadklsjfnadkdfnsksdjfn"))); | |||
index(); | |||
// partial match at word boundary |
@@ -139,7 +139,8 @@ public class RuleIndexerTest { | |||
public void index_long_rule_description() { | |||
String description = IntStream.range(0, 100000).map(i -> i % 100).mapToObj(Integer::toString).collect(joining(" ")); | |||
RuleDescriptionSectionDto ruleDescriptionSectionDto = createDefaultRuleDescriptionSection(uuidFactory.create(), description); | |||
RuleDto rule = dbTester.rules().insert(r -> r.addOrReplaceRuleDescriptionSectionDto(ruleDescriptionSectionDto)); | |||
RuleDto rule = dbTester.rules().insert(r -> r.replaceRuleDescriptionSectionDtos(ruleDescriptionSectionDto)); | |||
underTest.commitAndIndex(dbTester.getSession(), rule.getUuid()); | |||
assertThat(es.countDocuments(TYPE_RULE)).isOne(); | |||
@@ -148,7 +149,7 @@ public class RuleIndexerTest { | |||
@Test | |||
public void index_long_rule_with_several_sections() { | |||
RuleDto rule = dbTester.rules().insert(r -> { | |||
r.addOrReplaceRuleDescriptionSectionDto(RULE_DESCRIPTION_SECTION_DTO); | |||
r.replaceRuleDescriptionSectionDtos(RULE_DESCRIPTION_SECTION_DTO); | |||
r.addRuleDescriptionSectionDto(RULE_DESCRIPTION_SECTION_DTO2); | |||
}); | |||
@@ -166,7 +167,7 @@ public class RuleIndexerTest { | |||
public void index_long_rule_with_section_in_markdown() { | |||
RuleDto rule = dbTester.rules().insert(r -> { | |||
r.setDescriptionFormat(RuleDto.Format.MARKDOWN); | |||
r.addOrReplaceRuleDescriptionSectionDto(RULE_DESCRIPTION_SECTION_DTO); | |||
r.replaceRuleDescriptionSectionDtos(RULE_DESCRIPTION_SECTION_DTO); | |||
}); | |||
underTest.commitAndIndex(dbTester.getSession(), rule.getUuid()); |
@@ -517,15 +517,17 @@ public class RegisterRules implements Startable { | |||
} | |||
private static boolean ruleDescriptionSectionsUnchanged(RuleDto ruleDto, Set<RuleDescriptionSectionDto> newRuleDescriptionSectionDtos) { | |||
Map<String, String> oldKeysToSections = toMap(ruleDto.getRuleDescriptionSectionDtos()); | |||
Map<String, String> newKeysToSections = toMap(newRuleDescriptionSectionDtos); | |||
return oldKeysToSections.equals(newKeysToSections); | |||
if (ruleDto.getRuleDescriptionSectionDtos().size() != newRuleDescriptionSectionDtos.size()) { | |||
return false; | |||
} | |||
return ruleDto.getRuleDescriptionSectionDtos().stream() | |||
.allMatch(sectionDto -> contains(newRuleDescriptionSectionDtos, sectionDto)); | |||
} | |||
private static Map<String, String> toMap(Set<RuleDescriptionSectionDto> ruleDto) { | |||
return ruleDto | |||
.stream() | |||
.collect(Collectors.toMap(RuleDescriptionSectionDto::getKey, RuleDescriptionSectionDto::getContent)); | |||
private static boolean contains(Set<RuleDescriptionSectionDto> sectionDtos, RuleDescriptionSectionDto sectionDto) { | |||
return sectionDtos.stream() | |||
.filter(s -> s.getKey().equals(sectionDto.getKey()) && s.getContent().equals(sectionDto.getContent())) | |||
.anyMatch(s -> Objects.equals(s.getContext(), sectionDto.getContext())); | |||
} | |||
private static boolean mergeDebtDefinitions(RulesDefinition.Rule def, RuleDto dto) { |
@@ -71,7 +71,7 @@ public class QualityProfileChangeEventServiceImplTest { | |||
.setRepositoryKey("repo") | |||
.setRuleKey("ruleKey") | |||
.setDescriptionFormat(RuleDto.Format.MARKDOWN) | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("uuid", "<div>line1\nline2</div>")); | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection("uuid", "<div>line1\nline2</div>")); | |||
db.rules().insert(rule1); | |||
ActiveRuleDto activeRuleDto = ActiveRuleDto.createFor(qualityProfileDto, rule1); |
@@ -142,7 +142,7 @@ public class RuleUpdater { | |||
} | |||
RuleDescriptionSectionDto descriptionSectionDto = createDefaultRuleDescriptionSection(uuidFactory.create(), description); | |||
rule.setDescriptionFormat(RuleDto.Format.MARKDOWN); | |||
rule.addOrReplaceRuleDescriptionSectionDto(descriptionSectionDto); | |||
rule.replaceRuleDescriptionSectionDtos(List.of(descriptionSectionDto)); | |||
} | |||
private static void updateSeverity(RuleUpdate update, RuleDto rule) { |
@@ -1489,7 +1489,7 @@ public class SearchActionTest { | |||
private RuleDto newIssueRule() { | |||
RuleDto rule = RuleTesting.newXooX1() | |||
.setName("Rule name") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Rule desc")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "Rule desc")) | |||
.setStatus(RuleStatus.READY); | |||
db.rules().insert(rule); | |||
return rule; | |||
@@ -1498,7 +1498,7 @@ public class SearchActionTest { | |||
private RuleDto newHotspotRule() { | |||
RuleDto rule = RuleTesting.newXooX2() | |||
.setName("Rule name") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Rule desc")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "Rule desc")) | |||
.setStatus(RuleStatus.READY) | |||
.setType(SECURITY_HOTSPOT_VALUE); | |||
db.rules().insert(rule); |
@@ -145,7 +145,7 @@ public class QProfileBackuperImplTest { | |||
RuleDto templateRule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto | |||
.setIsTemplate(true)); | |||
RuleDto rule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(UuidFactoryFast.getInstance().create(), "custom rule description")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(UuidFactoryFast.getInstance().create(), "custom rule description")) | |||
.setName("custom rule name") | |||
.setStatus(RuleStatus.READY) | |||
.setTemplateUuid(templateRule.getUuid())); | |||
@@ -404,7 +404,7 @@ public class QProfileBackuperImplTest { | |||
RuleDto templateRule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto | |||
.setIsTemplate(true)); | |||
RuleDto rule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(UuidFactoryFast.getInstance().create(), "custom rule description")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(UuidFactoryFast.getInstance().create(), "custom rule description")) | |||
.setName("custom rule name") | |||
.setStatus(RuleStatus.READY) | |||
.setTemplateUuid(templateRule.getUuid())); |
@@ -323,7 +323,7 @@ public class RuleCreatorTest { | |||
.setRuleKey(key) | |||
.setStatus(RuleStatus.REMOVED) | |||
.setName("Old name") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description")) | |||
.setDescriptionFormat(Format.MARKDOWN) | |||
.setSeverity(Severity.INFO); | |||
dbTester.rules().insert(rule); | |||
@@ -363,7 +363,7 @@ public class RuleCreatorTest { | |||
.setRuleKey(key) | |||
.setStatus(RuleStatus.REMOVED) | |||
.setName("Old name") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description")) | |||
.setSeverity(Severity.INFO); | |||
dbTester.rules().insert(rule); | |||
dbTester.rules().insertRuleParam(rule, param -> param.setDefaultValue("a.*")); |
@@ -338,7 +338,7 @@ public class RuleUpdaterTest { | |||
// Create custom rule | |||
RuleDto customRule = newCustomRule(templateRule) | |||
.setName("Old name") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description")) | |||
.setSeverity(Severity.MINOR) | |||
.setStatus(RuleStatus.BETA) | |||
; | |||
@@ -386,7 +386,7 @@ public class RuleUpdaterTest { | |||
// Create custom rule | |||
RuleDto customRule = newCustomRule(templateRule) | |||
.setName("Old name") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description")) | |||
.setSeverity(Severity.MINOR) | |||
.setStatus(RuleStatus.BETA); | |||
db.rules().insert(customRule); |
@@ -140,7 +140,7 @@ public class CreateActionTest { | |||
.setRuleKey("MY_CUSTOM") | |||
.setStatus(RuleStatus.REMOVED) | |||
.setName("My custom rule") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Description")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "Description")) | |||
.setDescriptionFormat(RuleDto.Format.MARKDOWN) | |||
.setSeverity(Severity.MAJOR); | |||
db.rules().insert(customRule); |
@@ -84,6 +84,7 @@ import static org.mockito.Mockito.doReturn; | |||
import static org.mockito.Mockito.mock; | |||
import static org.sonar.api.rule.Severity.BLOCKER; | |||
import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; | |||
import static org.sonar.db.rule.RuleTesting.newRuleWithoutDescriptionSection; | |||
import static org.sonar.db.rule.RuleTesting.setSystemTags; | |||
import static org.sonar.db.rule.RuleTesting.setTags; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION; | |||
@@ -239,7 +240,7 @@ public class SearchActionTest { | |||
RuleDto rule1 = db.rules() | |||
.insert( | |||
r1 -> r1 | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "This is the <bold>best</bold> rule now&for<b>ever</b>")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "This is the <bold>best</bold> rule now&for<b>ever</b>")) | |||
.setNoteUserUuid(null)); | |||
db.rules().insert(r1 -> r1.setName("Some other stuff").setNoteUserUuid(null)); | |||
indexRules(); | |||
@@ -251,10 +252,10 @@ public class SearchActionTest { | |||
@Test | |||
public void filter_by_rule_name_or_descriptions_requires_all_words_to_match_anywhere() { | |||
RuleDto rule1 = db.rules().insert(r1 -> r1.setName("Best rule ever").setNoteUserUuid(null) | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "This is a good rule"))); | |||
db.rules().insert(r1 -> r1.setName("Another thing").setNoteUserUuid(null) | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Another thing"))); | |||
RuleDto rule1 = db.rules().insert(newRuleWithoutDescriptionSection().setName("Best rule ever").setNoteUserUuid(null) | |||
.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "This is a good rule"))); | |||
db.rules().insert(newRuleWithoutDescriptionSection().setName("Another thing").setNoteUserUuid(null) | |||
.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Another thing"))); | |||
indexRules(); | |||
verify(r -> r.setParam("q", "Best good"), rule1); |
@@ -297,7 +297,7 @@ public class ShowActionTest { | |||
db.rules().insert(templateRule); | |||
// Custom rule | |||
RuleDto customRule = newCustomRule(templateRule) | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "<div>line1\nline2</div>")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "<div>line1\nline2</div>")) | |||
.setDescriptionFormat(MARKDOWN) | |||
.setNoteUserUuid(userDto.getUuid()); | |||
db.rules().insert(customRule); | |||
@@ -435,7 +435,7 @@ public class ShowActionTest { | |||
.setIsExternal(true) | |||
.setIsAdHoc(true) | |||
.setName("predefined name") | |||
.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "<div>predefined desc</div>")) | |||
.replaceRuleDescriptionSectionDtos(createDefaultRuleDescriptionSection(uuidFactory.create(), "<div>predefined desc</div>")) | |||
.setSeverity(Severity.BLOCKER) | |||
.setType(RuleType.VULNERABILITY) | |||
.setAdHocName("adhoc name") |
@@ -113,7 +113,7 @@ public class UpdateActionTest { | |||
RuleDto customRule = db.rules().insert( | |||
r -> r.setRuleKey(RuleKey.of("java", "MY_CUSTOM")), | |||
r -> r.setName("Old custom"), | |||
r -> r.addOrReplaceRuleDescriptionSectionDto(createRuleDescriptionSectionDto()), | |||
r -> r.replaceRuleDescriptionSectionDtos(createRuleDescriptionSectionDto()), | |||
r -> r.setSeverity(Severity.MINOR), | |||
r -> r.setStatus(RuleStatus.BETA), | |||
r -> r.setTemplateUuid(templateRule.getUuid()), | |||
@@ -249,7 +249,7 @@ public class UpdateActionTest { | |||
RuleDto customRule = db.rules().insert( | |||
r -> r.setRuleKey(RuleKey.of("java", "MY_CUSTOM")), | |||
r -> r.setName("Old custom"), | |||
r -> r.addOrReplaceRuleDescriptionSectionDto(createRuleDescriptionSectionDto()), | |||
r -> r.replaceRuleDescriptionSectionDtos(createRuleDescriptionSectionDto()), | |||
r -> r.setTemplateUuid(templateRule.getUuid()), | |||
r -> r.setCreatedAt(PAST), | |||
r -> r.setUpdatedAt(PAST)); |