From 0253f591bd017cb9a9c8e365fa074a7bc4b0a2a0 Mon Sep 17 00:00:00 2001 From: Damien Urruty Date: Thu, 24 Feb 2022 14:51:07 +0100 Subject: [PATCH] SONAR-15919 Rename RuleSetChangedEvent and fix payload format --- .../AppNodesClusterHostsConsistencyTest.java | 4 +- .../process/cluster/hz/HazelcastMember.java | 4 +- .../cluster/hz/HazelcastMemberImpl.java | 8 +- ...ributedRuleActivatorEventsDistributor.java | 4 +- .../QualityProfileChangeEventServiceImpl.java | 6 +- .../RuleActivatorEventsDistributor.java | 6 +- ...ndaloneRuleActivatorEventsDistributor.java | 4 +- .../sonarlint/SonarLintClientsRegistry.java | 42 +++--- .../SonarLintClientsRegistryTest.java | 34 ++--- ...lityProfileChangeEventServiceImplTest.java | 46 +++---- .../sonarlint/rule-change-event-data.json | 35 +++++ .../pushapi/sonarlint/rule-change-event.json | 38 ------ .../core/util/RuleActivationListener.java | 4 +- ...ngeEvent.java => RuleSetChangedEvent.java} | 28 ++-- ...Test.java => RuleSetChangedEventTest.java} | 8 +- .../main/java/org/sonar/test/EventAssert.java | 123 ++++++++++++++++++ .../java/org/sonar/test/EventAssertTest.java | 111 ++++++++++++++++ .../sonar/test/EventAssertTest/sample.json | 1 + 18 files changed, 363 insertions(+), 143 deletions(-) create mode 100644 server/sonar-webserver-pushapi/src/test/resources/org/sonar/server/pushapi/sonarlint/rule-change-event-data.json delete mode 100644 server/sonar-webserver-pushapi/src/test/resources/org/sonar/server/pushapi/sonarlint/rule-change-event.json rename sonar-core/src/main/java/org/sonar/core/util/{RuleSetChangeEvent.java => RuleSetChangedEvent.java} (69%) rename sonar-core/src/test/java/org/sonar/core/util/{RuleSetChangeEventTest.java => RuleSetChangedEventTest.java} (86%) create mode 100644 sonar-testing-harness/src/main/java/org/sonar/test/EventAssert.java create mode 100644 sonar-testing-harness/src/test/java/org/sonar/test/EventAssertTest.java create mode 100644 sonar-testing-harness/src/test/resources/org/sonar/test/EventAssertTest/sample.json diff --git a/server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java b/server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java index cf0c8a1cd9e..3c4b5235f5c 100644 --- a/server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java +++ b/server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java @@ -41,7 +41,7 @@ import org.junit.Before; import org.junit.Test; import org.sonar.application.config.TestAppSettings; import org.sonar.core.util.RuleActivationListener; -import org.sonar.core.util.RuleSetChangeEvent; +import org.sonar.core.util.RuleSetChangedEvent; import org.sonar.process.cluster.hz.DistributedAnswer; import org.sonar.process.cluster.hz.DistributedCall; import org.sonar.process.cluster.hz.DistributedCallback; @@ -202,7 +202,7 @@ public class AppNodesClusterHostsConsistencyTest { } @Override - public void publishEvent(RuleSetChangeEvent event) { + public void publishEvent(RuleSetChangedEvent event) { } diff --git a/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java b/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java index 320a867eb29..2f0ec8cb8e0 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java +++ b/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java @@ -27,7 +27,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.locks.Lock; import org.sonar.core.util.RuleActivationListener; -import org.sonar.core.util.RuleSetChangeEvent; +import org.sonar.core.util.RuleSetChangedEvent; import org.sonar.process.ProcessId; public interface HazelcastMember extends AutoCloseable { @@ -110,7 +110,7 @@ public interface HazelcastMember extends AutoCloseable { void subscribeRuleActivationTopic(RuleActivationListener listener); - void publishEvent(RuleSetChangeEvent event); + void publishEvent(RuleSetChangedEvent event); @Override void close(); diff --git a/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java b/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java index 9b223cb0c61..89a9b75f994 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java +++ b/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java @@ -40,7 +40,7 @@ import java.util.concurrent.locks.Lock; import java.util.stream.Collectors; import org.slf4j.LoggerFactory; import org.sonar.core.util.RuleActivationListener; -import org.sonar.core.util.RuleSetChangeEvent; +import org.sonar.core.util.RuleSetChangedEvent; class HazelcastMemberImpl implements HazelcastMember { @@ -131,13 +131,13 @@ class HazelcastMemberImpl implements HazelcastMember { @Override public void subscribeRuleActivationTopic(RuleActivationListener listener) { - ITopic topic = hzInstance.getTopic("ruleActivated"); - MessageListener hzListener = message -> listener.listen(message.getMessageObject()); + ITopic topic = hzInstance.getTopic("ruleActivated"); + MessageListener hzListener = message -> listener.listen(message.getMessageObject()); topic.addMessageListener(hzListener); } @Override - public void publishEvent(RuleSetChangeEvent event) { + public void publishEvent(RuleSetChangedEvent event) { hzInstance.getTopic("ruleActivated").publish(event); } diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/DistributedRuleActivatorEventsDistributor.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/DistributedRuleActivatorEventsDistributor.java index 7e63b06c112..c5b4b9fae9d 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/DistributedRuleActivatorEventsDistributor.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/DistributedRuleActivatorEventsDistributor.java @@ -21,7 +21,7 @@ package org.sonar.server.pushapi.qualityprofile; import org.sonar.api.server.ServerSide; import org.sonar.core.util.RuleActivationListener; -import org.sonar.core.util.RuleSetChangeEvent; +import org.sonar.core.util.RuleSetChangedEvent; import org.sonar.process.cluster.hz.HazelcastMember; @ServerSide @@ -39,7 +39,7 @@ public class DistributedRuleActivatorEventsDistributor implements RuleActivatorE } @Override - public void pushEvent(RuleSetChangeEvent event) { + public void pushEvent(RuleSetChangedEvent event) { hazelcastMember.publishEvent(event); } } diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/QualityProfileChangeEventServiceImpl.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/QualityProfileChangeEventServiceImpl.java index 809483f1355..c5386a36ef7 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/QualityProfileChangeEventServiceImpl.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/QualityProfileChangeEventServiceImpl.java @@ -33,7 +33,7 @@ import org.jetbrains.annotations.NotNull; import org.sonar.api.server.ServerSide; import org.sonar.core.util.ParamChange; import org.sonar.core.util.RuleChange; -import org.sonar.core.util.RuleSetChangeEvent; +import org.sonar.core.util.RuleSetChangedEvent; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.project.ProjectDto; @@ -73,7 +73,7 @@ public class QualityProfileChangeEventServiceImpl implements QualityProfileChang return; } - RuleSetChangeEvent event = new RuleSetChangeEvent(new String[] {project.getKey()}, activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new RuleChange[0])); + RuleSetChangedEvent event = new RuleSetChangedEvent(new String[] {project.getKey()}, activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new RuleChange[0])); eventsDistributor.pushEvent(event); } @@ -173,7 +173,7 @@ public class QualityProfileChangeEventServiceImpl implements QualityProfileChang return; } - RuleSetChangeEvent event = new RuleSetChangeEvent(projectKeys.toArray(new String[0]), activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new RuleChange[0])); + RuleSetChangedEvent event = new RuleSetChangedEvent(projectKeys.toArray(new String[0]), activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new RuleChange[0])); eventsDistributor.pushEvent(event); } diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/RuleActivatorEventsDistributor.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/RuleActivatorEventsDistributor.java index 366ffc66d81..0d8a6c26c8b 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/RuleActivatorEventsDistributor.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/RuleActivatorEventsDistributor.java @@ -20,11 +20,11 @@ package org.sonar.server.pushapi.qualityprofile; import org.sonar.core.util.RuleActivationListener; -import org.sonar.core.util.RuleSetChangeEvent; +import org.sonar.core.util.RuleSetChangedEvent; public interface RuleActivatorEventsDistributor { void subscribe(RuleActivationListener listener); - void pushEvent(RuleSetChangeEvent event); -} \ No newline at end of file + void pushEvent(RuleSetChangedEvent event); +} diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/StandaloneRuleActivatorEventsDistributor.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/StandaloneRuleActivatorEventsDistributor.java index 656ec753076..3207154ac2e 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/StandaloneRuleActivatorEventsDistributor.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/StandaloneRuleActivatorEventsDistributor.java @@ -23,7 +23,7 @@ import java.util.ArrayList; import java.util.List; import org.sonar.api.server.ServerSide; import org.sonar.core.util.RuleActivationListener; -import org.sonar.core.util.RuleSetChangeEvent; +import org.sonar.core.util.RuleSetChangedEvent; @ServerSide public class StandaloneRuleActivatorEventsDistributor implements RuleActivatorEventsDistributor { @@ -36,7 +36,7 @@ public class StandaloneRuleActivatorEventsDistributor implements RuleActivatorEv } @Override - public void pushEvent(RuleSetChangeEvent event) { + public void pushEvent(RuleSetChangedEvent event) { listeners.forEach(l -> l.listen(event)); } } diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java index 5b73339991d..fa712ace19c 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java @@ -36,7 +36,7 @@ import org.sonar.api.utils.log.Loggers; import org.sonar.core.util.ParamChange; import org.sonar.core.util.RuleActivationListener; import org.sonar.core.util.RuleChange; -import org.sonar.core.util.RuleSetChangeEvent; +import org.sonar.core.util.RuleSetChangedEvent; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.pushapi.qualityprofile.RuleActivatorEventsDistributor; @@ -89,29 +89,29 @@ public class SonarLintClientsRegistry implements RuleActivationListener { } @Override - public void listen(RuleSetChangeEvent ruleChangeEvent) { - broadcastMessage(ruleChangeEvent, getFilterForEvent(ruleChangeEvent)); + public void listen(RuleSetChangedEvent ruleSetChangedEvent) { + broadcastMessage(ruleSetChangedEvent, getFilterForEvent(ruleSetChangedEvent)); } - private static Predicate getFilterForEvent(RuleSetChangeEvent ruleChangeEvent) { - List affectedProjects = asList(ruleChangeEvent.getProjects()); + private static Predicate getFilterForEvent(RuleSetChangedEvent ruleSetChangedEvent) { + List affectedProjects = asList(ruleSetChangedEvent.getProjects()); return client -> { Set clientProjectKeys = client.getClientProjectKeys(); Set languages = client.getLanguages(); - return !Collections.disjoint(clientProjectKeys, affectedProjects) && languages.contains(ruleChangeEvent.getLanguage()); + return !Collections.disjoint(clientProjectKeys, affectedProjects) && languages.contains(ruleSetChangedEvent.getLanguage()); }; } - public void broadcastMessage(RuleSetChangeEvent message, Predicate filter) { + public void broadcastMessage(RuleSetChangedEvent event, Predicate filter) { clients.stream().filter(filter).forEach(c -> { Set projectKeysInterestingForClient = new HashSet<>(c.getClientProjectKeys()); - projectKeysInterestingForClient.retainAll(Set.of(message.getProjects())); + projectKeysInterestingForClient.retainAll(Set.of(event.getProjects())); try { sonarLintClientPermissionsValidator.validateUserCanReceivePushEventForProjects(c.getUserUuid(), projectKeysInterestingForClient); - RuleSetChangeEvent personalizedEvent = new RuleSetChangeEvent(projectKeysInterestingForClient.toArray(String[]::new), message.getActivatedRules(), - message.getDeactivatedRules()); - String jsonString = getJSONString(personalizedEvent); - c.writeAndFlush(jsonString); + RuleSetChangedEvent personalizedEvent = new RuleSetChangedEvent(projectKeysInterestingForClient.toArray(String[]::new), event.getActivatedRules(), + event.getDeactivatedRules()); + String message = getMessage(personalizedEvent); + c.writeAndFlush(message); } catch (ForbiddenException forbiddenException) { LOG.debug("Client is no longer authenticated: " + forbiddenException.getMessage()); unregisterClient(c); @@ -121,28 +121,28 @@ public class SonarLintClientsRegistry implements RuleActivationListener { } }); } + private static String getMessage(RuleSetChangedEvent ruleSetChangedEvent) { + return "event: " + ruleSetChangedEvent.getEvent() + "\n" + + "data: " + toJson(ruleSetChangedEvent); + } - public String getJSONString(RuleSetChangeEvent ruleSetChangeEvent) { - JSONObject result = new JSONObject(); - result.put("event", ruleSetChangeEvent.getEvent()); - + private static String toJson(RuleSetChangedEvent ruleSetChangedEvent) { JSONObject data = new JSONObject(); - data.put("projects", ruleSetChangeEvent.getProjects()); + data.put("projects", ruleSetChangedEvent.getProjects()); JSONArray activatedRulesJson = new JSONArray(); - for (RuleChange rule : ruleSetChangeEvent.getActivatedRules()) { + for (RuleChange rule : ruleSetChangedEvent.getActivatedRules()) { activatedRulesJson.put(toJson(rule)); } data.put("activatedRules", activatedRulesJson); JSONArray deactivatedRulesJson = new JSONArray(); - for (RuleChange rule : ruleSetChangeEvent.getDeactivatedRules()) { + for (RuleChange rule : ruleSetChangedEvent.getDeactivatedRules()) { deactivatedRulesJson.put(toJson(rule)); } data.put("deactivatedRules", deactivatedRulesJson); - result.put("data", data); - return result.toString(); + return data.toString(); } private static JSONObject toJson(RuleChange rule) { diff --git a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java index 0e222a9aeb0..d82e71a2fcb 100644 --- a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java +++ b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java @@ -30,7 +30,7 @@ import org.mockito.ArgumentCaptor; import org.sonar.api.rule.Severity; import org.sonar.core.util.ParamChange; import org.sonar.core.util.RuleChange; -import org.sonar.core.util.RuleSetChangeEvent; +import org.sonar.core.util.RuleSetChangedEvent; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.pushapi.qualityprofile.StandaloneRuleActivatorEventsDistributor; @@ -45,7 +45,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; -import static org.sonar.test.JsonAssert.assertJson; +import static org.sonar.test.EventAssert.assertThatEvent; public class SonarLintClientsRegistryTest { @@ -105,15 +105,15 @@ public class SonarLintClientsRegistryTest { RuleChange[] activatedRules = {javaRule}; RuleChange[] deactivatedRules = {javaRule}; - RuleSetChangeEvent ruleChangeEvent = new RuleSetChangeEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules); - underTest.listen(ruleChangeEvent); + RuleSetChangedEvent ruleSetChangedEvent = new RuleSetChangedEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules); + underTest.listen(ruleSetChangedEvent); ArgumentCaptor captor = ArgumentCaptor.forClass(byte[].class); verify(outputStream).write(captor.capture()); - String json = new String(captor.getValue()); - assertJson(json) - .withStrictArrayOrder() - .isSimilarTo(getClass().getResource("rule-change-event.json")); + String message = new String(captor.getValue()); + assertThatEvent(message) + .hasType("RuleSetChanged") + .hasJsonData(getClass().getResource("rule-change-event-data.json")); } @Test @@ -129,8 +129,8 @@ public class SonarLintClientsRegistryTest { RuleChange[] activatedRules = {}; RuleChange[] deactivatedRules = {javaRuleChange}; - RuleSetChangeEvent ruleChangeEvent = new RuleSetChangeEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules); - underTest.listen(ruleChangeEvent); + RuleSetChangedEvent ruleSetChangedEvent = new RuleSetChangedEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules); + underTest.listen(ruleSetChangedEvent); verifyNoInteractions(outputStream); } @@ -149,8 +149,8 @@ public class SonarLintClientsRegistryTest { RuleChange[] activatedRules = {}; RuleChange[] deactivatedRules = {javaRuleChange}; - RuleSetChangeEvent ruleChangeEvent = new RuleSetChangeEvent(eventProjectKeys.toArray(String[]::new), activatedRules, deactivatedRules); - underTest.listen(ruleChangeEvent); + RuleSetChangedEvent ruleSetChangedEvent = new RuleSetChangedEvent(eventProjectKeys.toArray(String[]::new), activatedRules, deactivatedRules); + underTest.listen(ruleSetChangedEvent); ArgumentCaptor> argument = ArgumentCaptor.forClass(Set.class); verify(permissionsValidator).validateUserCanReceivePushEventForProjects(anyString(), argument.capture()); @@ -162,13 +162,13 @@ public class SonarLintClientsRegistryTest { RuleChange javaRuleChange = createRuleChange(); RuleChange[] activatedRules = {}; RuleChange[] deactivatedRules = {javaRuleChange}; - RuleSetChangeEvent ruleChangeEvent = new RuleSetChangeEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules); + RuleSetChangedEvent ruleSetChangedEvent = new RuleSetChangedEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules); SonarLintClient sonarLintClient = createSampleSLClient(); underTest.registerClient(sonarLintClient); doThrow(new ForbiddenException("Access forbidden")).when(permissionsValidator).validateUserCanReceivePushEventForProjects(anyString(), anySet()); - underTest.listen(ruleChangeEvent); + underTest.listen(ruleSetChangedEvent); verify(sonarLintClient).close(); } @@ -178,18 +178,18 @@ public class SonarLintClientsRegistryTest { RuleChange javaRuleChange = createRuleChange(); RuleChange[] activatedRules = {}; RuleChange[] deactivatedRules = {javaRuleChange}; - RuleSetChangeEvent ruleChangeEvent = new RuleSetChangeEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules); + RuleSetChangedEvent ruleSetChangedEvent = new RuleSetChangedEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules); SonarLintClient sonarLintClient = createSampleSLClient(); underTest.registerClient(sonarLintClient); doThrow(new IOException("Broken pipe")).when(sonarLintClient).writeAndFlush(anyString()); - underTest.listen(ruleChangeEvent); + underTest.listen(ruleSetChangedEvent); underTest.registerClient(sonarLintClient); doThrow(new IllegalStateException("Things went wrong")).when(sonarLintClient).writeAndFlush(anyString()); - underTest.listen(ruleChangeEvent); + underTest.listen(ruleSetChangedEvent); verify(sonarLintClient, times(2)).close(); } diff --git a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/qualityprofile/builtin/QualityProfileChangeEventServiceImplTest.java b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/qualityprofile/builtin/QualityProfileChangeEventServiceImplTest.java index 4d731df20c9..b72c6961979 100644 --- a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/qualityprofile/builtin/QualityProfileChangeEventServiceImplTest.java +++ b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/qualityprofile/builtin/QualityProfileChangeEventServiceImplTest.java @@ -27,7 +27,7 @@ import org.mockito.ArgumentCaptor; import org.sonar.api.rule.RuleKey; import org.sonar.core.util.ParamChange; import org.sonar.core.util.RuleChange; -import org.sonar.core.util.RuleSetChangeEvent; +import org.sonar.core.util.RuleSetChangedEvent; import org.sonar.db.DbTester; import org.sonar.db.project.ProjectDto; import org.sonar.db.qualityprofile.ActiveRuleDto; @@ -87,27 +87,27 @@ public class QualityProfileChangeEventServiceImplTest { underTest.distributeRuleChangeEvent(profiles, of(activeRuleChange), "xoo"); - ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(RuleSetChangeEvent.class); + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(RuleSetChangedEvent.class); verify(eventsDistributor).pushEvent(eventCaptor.capture()); - RuleSetChangeEvent ruleSetChangeEvent = eventCaptor.getValue(); - assertThat(ruleSetChangeEvent).isNotNull(); - assertThat(ruleSetChangeEvent).extracting(RuleSetChangeEvent::getEvent, - RuleSetChangeEvent::getLanguage, RuleSetChangeEvent::getProjects) - .containsExactly("RuleSetChange", "xoo", new String[]{project.getKey()}); + RuleSetChangedEvent ruleSetChangedEvent = eventCaptor.getValue(); + assertThat(ruleSetChangedEvent).isNotNull(); + assertThat(ruleSetChangedEvent).extracting(RuleSetChangedEvent::getEvent, + RuleSetChangedEvent::getLanguage, RuleSetChangedEvent::getProjects) + .containsExactly("RuleSetChanged", "xoo", new String[]{project.getKey()}); - assertThat(ruleSetChangeEvent.getActivatedRules()) + assertThat(ruleSetChangedEvent.getActivatedRules()) .extracting(RuleChange::getKey, RuleChange::getLanguage, RuleChange::getSeverity, RuleChange::getTemplateKey) .containsExactly(tuple(rule1.getRuleKey(), "xoo", null, "template-key")); - assertThat(ruleSetChangeEvent.getActivatedRules()[0].getParams()).hasSize(1); - ParamChange actualParamChange = ruleSetChangeEvent.getActivatedRules()[0].getParams()[0]; + assertThat(ruleSetChangedEvent.getActivatedRules()[0].getParams()).hasSize(1); + ParamChange actualParamChange = ruleSetChangedEvent.getActivatedRules()[0].getParams()[0]; assertThat(actualParamChange) .extracting(ParamChange::getKey, ParamChange::getValue) .containsExactly("paramChangeKey", "paramChangeValue"); - assertThat(ruleSetChangeEvent.getDeactivatedRules()).isEmpty(); + assertThat(ruleSetChangedEvent.getDeactivatedRules()).isEmpty(); } @@ -136,35 +136,35 @@ public class QualityProfileChangeEventServiceImplTest { underTest.publishRuleActivationToSonarLintClients(projectDao, activatedQualityProfile, deactivatedQualityProfile); - ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(RuleSetChangeEvent.class); + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(RuleSetChangedEvent.class); verify(eventsDistributor).pushEvent(eventCaptor.capture()); - RuleSetChangeEvent ruleSetChangeEvent = eventCaptor.getValue(); - assertThat(ruleSetChangeEvent).isNotNull(); - assertThat(ruleSetChangeEvent).extracting(RuleSetChangeEvent::getEvent, - RuleSetChangeEvent::getLanguage, RuleSetChangeEvent::getProjects) - .containsExactly("RuleSetChange", "xoo", new String[]{null}); + RuleSetChangedEvent ruleSetChangedEvent = eventCaptor.getValue(); + assertThat(ruleSetChangedEvent).isNotNull(); + assertThat(ruleSetChangedEvent).extracting(RuleSetChangedEvent::getEvent, + RuleSetChangedEvent::getLanguage, RuleSetChangedEvent::getProjects) + .containsExactly("RuleSetChanged", "xoo", new String[]{null}); // activated rule - assertThat(ruleSetChangeEvent.getActivatedRules()) + assertThat(ruleSetChangedEvent.getActivatedRules()) .extracting(RuleChange::getKey, RuleChange::getLanguage, RuleChange::getSeverity, RuleChange::getTemplateKey) .containsExactly(tuple(rule1.getRuleKey(), "xoo", rule1.getSeverityString(), null)); - assertThat(ruleSetChangeEvent.getActivatedRules()[0].getParams()).hasSize(1); - ParamChange actualParamChange = ruleSetChangeEvent.getActivatedRules()[0].getParams()[0]; + assertThat(ruleSetChangedEvent.getActivatedRules()[0].getParams()).hasSize(1); + ParamChange actualParamChange = ruleSetChangedEvent.getActivatedRules()[0].getParams()[0]; assertThat(actualParamChange) .extracting(ParamChange::getKey, ParamChange::getValue) .containsExactly(activeRuleParam1.getKey(), activeRuleParam1.getValue()); // deactivated rule - assertThat(ruleSetChangeEvent.getDeactivatedRules()) + assertThat(ruleSetChangedEvent.getDeactivatedRules()) .extracting(RuleChange::getKey, RuleChange::getLanguage, RuleChange::getSeverity, RuleChange::getTemplateKey) .containsExactly(tuple(rule2.getRuleKey(), "xoo", rule2.getSeverityString(), null)); - assertThat(ruleSetChangeEvent.getDeactivatedRules()[0].getParams()).hasSize(1); - ParamChange actualParamChangeDeactivated = ruleSetChangeEvent.getDeactivatedRules()[0].getParams()[0]; + assertThat(ruleSetChangedEvent.getDeactivatedRules()[0].getParams()).hasSize(1); + ParamChange actualParamChangeDeactivated = ruleSetChangedEvent.getDeactivatedRules()[0].getParams()[0]; assertThat(actualParamChangeDeactivated) .extracting(ParamChange::getKey, ParamChange::getValue) .containsExactly(activeRuleParam2.getKey(), activeRuleParam2.getValue()); diff --git a/server/sonar-webserver-pushapi/src/test/resources/org/sonar/server/pushapi/sonarlint/rule-change-event-data.json b/server/sonar-webserver-pushapi/src/test/resources/org/sonar/server/pushapi/sonarlint/rule-change-event-data.json new file mode 100644 index 00000000000..b70bd0dd2ab --- /dev/null +++ b/server/sonar-webserver-pushapi/src/test/resources/org/sonar/server/pushapi/sonarlint/rule-change-event-data.json @@ -0,0 +1,35 @@ +{ + "projects": [ + "project2", + "project1", + "project3" + ], + "activatedRules": [ + { + "key": "rule-key", + "templateKey": "template-key", + "severity": "CRITICAL", + "language": "java", + "params": [ + { + "value": "param-value", + "key": "param-key" + } + ] + } + ], + "deactivatedRules": [ + { + "key": "rule-key", + "templateKey": "template-key", + "severity": "CRITICAL", + "language": "java", + "params": [ + { + "value": "param-value", + "key": "param-key" + } + ] + } + ] +} diff --git a/server/sonar-webserver-pushapi/src/test/resources/org/sonar/server/pushapi/sonarlint/rule-change-event.json b/server/sonar-webserver-pushapi/src/test/resources/org/sonar/server/pushapi/sonarlint/rule-change-event.json deleted file mode 100644 index b60f1b83245..00000000000 --- a/server/sonar-webserver-pushapi/src/test/resources/org/sonar/server/pushapi/sonarlint/rule-change-event.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "data": { - "projects": [ - "project2", - "project1", - "project3" - ], - "activatedRules": [ - { - "key": "rule-key", - "templateKey": "template-key", - "severity": "CRITICAL", - "language": "java", - "params": [ - { - "value": "param-value", - "key": "param-key" - } - ] - } - ], - "deactivatedRules": [ - { - "key": "rule-key", - "templateKey": "template-key", - "severity": "CRITICAL", - "language": "java", - "params": [ - { - "value": "param-value", - "key": "param-key" - } - ] - } - ] - }, - "event": "RuleSetChange" -} diff --git a/sonar-core/src/main/java/org/sonar/core/util/RuleActivationListener.java b/sonar-core/src/main/java/org/sonar/core/util/RuleActivationListener.java index d234d200d71..3a50054ca98 100644 --- a/sonar-core/src/main/java/org/sonar/core/util/RuleActivationListener.java +++ b/sonar-core/src/main/java/org/sonar/core/util/RuleActivationListener.java @@ -21,5 +21,5 @@ package org.sonar.core.util; public interface RuleActivationListener { - void listen(RuleSetChangeEvent event); -} \ No newline at end of file + void listen(RuleSetChangedEvent event); +} diff --git a/sonar-core/src/main/java/org/sonar/core/util/RuleSetChangeEvent.java b/sonar-core/src/main/java/org/sonar/core/util/RuleSetChangedEvent.java similarity index 69% rename from sonar-core/src/main/java/org/sonar/core/util/RuleSetChangeEvent.java rename to sonar-core/src/main/java/org/sonar/core/util/RuleSetChangedEvent.java index 5c9825a0522..5f3c3f95982 100644 --- a/sonar-core/src/main/java/org/sonar/core/util/RuleSetChangeEvent.java +++ b/sonar-core/src/main/java/org/sonar/core/util/RuleSetChangedEvent.java @@ -21,37 +21,25 @@ package org.sonar.core.util; import java.io.Serializable; -public class RuleSetChangeEvent implements Serializable { +public class RuleSetChangedEvent implements Serializable { - private static final String EVENT = "RuleSetChange"; + private static final String EVENT = "RuleSetChanged"; - private String[] projects; - private String language; - private RuleChange[] activatedRules; - private RuleChange[] deactivatedRules; + private final String[] projects; + private final String language; + private final RuleChange[] activatedRules; + private final RuleChange[] deactivatedRules; - public RuleSetChangeEvent(String[] projects, RuleChange[] activatedRules, RuleChange[] deactivatedRules) { + public RuleSetChangedEvent(String[] projects, RuleChange[] activatedRules, RuleChange[] deactivatedRules) { this.projects = projects; this.activatedRules = activatedRules; this.deactivatedRules = deactivatedRules; if (activatedRules.length == 0 && deactivatedRules.length == 0) { - throw new IllegalArgumentException("Can't create RuleSetChangeEvent without any rules that have changed"); + throw new IllegalArgumentException("Can't create RuleSetChangedEvent without any rules that have changed"); } this.language = activatedRules.length > 0 ? activatedRules[0].getLanguage() : deactivatedRules[0].getLanguage(); } - public void setProjects(String[] projects) { - this.projects = projects; - } - - public void setActivatedRules(RuleChange[] activatedRules) { - this.activatedRules = activatedRules; - } - - public void setDeactivatedRules(RuleChange[] deactivatedRules) { - this.deactivatedRules = deactivatedRules; - } - public String getEvent() { return EVENT; } diff --git a/sonar-core/src/test/java/org/sonar/core/util/RuleSetChangeEventTest.java b/sonar-core/src/test/java/org/sonar/core/util/RuleSetChangedEventTest.java similarity index 86% rename from sonar-core/src/test/java/org/sonar/core/util/RuleSetChangeEventTest.java rename to sonar-core/src/test/java/org/sonar/core/util/RuleSetChangedEventTest.java index 71166586600..637d324ef5b 100644 --- a/sonar-core/src/test/java/org/sonar/core/util/RuleSetChangeEventTest.java +++ b/sonar-core/src/test/java/org/sonar/core/util/RuleSetChangedEventTest.java @@ -24,14 +24,14 @@ import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -public class RuleSetChangeEventTest { +public class RuleSetChangedEventTest { @Test public void getLanguage_givenNoDeactivatedRules_languageIsCorrectlyIdentified() { String[] projects = {"sonarqube"}; RuleChange[] activatedRules = {createRuleChange("java")}; RuleChange[] deactivatedRules = {}; - RuleSetChangeEvent event = new RuleSetChangeEvent(projects, activatedRules, deactivatedRules); + RuleSetChangedEvent event = new RuleSetChangedEvent(projects, activatedRules, deactivatedRules); String language = event.getLanguage(); @@ -43,7 +43,7 @@ public class RuleSetChangeEventTest { String[] projects = {"sonarqube"}; RuleChange[] activatedRules = {}; RuleChange[] deactivatedRules = {createRuleChange("java")}; - RuleSetChangeEvent event = new RuleSetChangeEvent(projects, activatedRules, deactivatedRules); + RuleSetChangedEvent event = new RuleSetChangedEvent(projects, activatedRules, deactivatedRules); String language = event.getLanguage(); @@ -56,7 +56,7 @@ public class RuleSetChangeEventTest { RuleChange[] activatedRules = {}; RuleChange[] deactivatedRules = {}; - assertThatThrownBy(() -> new RuleSetChangeEvent(projects, activatedRules, deactivatedRules)) + assertThatThrownBy(() -> new RuleSetChangedEvent(projects, activatedRules, deactivatedRules)) .isInstanceOf(IllegalArgumentException.class); } diff --git a/sonar-testing-harness/src/main/java/org/sonar/test/EventAssert.java b/sonar-testing-harness/src/main/java/org/sonar/test/EventAssert.java new file mode 100644 index 00000000000..5de4aa1ba23 --- /dev/null +++ b/sonar-testing-harness/src/main/java/org/sonar/test/EventAssert.java @@ -0,0 +1,123 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.test; + +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static org.junit.Assert.fail; +import static org.sonar.test.JsonAssert.assertJson; + +/** + * Assertion methods to compare server-sent events messages. + * + *

Usage

+ *
+ * String actual = "";
+ * String expected = "event: E\ndata: D";
+ * EventAssert.assertEvent(actual).hasType("E");
+ * 
+ * + * @since 9.4 + */ +public class EventAssert { + + private static final String EVENT = "event"; + private static final String DATA = "data"; + private static final String ID = "id"; + private static final String RETRY = "retry"; + + private static final Set ALLOWED_FIELDS = new HashSet<>(Arrays.asList(EVENT, DATA, ID, RETRY)); + + private final String eventPayload; + + private EventAssert(String eventPayload) { + this.eventPayload = eventPayload; + } + + public static EventAssert assertThatEvent(String eventPayload) { + return new EventAssert(eventPayload); + } + + public EventAssert isValid() { + extractFields(); + return this; + } + + public EventAssert hasField(String name) { + isValid(); + if (!extractFields().containsKey(name)) { + fail("Expected event to contain field '" + name + "'. Actual event was: '" + eventPayload + "'"); + } + return this; + } + + public EventAssert hasType(String value) { + return hasField(EVENT, value); + } + + public EventAssert hasData(String value) { + return hasField(DATA, value); + } + + public EventAssert hasField(String name, String value) { + isValid(); + hasField(name); + String actual = extractFields().get(name); + if (!Objects.equals(actual, value)) { + fail("Expected field '" + name + "' to contain '" + value + "' but was '" + actual + "'"); + } + return this; + } + + public EventAssert hasJsonData(URL url) { + isValid(); + hasField(DATA); + assertJson(extractFields().get(DATA)) + .withStrictArrayOrder() + .isSimilarTo(url); + return this; + } + + private Map extractFields() { + Map fields = new HashMap<>(); + Arrays.stream(eventPayload.split("\n")).forEach(line -> { + String trimmed = line.trim(); + if (!trimmed.isEmpty()) { + int fieldDelimiterIndex = line.indexOf(':'); + if (fieldDelimiterIndex != -1) { + String fieldName = line.substring(0, fieldDelimiterIndex); + if (!ALLOWED_FIELDS.contains(fieldName)) { + fail("Unknown field in event: '" + fieldName + "'"); + } + fields.put(fieldName, line.substring(fieldDelimiterIndex + 1).trim()); + } else { + fail("Invalid line in event: '" + line + "'"); + } + } + }); + return fields; + } +} diff --git a/sonar-testing-harness/src/test/java/org/sonar/test/EventAssertTest.java b/sonar-testing-harness/src/test/java/org/sonar/test/EventAssertTest.java new file mode 100644 index 00000000000..ac2cce1c2dc --- /dev/null +++ b/sonar-testing-harness/src/test/java/org/sonar/test/EventAssertTest.java @@ -0,0 +1,111 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.test; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.sonar.test.EventAssert.assertThatEvent; + +public class EventAssertTest { + @Test + public void isValid_no_field() { + assertThatThrownBy(() -> assertThatEvent("line without field").isValid()) + .isInstanceOf(AssertionError.class) + .hasMessage("Invalid line in event: 'line without field'"); + } + @Test + public void isValid_unknown_field() { + assertThatThrownBy(() -> assertThatEvent("field: value").isValid()) + .isInstanceOf(AssertionError.class) + .hasMessage("Unknown field in event: 'field'"); + } + + @Test + public void isValid_correct() { + assertThatEvent("").isValid(); + assertThatEvent("\n\n").isValid(); + assertThatEvent("data: D").isValid(); + assertThatEvent("event: E\ndata: D").isValid(); + } + + @Test + public void hasField_invalid() { + assertThatThrownBy(() -> assertThatEvent("line without field").hasField("event")) + .isInstanceOf(AssertionError.class) + .hasMessage("Invalid line in event: 'line without field'"); + } + + @Test + public void hasField_no_field() { + assertThatThrownBy(() -> assertThatEvent("event: E").hasField("data")) + .isInstanceOf(AssertionError.class) + .hasMessage("Expected event to contain field 'data'. Actual event was: 'event: E'"); + } + + @Test + public void hasField_correct() { + assertThatEvent("event: E\ndata: D").hasField("data"); + assertThatEvent("event: E\ndata: D").hasField("event"); + } + + @Test + public void hasType_invalid() { + assertThatThrownBy(() -> assertThatEvent("line without field").hasType("E")) + .isInstanceOf(AssertionError.class) + .hasMessage("Invalid line in event: 'line without field'"); + } + + @Test + public void hasType_without_type() { + assertThatThrownBy(() -> assertThatEvent("data: D").hasType("E")) + .isInstanceOf(AssertionError.class) + .hasMessage("Expected event to contain field 'event'. Actual event was: 'data: D'"); + } + + @Test + public void hasType_correct() { + assertThatEvent("event: E\ndata: D").hasType("E"); + } + + @Test + public void hasData_invalid() { + assertThatThrownBy(() -> assertThatEvent("line without field").hasData("D")) + .isInstanceOf(AssertionError.class) + .hasMessage("Invalid line in event: 'line without field'"); + } + + @Test + public void hasData_correct() { + assertThatEvent("data:D").hasData("D"); + } + + @Test + public void hasJsonData_invalid() { + assertThatThrownBy(() -> assertThatEvent("line without field").hasJsonData(getClass().getResource("EventAssertTest/sample.json"))) + .isInstanceOf(AssertionError.class) + .hasMessage("Invalid line in event: 'line without field'"); + } + + @Test + public void hasJsonData_correct() { + assertThatEvent("data: {}").hasJsonData(getClass().getResource("EventAssertTest/sample.json")); + } +} diff --git a/sonar-testing-harness/src/test/resources/org/sonar/test/EventAssertTest/sample.json b/sonar-testing-harness/src/test/resources/org/sonar/test/EventAssertTest/sample.json new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/sonar-testing-harness/src/test/resources/org/sonar/test/EventAssertTest/sample.json @@ -0,0 +1 @@ +{} -- 2.39.5