@@ -87,10 +87,10 @@ public class LoadFileHashesAndStatusStepIT { | |||
assertThat(previousFileHashesRepository.getMap()).hasSize(2); | |||
assertThat(previousFileHashesRepository.getDbFile(reportFile1).get()) | |||
.extracting(FileHashesDto::getSrcHash, FileHashesDto::getRevision, FileHashesDto::getDataHash) | |||
.containsOnly("srcHash" + dbFile1.getKey(), "revision" + dbFile1.getKey(), "dataHash" + dbFile1.getKey()); | |||
.containsOnly("SH" + dbFile1.getKey(), "revision" + dbFile1.getKey(), "DH" + dbFile1.getKey()); | |||
assertThat(previousFileHashesRepository.getDbFile(reportFile2).get()) | |||
.extracting(FileHashesDto::getSrcHash, FileHashesDto::getRevision, FileHashesDto::getDataHash) | |||
.containsOnly("srcHash" + dbFile2.getKey(), "revision" + dbFile2.getKey(), "dataHash" + dbFile2.getKey()); | |||
.containsOnly("SH" + dbFile2.getKey(), "revision" + dbFile2.getKey(), "DH" + dbFile2.getKey()); | |||
assertThat(previousFileHashesRepository.getDbFile(reportFile3)).isEmpty(); | |||
} | |||
@@ -118,9 +118,9 @@ public class LoadFileHashesAndStatusStepIT { | |||
private void insertFileSources(ComponentDto... files) { | |||
for (ComponentDto file : files) { | |||
db.fileSources().insertFileSource(file, f -> f | |||
.setSrcHash("srcHash" + file.getKey()) | |||
.setSrcHash("SH" + file.getKey()) | |||
.setRevision("revision" + file.getKey()) | |||
.setDataHash("dataHash" + file.getKey())); | |||
.setDataHash("DH" + file.getKey())); | |||
} | |||
} | |||
} |
@@ -34,7 +34,6 @@ import org.sonar.ce.task.projectexport.component.MutableComponentRepository; | |||
import org.sonar.ce.task.projectexport.steps.DumpElement; | |||
import org.sonar.ce.task.projectexport.steps.FakeDumpWriter; | |||
import org.sonar.ce.task.step.TestComputationStepContext; | |||
import org.sonar.core.util.Uuids; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
@@ -63,6 +62,7 @@ public class ExportLineHashesStepIT { | |||
private final DbSession dbSession = dbClient.openSession(false); | |||
private final FakeDumpWriter dumpWriter = new FakeDumpWriter(); | |||
private final MutableComponentRepository componentRepository = new ComponentRepositoryImpl(); | |||
private int fileId = 0; | |||
private final ExportLineHashesStep underTest = new ExportLineHashesStep(dbClient, dumpWriter, componentRepository); | |||
@Before | |||
@@ -188,7 +188,7 @@ public class ExportLineHashesStepIT { | |||
private FileSourceDto createDto(String fileUuid, String componentUuid, String hashes) { | |||
FileSourceDto fileSourceDto = new FileSourceDto() | |||
.setUuid(Uuids.createFast()) | |||
.setUuid("file_uuid_" + fileId++) | |||
.setFileUuid(fileUuid) | |||
.setProjectUuid(componentUuid); | |||
fileSourceDto.setRawLineHashes(hashes); |
@@ -35,6 +35,7 @@ import static org.mockito.ArgumentMatchers.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.core.util.SequenceUuidFactory.UUID_1; | |||
import static org.sonar.db.almsettings.AlmSettingsTesting.newGithubAlmSettingDto; | |||
public class AlmSettingDaoWithPersisterIT { | |||
@@ -71,7 +72,8 @@ public class AlmSettingDaoWithPersisterIT { | |||
.extracting("devOpsPlatformSettingUuid", "key") | |||
.containsExactly(almSettingDto.getUuid(), almSettingDto.getKey()); | |||
assertThat(newValue) | |||
.hasToString("{\"devOpsPlatformSettingUuid\": \"1\", \"key\": \"key\", \"devOpsPlatformName\": \"id1\", \"url\": \"url\", \"appId\": \"id1\", \"clientId\": \"cid1\" }"); | |||
.hasToString("{\"devOpsPlatformSettingUuid\": \"" + UUID_1 + "\", \"key\": \"key\", " + | |||
"\"devOpsPlatformName\": \"id1\", \"url\": \"url\", \"appId\": \"id1\", \"clientId\": \"cid1\" }"); | |||
almSettingDto.setPrivateKey("updated private key"); | |||
almSettingDto.setAppId("updated app id"); | |||
@@ -89,8 +91,10 @@ public class AlmSettingDaoWithPersisterIT { | |||
newValue = newValueCaptor.getValue(); | |||
assertThat(newValue) | |||
.extracting("devOpsPlatformSettingUuid", "key", "appId", "devOpsPlatformName", "url", "clientId") | |||
.containsExactly(almSettingDto.getUuid(), almSettingDto.getKey(), almSettingDto.getAppId(), almSettingDto.getAppId(), almSettingDto.getUrl(), almSettingDto.getClientId()); | |||
assertThat(newValue).hasToString("{\"devOpsPlatformSettingUuid\": \"1\", \"key\": \"updated key\", \"devOpsPlatformName\": \"updated app id\", " | |||
.containsExactly(almSettingDto.getUuid(), almSettingDto.getKey(), almSettingDto.getAppId(), almSettingDto.getAppId(), | |||
almSettingDto.getUrl(), almSettingDto.getClientId()); | |||
assertThat(newValue).hasToString("{\"devOpsPlatformSettingUuid\": \"" + UUID_1 + "\", " + | |||
"\"key\": \"updated key\", \"devOpsPlatformName\": \"updated app id\", " | |||
+ "\"url\": \"updated url\", \"appId\": \"updated app id\", \"clientId\": \"cid1\" }"); | |||
} | |||
@@ -126,7 +126,7 @@ public class ProjectAlmSettingDaoIT { | |||
private ProjectAlmSettingDto createAlmProject(AlmSettingDto almSettingsDto) { | |||
ProjectDto project = db.components().insertPrivateProject().getProjectDto(); | |||
when(uuidFactory.create()).thenReturn(project.getUuid() + "_forSetting"); | |||
when(uuidFactory.create()).thenReturn(project.getUuid() + "_set"); | |||
ProjectAlmSettingDto githubProjectAlmSettingDto = newGithubProjectAlmSettingDto(almSettingsDto, project); | |||
underTest.insertOrUpdate(dbSession, githubProjectAlmSettingDto, almSettingsDto.getKey(), project.getName(), project.getKey()); | |||
return githubProjectAlmSettingDto; |
@@ -44,6 +44,8 @@ import static java.util.Arrays.asList; | |||
import static java.util.Collections.emptyList; | |||
import static java.util.Collections.singleton; | |||
import static java.util.Collections.singletonList; | |||
import static java.util.function.Function.identity; | |||
import static java.util.stream.Collectors.toMap; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.groups.Tuple.tuple; | |||
import static org.sonar.api.measures.Metric.ValueType.DATA; | |||
@@ -61,6 +63,8 @@ public class LiveMeasureDaoIT { | |||
private final LiveMeasureDao underTest = db.getDbClient().liveMeasureDao(); | |||
private MetricDto metric; | |||
private int branchId = 0; | |||
@Before | |||
public void setUp() { | |||
metric = db.measures().insertMetric(); | |||
@@ -398,14 +402,17 @@ public class LiveMeasureDaoIT { | |||
MetricDto ncloc = metrics.get("ncloc"); | |||
setupProjectsWithLoc(ncloc, metrics.get("ncloc_language_distribution"), metrics.get("lines")); | |||
List<LargestBranchNclocDto> results = underTest.getLargestBranchNclocPerProject(db.getSession(), ncloc.getUuid()); | |||
Map<String, LargestBranchNclocDto> results = underTest.getLargestBranchNclocPerProject(db.getSession(), ncloc.getUuid()) | |||
.stream() | |||
.collect(toMap(largestBranchNclocDto -> largestBranchNclocDto.getProjectKey() + " " + largestBranchNclocDto.getBranchName(), | |||
identity(), (a, b) -> a)); | |||
assertThat(results).hasSize(5); | |||
assertLocForProject(results.get(0), "projectWithTieOnBranchSize", DEFAULT_MAIN_BRANCH_NAME, 250); | |||
assertLocForProject(results.get(1), "projectWithTieOnOtherBranches", "tieBranch1", 230); | |||
assertLocForProject(results.get(2), "projectWithBranchBiggerThanMaster", "notMasterBranch", 200); | |||
assertLocForProject(results.get(3), "simpleProject", DEFAULT_MAIN_BRANCH_NAME, 10); | |||
assertLocForProject(results.get(4), "projectWithLinesButNoLoc", DEFAULT_MAIN_BRANCH_NAME, 0); | |||
assertLocForProject(results.get("projectWithTieOnBranchSize main"), DEFAULT_MAIN_BRANCH_NAME, 250); | |||
assertLocForProject(results.get("projectWithTieOnOtherBranches tieBranch1"), "tieBranch1", 230); | |||
assertLocForProject(results.get("projectWithBranchBiggerThanMaster notMasterBranch"), "notMasterBranch", 200); | |||
assertLocForProject(results.get("simpleProject main"), DEFAULT_MAIN_BRANCH_NAME, 10); | |||
assertLocForProject(results.get("projectWithLinesButNoLoc main"), DEFAULT_MAIN_BRANCH_NAME, 0); | |||
} | |||
@Test | |||
@@ -417,10 +424,16 @@ public class LiveMeasureDaoIT { | |||
List<ProjectLocDistributionDto> results = underTest.selectLargestBranchesLocDistribution(db.getSession(), ncloc.getUuid(), nclocLanguageDistribution.getUuid()); | |||
String firstBranchOfProjectUuid = db.getDbClient().branchDao().selectByProjectUuid(db.getSession(), "projectWithTieOnOtherBranches").stream() | |||
.filter(branchDto -> !branchDto.isMain()) | |||
.map(BranchDto::getUuid) | |||
.sorted() | |||
.findFirst().orElseThrow(); | |||
assertThat(results) | |||
.containsExactlyInAnyOrder( | |||
new ProjectLocDistributionDto("projectWithTieOnBranchSize", getMainBranchUuid("projectWithTieOnBranchSize"), "java=250;js=0"), | |||
new ProjectLocDistributionDto("projectWithTieOnOtherBranches", getBranchUuid("projectWithTieOnOtherBranches", "tieBranch1"), "java=230;js=0"), | |||
new ProjectLocDistributionDto("projectWithTieOnOtherBranches", firstBranchOfProjectUuid, "java=230;js=0"), | |||
new ProjectLocDistributionDto("projectWithBranchBiggerThanMaster", getBranchUuid("projectWithBranchBiggerThanMaster", "notMasterBranch"), "java=100;js=100"), | |||
new ProjectLocDistributionDto("simpleProject", getMainBranchUuid("simpleProject"), "java=10;js=0"), | |||
new ProjectLocDistributionDto("projectWithLinesButNoLoc", getMainBranchUuid("projectWithLinesButNoLoc"), "java=0;js=0")); | |||
@@ -775,13 +788,13 @@ public class LiveMeasureDaoIT { | |||
private BranchDto addBranchToProjectWithMeasure(ProjectData project, String branchKey, MetricDto metric, double metricValue) { | |||
BranchDto branch = db.components() | |||
.insertProjectBranch(project.getProjectDto(), b -> b.setBranchType(BranchType.BRANCH).setKey(branchKey)); | |||
.insertProjectBranch(project.getProjectDto(), b -> b.setBranchType(BranchType.BRANCH).setKey(branchKey), | |||
branchDto -> branchDto.setUuid("uuid_" + branchId++)); | |||
addMeasureToBranch(branch, metric, metricValue, true); | |||
return branch; | |||
} | |||
private void assertLocForProject(LargestBranchNclocDto result, String projectKey, String branchKey, long linesOfCode) { | |||
assertThat(result.getProjectKey()).isEqualTo(projectKey); | |||
private void assertLocForProject(LargestBranchNclocDto result, String branchKey, long linesOfCode) { | |||
assertThat(result.getBranchName()).isEqualTo(branchKey); | |||
assertThat(result.getLoc()).isEqualTo(linesOfCode); | |||
} |
@@ -19,9 +19,6 @@ | |||
*/ | |||
package org.sonar.db.notification; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.stream.IntStream; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.mockito.invocation.InvocationOnMock; | |||
@@ -30,10 +27,15 @@ import org.sonar.api.notifications.Notification; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbTester; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.stream.IntStream; | |||
import static java.util.stream.Collectors.toList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.core.util.SequenceUuidFactory.*; | |||
import static org.sonar.db.notification.NotificationQueueDto.toNotificationQueueDto; | |||
public class NotificationQueueDaoIT { | |||
@@ -101,7 +103,7 @@ public class NotificationQueueDaoIT { | |||
assertThat(dao.selectOldest(3)) | |||
.extracting(NotificationQueueDto::getUuid) | |||
.containsExactlyElementsOf(Arrays.asList("1", "2", "3")); | |||
.containsExactlyElementsOf(List.of(UUID_1, UUID_2, UUID_3)); | |||
} | |||
private List<String> selectAllUuid() { |
@@ -25,6 +25,7 @@ import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Random; | |||
import java.util.Set; | |||
import java.util.TreeSet; | |||
import java.util.stream.IntStream; | |||
import java.util.stream.Stream; | |||
import org.junit.Rule; | |||
@@ -453,8 +454,10 @@ public class GroupPermissionDaoIT { | |||
db.users().insertEntityPermissionOnAnyone("perm1", project1); | |||
db.users().insertEntityPermissionOnAnyone("perm1", project2); | |||
db.users().insertEntityPermissionOnAnyone("perm1", project3); | |||
TreeSet<String> sortedProjectKeys = new TreeSet<>(Set.of(project1.getKey(), project2.getKey(), project3.getKey())); | |||
assertThat(underTest.selectProjectKeysWithAnyonePermissions(dbSession, 3)) | |||
.containsExactly(project1.getKey(), project2.getKey(), project3.getKey()); | |||
.containsExactlyElementsOf(sortedProjectKeys); | |||
} | |||
@Test |
@@ -39,6 +39,7 @@ import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyNoInteractions; | |||
import static org.sonar.api.web.UserRole.ADMIN; | |||
import static org.sonar.core.util.SequenceUuidFactory.UUID_1; | |||
public class GroupPermissionDaoWithPersisterIT { | |||
private final AuditPersister auditPersister = mock(AuditPersister.class); | |||
@@ -62,7 +63,8 @@ public class GroupPermissionDaoWithPersisterIT { | |||
verify(auditPersister).addGroupPermission(eq(dbSession), newValueCaptor.capture()); | |||
GroupPermissionNewValue newValue = newValueCaptor.getValue(); | |||
assertNewValue(newValue, dto.getUuid(), group.getUuid(), group.getName(), null, dto.getRole(), null, null, null); | |||
assertThat(newValue).hasToString("{\"permissionUuid\": \"1\", \"permission\": \"admin\", \"groupUuid\": \"guuid\", \"groupName\": \"gname\" }"); | |||
assertThat(newValue).hasToString("{\"permissionUuid\": \"" + UUID_1 + "\", \"permission\": \"admin\", \"groupUuid\": \"guuid\", " + | |||
"\"groupName\": \"gname\" }"); | |||
underTest.delete(dbSession, ADMIN, group.getUuid(), group.getName(), null); | |||
@@ -88,7 +90,8 @@ public class GroupPermissionDaoWithPersisterIT { | |||
assertNewValue(newValue, dto.getUuid(), group.getUuid(), group.getName(), project.projectUuid(), dto.getRole(), project.projectKey(), | |||
project.getProjectDto().getName(), "TRK"); | |||
assertThat(newValue).hasToString("{\"permissionUuid\": \"1\", \"permission\": \"admin\", \"groupUuid\": \"guuid\", \"groupName\": \"gname\"," + | |||
assertThat(newValue).hasToString("{\"permissionUuid\": \"" + UUID_1 + "\", \"permission\": \"admin\", \"groupUuid\": \"guuid\", \"groupName\": " + | |||
"\"gname\"," + | |||
" \"componentUuid\": \"projectUuid\", \"componentKey\": \"cKey\", \"componentName\": \"cname\", \"qualifier\": \"project\" }"); | |||
underTest.deleteByEntityUuid(dbSession, project.getProjectDto()); | |||
@@ -97,7 +100,8 @@ public class GroupPermissionDaoWithPersisterIT { | |||
newValue = newValueCaptor.getValue(); | |||
assertNewValue(newValue, null, null, null, project.projectUuid(), null, project.projectKey(), project.getProjectDto().getName(), "TRK"); | |||
assertThat(newValue).hasToString("{\"componentUuid\": \"projectUuid\", \"componentKey\": \"cKey\", \"componentName\": \"cname\", \"qualifier\": \"project\" }"); | |||
assertThat(newValue).hasToString("{\"componentUuid\": \"projectUuid\", \"componentKey\": \"cKey\", \"componentName\": \"cname\", " + | |||
"\"qualifier\": \"project\" }"); | |||
} | |||
@Test | |||
@@ -116,8 +120,9 @@ public class GroupPermissionDaoWithPersisterIT { | |||
verify(auditPersister).addGroupPermission(eq(dbSession), newValueCaptor.capture()); | |||
GroupPermissionNewValue newValue = newValueCaptor.getValue(); | |||
assertNewValue(newValue, dto.getUuid(), null, null, project.projectUuid(), dto.getRole(), project.projectKey(), project.getProjectDto().getName(), "TRK"); | |||
assertThat(newValue).hasToString("{\"permissionUuid\": \"1\", \"permission\": \"admin\", \"componentUuid\": \"projectUuid\", " | |||
assertNewValue(newValue, dto.getUuid(), null, null, project.projectUuid(), dto.getRole(), project.projectKey(), | |||
project.getProjectDto().getName(), "TRK"); | |||
assertThat(newValue).hasToString("{\"permissionUuid\": \"" + UUID_1 + "\", \"permission\": \"admin\", \"componentUuid\": \"projectUuid\", " | |||
+ "\"componentKey\": \"cKey\", \"componentName\": \"cname\", \"qualifier\": \"project\" }"); | |||
underTest.deleteByEntityUuidForAnyOne(dbSession, project.getProjectDto()); | |||
@@ -145,15 +150,18 @@ public class GroupPermissionDaoWithPersisterIT { | |||
verify(auditPersister).addGroupPermission(eq(dbSession), newValueCaptor.capture()); | |||
GroupPermissionNewValue newValue = newValueCaptor.getValue(); | |||
assertNewValue(newValue, dto.getUuid(), group.getUuid(), group.getName(), project.projectUuid(), dto.getRole(), project.projectKey(), project.getProjectDto().getName(), "TRK"); | |||
assertThat(newValue).hasToString("{\"permissionUuid\": \"1\", \"permission\": \"admin\", \"groupUuid\": \"guuid\", \"groupName\": \"gname\", " | |||
assertNewValue(newValue, dto.getUuid(), group.getUuid(), group.getName(), project.projectUuid(), dto.getRole(), project.projectKey(), | |||
project.getProjectDto().getName(), "TRK"); | |||
assertThat(newValue).hasToString("{\"permissionUuid\": \"" + UUID_1 + "\", \"permission\": \"admin\", \"groupUuid\": \"guuid\", \"groupName\": " + | |||
"\"gname\", " | |||
+ "\"componentUuid\": \"projectUuid\", \"componentKey\": \"cKey\", \"componentName\": \"cname\", \"qualifier\": \"project\" }"); | |||
underTest.deleteByEntityAndPermission(dbSession, dto.getRole(), project.getProjectDto()); | |||
verify(auditPersister).deleteGroupPermission(eq(dbSession), newValueCaptor.capture()); | |||
newValue = newValueCaptor.getValue(); | |||
assertNewValue(newValue, null, null, null, project.projectUuid(), ADMIN, project.projectKey(), project.getProjectDto().getName(), "TRK"); | |||
assertNewValue(newValue, null, null, null, project.projectUuid(), ADMIN, project.projectKey(), project.getProjectDto().getName(), | |||
"TRK"); | |||
assertThat(newValue).hasToString("{\"permission\": \"admin\", \"componentUuid\": \"projectUuid\", \"componentKey\": \"cKey\"," + | |||
" \"componentName\": \"cname\", \"qualifier\": \"project\" }"); | |||
} | |||
@@ -169,7 +177,8 @@ public class GroupPermissionDaoWithPersisterIT { | |||
verifyNoInteractions(auditPersister); | |||
} | |||
private void assertNewValue(GroupPermissionNewValue newValue, String uuid, String groupUuid, String groupName, String cUuid, String permission, | |||
private void assertNewValue(GroupPermissionNewValue newValue, String uuid, String groupUuid, String groupName, String cUuid, | |||
String permission, | |||
String componentKey, String cName, String qualifier) { | |||
assertThat(newValue) | |||
.extracting("permissionUuid", "groupUuid", "groupName", "componentUuid", "permission", "componentKey", "componentName", "qualifier") | |||
@@ -202,7 +211,7 @@ public class GroupPermissionDaoWithPersisterIT { | |||
.setGroupName(group != null ? group.getName() : null) | |||
.setRole(ADMIN) | |||
.setEntityUuid(project != null ? project.getUuid() : null) | |||
.setEntityName(project != null ? project.getName(): null); | |||
.setEntityName(project != null ? project.getName() : null); | |||
} | |||
private GroupPermissionDto getGroupPermission(GroupDto group) { |
@@ -815,15 +815,15 @@ public class PurgeCommandsIT { | |||
Instant okCreationDate = Instant.now().minus(29, ChronoUnit.DAYS); | |||
Instant oldCreationDate = Instant.now().minus(31, ChronoUnit.DAYS); | |||
dbTester.getDbClient().anticipatedTransitionDao().insert(dbTester.getSession(), getAnticipatedTransitionsDto(projectDto.uuid() + "okTransition", projectDto.uuid(), okCreationDate)); | |||
dbTester.getDbClient().anticipatedTransitionDao().insert(dbTester.getSession(), getAnticipatedTransitionsDto(projectDto.uuid() + "oldTransition", projectDto.uuid(), oldCreationDate)); | |||
dbTester.getDbClient().anticipatedTransitionDao().insert(dbTester.getSession(), getAnticipatedTransitionsDto(projectDto.uuid() + "ok", projectDto.uuid(), okCreationDate)); | |||
dbTester.getDbClient().anticipatedTransitionDao().insert(dbTester.getSession(), getAnticipatedTransitionsDto(projectDto.uuid() + "old", projectDto.uuid(), oldCreationDate)); | |||
underTest.deleteAnticipatedTransitions(projectDto.uuid(), Instant.now().minus(30, ChronoUnit.DAYS).toEpochMilli()); | |||
List<AnticipatedTransitionDto> anticipatedTransitionDtos = dbTester.getDbClient().anticipatedTransitionDao().selectByProjectUuid(dbTester.getSession(), projectDto.uuid()); | |||
assertThat(anticipatedTransitionDtos).hasSize(1); | |||
assertThat(anticipatedTransitionDtos.get(0).getUuid()).isEqualTo(projectDto.uuid() + "okTransition"); | |||
assertThat(anticipatedTransitionDtos.get(0).getUuid()).isEqualTo(projectDto.uuid() + "ok"); | |||
} | |||
@Test |
@@ -19,8 +19,11 @@ | |||
*/ | |||
package org.sonar.db.pushevent; | |||
import java.util.Comparator; | |||
import java.util.List; | |||
import java.util.Set; | |||
import java.util.stream.IntStream; | |||
import java.util.stream.Stream; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.api.impl.utils.TestSystem2; | |||
@@ -69,7 +72,8 @@ public class PushEventDaoIT { | |||
assertThat(underTest.selectByUuid(session, generatedUuid.getUuid())) | |||
.extracting(PushEventDto::getUuid, PushEventDto::getProjectUuid, PushEventDto::getPayload, PushEventDto::getCreatedAt) | |||
.containsExactly(eventDtoSecond.getUuid(), eventDtoSecond.getProjectUuid(), eventDtoSecond.getPayload(), eventDtoSecond.getCreatedAt()); | |||
.containsExactly(eventDtoSecond.getUuid(), eventDtoSecond.getProjectUuid(), eventDtoSecond.getPayload(), | |||
eventDtoSecond.getCreatedAt()); | |||
} | |||
@@ -147,7 +151,12 @@ public class PushEventDaoIT { | |||
var eventDto7 = generatePushEvent("proj2"); | |||
events = underTest.selectChunkByProjectUuids(session, Set.of("proj1", "proj2"), eventDto4.getCreatedAt(), eventDto4.getUuid(), 10); | |||
assertThat(events).extracting(PushEventDto::getUuid).containsExactly(eventDto5.getUuid(), eventDto6.getUuid(), eventDto7.getUuid()); | |||
List<String> sortedUuids = Stream.of(eventDto5, eventDto6, eventDto7) | |||
.sorted(Comparator.comparing(PushEventDto::getCreatedAt).thenComparing(PushEventDto::getUuid)) | |||
.map(PushEventDto::getUuid) | |||
.toList(); | |||
assertThat(events).extracting(PushEventDto::getUuid).containsExactlyElementsOf(sortedUuids); | |||
} | |||
@Test |
@@ -19,7 +19,7 @@ | |||
*/ | |||
package org.sonar.db.qualityprofile; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.mockito.ArgumentCaptor; | |||
@@ -35,6 +35,8 @@ import org.sonar.db.user.SearchGroupMembershipDto; | |||
import static java.util.Arrays.asList; | |||
import static java.util.Collections.emptyList; | |||
import static java.util.Collections.singletonList; | |||
import static java.util.function.Function.identity; | |||
import static java.util.stream.Collectors.toMap; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.entry; | |||
import static org.assertj.core.api.Assertions.tuple; | |||
@@ -282,17 +284,19 @@ public class QProfileEditGroupsDaoIT { | |||
verify(auditPersister, times(2)).deleteQualityProfileEditor(eq(db.getSession()), newValueCaptor.capture()); | |||
List<GroupEditorNewValue> newValues = newValueCaptor.getAllValues(); | |||
assertThat(newValues.get(0)) | |||
Map<String, GroupEditorNewValue> newValues = newValueCaptor.getAllValues().stream() | |||
.collect(toMap(GroupEditorNewValue::getQualityProfileName, identity())); | |||
assertThat(newValues.get(profile1.getName())) | |||
.extracting(GroupEditorNewValue::getQualityProfileName, GroupEditorNewValue::getQualityProfileUuid, | |||
GroupEditorNewValue::getGroupName, GroupEditorNewValue::getGroupUuid) | |||
.containsExactly(profile1.getName(), profile1.getKee(), null, null); | |||
assertThat(newValues.get(0).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); | |||
assertThat(newValues.get(1)) | |||
assertThat(newValues.get(profile1.getName()).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); | |||
assertThat(newValues.get(profile2.getName())) | |||
.extracting(GroupEditorNewValue::getQualityProfileName, GroupEditorNewValue::getQualityProfileUuid, | |||
GroupEditorNewValue::getGroupName, GroupEditorNewValue::getGroupUuid) | |||
.containsExactly(profile2.getName(), profile2.getKee(), null, null); | |||
assertThat(newValues.get(1).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); | |||
assertThat(newValues.get(profile2.getName()).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); | |||
assertThat(underTest.exists(db.getSession(), profile1, group1)).isFalse(); | |||
assertThat(underTest.exists(db.getSession(), profile2, group2)).isFalse(); |
@@ -20,7 +20,7 @@ | |||
package org.sonar.db.qualityprofile; | |||
import java.sql.SQLException; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.mockito.ArgumentCaptor; | |||
@@ -34,6 +34,8 @@ import org.sonar.db.user.SearchUserMembershipDto; | |||
import org.sonar.db.user.UserDto; | |||
import static java.util.Arrays.asList; | |||
import static java.util.function.Function.identity; | |||
import static java.util.stream.Collectors.toMap; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.entry; | |||
import static org.assertj.core.api.Assertions.tuple; | |||
@@ -302,17 +304,16 @@ public class QProfileEditUsersDaoIT { | |||
verify(auditPersister, times(2)).deleteQualityProfileEditor(eq(db.getSession()), newValueCaptor.capture()); | |||
List<UserEditorNewValue> newValues = newValueCaptor.getAllValues(); | |||
assertThat(newValues.get(0)) | |||
.extracting(UserEditorNewValue::getQualityProfileName, UserEditorNewValue::getQualityProfileUuid, | |||
UserEditorNewValue::getUserLogin, UserEditorNewValue::getUserUuid) | |||
.containsExactly(profile1.getName(), profile1.getKee(), null, null); | |||
assertThat(newValues.get(0).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); | |||
assertThat(newValues.get(1)) | |||
.extracting(UserEditorNewValue::getQualityProfileName, UserEditorNewValue::getQualityProfileUuid, | |||
UserEditorNewValue::getUserLogin, UserEditorNewValue::getUserUuid) | |||
.containsExactly(profile2.getName(), profile2.getKee(), null, null); | |||
assertThat(newValues.get(1).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); | |||
Map<String, UserEditorNewValue> newValues = newValueCaptor.getAllValues().stream() | |||
.collect(toMap(UserEditorNewValue::getQualityProfileName, identity())); | |||
assertThat(newValues.get(profile1.getName())) | |||
.extracting(UserEditorNewValue::getQualityProfileUuid, UserEditorNewValue::getUserLogin, UserEditorNewValue::getUserUuid) | |||
.containsExactly(profile1.getKee(), null, null); | |||
assertThat(newValues.get(profile1.getName()).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); | |||
assertThat(newValues.get(profile2.getName())) | |||
.extracting(UserEditorNewValue::getQualityProfileUuid, UserEditorNewValue::getUserLogin, UserEditorNewValue::getUserUuid) | |||
.containsExactly(profile2.getKee(), null, null); | |||
assertThat(newValues.get(profile2.getName()).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); | |||
assertThat(underTest.exists(db.getSession(), profile1, user1)).isFalse(); | |||
assertThat(underTest.exists(db.getSession(), profile2, user2)).isFalse(); |
@@ -699,7 +699,7 @@ public class QualityProfileDaoIT { | |||
db.qualityProfiles().associateWithProject(project2, jsProfile); | |||
assertThat(underTest.selectQProfilesByProjectUuid(dbSession, project1.getUuid())) | |||
.containsExactly(javaProfile, cProfile); | |||
.containsExactlyInAnyOrder(javaProfile, cProfile); | |||
assertThat(underTest.selectQProfilesByProjectUuid(dbSession, project2.getUuid())) | |||
.containsExactly(jsProfile); |
@@ -57,8 +57,8 @@ public class RoleDaoIT { | |||
public void setUp() { | |||
user1 = db.users().insertUser(); | |||
user2 = db.users().insertUser(); | |||
project1 = db.components().insertPrivateProject().getProjectDto(); | |||
project2 = db.components().insertPrivateProject().getProjectDto(); | |||
project1 = db.components().insertPrivateProject(project -> project.setName("project1")).getProjectDto(); | |||
project2 = db.components().insertPrivateProject(project -> project.setName("project2")).getProjectDto(); | |||
} | |||
@Test | |||
@@ -111,7 +111,7 @@ public class RoleDaoIT { | |||
List<String> result = underTest.selectEntityUuidsByPermissionAndUserUuidAndQualifier(dbSession, UserRole.ADMIN, user1.getUuid(), PROJECT_QUALIFIER); | |||
assertThat(result).containsExactly(project1.getUuid(), project2.getUuid()); | |||
assertThat(result).containsExactlyInAnyOrder(project1.getUuid(), project2.getUuid()); | |||
} | |||
@Test |
@@ -32,6 +32,9 @@ import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.core.util.SequenceUuidFactory.UUID_1; | |||
import static org.sonar.core.util.SequenceUuidFactory.UUID_2; | |||
import static org.sonar.core.util.SequenceUuidFactory.UUID_3; | |||
@RunWith(DataProviderRunner.class) | |||
public class UserGroupDaoIT { | |||
@@ -129,40 +132,40 @@ public class UserGroupDaoIT { | |||
return new Object[][] { | |||
{new UserGroupQuery(null, null, null), | |||
List.of( | |||
new UserGroupDto().setUuid("3").setGroupUuid("group_a").setUserUuid("1"), | |||
new UserGroupDto().setUuid("4").setGroupUuid("group_a").setUserUuid("2"), | |||
new UserGroupDto().setUuid("5").setGroupUuid("group_b").setUserUuid("1"), | |||
new UserGroupDto().setUuid("6").setGroupUuid("group_b").setUserUuid("2") | |||
new UserGroupDto().setUuid("3").setGroupUuid("group_a").setUserUuid(UUID_1), | |||
new UserGroupDto().setUuid("4").setGroupUuid("group_a").setUserUuid(UUID_2), | |||
new UserGroupDto().setUuid("5").setGroupUuid("group_b").setUserUuid(UUID_1), | |||
new UserGroupDto().setUuid("6").setGroupUuid("group_b").setUserUuid(UUID_2) | |||
)}, | |||
{new UserGroupQuery("3", null, null), | |||
{new UserGroupQuery(UUID_3, null, null), | |||
List.of( | |||
new UserGroupDto().setUuid("3").setGroupUuid("group_a").setUserUuid("1") | |||
new UserGroupDto().setUuid(UUID_3).setGroupUuid("group_a").setUserUuid(UUID_1) | |||
)}, | |||
{new UserGroupQuery("3", "group_a", "1"), | |||
{new UserGroupQuery(UUID_3, "group_a", UUID_1), | |||
List.of( | |||
new UserGroupDto().setUuid("3").setGroupUuid("group_a").setUserUuid("1") | |||
new UserGroupDto().setUuid(UUID_3).setGroupUuid("group_a").setUserUuid(UUID_1) | |||
)}, | |||
{new UserGroupQuery("3", "group_b", "1"), | |||
{new UserGroupQuery(UUID_3, "group_b", UUID_1), | |||
List.of()}, | |||
{new UserGroupQuery(null,"group_b", null), | |||
List.of( | |||
new UserGroupDto().setUuid("5").setGroupUuid("group_b").setUserUuid("1"), | |||
new UserGroupDto().setUuid("6").setGroupUuid("group_b").setUserUuid("2") | |||
new UserGroupDto().setUuid("5").setGroupUuid("group_b").setUserUuid(UUID_1), | |||
new UserGroupDto().setUuid("6").setGroupUuid("group_b").setUserUuid(UUID_2) | |||
)}, | |||
{new UserGroupQuery(null,null, "2"), | |||
{new UserGroupQuery(null,null, UUID_2), | |||
List.of( | |||
new UserGroupDto().setUuid("4").setGroupUuid("group_a").setUserUuid("2"), | |||
new UserGroupDto().setUuid("6").setGroupUuid("group_b").setUserUuid("2") | |||
new UserGroupDto().setUuid("4").setGroupUuid("group_a").setUserUuid(UUID_2), | |||
new UserGroupDto().setUuid("6").setGroupUuid("group_b").setUserUuid(UUID_2) | |||
)}, | |||
{new UserGroupQuery(null,"group_a", "2"), | |||
{new UserGroupQuery(null,"group_a", UUID_2), | |||
List.of( | |||
new UserGroupDto().setUuid("4").setGroupUuid("group_a").setUserUuid("2") | |||
new UserGroupDto().setUuid("4").setGroupUuid("group_a").setUserUuid(UUID_2) | |||
)}, | |||
{new UserGroupQuery(null,"group_c", null), | |||
List.of()}, | |||
{new UserGroupQuery(null,"group_c", "2"), | |||
{new UserGroupQuery(null,"group_c", UUID_2), | |||
List.of()}, | |||
{new UserGroupQuery(null,"group_a", "3"), | |||
{new UserGroupQuery(null,"group_a", UUID_3), | |||
List.of()} | |||
}; | |||
} | |||
@@ -174,7 +177,7 @@ public class UserGroupDaoIT { | |||
List<UserGroupDto> userGroupDtos = underTest.selectByQuery(dbTester.getSession(), userQuery, 1, 100); | |||
assertThat(userGroupDtos).usingRecursiveFieldByFieldElementComparator().isEqualTo(expectedUserGroupDtos); | |||
assertThat(userGroupDtos).usingRecursiveFieldByFieldElementComparatorIgnoringFields("uuid").isEqualTo(expectedUserGroupDtos); | |||
assertThat(underTest.countByQuery(dbTester.getSession(), userQuery)).isEqualTo(expectedUserGroupDtos.size()); | |||
} | |||
@@ -73,4 +73,5 @@ public class UserGroupDto { | |||
public int hashCode() { | |||
return Objects.hash(userUuid, groupUuid); | |||
} | |||
} |
@@ -91,7 +91,7 @@ | |||
pb.kee as branchName, | |||
pb.branch_type as branchType, | |||
lm.value as ncloc, | |||
row_number() over (partition by pb.project_uuid order by lm.value desc, pb.uuid asc) row_number | |||
row_number() over (partition by pb.project_uuid order by lm.value desc, pb.is_main desc, pb.uuid asc) row_number | |||
from live_measures lm | |||
inner join project_branches pb on pb.uuid = lm.component_uuid | |||
inner join projects p on p.uuid = pb.project_uuid | |||
@@ -109,7 +109,7 @@ | |||
SELECT loc_grouped_branches.uuid, loc_grouped_branches.project_uuid | |||
FROM ( | |||
SELECT b.uuid, b.project_uuid, ROW_NUMBER() OVER (PARTITION BY | |||
b.project_uuid ORDER BY lm.value desc, b.uuid asc) row_number | |||
b.project_uuid ORDER BY lm.value desc, b.is_main desc, b.uuid asc) row_number | |||
from live_measures lm | |||
inner join project_branches b on b.uuid = lm.component_uuid | |||
inner join projects p on p.uuid = b.project_uuid |
@@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.tuple; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.core.util.SequenceUuidFactory.UUID_1; | |||
public class RemoveOrphanRulesFromQualityProfilesIT { | |||
@@ -121,7 +122,7 @@ public class RemoveOrphanRulesFromQualityProfilesIT { | |||
private void assertQualityProfileChanges() { | |||
assertThat(db.select("SELECT * from qprofile_changes")) | |||
.extracting(r -> r.get("KEE"), r -> r.get("RULES_PROFILE_UUID"), r -> r.get("CHANGE_TYPE"), r -> r.get("USER_UUID"), r -> r.get("CHANGE_DATA"), r -> r.get("CREATED_AT")) | |||
.containsExactly(tuple("1", "uuid-profile-1", "DEACTIVATED", null, "ruleUuid=uuid-rule-2", 1L)); | |||
.containsExactly(tuple(UUID_1, "uuid-profile-1", "DEACTIVATED", null, "ruleUuid=uuid-rule-2", 1L)); | |||
} | |||
@@ -17,7 +17,7 @@ | |||
* 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; | |||
package org.sonar.server.platform.serverid; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import java.net.NetworkInterface; | |||
@@ -28,6 +28,7 @@ import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.core.util.UuidFactoryImpl; | |||
/** | |||
* Used by {@link UuidFactoryImpl}. Heavily inspired by https://github.com/elastic/elasticsearch/blob/master/core/src/main/java/org/elasticsearch/common/MacAddressProvider.java |
@@ -17,27 +17,22 @@ | |||
* 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; | |||
package org.sonar.server.platform.serverid; | |||
import java.security.SecureRandom; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
import java.util.function.Supplier; | |||
import org.apache.commons.codec.binary.Base64; | |||
public final class UuidGeneratorImpl implements UuidGenerator { | |||
public final class ServerIdGenerator { | |||
private final FullNewUuidGenerator fullNewUuidGenerator = new FullNewUuidGenerator(); | |||
private final FullNewIdGenerator fullNewIdGenerator = new FullNewIdGenerator(); | |||
@Override | |||
public byte[] generate() { | |||
return fullNewUuidGenerator.get(); | |||
public String generate() { | |||
return Base64.encodeBase64URLSafeString(fullNewIdGenerator.get()); | |||
} | |||
@Override | |||
public WithFixedBase withFixedBase() { | |||
return new FixedBasedUuidGenerator(); | |||
} | |||
private static class UuidGeneratorBase { | |||
private static class IdGeneratorBase { | |||
// We only use bottom 3 bytes for the sequence number. Paranoia: init with random int so that if JVM/OS/machine goes down, clock slips | |||
// backwards, and JVM comes back up, we are less likely to be on the same sequenceNumber at the same time: | |||
private final AtomicInteger sequenceNumber = new AtomicInteger(new SecureRandom().nextInt()); | |||
@@ -80,7 +75,9 @@ public final class UuidGeneratorImpl implements UuidGenerator { | |||
return sequenceNumber.incrementAndGet() & 0xffffff; | |||
} | |||
/** Puts the lower numberOfLongBytes from l into the array, starting index pos. */ | |||
/** | |||
* Puts the lower numberOfLongBytes from l into the array, starting index pos. | |||
*/ | |||
private static void putLong(byte[] array, long l, int pos, int numberOfLongBytes) { | |||
for (int i = 0; i < numberOfLongBytes; ++i) { | |||
array[pos + numberOfLongBytes - i - 1] = (byte) (l >>> (i * 8)); | |||
@@ -88,7 +85,7 @@ public final class UuidGeneratorImpl implements UuidGenerator { | |||
} | |||
} | |||
private static final class FullNewUuidGenerator extends UuidGeneratorBase implements Supplier<byte[]> { | |||
private static final class FullNewIdGenerator extends IdGeneratorBase implements Supplier<byte[]> { | |||
@Override | |||
public byte[] get() { | |||
@@ -99,19 +96,4 @@ public final class UuidGeneratorImpl implements UuidGenerator { | |||
} | |||
} | |||
private static class FixedBasedUuidGenerator extends UuidGeneratorBase implements WithFixedBase { | |||
private final byte[] base = new byte[15]; | |||
FixedBasedUuidGenerator() { | |||
int sequenceId = getSequenceId(); | |||
initBase(base, sequenceId); | |||
} | |||
@Override | |||
public byte[] generate(int increment) { | |||
byte[] buffer = new byte[15]; | |||
System.arraycopy(base, 0, buffer, 0, buffer.length); | |||
return super.generate(buffer, increment); | |||
} | |||
} | |||
} |
@@ -17,7 +17,7 @@ | |||
* 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; | |||
package org.sonar.server.platform.serverid; | |||
import org.junit.Test; | |||
@@ -17,17 +17,43 @@ | |||
* 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.web.requestid; | |||
package org.sonar.server.platform.serverid; | |||
import java.util.ArrayList; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class RequestIdConfigurationTest { | |||
private RequestIdConfiguration underTest = new RequestIdConfiguration(50); | |||
public class ServerIdGeneratorTest { | |||
private final ServerIdGenerator underTest = new ServerIdGenerator(); | |||
@Test | |||
public void getUidGeneratorRenewalCount_returns_value_provided_from_constructor() { | |||
assertThat(underTest.getUidGeneratorRenewalCount()).isEqualTo(50); | |||
public void generate_concurrent_test() throws InterruptedException { | |||
int rounds = 500; | |||
List<String> ids1 = new ArrayList<>(rounds); | |||
List<String> ids2 = new ArrayList<>(rounds); | |||
Thread t1 = new Thread(() -> { | |||
for (int i = 0; i < rounds; i++) { | |||
ids1.add(underTest.generate()); | |||
} | |||
}); | |||
Thread t2 = new Thread(() -> { | |||
for (int i = 0; i < rounds; i++) { | |||
ids2.add(underTest.generate()); | |||
} | |||
}); | |||
t1.start(); | |||
t2.start(); | |||
t1.join(); | |||
t2.join(); | |||
Set<String> ids = new HashSet<>(rounds * 2); | |||
ids.addAll(ids1); | |||
ids.addAll(ids2); | |||
assertThat(ids).hasSize(rounds * 2); | |||
} | |||
} |
@@ -26,25 +26,24 @@ import java.util.zip.CRC32; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.config.Configuration; | |||
import org.sonar.core.platform.ServerId; | |||
import org.sonar.core.util.UuidFactory; | |||
import static org.sonar.process.ProcessProperties.Property.JDBC_URL; | |||
public class ServerIdFactoryImpl implements ServerIdFactory { | |||
private final Configuration config; | |||
private final UuidFactory uuidFactory; | |||
private final ServerIdGenerator serverIdGenerator; | |||
private final JdbcUrlSanitizer jdbcUrlSanitizer; | |||
public ServerIdFactoryImpl(Configuration config, UuidFactory uuidFactory, JdbcUrlSanitizer jdbcUrlSanitizer) { | |||
public ServerIdFactoryImpl(Configuration config, ServerIdGenerator serverIdGenerator, JdbcUrlSanitizer jdbcUrlSanitizer) { | |||
this.config = config; | |||
this.uuidFactory = uuidFactory; | |||
this.serverIdGenerator = serverIdGenerator; | |||
this.jdbcUrlSanitizer = jdbcUrlSanitizer; | |||
} | |||
@Override | |||
public ServerId create() { | |||
return ServerId.of(computeDatabaseId(), uuidFactory.create()); | |||
return ServerId.of(computeDatabaseId(), serverIdGenerator.generate()); | |||
} | |||
@Override |
@@ -28,8 +28,8 @@ public class ServerIdModule extends Module { | |||
ServerIdFactoryImpl.class, | |||
JdbcUrlSanitizer.class, | |||
ServerIdChecksum.class, | |||
ServerIdManager.class | |||
ServerIdManager.class, | |||
ServerIdGenerator.class | |||
); | |||
} | |||
} |
@@ -24,8 +24,6 @@ import org.sonar.core.platform.Module; | |||
public class HttpRequestIdModule extends Module { | |||
@Override | |||
protected void configureModule() { | |||
add(new RequestIdConfiguration(RequestIdGeneratorImpl.UUID_GENERATOR_RENEWAL_COUNT), | |||
RequestIdGeneratorBaseImpl.class, | |||
RequestIdGeneratorImpl.class); | |||
add(RequestIdGeneratorImpl.class); | |||
} | |||
} |
@@ -1,35 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.platform.web.requestid; | |||
public class RequestIdConfiguration { | |||
/** | |||
* @see RequestIdGeneratorImpl#mustRenewUuidGenerator(long) | |||
*/ | |||
private final long uuidGeneratorRenewalCount; | |||
public RequestIdConfiguration(long uuidGeneratorRenewalCount) { | |||
this.uuidGeneratorRenewalCount = uuidGeneratorRenewalCount; | |||
} | |||
public long getUidGeneratorRenewalCount() { | |||
return uuidGeneratorRenewalCount; | |||
} | |||
} |
@@ -1,29 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.platform.web.requestid; | |||
import org.sonar.core.util.UuidGenerator; | |||
public interface RequestIdGeneratorBase { | |||
/** | |||
* Provides a new instance of {@link UuidGenerator.WithFixedBase} to be used by {@link RequestIdGeneratorImpl}. | |||
*/ | |||
UuidGenerator.WithFixedBase createNew(); | |||
} |
@@ -1,31 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.platform.web.requestid; | |||
import org.sonar.core.util.UuidGenerator; | |||
import org.sonar.core.util.UuidGeneratorImpl; | |||
public class RequestIdGeneratorBaseImpl implements RequestIdGeneratorBase { | |||
@Override | |||
public UuidGenerator.WithFixedBase createNew() { | |||
return new UuidGeneratorImpl().withFixedBase(); | |||
} | |||
} |
@@ -19,78 +19,13 @@ | |||
*/ | |||
package org.sonar.server.platform.web.requestid; | |||
import java.util.Base64; | |||
import java.util.concurrent.atomic.AtomicLong; | |||
import java.util.concurrent.atomic.AtomicReference; | |||
import org.sonar.core.util.UuidGenerator; | |||
import java.util.UUID; | |||
/** | |||
* This implementation of {@link RequestIdGenerator} creates unique identifiers for HTTP requests leveraging | |||
* {@link UuidGenerator.WithFixedBase#generate(int)} and a counter of HTTP requests. | |||
* <p> | |||
* To work around the limit of unique values produced by {@link UuidGenerator.WithFixedBase#generate(int)}, the | |||
* {@link UuidGenerator.WithFixedBase} instance will be renewed every | |||
* {@link RequestIdConfiguration#getUidGeneratorRenewalCount() RequestIdConfiguration#uidGeneratorRenewalCount} | |||
* HTTP requests. | |||
* </p> | |||
* <p> | |||
* This implementation is Thread safe. | |||
* </p> | |||
*/ | |||
public class RequestIdGeneratorImpl implements RequestIdGenerator { | |||
/** | |||
* The value to which the HTTP request count will be compared to (using a modulo operator, | |||
* see {@link #mustRenewUuidGenerator(long)}). | |||
* | |||
* <p> | |||
* This value can't be the last value before {@link UuidGenerator.WithFixedBase#generate(int)} returns a non unique | |||
* value, ie. 2^23-1 because there is no guarantee the renewal will happen before any other thread calls | |||
* {@link UuidGenerator.WithFixedBase#generate(int)} method of the deplated {@link UuidGenerator.WithFixedBase} instance. | |||
* </p> | |||
* | |||
* <p> | |||
* To keep a comfortable margin of error, 2^22 will be used. | |||
* </p> | |||
*/ | |||
public static final long UUID_GENERATOR_RENEWAL_COUNT = 4_194_304; | |||
private final AtomicLong counter = new AtomicLong(); | |||
private final RequestIdGeneratorBase requestIdGeneratorBase; | |||
private final RequestIdConfiguration requestIdConfiguration; | |||
private final AtomicReference<UuidGenerator.WithFixedBase> uuidGenerator; | |||
public RequestIdGeneratorImpl(RequestIdGeneratorBase requestIdGeneratorBase, RequestIdConfiguration requestIdConfiguration) { | |||
this.requestIdGeneratorBase = requestIdGeneratorBase; | |||
this.uuidGenerator = new AtomicReference<>(requestIdGeneratorBase.createNew()); | |||
this.requestIdConfiguration = requestIdConfiguration; | |||
} | |||
@Override | |||
public String generate() { | |||
UuidGenerator.WithFixedBase currentUuidGenerator = this.uuidGenerator.get(); | |||
long counterValue = counter.getAndIncrement(); | |||
if (counterValue != 0 && mustRenewUuidGenerator(counterValue)) { | |||
UuidGenerator.WithFixedBase newUuidGenerator = requestIdGeneratorBase.createNew(); | |||
uuidGenerator.set(newUuidGenerator); | |||
return generate(newUuidGenerator, counterValue); | |||
} | |||
return generate(currentUuidGenerator, counterValue); | |||
} | |||
/** | |||
* Since renewal of {@link UuidGenerator.WithFixedBase} instance is based on the HTTP request counter, only a single | |||
* thread can get the right value which will make this method return true. So, this is thread-safe by design, therefor | |||
* this method doesn't need external synchronization. | |||
* <p> | |||
* The value to which the counter is compared should however be chosen with caution: see {@link #UUID_GENERATOR_RENEWAL_COUNT}. | |||
* </p> | |||
*/ | |||
private boolean mustRenewUuidGenerator(long counter) { | |||
return counter % requestIdConfiguration.getUidGeneratorRenewalCount() == 0; | |||
} | |||
private static String generate(UuidGenerator.WithFixedBase uuidGenerator, long increment) { | |||
return Base64.getEncoder().encodeToString(uuidGenerator.generate((int) increment)); | |||
return UUID.randomUUID().toString(); | |||
} | |||
} |
@@ -30,13 +30,12 @@ import org.junit.runner.RunWith; | |||
import org.sonar.api.config.Configuration; | |||
import org.sonar.api.config.internal.MapSettings; | |||
import org.sonar.core.platform.ServerId; | |||
import org.sonar.core.util.UuidFactory; | |||
import org.sonar.core.util.Uuids; | |||
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.spy; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.core.platform.ServerId.DATABASE_ID_LENGTH; | |||
import static org.sonar.core.platform.ServerId.NOT_UUID_DATASET_ID_LENGTH; | |||
@@ -48,12 +47,11 @@ import static org.sonar.server.platform.serverid.ServerIdFactoryImpl.crc32Hex; | |||
public class ServerIdFactoryImplTest { | |||
private static final ServerId A_SERVERID = ServerId.of(randomAlphabetic(DATABASE_ID_LENGTH), randomAlphabetic(UUID_DATASET_ID_LENGTH)); | |||
private MapSettings settings = new MapSettings(); | |||
private Configuration config = settings.asConfig(); | |||
private UuidFactory uuidFactory = mock(UuidFactory.class); | |||
private ServerIdGenerator serverIdGenerator = spy(new ServerIdGenerator()); | |||
private JdbcUrlSanitizer jdbcUrlSanitizer = mock(JdbcUrlSanitizer.class); | |||
private ServerIdFactoryImpl underTest = new ServerIdFactoryImpl(config, uuidFactory, jdbcUrlSanitizer); | |||
private ServerIdFactoryImpl underTest = new ServerIdFactoryImpl(config, serverIdGenerator, jdbcUrlSanitizer); | |||
@Test | |||
public void create_from_scratch_fails_with_ISE_if_JDBC_property_not_set() { | |||
@@ -63,10 +61,12 @@ public class ServerIdFactoryImplTest { | |||
@Test | |||
public void create_from_scratch_creates_ServerId_from_JDBC_URL_and_new_uuid() { | |||
String jdbcUrl = "jdbc"; | |||
String uuid = Uuids.create(); | |||
String sanitizedJdbcUrl = "sanitized_jdbc"; | |||
String uuid = serverIdGenerator.generate(); | |||
when(serverIdGenerator.generate()).thenReturn(uuid); | |||
settings.setProperty(JDBC_URL.getKey(), jdbcUrl); | |||
when(uuidFactory.create()).thenReturn(uuid); | |||
when(jdbcUrlSanitizer.sanitize(jdbcUrl)).thenReturn(sanitizedJdbcUrl); | |||
ServerId serverId = underTest.create(); | |||
@@ -86,7 +86,7 @@ public class ServerIdFactoryImplTest { | |||
String jdbcUrl = "jdbc"; | |||
String sanitizedJdbcUrl = "sanitized_jdbc"; | |||
settings.setProperty(JDBC_URL.getKey(), jdbcUrl); | |||
when(uuidFactory.create()).thenThrow(new IllegalStateException("UuidFactory.create() should not be called")); | |||
when(serverIdGenerator.generate()).thenThrow(new IllegalStateException("generate should not be called")); | |||
when(jdbcUrlSanitizer.sanitize(jdbcUrl)).thenReturn(sanitizedJdbcUrl); | |||
ServerId serverId = underTest.create(currentServerId); |
@@ -31,6 +31,6 @@ public class ServerIdModuleTest { | |||
public void verify_count_of_added_components() { | |||
ListContainer container = new ListContainer(); | |||
underTest.configure(container); | |||
assertThat(container.getAddedObjects()).hasSize(4); | |||
assertThat(container.getAddedObjects()).hasSize(5); | |||
} | |||
} |
@@ -31,6 +31,6 @@ public class HttpRequestIdModuleTest { | |||
public void count_components_in_module() { | |||
ListContainer container = new ListContainer(); | |||
underTest.configure(container); | |||
assertThat(container.getAddedObjects()).hasSize(3); | |||
assertThat(container.getAddedObjects()).hasSize(1); | |||
} | |||
} |
@@ -20,69 +20,19 @@ | |||
package org.sonar.server.platform.web.requestid; | |||
import org.junit.Test; | |||
import org.sonar.core.util.UuidGenerator; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
public class RequestIdGeneratorImplTest { | |||
private UuidGenerator.WithFixedBase generator1 = increment -> new byte[] {124, 22, 66, 96, 55, 88, 2, 9}; | |||
private UuidGenerator.WithFixedBase generator2 = increment -> new byte[] {0, 5, 88, 81, 8, 6, 44, 19}; | |||
private UuidGenerator.WithFixedBase generator3 = increment -> new byte[] {126, 9, 35, 76, 2, 1, 2}; | |||
private RequestIdGeneratorBase uidGeneratorBase = mock(RequestIdGeneratorBase.class); | |||
private IllegalStateException expected = new IllegalStateException("Unexpected third call to createNew"); | |||
RequestIdGeneratorImpl requestIdGenerator = new RequestIdGeneratorImpl(); | |||
@Test | |||
public void generate_renews_inner_UuidGenerator_instance_every_number_of_calls_to_generate_specified_in_RequestIdConfiguration_supports_2() { | |||
when(uidGeneratorBase.createNew()) | |||
.thenReturn(generator1) | |||
.thenReturn(generator2) | |||
.thenReturn(generator3) | |||
.thenThrow(expected); | |||
public void generate_shouldGenerateUniqueIds() { | |||
String requestId1 = requestIdGenerator.generate(); | |||
String requestId2 = requestIdGenerator.generate(); | |||
String requestId3 = requestIdGenerator.generate(); | |||
RequestIdGeneratorImpl underTest = new RequestIdGeneratorImpl(uidGeneratorBase, new RequestIdConfiguration(2)); | |||
assertThat(underTest.generate()).isEqualTo("fBZCYDdYAgk="); // using generator1 | |||
assertThat(underTest.generate()).isEqualTo("fBZCYDdYAgk="); // still using generator1 | |||
assertThat(underTest.generate()).isEqualTo("AAVYUQgGLBM="); // renewing generator and using generator2 | |||
assertThat(underTest.generate()).isEqualTo("AAVYUQgGLBM="); // still using generator2 | |||
assertThat(underTest.generate()).isEqualTo("fgkjTAIBAg=="); // renewing generator and using generator3 | |||
assertThat(underTest.generate()).isEqualTo("fgkjTAIBAg=="); // using generator3 | |||
assertThatThrownBy(() -> { | |||
underTest.generate(); // renewing generator and failing | |||
}) | |||
.isInstanceOf(IllegalStateException.class) | |||
.hasMessage(expected.getMessage()); | |||
} | |||
@Test | |||
public void generate_renews_inner_UuidGenerator_instance_every_number_of_calls_to_generate_specified_in_RequestIdConfiguration_supports_3() { | |||
when(uidGeneratorBase.createNew()) | |||
.thenReturn(generator1) | |||
.thenReturn(generator2) | |||
.thenReturn(generator3) | |||
.thenThrow(expected); | |||
RequestIdGeneratorImpl underTest = new RequestIdGeneratorImpl(uidGeneratorBase, new RequestIdConfiguration(3)); | |||
assertThat(underTest.generate()).isEqualTo("fBZCYDdYAgk="); // using generator1 | |||
assertThat(underTest.generate()).isEqualTo("fBZCYDdYAgk="); // still using generator1 | |||
assertThat(underTest.generate()).isEqualTo("fBZCYDdYAgk="); // still using generator1 | |||
assertThat(underTest.generate()).isEqualTo("AAVYUQgGLBM="); // renewing generator and using it | |||
assertThat(underTest.generate()).isEqualTo("AAVYUQgGLBM="); // still using generator2 | |||
assertThat(underTest.generate()).isEqualTo("AAVYUQgGLBM="); // still using generator2 | |||
assertThat(underTest.generate()).isEqualTo("fgkjTAIBAg=="); // renewing generator and using it | |||
assertThat(underTest.generate()).isEqualTo("fgkjTAIBAg=="); // using generator3 | |||
assertThat(underTest.generate()).isEqualTo("fgkjTAIBAg=="); // using generator3 | |||
assertThatThrownBy(() -> { | |||
underTest.generate(); // renewing generator and failing | |||
}) | |||
.isInstanceOf(IllegalStateException.class) | |||
.hasMessage(expected.getMessage()); | |||
assertThat(requestId1).isNotEqualTo(requestId2).isNotEqualTo(requestId3); | |||
} | |||
} |
@@ -64,6 +64,7 @@ public class SearchAzureReposActionIT { | |||
private final AzureDevOpsHttpClient azureDevOpsHttpClient = mock(AzureDevOpsHttpClient.class); | |||
private final Encryption encryption = mock(Encryption.class); | |||
private final WsActionTester ws = new WsActionTester(new SearchAzureReposAction(db.getDbClient(), userSession, azureDevOpsHttpClient)); | |||
private int projectId = 0; | |||
@Before | |||
public void before() { | |||
@@ -334,7 +335,8 @@ public class SearchAzureReposActionIT { | |||
} | |||
private ProjectDto insertProject(AlmSettingDto almSetting, String repoName, String projectName) { | |||
ProjectDto projectDto1 = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectDto projectDto1 = | |||
db.components().insertPrivateProject(dto -> dto.setKey("key_" + projectId).setName("name" + projectId++)).getProjectDto(); | |||
db.almSettings().insertAzureProjectAlmSetting(almSetting, projectDto1, projectAlmSettingDto -> projectAlmSettingDto.setAlmRepo(repoName), | |||
projectAlmSettingDto -> projectAlmSettingDto.setAlmSlug(projectName)); | |||
return projectDto1; |
@@ -86,7 +86,7 @@ public class SearchBitbucketServerReposActionIT { | |||
dto.setAlmSettingUuid(almSetting.getUuid()); | |||
dto.setUserUuid(user.getUuid()); | |||
}); | |||
ProjectDto projectDto = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectDto projectDto = db.components().insertPrivateProject(dto -> dto.setKey("proj_key_1").setName("proj_name_1")).getProjectDto(); | |||
db.almSettings().insertBitbucketProjectAlmSetting(almSetting, projectDto, s -> s.setAlmRepo("projectKey2"), s -> s.setAlmSlug("repo-slug-2")); | |||
AlmIntegrations.SearchBitbucketserverReposWsResponse response = ws.newRequest() | |||
@@ -135,9 +135,9 @@ public class SearchBitbucketServerReposActionIT { | |||
dto.setAlmSettingUuid(almSetting.getUuid()); | |||
dto.setUserUuid(user.getUuid()); | |||
}); | |||
ProjectDto projectDto = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectDto projectDto = db.components().insertPrivateProject(dto -> dto.setName("proj_1").setKey("proj_key_1")).getProjectDto(); | |||
db.almSettings().insertBitbucketProjectAlmSetting(almSetting, projectDto, s -> s.setAlmRepo("projectKey2"), s -> s.setAlmSlug("repo-slug-2")); | |||
db.almSettings().insertBitbucketProjectAlmSetting(almSetting, db.components().insertPrivateProject().getProjectDto(), s -> s.setAlmRepo("projectKey2"), s -> s.setAlmSlug("repo-slug-2")); | |||
db.almSettings().insertBitbucketProjectAlmSetting(almSetting, db.components().insertPrivateProject(dto -> dto.setName("proj_2").setKey("proj_key_2")).getProjectDto(), s -> s.setAlmRepo("projectKey2"), s -> s.setAlmSlug("repo-slug-2")); | |||
AlmIntegrations.SearchBitbucketserverReposWsResponse response = ws.newRequest() | |||
.setParam("almSetting", almSetting.getKey()) |
@@ -124,16 +124,16 @@ public class SearchGitlabReposActionIT { | |||
dto.setUserUuid(user.getUuid()); | |||
dto.setPersonalAccessToken("some-pat"); | |||
}); | |||
ProjectDto projectDto1 = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectDto projectDto1 = db.components().insertPrivateProject(dto -> dto.setKey("proj_key_1").setName("proj_name_1")).getProjectDto(); | |||
db.almSettings().insertGitlabProjectAlmSetting(almSetting, projectDto1); | |||
ProjectDto projectDto2 = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectDto projectDto2 = db.components().insertPrivateProject(dto -> dto.setKey("proj_key_2").setName("proj_name_2")).getProjectDto(); | |||
db.almSettings().insertGitlabProjectAlmSetting(almSetting, projectDto2, projectAlmSettingDto -> projectAlmSettingDto.setAlmRepo("2")); | |||
ProjectDto projectDto3 = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectDto projectDto3 = db.components().insertPrivateProject(dto -> dto.setKey("proj_key_3").setName("proj_name_3")).getProjectDto(); | |||
db.almSettings().insertGitlabProjectAlmSetting(almSetting, projectDto3, projectAlmSettingDto -> projectAlmSettingDto.setAlmRepo("3")); | |||
ProjectDto projectDto4 = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectDto projectDto4 = db.components().insertPrivateProject(dto -> dto.setKey("proj_key_4").setName("proj_name_4")).getProjectDto(); | |||
db.almSettings().insertGitlabProjectAlmSetting(almSetting, projectDto4, projectAlmSettingDto -> projectAlmSettingDto.setAlmRepo("3")); | |||
AlmIntegrations.SearchGitlabReposWsResponse response = ws.newRequest() |
@@ -123,27 +123,27 @@ public class SearchProjectsActionIT { | |||
@DataProvider | |||
public static Object[][] rating_metric_keys() { | |||
return new Object[][] {{SQALE_RATING_KEY}, {RELIABILITY_RATING_KEY}, {SECURITY_RATING_KEY}}; | |||
return new Object[][]{{SQALE_RATING_KEY}, {RELIABILITY_RATING_KEY}, {SECURITY_RATING_KEY}}; | |||
} | |||
@DataProvider | |||
public static Object[][] new_rating_metric_keys() { | |||
return new Object[][] {{NEW_MAINTAINABILITY_RATING_KEY}, {NEW_RELIABILITY_RATING_KEY}, {NEW_SECURITY_RATING_KEY}}; | |||
return new Object[][]{{NEW_MAINTAINABILITY_RATING_KEY}, {NEW_RELIABILITY_RATING_KEY}, {NEW_SECURITY_RATING_KEY}}; | |||
} | |||
@DataProvider | |||
public static Object[][] component_qualifiers_for_valid_editions() { | |||
return new Object[][] { | |||
{new String[] {Qualifiers.PROJECT}, Edition.COMMUNITY}, | |||
{new String[] {Qualifiers.APP, Qualifiers.PROJECT}, Edition.DEVELOPER}, | |||
{new String[] {Qualifiers.APP, Qualifiers.PROJECT}, Edition.ENTERPRISE}, | |||
{new String[] {Qualifiers.APP, Qualifiers.PROJECT}, Edition.DATACENTER}, | |||
return new Object[][]{ | |||
{new String[]{Qualifiers.PROJECT}, Edition.COMMUNITY}, | |||
{new String[]{Qualifiers.APP, Qualifiers.PROJECT}, Edition.DEVELOPER}, | |||
{new String[]{Qualifiers.APP, Qualifiers.PROJECT}, Edition.ENTERPRISE}, | |||
{new String[]{Qualifiers.APP, Qualifiers.PROJECT}, Edition.DATACENTER}, | |||
}; | |||
} | |||
@DataProvider | |||
public static Object[][] community_or_developer_edition() { | |||
return new Object[][] { | |||
return new Object[][]{ | |||
{Edition.COMMUNITY}, | |||
{Edition.DEVELOPER}, | |||
}; | |||
@@ -151,7 +151,7 @@ public class SearchProjectsActionIT { | |||
@DataProvider | |||
public static Object[][] enterprise_or_datacenter_edition() { | |||
return new Object[][] { | |||
return new Object[][]{ | |||
{Edition.ENTERPRISE}, | |||
{Edition.DATACENTER}, | |||
}; | |||
@@ -161,8 +161,10 @@ public class SearchProjectsActionIT { | |||
private DbSession dbSession = db.getSession(); | |||
private PlatformEditionProvider editionProviderMock = mock(PlatformEditionProvider.class); | |||
private PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, new ProjectMeasuresIndexer(dbClient, es.client())); | |||
private ProjectMeasuresIndex index = new ProjectMeasuresIndex(es.client(), new WebAuthorizationTypeSupport(userSession), System2.INSTANCE); | |||
private PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(es, new ProjectMeasuresIndexer(dbClient, | |||
es.client())); | |||
private ProjectMeasuresIndex index = new ProjectMeasuresIndex(es.client(), new WebAuthorizationTypeSupport(userSession), | |||
System2.INSTANCE); | |||
private ProjectMeasuresIndexer projectMeasuresIndexer = new ProjectMeasuresIndexer(db.getDbClient(), es.client()); | |||
private WsActionTester ws = new WsActionTester(new SearchProjectsAction(dbClient, index, userSession, editionProviderMock)); | |||
@@ -217,8 +219,10 @@ public class SearchProjectsActionIT { | |||
Param facets = def.param("facets"); | |||
assertThat(facets.defaultValue()).isNull(); | |||
assertThat(facets.possibleValues()).containsOnly("ncloc", "duplicated_lines_density", "coverage", "sqale_rating", "reliability_rating", "security_rating", "alert_status", | |||
"languages", "tags", "qualifier", "new_reliability_rating", "new_security_rating", "new_maintainability_rating", "new_coverage", "new_duplicated_lines_density", "new_lines", | |||
assertThat(facets.possibleValues()).containsOnly("ncloc", "duplicated_lines_density", "coverage", "sqale_rating", "reliability_rating" | |||
, "security_rating", "alert_status", | |||
"languages", "tags", "qualifier", "new_reliability_rating", "new_security_rating", "new_maintainability_rating", "new_coverage", | |||
"new_duplicated_lines_density", "new_lines", | |||
"security_review_rating", "security_hotspots_reviewed", "new_security_hotspots_reviewed", "new_security_review_rating"); | |||
} | |||
@@ -343,19 +347,22 @@ public class SearchProjectsActionIT { | |||
userSession.logIn(); | |||
MetricDto nclocMetric = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name())); | |||
MetricDto languagesDistributionMetric = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType("DATA")); | |||
ComponentDto project1 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("<null>=2;java=6;xoo=18"))); | |||
ComponentDto project1 = insertProject(new Measure(languagesDistributionMetric, | |||
c -> c.setValue(null).setData("<null>=2;java=6;xoo=18"))); | |||
db.measures().insertLiveMeasure(project1, nclocMetric, m -> m.setValue(26d)); | |||
ComponentDto project2 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("java=3;xoo=9"))); | |||
db.measures().insertLiveMeasure(project2, nclocMetric, m -> m.setValue(12d)); | |||
ComponentDto project3 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("xoo=1"))); | |||
db.measures().insertLiveMeasure(project3, nclocMetric, m -> m.setValue(1d)); | |||
ComponentDto project4 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("<null>=1;java=5;xoo=13"))); | |||
ComponentDto project4 = insertProject(new Measure(languagesDistributionMetric, | |||
c -> c.setValue(null).setData("<null>=1;java=5;xoo=13"))); | |||
db.measures().insertLiveMeasure(project4, nclocMetric, m -> m.setValue(19d)); | |||
index(); | |||
SearchProjectsWsResponse result = call(request.setFilter("languages IN (java, js, <null>)")); | |||
assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project2.getKey(), project4.getKey()); | |||
assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project2.getKey(), | |||
project4.getKey()); | |||
} | |||
@Test | |||
@@ -522,10 +529,13 @@ public class SearchProjectsActionIT { | |||
insertProject(c -> c.setKey("sonarqube").setName("Sonar Qube")); | |||
index(); | |||
assertThat(call(request.setFilter("query = \"Groovy\"")).getComponentsList()).extracting(Component::getName).containsOnly("Sonar Groovy"); | |||
assertThat(call(request.setFilter("query = \"oNar\"")).getComponentsList()).extracting(Component::getName).containsOnly("Sonar Java", "Sonar Groovy", "Sonar Markdown", | |||
assertThat(call(request.setFilter("query = \"Groovy\"")).getComponentsList()).extracting(Component::getName).containsOnly("Sonar " + | |||
"Groovy"); | |||
assertThat(call(request.setFilter("query = \"oNar\"")).getComponentsList()).extracting(Component::getName).containsOnly("Sonar Java", | |||
"Sonar Groovy", "Sonar Markdown", | |||
"Sonar Qube"); | |||
assertThat(call(request.setFilter("query = \"sonar-java\"")).getComponentsList()).extracting(Component::getName).containsOnly("Sonar Java"); | |||
assertThat(call(request.setFilter("query = \"sonar-java\"")).getComponentsList()).extracting(Component::getName).containsOnly("Sonar " + | |||
"Java"); | |||
} | |||
@Test | |||
@@ -769,7 +779,8 @@ public class SearchProjectsActionIT { | |||
userSession.logIn(); | |||
MetricDto nclocMetric = db.measures().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name())); | |||
MetricDto languagesDistributionMetric = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType("DATA")); | |||
ComponentDto project1 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("<null>=2;java=6;xoo=18"))); | |||
ComponentDto project1 = insertProject(new Measure(languagesDistributionMetric, | |||
c -> c.setValue(null).setData("<null>=2;java=6;xoo=18"))); | |||
db.measures().insertLiveMeasure(project1, nclocMetric, m -> m.setValue(26d)); | |||
ComponentDto project2 = insertProject(new Measure(languagesDistributionMetric, c -> c.setValue(null).setData("java=5;xoo=19"))); | |||
db.measures().insertLiveMeasure(project2, nclocMetric, m -> m.setValue(24d)); | |||
@@ -1116,7 +1127,8 @@ public class SearchProjectsActionIT { | |||
SearchProjectsWsResponse result = call(request); | |||
assertThat(result.getComponentsList()).extracting(Component::getName).containsExactly("Sonar Groovy", "Sonar Java", "Sonar Markdown", "Sonar Qube"); | |||
assertThat(result.getComponentsList()).extracting(Component::getName).containsExactly("Sonar Groovy", "Sonar Java", "Sonar Markdown", | |||
"Sonar Qube"); | |||
} | |||
@Test | |||
@@ -1154,10 +1166,14 @@ public class SearchProjectsActionIT { | |||
public void sort_by_quality_gate_then_by_name() { | |||
userSession.logIn(); | |||
MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name())); | |||
ComponentDto project1 = insertProject(c -> c.setName("Sonar Java"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR"))); | |||
ComponentDto project2 = insertProject(c -> c.setName("Sonar Groovy"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR"))); | |||
ComponentDto project3 = insertProject(c -> c.setName("Sonar Markdown"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK"))); | |||
ComponentDto project4 = insertProject(c -> c.setName("Sonar Qube"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK"))); | |||
ComponentDto project1 = insertProject(c -> c.setName("Sonar Java"), new Measure(qualityGateStatus, c -> c.setValue(null).setData( | |||
"ERROR"))); | |||
ComponentDto project2 = insertProject(c -> c.setName("Sonar Groovy"), new Measure(qualityGateStatus, c -> c.setValue(null).setData( | |||
"ERROR"))); | |||
ComponentDto project3 = insertProject(c -> c.setName("Sonar Markdown"), new Measure(qualityGateStatus, c -> c.setValue(null).setData( | |||
"OK"))); | |||
ComponentDto project4 = insertProject(c -> c.setName("Sonar Qube"), new Measure(qualityGateStatus, | |||
c -> c.setValue(null).setData("OK"))); | |||
index(); | |||
assertThat(call(request.setSort(QUALITY_GATE_STATUS).setAsc(true)).getComponentsList()).extracting(Component::getKey) | |||
@@ -1235,7 +1251,8 @@ public class SearchProjectsActionIT { | |||
MetricDto leakProjects = db.measures().insertMetric(c -> c.setKey(LEAK_PROJECTS_KEY).setValueType(DATA.name())); | |||
ComponentDto application1 = insertApplication( | |||
new Measure(leakProjects, c -> c.setData("{\"leakProjects\":[{\"id\": 1, \"leak\":20000000000}, {\"id\": 2, \"leak\":10000000000}]}"))); | |||
new Measure(leakProjects, c -> c.setData("{\"leakProjects\":[{\"id\": 1, \"leak\":20000000000}, {\"id\": 2, " + | |||
"\"leak\":10000000000}]}"))); | |||
db.components().insertSnapshot(application1); | |||
authorizationIndexerTester.allowOnlyAnyone(db.components().getProjectDtoByMainBranch(application1)); | |||
@@ -1254,9 +1271,9 @@ public class SearchProjectsActionIT { | |||
@Test | |||
public void return_visibility_flag() { | |||
userSession.logIn(); | |||
ProjectDto privateProject = db.components().insertPublicProject().getProjectDto(); | |||
ProjectDto privateProject = db.components().insertPublicProject(componentDto -> componentDto.setName("proj_A")).getProjectDto(); | |||
authorizationIndexerTester.allowOnlyAnyone(privateProject); | |||
ProjectDto publicProject = db.components().insertPrivateProject().getProjectDto(); | |||
ProjectDto publicProject = db.components().insertPrivateProject(componentDto -> componentDto.setName("proj_B")).getProjectDto(); | |||
authorizationIndexerTester.allowOnlyAnyone(publicProject); | |||
index(); | |||
@@ -1328,14 +1345,17 @@ public class SearchProjectsActionIT { | |||
addFavourite(project.getUuid(), project.getKey(), project.getName(), project.getQualifier()); | |||
} | |||
private void addFavourite(@Nullable String entityUuid, @Nullable String entityKey, @Nullable String entityName, @Nullable String qualifier) { | |||
private void addFavourite(@Nullable String entityUuid, @Nullable String entityKey, @Nullable String entityName, | |||
@Nullable String qualifier) { | |||
dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto().setKey("favourite") | |||
.setEntityUuid(entityUuid).setUserUuid(userSession.getUuid()), userSession.getLogin(), entityKey, entityName, qualifier); | |||
dbSession.commit(); | |||
} | |||
private int projectCount = 0; | |||
private ComponentDto insertProject(Measure... measures) { | |||
return insertProject(defaults(), defaults(), measures); | |||
return insertProject(defaults(), projectDto -> projectDto.setName("project_" + projectCount++), measures); | |||
} | |||
private ComponentDto insertProject(Consumer<ComponentDto> componentConsumer, Measure... measures) { | |||
@@ -1348,8 +1368,10 @@ public class SearchProjectsActionIT { | |||
return project; | |||
} | |||
private int applicationCount = 0; | |||
private ComponentDto insertApplication(Measure... measures) { | |||
return insertApplication(defaults(), measures); | |||
return insertApplication(componentDto -> componentDto.setName("app_" + applicationCount++), measures); | |||
} | |||
private ComponentDto insertApplication(Consumer<ComponentDto> componentConsumer, Measure... measures) { |
@@ -167,9 +167,9 @@ public class SearchActionIT { | |||
@Test | |||
public void test_paging() { | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(); | |||
ProjectDto project1 = db.components().insertPublicProject().getProjectDto(); | |||
ProjectDto project2 = db.components().insertPublicProject().getProjectDto(); | |||
ProjectDto project3 = db.components().insertPublicProject().getProjectDto(); | |||
ProjectDto project1 = db.components().insertPublicProject(dto -> dto.setName("proj_1")).getProjectDto(); | |||
ProjectDto project2 = db.components().insertPublicProject(dto -> dto.setName("proj_2")).getProjectDto(); | |||
ProjectDto project3 = db.components().insertPublicProject(dto -> dto.setName("proj_3")).getProjectDto(); | |||
db.qualityGates().associateProjectToQualityGate(project1, qualityGate); | |||
// Return partial result on first page |
@@ -49,6 +49,7 @@ import org.sonar.server.ws.WsActionTester; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | |||
import static org.mockito.Mockito.mock; | |||
import static org.sonar.core.util.SequenceUuidFactory.UUID_1; | |||
import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_PROFILES; | |||
import static org.sonar.test.JsonAssert.assertJson; | |||
@@ -116,9 +117,8 @@ public class CopyActionIT { | |||
.setParam("toName", "target-name") | |||
.execute(); | |||
String generatedUuid = "1"; | |||
assertJson(response.getInput()).isSimilarTo("{" + | |||
" \"key\": \"" + generatedUuid + "\"," + | |||
" \"key\": \"" + UUID_1 + "\"," + | |||
" \"name\": \"target-name\"," + | |||
" \"language\": \"lang1\"," + | |||
" \"languageName\": \"Lang1\"," + | |||
@@ -126,13 +126,13 @@ public class CopyActionIT { | |||
" \"isInherited\": false" + | |||
"}"); | |||
QProfileDto loadedProfile = db.getDbClient().qualityProfileDao().selectByNameAndLanguage(db.getSession(), "target-name", sourceProfile.getLanguage()); | |||
assertThat(loadedProfile.getKee()).isEqualTo(generatedUuid); | |||
assertThat(loadedProfile.getKee()).isEqualTo(UUID_1); | |||
assertThat(loadedProfile.getParentKee()).isNull(); | |||
assertThat(backuper.copiedProfile.getKee()).isEqualTo(sourceProfile.getKee()); | |||
assertThat(backuper.toProfile.getLanguage()).isEqualTo(sourceProfile.getLanguage()); | |||
assertThat(backuper.toProfile.getName()).isEqualTo("target-name"); | |||
assertThat(backuper.toProfile.getKee()).isEqualTo(generatedUuid); | |||
assertThat(backuper.toProfile.getKee()).isEqualTo(UUID_1); | |||
assertThat(backuper.toProfile.getParentKee()).isNull(); | |||
} | |||
@@ -176,9 +176,8 @@ public class CopyActionIT { | |||
.setParam("toName", "target-name") | |||
.execute(); | |||
String generatedUuid = "1"; | |||
assertJson(response.getInput()).isSimilarTo("{" + | |||
" \"key\": \"" + generatedUuid + "\"," + | |||
" \"key\": \"" + UUID_1 + "\"," + | |||
" \"name\": \"target-name\"," + | |||
" \"language\": \"lang1\"," + | |||
" \"languageName\": \"Lang1\"," + | |||
@@ -186,13 +185,13 @@ public class CopyActionIT { | |||
" \"isInherited\": true" + | |||
"}"); | |||
QProfileDto loadedProfile = db.getDbClient().qualityProfileDao().selectByNameAndLanguage(db.getSession(), "target-name", sourceProfile.getLanguage()); | |||
assertThat(loadedProfile.getKee()).isEqualTo(generatedUuid); | |||
assertThat(loadedProfile.getKee()).isEqualTo(UUID_1); | |||
assertThat(loadedProfile.getParentKee()).isEqualTo(parentProfile.getKee()); | |||
assertThat(backuper.copiedProfile.getKee()).isEqualTo(sourceProfile.getKee()); | |||
assertThat(backuper.toProfile.getLanguage()).isEqualTo(sourceProfile.getLanguage()); | |||
assertThat(backuper.toProfile.getName()).isEqualTo("target-name"); | |||
assertThat(backuper.toProfile.getKee()).isEqualTo(generatedUuid); | |||
assertThat(backuper.toProfile.getKee()).isEqualTo(UUID_1); | |||
assertThat(backuper.toProfile.getParentKee()).isEqualTo(parentProfile.getKee()); | |||
} | |||
@@ -19,18 +19,28 @@ | |||
*/ | |||
package org.sonar.core.util; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import org.apache.commons.lang.StringUtils; | |||
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". | |||
* ids with the structure of a UUID v4, but containing a sequence of integers ("1", "2", ...). It starts with "1". | |||
*/ | |||
@VisibleForTesting | |||
public class SequenceUuidFactory implements UuidFactory { | |||
private static final String UUID_PREFIX = "00000000-0000-0000-0000-"; | |||
public static final String UUID_1 = UUID_PREFIX + "000000000001"; | |||
public static final String UUID_2 = UUID_PREFIX + "000000000002"; | |||
public static final String UUID_3 = UUID_PREFIX + "000000000003"; | |||
private final AtomicInteger id = new AtomicInteger(1); | |||
@Override | |||
public String create() { | |||
return String.valueOf(id.getAndIncrement()); | |||
int currentId = id.getAndIncrement(); | |||
return UUID_PREFIX + StringUtils.leftPad(String.valueOf(currentId), 12, "0"); | |||
} | |||
} |
@@ -19,17 +19,23 @@ | |||
*/ | |||
package org.sonar.core.util; | |||
import java.security.SecureRandom; | |||
import com.google.common.annotations.VisibleForTesting; | |||
/** | |||
* NOT thread safe | |||
* About 10x faster than {@link UuidFactoryImpl} | |||
* It does not take into account the MAC address to calculate the ids, so it is machine-independent. | |||
* This class must only be used for testing. | |||
* @deprecated use {@link UuidFactoryImpl} or {@link SequenceUuidFactory} instead. | |||
*/ | |||
@VisibleForTesting | |||
@Deprecated(since = "10.4") | |||
public class UuidFactoryFast implements UuidFactory { | |||
private static UuidFactoryFast instance = new UuidFactoryFast(); | |||
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); | |||
private static int sequenceNumber = new SecureRandom().nextInt(); | |||
public static UuidFactoryFast getInstance() { | |||
return instance; | |||
} | |||
private UuidFactoryFast() { | |||
// | |||
@@ -37,42 +43,8 @@ public class UuidFactoryFast implements UuidFactory { | |||
@Override | |||
public String create() { | |||
long timestamp = System.currentTimeMillis(); | |||
byte[] uuidBytes = new byte[9]; | |||
// Only use lower 6 bytes of the timestamp (this will suffice beyond the year 10000): | |||
putLong(uuidBytes, timestamp, 0, 6); | |||
// Sequence number adds 3 bytes: | |||
putLong(uuidBytes, getSequenceNumber(), 6, 3); | |||
return byteArrayToHex(uuidBytes); | |||
return UuidFactoryImpl.INSTANCE.create(); | |||
} | |||
public static UuidFactoryFast getInstance() { | |||
return instance; | |||
} | |||
private static int getSequenceNumber() { | |||
return sequenceNumber++; | |||
} | |||
/** Puts the lower numberOfLongBytes from l into the array, starting index pos. */ | |||
private static void putLong(byte[] array, long l, int pos, int numberOfLongBytes) { | |||
for (int i = 0; i < numberOfLongBytes; ++i) { | |||
array[pos + numberOfLongBytes - i - 1] = (byte) (l >>> (i * 8)); | |||
} | |||
} | |||
public static String byteArrayToHex(byte[] bytes) { | |||
char[] hexChars = new char[bytes.length * 2]; | |||
for (int j = 0; j < bytes.length; j++) { | |||
int v = bytes[j] & 0xFF; | |||
hexChars[j * 2] = HEX_ARRAY[v >>> 4]; | |||
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; | |||
} | |||
return new String(hexChars); | |||
} | |||
} |
@@ -19,7 +19,7 @@ | |||
*/ | |||
package org.sonar.core.util; | |||
import org.apache.commons.codec.binary.Base64; | |||
import java.util.UUID; | |||
/** | |||
*/ | |||
@@ -31,11 +31,9 @@ public enum UuidFactoryImpl implements UuidFactory { | |||
*/ | |||
INSTANCE; | |||
private final UuidGenerator uuidGenerator = new UuidGeneratorImpl(); | |||
@Override | |||
public String create() { | |||
return Base64.encodeBase64URLSafeString(uuidGenerator.generate()); | |||
return UUID.randomUUID().toString(); | |||
} | |||
} |
@@ -1,80 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.util; | |||
/** | |||
* A generator of UUID as a byte array which is made of two parts: | |||
* <ul> | |||
* <li>a base, which is machine and time dependant and therefor will change with time</li> | |||
* <li>an increment</li> | |||
* </ul> | |||
* | |||
* <p> | |||
* This generator can be used in two ways: | |||
* <ul> | |||
* <li>either the base and the increment are changed for each UUID (with time for the base, with each call to | |||
* {@link #generate()} for the increment) and {@link #generate()} should be used</li> | |||
* <li>or the increment can be the only changing part (for performance boost and less concurrency) and | |||
* {@link #withFixedBase()} should be used.</li> | |||
* </ul> | |||
* </p> | |||
* | |||
* <p> | |||
* <strong>Warning:</strong> {@link WithFixedBase#generate(int)} can be considerably faster than {@link #generate()} but | |||
* is limited to generate only 2^23-1 unique values. | |||
* </p> | |||
* | |||
* <p> | |||
* Heavily inspired from Elasticsearch {@code TimeBasedUUIDGenerator}, which could be directly | |||
* used the day {@code UuidFactoryImpl} is moved outside module sonar-core. | |||
* See https://github.com/elastic/elasticsearch/blob/master/core/src/main/java/org/elasticsearch/common/TimeBasedUUIDGenerator.java | |||
* </p> | |||
*/ | |||
public interface UuidGenerator { | |||
/** | |||
* Generates a UUID which base and increment are always different from any other value provided by this method. | |||
*/ | |||
byte[] generate(); | |||
/** | |||
* Provide a new UUID generating instance which will allow generation of UUIDs which base is constant and can | |||
* vary according to a provided increment value (see {@link WithFixedBase#generate(int)}). | |||
*/ | |||
WithFixedBase withFixedBase(); | |||
@FunctionalInterface | |||
interface WithFixedBase { | |||
/** | |||
* Generate a new unique universal identifier using the last 3 bytes of the specified int. | |||
* <p> | |||
* <strong>This implies that this method of a given {@link WithFixedBase} instance can only generate up to | |||
* 2^23-1 unique values if provided with unique int arguments.</strong> | |||
* </p> | |||
* <p> | |||
* This is up to the caller to ensure that unique int values are provided to a given instance's {@link #generate(int)} | |||
* method. | |||
* </p> | |||
* <p> | |||
* This method is faster than {@link UuidGenerator#generate()} due to less computation and less internal concurrency. | |||
* </p> | |||
*/ | |||
byte[] generate(int increment); | |||
} | |||
} |
@@ -29,7 +29,6 @@ import java.util.stream.IntStream; | |||
import java.util.stream.Stream; | |||
import org.junit.Test; | |||
import org.junit.runner.RunWith; | |||
import org.sonar.core.util.UuidFactoryImpl; | |||
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; | |||
import static org.apache.commons.lang.StringUtils.repeat; | |||
@@ -47,6 +46,8 @@ import static org.sonar.core.platform.ServerId.UUID_DATASET_ID_LENGTH; | |||
@RunWith(DataProviderRunner.class) | |||
public class ServerIdTest { | |||
private static final String OLD_UUID_FORMAT = "AY0XR6neBaNHvsTBjkC2"; | |||
@Test | |||
public void parse_throws_NPE_if_argument_is_null() { | |||
assertThatThrownBy(() -> ServerId.parse(null)) | |||
@@ -84,7 +85,7 @@ public class ServerIdTest { | |||
String startWithSplitChar = SPLIT_CHARACTER + randomAlphabetic(DATABASE_ID_LENGTH - 1); | |||
Stream<String> databaseIds = Stream.of( | |||
UuidFactoryImpl.INSTANCE.create(), | |||
OLD_UUID_FORMAT, | |||
randomAlphabetic(NOT_UUID_DATASET_ID_LENGTH), | |||
randomAlphabetic(UUID_DATASET_ID_LENGTH), | |||
repeat(SPLIT_CHARACTER + "", NOT_UUID_DATASET_ID_LENGTH), | |||
@@ -130,7 +131,7 @@ public class ServerIdTest { | |||
@DataProvider | |||
public static Object[][] validOldFormatServerIds() { | |||
return new Object[][] { | |||
{UuidFactoryImpl.INSTANCE.create()}, | |||
{OLD_UUID_FORMAT}, | |||
{randomAlphabetic(NOT_UUID_DATASET_ID_LENGTH)}, | |||
{repeat(SPLIT_CHARACTER + "", NOT_UUID_DATASET_ID_LENGTH)}, | |||
{randomAlphabetic(UUID_DATASET_ID_LENGTH)}, | |||
@@ -158,7 +159,7 @@ public class ServerIdTest { | |||
{randomAlphabetic(DATABASE_ID_LENGTH), randomAlphabetic(UUID_DATASET_ID_LENGTH)}, | |||
{randomAlphabetic(DATABASE_ID_LENGTH), repeat(SPLIT_CHARACTER + "", NOT_UUID_DATASET_ID_LENGTH)}, | |||
{randomAlphabetic(DATABASE_ID_LENGTH), repeat(SPLIT_CHARACTER + "", UUID_DATASET_ID_LENGTH)}, | |||
{randomAlphabetic(DATABASE_ID_LENGTH), UuidFactoryImpl.INSTANCE.create()}, | |||
{randomAlphabetic(DATABASE_ID_LENGTH), OLD_UUID_FORMAT}, | |||
}; | |||
} | |||
@@ -26,12 +26,20 @@ import static org.assertj.core.api.Assertions.assertThat; | |||
public class SequenceUuidFactoryTest { | |||
private SequenceUuidFactory underTest = new SequenceUuidFactory(); | |||
private final 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)); | |||
} | |||
assertThat(underTest.create()).isEqualTo("00000000-0000-0000-0000-000000000001"); | |||
assertThat(underTest.create()).isEqualTo("00000000-0000-0000-0000-000000000002"); | |||
assertThat(underTest.create()).isEqualTo("00000000-0000-0000-0000-000000000003"); | |||
assertThat(underTest.create()).isEqualTo("00000000-0000-0000-0000-000000000004"); | |||
assertThat(underTest.create()).isEqualTo("00000000-0000-0000-0000-000000000005"); | |||
assertThat(underTest.create()).isEqualTo("00000000-0000-0000-0000-000000000006"); | |||
assertThat(underTest.create()).isEqualTo("00000000-0000-0000-0000-000000000007"); | |||
assertThat(underTest.create()).isEqualTo("00000000-0000-0000-0000-000000000008"); | |||
assertThat(underTest.create()).isEqualTo("00000000-0000-0000-0000-000000000009"); | |||
assertThat(underTest.create()).isEqualTo("00000000-0000-0000-0000-000000000010"); | |||
} | |||
} |
@@ -1,130 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.util; | |||
import java.util.ArrayList; | |||
import java.util.Base64; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Set; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class UuidGeneratorImplTest { | |||
private UuidGeneratorImpl underTest = new UuidGeneratorImpl(); | |||
@Test | |||
public void generate_returns_unique_values_without_common_initial_letter_given_more_than_one_millisecond_between_generate_calls() throws InterruptedException { | |||
Base64.Encoder encoder = Base64.getEncoder(); | |||
int count = 30; | |||
Set<String> uuids = new HashSet<>(count); | |||
for (int i = 0; i < count; i++) { | |||
Thread.sleep(5); | |||
uuids.add(encoder.encodeToString(underTest.generate())); | |||
} | |||
assertThat(uuids).hasSize(count); | |||
Iterator<String> iterator = uuids.iterator(); | |||
String firstUuid = iterator.next(); | |||
String base = firstUuid.substring(0, firstUuid.length() - 4); | |||
for (int i = 1; i < count; i++) { | |||
assertThat(iterator.next()).describedAs("i=" + i).doesNotStartWith(base); | |||
} | |||
} | |||
@Test | |||
public void generate_concurrent_test() throws InterruptedException { | |||
int rounds = 500; | |||
List<byte[]> uuids1 = new ArrayList<>(rounds); | |||
List<byte[]> uuids2 = new ArrayList<>(rounds); | |||
Thread t1 = new Thread(() -> { | |||
for (int i = 0; i < rounds; i++) { | |||
uuids1.add(underTest.generate()); | |||
} | |||
}); | |||
Thread t2 = new Thread(() -> { | |||
for (int i = 0; i < rounds; i++) { | |||
uuids2.add(underTest.generate()); | |||
} | |||
}); | |||
t1.start(); | |||
t2.start(); | |||
t1.join(); | |||
t2.join(); | |||
Base64.Encoder encoder = Base64.getEncoder(); | |||
Set<String> uuids = new HashSet<>(rounds * 2); | |||
uuids1.forEach(bytes -> uuids.add(encoder.encodeToString(bytes))); | |||
uuids2.forEach(bytes -> uuids.add(encoder.encodeToString(bytes))); | |||
assertThat(uuids).hasSize(rounds * 2); | |||
} | |||
@Test | |||
public void generate_from_FixedBase_returns_unique_values_where_only_last_4_later_letter_change() { | |||
Base64.Encoder encoder = Base64.getEncoder(); | |||
int count = 100_000; | |||
Set<String> uuids = new HashSet<>(count); | |||
UuidGenerator.WithFixedBase withFixedBase = underTest.withFixedBase(); | |||
for (int i = 0; i < count; i++) { | |||
uuids.add(encoder.encodeToString(withFixedBase.generate(i))); | |||
} | |||
assertThat(uuids).hasSize(count); | |||
Iterator<String> iterator = uuids.iterator(); | |||
String firstUuid = iterator.next(); | |||
String base = firstUuid.substring(0, firstUuid.length() - 4); | |||
while (iterator.hasNext()) { | |||
assertThat(iterator.next()).startsWith(base); | |||
} | |||
} | |||
@Test | |||
public void generate_from_FixedBase_concurrent_test() throws InterruptedException { | |||
UuidGenerator.WithFixedBase withFixedBase = underTest.withFixedBase(); | |||
int rounds = 500; | |||
List<byte[]> uuids1 = new ArrayList<>(rounds); | |||
List<byte[]> uuids2 = new ArrayList<>(rounds); | |||
AtomicInteger cnt = new AtomicInteger(); | |||
Thread t1 = new Thread(() -> { | |||
for (int i = 0; i < rounds; i++) { | |||
uuids1.add(withFixedBase.generate(cnt.getAndIncrement())); | |||
} | |||
}); | |||
Thread t2 = new Thread(() -> { | |||
for (int i = 0; i < rounds; i++) { | |||
uuids2.add(withFixedBase.generate(cnt.getAndIncrement())); | |||
} | |||
}); | |||
t1.start(); | |||
t2.start(); | |||
t1.join(); | |||
t2.join(); | |||
Base64.Encoder encoder = Base64.getEncoder(); | |||
Set<String> uuids = new HashSet<>(rounds * 2); | |||
uuids1.forEach(bytes -> uuids.add(encoder.encodeToString(bytes))); | |||
uuids2.forEach(bytes -> uuids.add(encoder.encodeToString(bytes))); | |||
assertThat(uuids).hasSize(rounds * 2); | |||
} | |||
} |