@@ -168,6 +168,16 @@ public class BulkUpdateKeyActionTest { | |||
callByKey(MY_PROJECT_KEY, FROM, "my?"); | |||
} | |||
@Test | |||
public void fail_to_dry_bulk_update_with_invalid_new_key() { | |||
insertMyProject(); | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("Malformed key for 'my?project'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit."); | |||
callDryRunByKey(MY_PROJECT_KEY, FROM, "my?"); | |||
} | |||
@Test | |||
public void fail_to_bulk_update_if_not_project_or_module() { | |||
ComponentDto project = insertMyProject(); |
@@ -26,6 +26,8 @@ import org.sonar.api.resources.Project; | |||
import org.sonar.api.resources.Resource; | |||
import org.sonar.api.resources.Scopes; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
public final class ComponentKeys { | |||
public static final int MAX_COMPONENT_KEY_LENGTH = 400; | |||
@@ -96,6 +98,15 @@ public final class ComponentKeys { | |||
return keyCandidate.matches(VALID_MODULE_KEY_REGEXP); | |||
} | |||
/** | |||
* Checks if given parameter is valid for a project/module following {@link #isValidModuleKey(String)} contract. | |||
* | |||
* @throws IllegalArgumentException if the format is incorrect | |||
*/ | |||
public static void checkModuleKey(String keyCandidate) { | |||
checkArgument(isValidModuleKey(keyCandidate), "Malformed key for '%s'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", keyCandidate); | |||
} | |||
/** | |||
* Same as {@link #isValidModuleKey(String)}, but allows additionally '/'. | |||
*/ |
@@ -19,7 +19,9 @@ | |||
*/ | |||
package org.sonar.core.component; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.batch.fs.InputFile; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.resources.Directory; | |||
@@ -28,6 +30,8 @@ import org.sonar.api.resources.Project; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class ComponentKeysTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Test | |||
public void create_effective_key() { | |||
@@ -57,7 +61,7 @@ public class ComponentKeysTest { | |||
assertThat(ComponentKeys.isValidModuleKey("ab_12")).isTrue(); | |||
assertThat(ComponentKeys.isValidModuleKey("ab/12")).isFalse(); | |||
} | |||
@Test | |||
public void isValidModuleKeyIssuesMode() { | |||
assertThat(ComponentKeys.isValidModuleKeyIssuesMode("")).isFalse(); | |||
@@ -80,4 +84,38 @@ public class ComponentKeysTest { | |||
assertThat(ComponentKeys.isValidBranch("ab\n")).isFalse(); | |||
} | |||
@Test | |||
public void checkModuleKey_with_correct_keys() { | |||
ComponentKeys.checkModuleKey("abc"); | |||
ComponentKeys.checkModuleKey("a-b_1.:2"); | |||
} | |||
@Test | |||
public void checkModuleKey_fail_if_only_digit() { | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("Malformed key for '0123'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit."); | |||
ComponentKeys.checkModuleKey("0123"); | |||
} | |||
@Test | |||
public void checkModuleKey_fail_if_key_is_empty() { | |||
expectedException.expect(IllegalArgumentException.class); | |||
ComponentKeys.checkModuleKey(""); | |||
} | |||
@Test | |||
public void checkModuleKey_fail_if_space() { | |||
expectedException.expect(IllegalArgumentException.class); | |||
ComponentKeys.checkModuleKey("ab 12"); | |||
} | |||
@Test | |||
public void checkModuleKey_fail_if_special_characters_not_allowed() { | |||
expectedException.expect(IllegalArgumentException.class); | |||
ComponentKeys.checkModuleKey("ab/12"); | |||
} | |||
} |
@@ -37,6 +37,7 @@ import org.sonar.db.DbSession; | |||
import org.sonar.db.MyBatis; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static org.sonar.core.component.ComponentKeys.checkModuleKey; | |||
import static org.sonar.core.component.ComponentKeys.isValidModuleKey; | |||
/** | |||
@@ -109,7 +110,11 @@ public class ComponentKeyUpdaterDao implements Dao { | |||
.stream() | |||
.collect(Collectors.toMap( | |||
ResourceDto::getKey, | |||
component -> computeNewKey(component.getKey(), stringToReplace, replacementString))); | |||
component -> { | |||
String newKey = computeNewKey(component.getKey(), stringToReplace, replacementString); | |||
checkModuleKey(newKey); | |||
return newKey; | |||
})); | |||
} | |||
/** |
@@ -221,6 +221,17 @@ public class ComponentKeyUpdaterDaoTest { | |||
.containsOnly(entry("project", "new-project"), entry("project:enabled-module", "new-project:enabled-module")); | |||
} | |||
@Test | |||
public void simulate_bulk_update_key_fails_if_invalid_componentKey() { | |||
ComponentDto project = componentDb.insertComponent(newProjectDto("A").setKey("project")); | |||
componentDb.insertComponent(newModuleDto(project).setKey("project:enabled-module")); | |||
componentDb.insertComponent(newModuleDto(project).setKey("project:disabled-module").setEnabled(false)); | |||
thrown.expect(IllegalArgumentException.class); | |||
underTest.simulateBulkUpdateKey(dbSession, "A", "project", "project?"); | |||
} | |||
@Test | |||
public void compute_new_key() { | |||
assertThat(computeNewKey("my_project", "my_", "your_")).isEqualTo("your_project"); |