/* * SonarQube * Copyright (C) 2009-2017 SonarSource SA * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.sonar.db.property; import com.google.common.collect.ImmutableMap; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; import java.sql.SQLException; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.organization.OrganizationTesting; import org.sonar.db.user.UserDto; import org.sonar.db.user.UserTesting; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Sets.newHashSet; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.groups.Tuple.tuple; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.sonar.db.property.PropertyTesting.newComponentPropertyDto; import static org.sonar.db.property.PropertyTesting.newGlobalPropertyDto; import static org.sonar.db.property.PropertyTesting.newUserPropertyDto; @RunWith(DataProviderRunner.class) public class PropertiesDaoTest { private static final String VALUE_SIZE_4000 = String.format("%1$4000.4000s", "*"); private static final String VALUE_SIZE_4001 = VALUE_SIZE_4000 + "P"; private static final long DATE_1 = 1_555_000L; private static final long DATE_2 = 1_666_000L; private static final long DATE_3 = 1_777_000L; private static final long DATE_4 = 1_888_000L; private static final long DATE_5 = 1_999_000L; private System2 system2 = mock(System2.class); @Rule public ExpectedException thrown = ExpectedException.none(); @Rule public DbTester dbTester = DbTester.create(system2); private DbClient dbClient = dbTester.getDbClient(); private DbSession session = dbTester.getSession(); private PropertiesDao underTest = dbTester.getDbClient().propertiesDao(); @Test public void shouldFindUsersForNotification() throws SQLException { ComponentDto project1 = insertProject("uuid_45"); ComponentDto project2 = insertProject("uuid_56"); int userId1 = insertUser("user1"); int userId2 = insertUser("user2"); int userId3 = insertUser("user3"); insertProperty("notification.NewViolations.Email", "true", project1.getId(), userId2); insertProperty("notification.NewViolations.Twitter", "true", null, userId3); insertProperty("notification.NewViolations.Twitter", "true", project2.getId(), userId1); insertProperty("notification.NewViolations.Twitter", "true", project2.getId(), userId3); assertThat(underTest.selectUsersForNotification("NewViolations", "Email", null)) .isEmpty(); assertThat(underTest.selectUsersForNotification("NewViolations", "Email", "uuid_78")) .isEmpty(); assertThat(underTest.selectUsersForNotification("NewViolations", "Email", "uuid_45")) .hasSize(1).containsOnly("user2"); assertThat(underTest.selectUsersForNotification("NewViolations", "Twitter", null)) .hasSize(1) .containsOnly("user3"); assertThat(underTest.selectUsersForNotification("NewViolations", "Twitter", "uuid_78")) .isEmpty(); assertThat(underTest.selectUsersForNotification("NewViolations", "Twitter", "uuid_56")) .hasSize(2) .containsOnly("user1", "user3"); } @Test public void findNotificationSubscribers() throws SQLException { int userId1 = insertUser("user1"); int userId2 = insertUser("user2"); ComponentDto projectDto = insertProject("PROJECT_A"); long projectId = projectDto.getId(); String projectKey = projectDto.key(); // global subscription insertProperty("notification.DispatcherWithGlobalSubscribers.Email", "true", null, userId2); // project subscription insertProperty("notification.DispatcherWithProjectSubscribers.Email", "true", projectId, userId1); insertProperty("notification.DispatcherWithGlobalAndProjectSubscribers.Email", "true", 56L, userId1); insertProperty("notification.DispatcherWithGlobalAndProjectSubscribers.Email", "true", projectId, userId1); // global subscription insertProperty("notification.DispatcherWithGlobalAndProjectSubscribers.Email", "true", null, userId2); // Nobody is subscribed assertThat(underTest.selectNotificationSubscribers("NotSexyDispatcher", "Email", projectKey)) .isEmpty(); // Global subscribers assertThat(underTest.selectNotificationSubscribers("DispatcherWithGlobalSubscribers", "Email", projectKey)) .containsOnly("user2"); assertThat(underTest.selectNotificationSubscribers("DispatcherWithGlobalSubscribers", "Email", null)) .containsOnly("user2"); // Project subscribers assertThat(underTest.selectNotificationSubscribers("DispatcherWithProjectSubscribers", "Email", projectKey)) .containsOnly("user1"); // Global + Project subscribers assertThat(underTest.selectNotificationSubscribers("DispatcherWithGlobalAndProjectSubscribers", "Email", projectKey)) .containsOnly("user1", "user2"); } @Test public void hasNotificationSubscribers() throws SQLException { int userId1 = insertUser("user1"); int userId2 = insertUser("user2"); Long projectId = insertProject("PROJECT_A").getId(); // global subscription insertProperty("notification.DispatcherWithGlobalSubscribers.Email", "true", null, userId2); // project subscription insertProperty("notification.DispatcherWithProjectSubscribers.Email", "true", projectId, userId1); insertProperty("notification.DispatcherWithGlobalAndProjectSubscribers.Email", "true", 56L, userId1); insertProperty("notification.DispatcherWithGlobalAndProjectSubscribers.Email", "true", projectId, userId1); // global subscription insertProperty("notification.DispatcherWithGlobalAndProjectSubscribers.Email", "true", null, userId2); // Nobody is subscribed assertThat(underTest.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", singletonList("NotSexyDispatcher"))) .isFalse(); // Global subscribers assertThat(underTest.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", singletonList("DispatcherWithGlobalSubscribers"))) .isTrue(); // Project subscribers assertThat(underTest.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", singletonList("DispatcherWithProjectSubscribers"))) .isTrue(); assertThat(underTest.hasProjectNotificationSubscribersForDispatchers("PROJECT_B", singletonList("DispatcherWithProjectSubscribers"))) .isFalse(); // Global + Project subscribers assertThat(underTest.hasProjectNotificationSubscribersForDispatchers("PROJECT_A", singletonList("DispatcherWithGlobalAndProjectSubscribers"))) .isTrue(); assertThat(underTest.hasProjectNotificationSubscribersForDispatchers("PROJECT_B", singletonList("DispatcherWithGlobalAndProjectSubscribers"))) .isTrue(); } @Test public void selectGlobalProperties() throws SQLException { // global long id1 = insertProperty("global.one", "one", null, null); long id2 = insertProperty("global.two", "two", null, null); List<PropertyDto> properties = underTest.selectGlobalProperties(); assertThat(properties.size()) .isEqualTo(2); assertThatDto(findByKey(properties, "global.one")) .hasKey("global.one") .hasNoUserId() .hasNoResourceId() .hasValue("one"); assertThatDto(findByKey(properties, "global.two")) .hasKey("global.two") .hasNoResourceId() .hasNoUserId() .hasValue("two"); } @Test @UseDataProvider("allValuesForSelect") public void selectGlobalProperties_supports_all_values(String dbValue, String expected) throws SQLException { insertProperty("global.one", dbValue, null, null); List<PropertyDto> dtos = underTest.selectGlobalProperties(); assertThat(dtos) .hasSize(1); assertThatDto(dtos.iterator().next()) .hasKey("global.one") .hasNoResourceId() .hasNoUserId() .hasValue(expected); } @Test public void selectGlobalProperty() throws SQLException { // global insertProperty("global.one", "one", null, null); insertProperty("global.two", "two", null, null); // project insertProperty("project.one", "one", 10L, null); // user insertProperty("user.one", "one", null, 100); assertThatDto(underTest.selectGlobalProperty("global.one")) .hasNoResourceId() .hasNoUserId() .hasValue("one"); assertThat(underTest.selectGlobalProperty("project.one")).isNull(); assertThat(underTest.selectGlobalProperty("user.one")).isNull(); assertThat(underTest.selectGlobalProperty("unexisting")).isNull(); } @Test @UseDataProvider("allValuesForSelect") public void selectGlobalProperty_supports_all_values(String dbValue, String expected) throws SQLException { insertProperty("global.one", dbValue, null, null); assertThatDto(underTest.selectGlobalProperty("global.one")) .hasNoResourceId() .hasNoUserId() .hasValue(expected); } @Test public void selectProjectProperties() throws SQLException { ComponentDto projectDto = insertProject("A"); long projectId = projectDto.getId(); // global insertProperty("global.one", "one", null, null); insertProperty("global.two", "two", null, null); // project insertProperty("project.one", "Pone", projectId, null); insertProperty("project.two", "Ptwo", projectId, null); List<PropertyDto> dtos = underTest.selectProjectProperties(projectDto.key()); assertThat(dtos) .hasSize(2); assertThatDto(findByKey(dtos, "project.one")) .hasKey("project.one") .hasResourceId(projectId) .hasValue("Pone"); assertThatDto(findByKey(dtos, "project.two")) .hasKey("project.two") .hasResourceId(projectId) .hasValue("Ptwo"); } @Test @UseDataProvider("allValuesForSelect") public void selectProjectProperties_supports_all_values(String dbValue, String expected) throws SQLException { ComponentDto projectDto = insertProject("A"); insertProperty("project.one", dbValue, projectDto.getId(), null); List<PropertyDto> dtos = underTest.selectProjectProperties(projectDto.key()); assertThat(dtos).hasSize(1); assertThatDto(dtos.iterator().next()) .hasKey("project.one") .hasResourceId(projectDto.getId()) .hasValue(expected); } @DataProvider public static Object[][] allValuesForSelect() { return new Object[][] { {null, ""}, {"", ""}, {"some value", "some value"}, {VALUE_SIZE_4000, VALUE_SIZE_4000}, {VALUE_SIZE_4001, VALUE_SIZE_4001} }; } @Test public void selectProjectProperty() throws SQLException { insertProperty("project.one", "one", 10L, null); PropertyDto property = underTest.selectProjectProperty(10L, "project.one"); assertThatDto(property) .hasKey("project.one") .hasResourceId(10L) .hasNoUserId() .hasValue("one"); } @Test public void selectEnabledDescendantModuleProperties() { dbTester.prepareDbUnit(getClass(), "select_module_properties_tree.xml"); List<PropertyDto> properties = underTest.selectEnabledDescendantModuleProperties("ABCD", dbTester.getSession()); assertThat(properties.size()).isEqualTo(4); assertThat(properties).extracting("key").containsOnly("struts.one", "core.one", "core.two", "data.one"); assertThat(properties).extracting("value").containsOnly("one", "two"); properties = underTest.selectEnabledDescendantModuleProperties("EFGH", dbTester.getSession()); assertThat(properties.size()).isEqualTo(3); assertThat(properties).extracting("key").containsOnly("core.one", "core.two", "data.one"); properties = underTest.selectEnabledDescendantModuleProperties("FGHI", dbTester.getSession()); assertThat(properties.size()).isEqualTo(1); assertThat(properties).extracting("key").containsOnly("data.one"); assertThat(underTest.selectEnabledDescendantModuleProperties("unknown-result.xml", dbTester.getSession()).size()).isEqualTo(0); } @Test @UseDataProvider("allValuesForSelect") public void selectEnabledDescendantModuleProperties_supports_all_values(String dbValue, String expected) throws SQLException { String projectUuid = "A"; ComponentDto project = ComponentTesting.newPrivateProjectDto(OrganizationTesting.newOrganizationDto(), projectUuid); dbClient.componentDao().insert(session, project); long projectId = project.getId(); insertProperty("project.one", dbValue, projectId, null); List<PropertyDto> dtos = underTest.selectEnabledDescendantModuleProperties(projectUuid, dbTester.getSession()); assertThat(dtos) .hasSize(1); assertThatDto(dtos.iterator().next()) .hasKey("project.one") .hasResourceId(projectId) .hasNoUserId() .hasValue(expected); } @Test public void select_by_query() throws SQLException { // global insertProperty("global.one", "one", null, null); insertProperty("global.two", "two", null, null); // struts insertProperty("struts.one", "one", 10L, null); // commons insertProperty("commonslang.one", "one", 11L, null); // user insertProperty("user.one", "one", null, 100); insertProperty("user.two", "two", 10L, 100); // other insertProperty("other.one", "one", 12L, null); List<PropertyDto> results = underTest.selectByQuery(PropertyQuery.builder().setKey("user.two").setComponentId(10L).setUserId(100).build(), dbTester.getSession()); assertThat(results).hasSize(1); assertThat(results.get(0).getValue()).isEqualTo("two"); results = underTest.selectByQuery(PropertyQuery.builder().setKey("user.one").setUserId(100).build(), dbTester.getSession()); assertThat(results).hasSize(1); assertThat(results.get(0).getValue()).isEqualTo("one"); } @Test public void select_global_properties_by_keys() throws Exception { insertProject("A"); int userId = insertUser("B"); String key = "key"; String anotherKey = "anotherKey"; insertProperty(key, "value", null, null); insertProperty(key, "value", 10L, null); insertProperty(key, "value", null, userId); insertProperty(anotherKey, "value", null, null); assertThat(underTest.selectGlobalPropertiesByKeys(session, newHashSet(key))) .extracting("key") .containsOnly(key); assertThat(underTest.selectGlobalPropertiesByKeys(session, newHashSet(key, anotherKey))) .extracting("key") .containsOnly(key, anotherKey); assertThat(underTest.selectGlobalPropertiesByKeys(session, newHashSet(key, anotherKey, "unknown"))) .extracting("key") .containsOnly(key, anotherKey); assertThat(underTest.selectGlobalPropertiesByKeys(session, newHashSet("unknown"))) .isEmpty(); } @Test public void select_component_properties_by_keys() throws Exception { ComponentDto project = dbTester.components().insertPrivateProject(); UserDto user = dbTester.users().insertUser(); String key = "key"; String anotherKey = "anotherKey"; insertProperties( newGlobalPropertyDto().setKey(key), newComponentPropertyDto(project).setKey(key), newUserPropertyDto(user).setKey(key), newComponentPropertyDto(project).setKey(anotherKey)); assertThat(underTest.selectPropertiesByKeysAndComponentId(session, newHashSet(key), project.getId())).extracting("key").containsOnly(key); assertThat(underTest.selectPropertiesByKeysAndComponentId(session, newHashSet(key, anotherKey), project.getId())).extracting("key").containsOnly(key, anotherKey); assertThat(underTest.selectPropertiesByKeysAndComponentId(session, newHashSet(key, anotherKey, "unknown"), project.getId())).extracting("key").containsOnly(key, anotherKey); assertThat(underTest.selectPropertiesByKeysAndComponentId(session, newHashSet("unknown"), project.getId())).isEmpty(); assertThat(underTest.selectPropertiesByKeysAndComponentId(session, newHashSet(key), 123456789L)).isEmpty(); } @Test public void select_component_properties_by_ids() throws Exception { ComponentDto project = dbTester.components().insertPrivateProject(); ComponentDto project2 = dbTester.components().insertPrivateProject(); UserDto user = UserTesting.newUserDto(); dbClient.userDao().insert(session, user); String key = "key"; String anotherKey = "anotherKey"; insertProperties( newGlobalPropertyDto().setKey(key), newComponentPropertyDto(project).setKey(key), newComponentPropertyDto(project2).setKey(key), newComponentPropertyDto(project2).setKey(anotherKey), newUserPropertyDto(user).setKey(key)); assertThat(underTest.selectPropertiesByComponentIds(session, newHashSet(project.getId()))) .extracting("key", "resourceId").containsOnly(tuple(key, project.getId())); assertThat(underTest.selectPropertiesByComponentIds(session, newHashSet(project.getId(), project2.getId()))) .extracting("key", "resourceId").containsOnly( tuple(key, project.getId()), tuple(key, project2.getId()), tuple(anotherKey, project2.getId())); assertThat(underTest.selectPropertiesByComponentIds(session, newHashSet(123456789L))).isEmpty(); } @Test public void select_properties_by_keys_and_component_ids() throws Exception { ComponentDto project = dbTester.components().insertPrivateProject(); ComponentDto project2 = dbTester.components().insertPrivateProject(); UserDto user = UserTesting.newUserDto(); dbClient.userDao().insert(session, user); String key = "key"; String anotherKey = "anotherKey"; insertProperties( newGlobalPropertyDto().setKey(key), newComponentPropertyDto(project).setKey(key), newComponentPropertyDto(project2).setKey(key), newComponentPropertyDto(project2).setKey(anotherKey), newUserPropertyDto(user).setKey(key)); assertThat(underTest.selectPropertiesByKeysAndComponentIds(session, newHashSet(key), newHashSet(project.getId()))) .extracting("key", "resourceId").containsOnly(tuple(key, project.getId())); assertThat(underTest.selectPropertiesByKeysAndComponentIds(session, newHashSet(key), newHashSet(project.getId(), project2.getId()))) .extracting("key", "resourceId").containsOnly( tuple(key, project.getId()), tuple(key, project2.getId())); assertThat(underTest.selectPropertiesByKeysAndComponentIds(session, newHashSet(key, anotherKey), newHashSet(project.getId(), project2.getId()))) .extracting("key", "resourceId").containsOnly( tuple(key, project.getId()), tuple(key, project2.getId()), tuple(anotherKey, project2.getId())); assertThat(underTest.selectPropertiesByKeysAndComponentIds(session, newHashSet("unknown"), newHashSet(project.getId()))).isEmpty(); assertThat(underTest.selectPropertiesByKeysAndComponentIds(session, newHashSet("key"), newHashSet(123456789L))).isEmpty(); assertThat(underTest.selectPropertiesByKeysAndComponentIds(session, newHashSet("unknown"), newHashSet(123456789L))).isEmpty(); } @Test public void select_global_properties_by_key_query() throws SQLException { // global insertProperty("sonar.plugin1.licenseHash.secured", "one", null, null); insertProperty("sonar.plugin2.licenseHash.secured", "two", null, null); // on component and user insertProperty("sonar.plugin1.licenseHash.secure", "one", 10L, null); insertProperty("sonar.plugin1.licenseHash.secure", "two", 10L, 100); assertThat(underTest.selectGlobalPropertiesByKeyQuery(dbTester.getSession(), ".licenseHash.secured")).extracting(PropertyDto::getKey, PropertyDto::getValue) .containsOnly(tuple("sonar.plugin1.licenseHash.secured", "one"), tuple("sonar.plugin2.licenseHash.secured", "two")); assertThat(underTest.selectGlobalPropertiesByKeyQuery(dbTester.getSession(), "plugin1.licenseHash.secured")).extracting(PropertyDto::getKey, PropertyDto::getValue) .containsOnly(tuple("sonar.plugin1.licenseHash.secured", "one")); assertThat(underTest.selectGlobalPropertiesByKeyQuery(dbTester.getSession(), "plugin1")).extracting(PropertyDto::getKey, PropertyDto::getValue) .containsOnly(tuple("sonar.plugin1.licenseHash.secured", "one")); assertThat(underTest.selectGlobalPropertiesByKeyQuery(dbTester.getSession(), "unknown")).isEmpty(); } @Test public void saveProperty_inserts_global_properties_when_they_do_not_exist_in_db() { when(system2.now()).thenReturn(DATE_1, DATE_2, DATE_3, DATE_4, DATE_5); underTest.saveProperty(new PropertyDto().setKey("global.null").setValue(null)); underTest.saveProperty(new PropertyDto().setKey("global.empty").setValue("")); underTest.saveProperty(new PropertyDto().setKey("global.text").setValue("some text")); underTest.saveProperty(new PropertyDto().setKey("global.4000").setValue(VALUE_SIZE_4000)); underTest.saveProperty(new PropertyDto().setKey("global.clob").setValue(VALUE_SIZE_4001)); assertThatPropertiesRow("global.null") .hasNoResourceId() .hasNoUserId() .isEmpty() .hasCreatedAt(DATE_1); assertThatPropertiesRow("global.empty") .hasNoResourceId() .hasNoUserId() .isEmpty() .hasCreatedAt(DATE_2); assertThatPropertiesRow("global.text") .hasNoResourceId() .hasNoUserId() .hasTextValue("some text") .hasCreatedAt(DATE_3); assertThatPropertiesRow("global.4000") .hasNoResourceId() .hasNoUserId() .hasTextValue(VALUE_SIZE_4000) .hasCreatedAt(DATE_4); assertThatPropertiesRow("global.clob") .hasNoResourceId() .hasNoUserId() .hasClobValue(VALUE_SIZE_4001) .hasCreatedAt(DATE_5); } @Test public void saveProperty_inserts_component_properties_when_they_do_not_exist_in_db() { when(system2.now()).thenReturn(DATE_1, DATE_2, DATE_3, DATE_4, DATE_5); long resourceId = 12; underTest.saveProperty(new PropertyDto().setKey("component.null").setResourceId(resourceId).setValue(null)); underTest.saveProperty(new PropertyDto().setKey("component.empty").setResourceId(resourceId).setValue("")); underTest.saveProperty(new PropertyDto().setKey("component.text").setResourceId(resourceId).setValue("some text")); underTest.saveProperty(new PropertyDto().setKey("component.4000").setResourceId(resourceId).setValue(VALUE_SIZE_4000)); underTest.saveProperty(new PropertyDto().setKey("component.clob").setResourceId(resourceId).setValue(VALUE_SIZE_4001)); assertThatPropertiesRow("component.null") .hasResourceId(resourceId) .hasNoUserId() .isEmpty() .hasCreatedAt(DATE_1); assertThatPropertiesRow("component.empty") .hasResourceId(resourceId) .hasNoUserId() .isEmpty() .hasCreatedAt(DATE_2); assertThatPropertiesRow("component.text") .hasResourceId(resourceId) .hasNoUserId() .hasTextValue("some text") .hasCreatedAt(DATE_3); assertThatPropertiesRow("component.4000") .hasResourceId(resourceId) .hasNoUserId() .hasTextValue(VALUE_SIZE_4000) .hasCreatedAt(DATE_4); assertThatPropertiesRow("component.clob") .hasResourceId(resourceId) .hasNoUserId() .hasClobValue(VALUE_SIZE_4001) .hasCreatedAt(DATE_5); } @Test public void saveProperty_inserts_user_properties_when_they_do_not_exist_in_db() { when(system2.now()).thenReturn(DATE_1, DATE_2, DATE_3, DATE_4, DATE_5); int userId = 100; underTest.saveProperty(new PropertyDto().setKey("user.null").setUserId(userId).setValue(null)); underTest.saveProperty(new PropertyDto().setKey("user.empty").setUserId(userId).setValue("")); underTest.saveProperty(new PropertyDto().setKey("user.text").setUserId(userId).setValue("some text")); underTest.saveProperty(new PropertyDto().setKey("user.4000").setUserId(userId).setValue(VALUE_SIZE_4000)); underTest.saveProperty(new PropertyDto().setKey("user.clob").setUserId(userId).setValue(VALUE_SIZE_4001)); assertThatPropertiesRow("user.null") .hasNoResourceId() .hasUserId(userId) .isEmpty() .hasCreatedAt(DATE_1); assertThatPropertiesRow("user.empty") .hasNoResourceId() .hasUserId(userId) .isEmpty() .hasCreatedAt(DATE_2); assertThatPropertiesRow("user.text") .hasNoResourceId() .hasUserId(userId) .hasTextValue("some text") .hasCreatedAt(DATE_3); assertThatPropertiesRow("user.4000") .hasNoResourceId() .hasUserId(userId) .hasTextValue(VALUE_SIZE_4000) .hasCreatedAt(DATE_4); assertThatPropertiesRow("user.clob") .hasNoResourceId() .hasUserId(userId) .hasClobValue(VALUE_SIZE_4001) .hasCreatedAt(DATE_5); } @Test @UseDataProvider("valueUpdatesDataProvider") public void saveProperty_deletes_then_inserts_global_properties_when_they_exist_in_db(@Nullable String oldValue, @Nullable String newValue) throws SQLException { long id = insertProperty("global", oldValue, null, null, DATE_1); when(system2.now()).thenReturn(DATE_4); underTest.saveProperty(new PropertyDto().setKey("global").setValue(newValue)); assertThatPropertiesRow(id) .doesNotExist(); PropertiesRowAssert propertiesRowAssert = assertThatPropertiesRow("global") .hasNoResourceId() .hasNoUserId() .hasCreatedAt(DATE_4); if (newValue == null || newValue.isEmpty()) { propertiesRowAssert.isEmpty(); } else if (newValue.length() > 4000) { propertiesRowAssert.hasClobValue(newValue); } else { propertiesRowAssert.hasTextValue(newValue); } } @Test @UseDataProvider("valueUpdatesDataProvider") public void saveProperty_deletes_then_inserts_component_properties_when_they_exist_in_db(@Nullable String oldValue, @Nullable String newValue) throws SQLException { long resourceId = 999L; long id = insertProperty("global", oldValue, resourceId, null, DATE_1); when(system2.now()).thenReturn(DATE_4); underTest.saveProperty(new PropertyDto().setKey("global").setResourceId(resourceId).setValue(newValue)); assertThatPropertiesRow(id) .doesNotExist(); PropertiesRowAssert propertiesRowAssert = assertThatPropertiesRow("global") .hasResourceId(resourceId) .hasNoUserId() .hasCreatedAt(DATE_4); if (newValue == null || newValue.isEmpty()) { propertiesRowAssert.isEmpty(); } else if (newValue.length() > 4000) { propertiesRowAssert.hasClobValue(newValue); } else { propertiesRowAssert.hasTextValue(newValue); } } @Test @UseDataProvider("valueUpdatesDataProvider") public void saveProperty_deletes_then_inserts_user_properties_when_they_exist_in_db(@Nullable String oldValue, @Nullable String newValue) throws SQLException { int userId = 90; long id = insertProperty("global", oldValue, null, userId, DATE_1); when(system2.now()).thenReturn(DATE_4); underTest.saveProperty(new PropertyDto().setKey("global").setUserId(userId).setValue(newValue)); assertThatPropertiesRow(id) .doesNotExist(); PropertiesRowAssert propertiesRowAssert = assertThatPropertiesRow("global") .hasNoResourceId() .hasUserId(userId) .hasCreatedAt(DATE_4); if (newValue == null || newValue.isEmpty()) { propertiesRowAssert.isEmpty(); } else if (newValue.length() > 4000) { propertiesRowAssert.hasClobValue(newValue); } else { propertiesRowAssert.hasTextValue(newValue); } } @DataProvider public static Object[][] valueUpdatesDataProvider() { return new Object[][] { {null, null}, {null, ""}, {null, "some value"}, {null, VALUE_SIZE_4000}, {null, VALUE_SIZE_4001}, {"", null}, {"", ""}, {"", "some value"}, {"", VALUE_SIZE_4000}, {"", VALUE_SIZE_4001}, {"a value", null}, {"a value", ""}, {"a value", "a value"}, {"a value", "some value"}, {"a value", VALUE_SIZE_4000}, {"a value", VALUE_SIZE_4001}, {VALUE_SIZE_4000, null}, {VALUE_SIZE_4000, ""}, {VALUE_SIZE_4000, "a value"}, {VALUE_SIZE_4000, VALUE_SIZE_4000}, {VALUE_SIZE_4000, VALUE_SIZE_4000.substring(1) + "a"}, {VALUE_SIZE_4000, VALUE_SIZE_4001}, {VALUE_SIZE_4001, null}, {VALUE_SIZE_4001, ""}, {VALUE_SIZE_4001, "a value"}, {VALUE_SIZE_4001, VALUE_SIZE_4000}, {VALUE_SIZE_4001, VALUE_SIZE_4001}, {VALUE_SIZE_4001, VALUE_SIZE_4001 + "dfsdfs"}, }; } @Test public void delete_project_property() throws SQLException { long projectId1 = insertProject("A").getId(); long projectId2 = insertProject("B").getId(); long projectId3 = insertProject("C").getId(); long id1 = insertProperty("global.one", "one", null, null); long id2 = insertProperty("global.two", "two", null, null); long id3 = insertProperty("struts.one", "one", projectId1, null); long id4 = insertProperty("commonslang.one", "one", projectId2, null); long id5 = insertProperty("user.one", "one", null, 100); long id6 = insertProperty("user.two", "two", null, 100); long id7 = insertProperty("other.one", "one", projectId3, null); underTest.deleteProjectProperty("struts.one", projectId1); assertThatPropertiesRow(id1) .hasKey("global.one") .hasNoResourceId() .hasNoUserId() .hasTextValue("one"); assertThatPropertiesRow(id2) .hasKey("global.two") .hasNoResourceId() .hasNoUserId() .hasTextValue("two"); assertThatPropertiesRow(id3) .doesNotExist(); assertThatPropertiesRow(id4) .hasKey("commonslang.one") .hasResourceId(projectId2) .hasNoUserId() .hasTextValue("one"); assertThatPropertiesRow(id5) .hasKey("user.one") .hasNoResourceId() .hasUserId(100) .hasTextValue("one"); assertThatPropertiesRow(id6) .hasKey("user.two") .hasNoResourceId() .hasUserId(100) .hasTextValue("two"); assertThatPropertiesRow(id7) .hasKey("other.one") .hasResourceId(projectId3) .hasNoUserId() .hasTextValue("one"); } @Test public void delete_project_properties() throws SQLException { long id1 = insertProperty("sonar.profile.java", "Sonar Way", 1L, null); long id2 = insertProperty("sonar.profile.java", "Sonar Way", 2L, null); long id3 = insertProperty("sonar.profile.java", "Sonar Way", null, null); long id4 = insertProperty("sonar.profile.js", "Sonar Way", 1L, null); long id5 = insertProperty("sonar.profile.js", "Sonar Way", 2L, null); long id6 = insertProperty("sonar.profile.js", "Sonar Way", null, null); underTest.deleteProjectProperties("sonar.profile.java", "Sonar Way"); assertThatPropertiesRow(id1) .doesNotExist(); assertThatPropertiesRow(id2) .doesNotExist(); assertThatPropertiesRow(id3) .hasKey("sonar.profile.java") .hasNoResourceId() .hasNoUserId() .hasTextValue("Sonar Way"); assertThatPropertiesRow(id4) .hasKey("sonar.profile.js") .hasResourceId(1) .hasNoUserId() .hasTextValue("Sonar Way"); assertThatPropertiesRow(id5) .hasKey("sonar.profile.js") .hasResourceId(2) .hasNoUserId() .hasTextValue("Sonar Way"); assertThatPropertiesRow(id6) .hasKey("sonar.profile.js") .hasNoResourceId() .hasNoUserId() .hasTextValue("Sonar Way"); } @Test public void deleteGlobalProperty() throws SQLException { // global long id1 = insertProperty("global.key", "new_global", null, null); long id2 = insertProperty("to_be_deleted", "xxx", null, null); // project - do not delete this project property that has the same key long id3 = insertProperty("to_be_deleted", "new_project", 10L, null); // user long id4 = insertProperty("user.key", "new_user", null, 100); underTest.deleteGlobalProperty("to_be_deleted"); assertThatPropertiesRow(id1) .hasKey("global.key") .hasNoUserId() .hasNoResourceId() .hasTextValue("new_global"); assertThatPropertiesRow(id2) .doesNotExist(); assertThatPropertiesRow("to_be_deleted", null, null) .doesNotExist(); assertThatPropertiesRow(id3) .hasKey("to_be_deleted") .hasResourceId(10) .hasNoUserId() .hasTextValue("new_project"); assertThatPropertiesRow(id4) .hasKey("user.key") .hasNoResourceId() .hasUserId(100) .hasTextValue("new_user"); } @Test public void delete_by_organization_and_user() throws SQLException { OrganizationDto organization = dbTester.organizations().insert(); OrganizationDto anotherOrganization = dbTester.organizations().insert(); ComponentDto project = dbTester.components().insertPrivateProject(organization); ComponentDto anotherProject = dbTester.components().insertPrivateProject(anotherOrganization); UserDto user = dbTester.users().insertUser(); UserDto anotherUser = dbTester.users().insertUser(); insertProperty("KEY_11", "VALUE", project.getId(), user.getId()); insertProperty("KEY_12", "VALUE", project.getId(), user.getId()); insertProperty("KEY_11", "VALUE", project.getId(), anotherUser.getId()); insertProperty("KEY_11", "VALUE", anotherProject.getId(), user.getId()); underTest.deleteByOrganizationAndUser(session, organization.getUuid(), user.getId()); assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentId(project.getId()).build(), session)) .hasSize(1) .extracting(PropertyDto::getUserId).containsOnly(anotherUser.getId()); assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentId(anotherProject.getId()).build(), session)).extracting(PropertyDto::getUserId) .hasSize(1).containsOnly(user.getId()); } @Test public void delete_by_organization_and_matching_login() throws SQLException { OrganizationDto organization = dbTester.organizations().insert(); OrganizationDto anotherOrganization = dbTester.organizations().insert(); ComponentDto project = dbTester.components().insertPrivateProject(organization); ComponentDto anotherProject = dbTester.components().insertPrivateProject(anotherOrganization); UserDto user = dbTester.users().insertUser(); UserDto anotherUser = dbTester.users().insertUser(); insertProperty("KEY_11", user.getLogin(), project.getId(), null); insertProperty("KEY_12", user.getLogin(), project.getId(), null); insertProperty("KEY_11", anotherUser.getLogin(), project.getId(), null); insertProperty("KEY_11", user.getLogin(), anotherProject.getId(), null); underTest.deleteByOrganizationAndMatchingLogin(session, organization.getUuid(), user.getLogin(), newArrayList("KEY_11", "KEY_12")); assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentId(project.getId()).build(), session)) .hasSize(1) .extracting(PropertyDto::getValue).containsOnly(anotherUser.getLogin()); assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentId(anotherProject.getId()).build(), session)).extracting(PropertyDto::getValue) .hasSize(1).containsOnly(user.getLogin()); } @Test public void delete_by_key_and_value() throws SQLException { ComponentDto project = dbTester.components().insertPrivateProject(); ComponentDto anotherProject = dbTester.components().insertPrivateProject(); insertProperty("KEY", "VALUE", null, null); insertProperty("KEY", "VALUE", project.getId(), null); insertProperty("KEY", "VALUE", null, 100); insertProperty("KEY", "VALUE", project.getId(), 100); insertProperty("KEY", "VALUE", anotherProject.getId(), null); // Should not be removed insertProperty("KEY", "ANOTHER_VALUE", null, null); insertProperty("ANOTHER_KEY", "VALUE", project.getId(), 100); underTest.deleteByKeyAndValue(session, "KEY", "VALUE"); dbTester.commit(); assertThat(dbTester.select("select prop_key as \"key\", text_value as \"value\", resource_id as \"projectId\", user_id as \"userId\" from properties")) .extracting((row) -> row.get("key"), (row) -> row.get("value"), (row) -> row.get("projectId"), (row) -> row.get("userId")) .containsOnly(tuple("KEY", "ANOTHER_VALUE", null, null), tuple("ANOTHER_KEY", "VALUE", project.getId(), 100L)); } @Test public void saveGlobalProperties_insert_property_if_does_not_exist_in_db() { when(system2.now()).thenReturn(DATE_1, DATE_2, DATE_3, DATE_4, DATE_5); underTest.saveGlobalProperties(mapOf( "null_value_property", null, "empty_value_property", "", "text_value_property", "dfdsfsd", "4000_char_value_property", VALUE_SIZE_4000, "clob_value_property", VALUE_SIZE_4001)); assertThatPropertiesRow("null_value_property") .hasNoResourceId() .hasNoUserId() .isEmpty() .hasCreatedAt(DATE_1); assertThatPropertiesRow("empty_value_property") .hasNoResourceId() .hasNoUserId() .isEmpty() .hasCreatedAt(DATE_2); assertThatPropertiesRow("text_value_property") .hasNoResourceId() .hasNoUserId() .hasTextValue("dfdsfsd") .hasCreatedAt(DATE_3); assertThatPropertiesRow("4000_char_value_property") .hasNoResourceId() .hasNoUserId() .hasTextValue(VALUE_SIZE_4000) .hasCreatedAt(DATE_4); assertThatPropertiesRow("clob_value_property") .hasNoResourceId() .hasNoUserId() .hasClobValue(VALUE_SIZE_4001) .hasCreatedAt(DATE_5); } @Test public void saveGlobalProperties_delete_and_insert_new_value_when_property_exists_in_db() throws SQLException { long id = insertProperty("to_be_updated", "old_value", null, null, DATE_1); when(system2.now()).thenReturn(DATE_3); underTest.saveGlobalProperties(ImmutableMap.of("to_be_updated", "new value")); assertThatPropertiesRow(id) .doesNotExist(); assertThatPropertiesRow("to_be_updated") .hasNoResourceId() .hasNoUserId() .hasTextValue("new value") .hasCreatedAt(DATE_3); } private static Map<String, String> mapOf(String... values) { // use LinkedHashMap to keep order of array Map<String, String> res = new LinkedHashMap<>(values.length / 2); Iterator<String> iterator = Arrays.asList(values).iterator(); while (iterator.hasNext()) { res.put(iterator.next(), iterator.next()); } return res; } @Test public void renamePropertyKey_updates_global_component_and_user_properties() throws SQLException { long id1 = insertProperty("foo", "bar", null, null, DATE_1); long id2 = insertProperty("old_name", "doc1", null, null, DATE_1); long id3 = insertProperty("old_name", "doc2", 15L, null, DATE_1); long id4 = insertProperty("old_name", "doc3", 16L, null, DATE_1); long id5 = insertProperty("old_name", "doc4", null, 100, DATE_1); long id6 = insertProperty("old_name", "doc5", null, 101, DATE_1); underTest.renamePropertyKey("old_name", "new_name"); assertThatPropertiesRow(id1) .hasKey("foo") .hasNoUserId() .hasNoResourceId() .hasTextValue("bar") .hasCreatedAt(DATE_1); assertThatPropertiesRow(id2) .hasKey("new_name") .hasNoResourceId() .hasNoUserId() .hasTextValue("doc1") .hasCreatedAt(DATE_1); assertThatPropertiesRow(id3) .hasKey("new_name") .hasResourceId(15) .hasNoUserId() .hasTextValue("doc2") .hasCreatedAt(DATE_1); assertThatPropertiesRow(id4) .hasKey("new_name") .hasResourceId(16) .hasNoUserId() .hasTextValue("doc3") .hasCreatedAt(DATE_1); assertThatPropertiesRow(id5) .hasKey("new_name") .hasNoResourceId() .hasUserId(100) .hasTextValue("doc4") .hasCreatedAt(DATE_1); assertThatPropertiesRow(id6) .hasKey("new_name") .hasNoResourceId() .hasUserId(101) .hasTextValue("doc5") .hasCreatedAt(DATE_1); } @Test public void rename_to_same_key_has_no_effect() throws SQLException { long now = 1_890_999L; long id = insertProperty("foo", "bar", null, null, now); assertThatPropertiesRow(id) .hasCreatedAt(now); underTest.renamePropertyKey("foo", "foo"); assertThatPropertiesRow(id) .hasKey("foo") .hasNoUserId() .hasNoResourceId() .hasTextValue("bar") .hasCreatedAt(now); } @Test public void should_not_rename_with_empty_key() { thrown.expect(IllegalArgumentException.class); underTest.renamePropertyKey("foo", ""); } @Test public void should_not_rename_an_empty_key() { thrown.expect(IllegalArgumentException.class); underTest.renamePropertyKey(null, "foo"); } private PropertyDto findByKey(List<PropertyDto> properties, String key) { for (PropertyDto property : properties) { if (key.equals(property.getKey())) { return property; } } return null; } private void insertProperties(PropertyDto... properties) { for (PropertyDto propertyDto : properties) { underTest.saveProperty(session, propertyDto); } session.commit(); } private long insertProperty(String key, @Nullable String value, @Nullable Long resourceId, @Nullable Integer userId, long createdAt) throws SQLException { when(system2.now()).thenReturn(createdAt); return insertProperty(key, value, resourceId, userId); } private long insertProperty(String key, @Nullable String value, @Nullable Long resourceId, @Nullable Integer userId) throws SQLException { PropertyDto dto = new PropertyDto().setKey(key) .setResourceId(resourceId) .setUserId(userId == null ? null : userId) .setValue(value); dbTester.properties().insertProperty(dto); return (long) dbTester.selectFirst(session, "select id as \"id\" from properties" + " where prop_key='" + key + "'" + " and user_id" + (userId == null ? " is null" : "='" + userId + "'") + " and resource_id" + (resourceId == null ? " is null" : "='" + resourceId + "'")).get("id"); } private ComponentDto insertProject(String uuid) { String key = "project" + uuid; ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization(), uuid).setKey(key); dbClient.componentDao().insert(session, project); dbTester.commit(); return project; } private int insertUser(String login) { UserDto dto = new UserDto().setLogin(login); DbSession session = dbTester.getSession(); dbClient.userDao().insert(session, dto); session.commit(); return dto.getId(); } private static PropertyDtoAssert assertThatDto(@Nullable PropertyDto dto) { return new PropertyDtoAssert(dto); } private PropertiesRowAssert assertThatPropertiesRow(String key, @Nullable Integer userId, @Nullable Integer componentId) { return new PropertiesRowAssert(dbTester, key, userId, componentId); } private PropertiesRowAssert assertThatPropertiesRow(String key) { return new PropertiesRowAssert(dbTester, key); } private PropertiesRowAssert assertThatPropertiesRow(long id) { return new PropertiesRowAssert(dbTester, id); } }