diff --git a/.github/workflows/backend-test-action.yml b/.github/workflows/backend-test-action.yml index 50f40c9fb2..428368d7ff 100644 --- a/.github/workflows/backend-test-action.yml +++ b/.github/workflows/backend-test-action.yml @@ -17,4 +17,4 @@ jobs: distribution: 'adopt' - name: Use Maven to run unittests and integration tests - run: mvn clean verify \ No newline at end of file + run: mvn clean verify -X \ No newline at end of file diff --git a/backend/src/main/java/ch/puzzle/okr/repository/TeamRepository.java b/backend/src/main/java/ch/puzzle/okr/repository/TeamRepository.java index 6a792921c5..14e48978ed 100644 --- a/backend/src/main/java/ch/puzzle/okr/repository/TeamRepository.java +++ b/backend/src/main/java/ch/puzzle/okr/repository/TeamRepository.java @@ -1,20 +1,11 @@ package ch.puzzle.okr.repository; import ch.puzzle.okr.models.Team; -import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; import java.util.List; public interface TeamRepository extends CrudRepository { - @Query(value = """ - select distinct teamOrg.team_id from team_organisation teamOrg - inner join organisation o on o.id = teamOrg.organisation_id - where o.org_name in (:organisationNames) - """, nativeQuery = true) - List findTeamIdsByOrganisationNames(@Param("organisationNames") List organisationNames); - List findTeamsByName(String name); } diff --git a/backend/src/main/java/ch/puzzle/okr/service/persistence/ObjectivePersistenceService.java b/backend/src/main/java/ch/puzzle/okr/service/persistence/ObjectivePersistenceService.java index 675d8ec249..9e7a2bcd34 100644 --- a/backend/src/main/java/ch/puzzle/okr/service/persistence/ObjectivePersistenceService.java +++ b/backend/src/main/java/ch/puzzle/okr/service/persistence/ObjectivePersistenceService.java @@ -40,6 +40,17 @@ public String getModelName() { return OBJECTIVE; } + /** + * Get the number of Objectives of a Team in a Quarter. The underling sql looks like "select count(*) from objective + * where teamId = team.id and quarterId = quarter.id." + * + * @param team + * Team + * @param quarter + * Quarter + * + * @return number of Objectives of team in quarter + */ public Integer countByTeamAndQuarter(Team team, Quarter quarter) { return getRepository().countByTeamAndQuarter(team, quarter); } diff --git a/backend/src/main/java/ch/puzzle/okr/service/persistence/TeamPersistenceService.java b/backend/src/main/java/ch/puzzle/okr/service/persistence/TeamPersistenceService.java index 45d3eba4ae..533949d848 100644 --- a/backend/src/main/java/ch/puzzle/okr/service/persistence/TeamPersistenceService.java +++ b/backend/src/main/java/ch/puzzle/okr/service/persistence/TeamPersistenceService.java @@ -20,14 +20,6 @@ public String getModelName() { return TEAM; } - public List findTeamIdsByOrganisationName(String organisationName) { - return findTeamIdsByOrganisationNames(List.of(organisationName)); - } - - public List findTeamIdsByOrganisationNames(List organisationNames) { - return getRepository().findTeamIdsByOrganisationNames(organisationNames); - } - public List findTeamsByName(String name) { return getRepository().findTeamsByName(name); } diff --git a/backend/src/main/java/ch/puzzle/okr/util/CollectionUtils.java b/backend/src/main/java/ch/puzzle/okr/util/CollectionUtils.java new file mode 100644 index 0000000000..5c3a002a28 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/okr/util/CollectionUtils.java @@ -0,0 +1,14 @@ +package ch.puzzle.okr.util; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class CollectionUtils { + + public static List iterableToList(Iterable iterable) { + return StreamSupport // + .stream(iterable.spliterator(), false) // + .collect(Collectors.toList()); + } +} diff --git a/backend/src/test/java/ch/puzzle/okr/SpringCachingConfigTest.java b/backend/src/test/java/ch/puzzle/okr/SpringCachingConfigTest.java index aa87d01441..1ca5d0825a 100644 --- a/backend/src/test/java/ch/puzzle/okr/SpringCachingConfigTest.java +++ b/backend/src/test/java/ch/puzzle/okr/SpringCachingConfigTest.java @@ -6,7 +6,10 @@ import ch.puzzle.okr.service.authorization.AuthorizationRegistrationService; import ch.puzzle.okr.test.SpringIntegrationTest; import ch.puzzle.okr.test.TestHelper; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -14,7 +17,6 @@ import static ch.puzzle.okr.SpringCachingConfig.AUTHORIZATION_USER_CACHE; import static ch.puzzle.okr.test.TestHelper.defaultUser; import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertNotNull; @SpringIntegrationTest class SpringCachingConfigTest { diff --git a/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationRegistrationServiceIT.java b/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationRegistrationServiceIT.java index 8ebcaa9351..4f0e3c77e4 100644 --- a/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationRegistrationServiceIT.java +++ b/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationRegistrationServiceIT.java @@ -6,10 +6,7 @@ import ch.puzzle.okr.multitenancy.TenantContext; import ch.puzzle.okr.service.persistence.UserPersistenceService; import ch.puzzle.okr.test.SpringIntegrationTest; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -34,6 +31,8 @@ class AuthorizationRegistrationServiceIT { private final String tenant = TestHelper.SCHEMA_PITC; private final String key = tenant + "_" + user.getEmail(); + private static final String EMAIL_WUNDERLAND = "wunderland@puzzle.ch"; + @BeforeEach void setUp() { TenantContext.setCurrentTenant(tenant); @@ -41,10 +40,30 @@ void setUp() { @AfterEach void tearDown() { + resetOkrChampionStatus(); + clearCache(); + TenantContext.setCurrentTenant(null); + } + + private void resetOkrChampionStatus() { + Optional userFromDb = userPersistenceService.findByEmail(EMAIL_WUNDERLAND); + assertTrue(userFromDb.isPresent()); + + userFromDb.get().setOkrChampion(false); + userPersistenceService.save(userFromDb.get()); + assertOkrChampionStatusInDb(userFromDb.get().getEmail(), false); + } + + private void clearCache() { Cache cache = cacheManager.getCache(AUTHORIZATION_USER_CACHE); assertNotNull(cache); cache.clear(); - TenantContext.setCurrentTenant(null); + } + + private void assertOkrChampionStatusInDb(String email, boolean expectedOkrChampionStatus) { + var userInDb = userPersistenceService.findByEmail(email); + assertTrue(userInDb.isPresent()); + assertEquals(expectedOkrChampionStatus, userInDb.get().isOkrChampion()); } @Test @@ -81,6 +100,8 @@ void registerAuthorizationUser_shouldSetOkrChampionsToFalse() { // assert assertFalse(processedUser.user().isOkrChampion()); Optional userFromDB = userPersistenceService.findByEmail(user.getEmail()); + + assertTrue(userFromDB.isPresent()); assertFalse(userFromDB.get().isOkrChampion()); // cleanup @@ -89,30 +110,31 @@ void registerAuthorizationUser_shouldSetOkrChampionsToFalse() { /* * Special test setup.
 - the user wunderland@puzzle.ch is an existing user in the H2 db (created via
-     * X_TestData.sql) - the user wunderland@puzzle.ch is also defined in application-integration-test.properties as
-     * user champion - with this combination we can test, that the user in the db (which has initial isOkrChampion ==
-     * false) is after calling updateOrAddAuthorizationUser() a user champion. - because the user wunderland@puzzle.ch
-     * exists before the test, we make no clean in db (we don't remove it) 
+ * V100_0_0__TestData.sql) - the user wunderland@puzzle.ch is also defined in + * application-integration-test.properties as user champion - with this combination we can test, that the user in + * the db (which has initial isOkrChampion == false) is after calling updateOrAddAuthorizationUser() a user + * champion. - the OkrChampion status must manually be reset (in the tearDown method) */ @Test @DisplayName("registerAuthorizationUser for a user with an email defined in the application-integration-test.properties should set OkrChampions to true") void registerAuthorizationUserShouldSetOkrChampionsToTrue() { // arrange - User user = User.Builder.builder() // - .withFirstname("Alice") // - .withLastname("Wunderland") // - .withEmail("wunderland@puzzle.ch") // user.champion.emails from application-integration-test.properties - .build(); - - userPersistenceService.getOrCreateUser(user); // updates input user with id from DB !!! + assertOkrChampionStatusInDb(EMAIL_WUNDERLAND, false); // pre-condition // act - AuthorizationUser processedUser = authorizationRegistrationService.updateOrAddAuthorizationUser(user); + // load user from db (by email) and set OkrChampion status based on property + // "okr.tenants.pitc.user.champion.emails" from application-integration-test.properties file + AuthorizationUser processedUser = authorizationRegistrationService + .updateOrAddAuthorizationUser(User.Builder.builder() // + .withFirstname("Alice") // + .withLastname("Wunderland") // + .withEmail(EMAIL_WUNDERLAND) // user.champion.emails from + // application-integration-test.properties + .build()); // assert assertTrue(processedUser.user().isOkrChampion()); - Optional userFromDB = userPersistenceService.findByEmail(user.getEmail()); - assertTrue(userFromDB.get().isOkrChampion()); + assertOkrChampionStatusInDb(processedUser.user().getEmail(), true); } @Test @@ -138,6 +160,7 @@ void registerAuthorizationUser_shouldSetFirstnameAndLastnameFromToken() { // assert Optional userFromDB = userPersistenceService.findByEmail(user.getEmail()); + assertTrue(userFromDB.isPresent()); assertEquals(userFromDB.get().getFirstname(), firstNameFromToken); assertEquals(userFromDB.get().getLastname(), lastNameFromToken); diff --git a/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationServiceTest.java b/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationServiceTest.java index eb4e88823f..957b27540f 100644 --- a/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationServiceTest.java +++ b/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationServiceTest.java @@ -1,6 +1,5 @@ package ch.puzzle.okr.service.authorization; -import ch.puzzle.okr.test.TestHelper; import ch.puzzle.okr.dto.ErrorDto; import ch.puzzle.okr.exception.OkrResponseStatusException; import ch.puzzle.okr.models.Objective; @@ -13,6 +12,7 @@ import ch.puzzle.okr.models.keyresult.KeyResultMetric; import ch.puzzle.okr.security.JwtHelper; import ch.puzzle.okr.service.persistence.ObjectivePersistenceService; +import ch.puzzle.okr.test.TestHelper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -28,9 +28,9 @@ import java.util.List; import static ch.puzzle.okr.ErrorKey.*; -import static ch.puzzle.okr.test.TestHelper.*; import static ch.puzzle.okr.service.authorization.AuthorizationService.hasRoleWriteAndReadAll; import static ch.puzzle.okr.service.authorization.AuthorizationService.hasRoleWriteForTeam; +import static ch.puzzle.okr.test.TestHelper.*; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; diff --git a/backend/src/test/java/ch/puzzle/okr/service/authorization/TeamAuthorizationServiceTest.java b/backend/src/test/java/ch/puzzle/okr/service/authorization/TeamAuthorizationServiceTest.java index 09243f4aac..6fb9ec4f34 100644 --- a/backend/src/test/java/ch/puzzle/okr/service/authorization/TeamAuthorizationServiceTest.java +++ b/backend/src/test/java/ch/puzzle/okr/service/authorization/TeamAuthorizationServiceTest.java @@ -130,7 +130,7 @@ void getAllTeamsShouldReturnAllTeams(boolean isWriteable) { if (isWriteable) { when(authorizationService.updateOrAddAuthorizationUser()).thenReturn(okrChampionUser); } else { - when(authorizationService.updateOrAddAuthorizationUser()).thenReturn(userWithoutWriteAllRole()); + when(authorizationService.updateOrAddAuthorizationUser()).thenReturn(defaultAuthorizationUser()); } when(teamBusinessService.getAllTeams(any())).thenReturn(teamList); diff --git a/backend/src/test/java/ch/puzzle/okr/service/persistence/AuthorizationCriteriaParametersTest.java b/backend/src/test/java/ch/puzzle/okr/service/persistence/AuthorizationCriteriaParametersTest.java new file mode 100644 index 0000000000..7cff83afd8 --- /dev/null +++ b/backend/src/test/java/ch/puzzle/okr/service/persistence/AuthorizationCriteriaParametersTest.java @@ -0,0 +1,327 @@ +package ch.puzzle.okr.service.persistence; + +import ch.puzzle.okr.models.Objective; +import ch.puzzle.okr.models.User; +import jakarta.persistence.*; +import org.apache.commons.lang3.NotImplementedException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.*; +import java.util.stream.Stream; + +import static ch.puzzle.okr.test.TestHelper.defaultAuthorizationUser; +import static ch.puzzle.okr.test.TestHelper.mockAuthorizationUser; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class AuthorizationCriteriaParametersTest { + + @DisplayName("setParameters() should be successful with default authorization user") + @Test + void setParametersShouldBeSuccessfulWithDefaultAuthorizationUser() { + // arrange + var criteria = new AuthorizationCriteria(); + TypedQueryMock typedQueryMock = new TypedQueryMock<>(); + + // act + criteria.setParameters(typedQueryMock, defaultAuthorizationUser()); + + // assert + var expected = """ + teamDraftState, State=DRAFT + userTeamIds, ListN=[1] + publishedStates, ListN=[ONGOING, SUCCESSFUL, NOTSUCCESSFUL] + """; + + assertEquals(expected, typedQueryMock.getLog()); + } + + @DisplayName("setParameters() should be successful when user is okr champion") + @Test + void setParametersShouldBeSuccessfulWhenUserIsOkrChampion() { + // arrange + var user = User.Builder.builder() // + .withId(23L) // + .withFirstname("Hanna") // + .withLastname("muster") // + .withEmail("hanna.muster@example.com") // + .withOkrChampion(true) // + .build(); + var criteria = new AuthorizationCriteria(); + TypedQueryMock typedQueryMock = new TypedQueryMock<>(); + + // act + criteria.setParameters(typedQueryMock, mockAuthorizationUser(user)); + + // assert + var expected = """ + allDraftState, State=DRAFT + publishedStates, ListN=[ONGOING, SUCCESSFUL, NOTSUCCESSFUL] + """; + + assertEquals(expected, typedQueryMock.getLog()); + } + + @DisplayName("setParameters() should be successful when team ids or objective query are empty") + @ParameterizedTest + @MethodSource("provideListAndString") + void setParametersShouldBeSuccessfulWhenTeamIdsOrObjectiveQueryAreEmpty(List teamIds, String objectiveQuery) { + // arrange + var criteria = new AuthorizationCriteria(); + TypedQueryMock typedQueryMock = new TypedQueryMock<>(); + + // act + criteria.setParameters(typedQueryMock, teamIds, objectiveQuery, defaultAuthorizationUser()); + + // assert + var expected = """ + teamDraftState, State=DRAFT + userTeamIds, ListN=[1] + publishedStates, ListN=[ONGOING, SUCCESSFUL, NOTSUCCESSFUL] + """; + + assertEquals(expected, typedQueryMock.getLog()); + } + + private static Stream provideListAndString() { + return Stream.of( // + Arguments.of(List.of(), null), // + Arguments.of(List.of(), ""), // + Arguments.of(null, null), // + Arguments.of(null, "")); + } + + @DisplayName("setParameters() should be successful when team ids and objective query are not empty") + @Test + void setParametersShouldBeSuccessfulWhenTeamIdsAndObjectiveQueryAreNotEmpty() { + // arrange + TypedQueryMock typedQueryMock = new TypedQueryMock<>(); + var criteria = new AuthorizationCriteria(); + var anyTeamIds = List.of(99L); + var anyNonEmptyString = "OBJECTIVEQUERY"; + + // act + criteria.setParameters(typedQueryMock, anyTeamIds, anyNonEmptyString, defaultAuthorizationUser()); + + // assert + var expected = """ + teamIds, List12=[99] + objectiveQuery, String=OBJECTIVEQUERY + teamDraftState, State=DRAFT + userTeamIds, ListN=[1] + publishedStates, ListN=[ONGOING, SUCCESSFUL, NOTSUCCESSFUL] + """; + + assertEquals(expected, typedQueryMock.getLog()); + } + + // TypedQuery implementation for testing. The setParameterX() methods calls are logged in an internal StringBuilder + // which is return by getLog(). This log can be used for checking the internal state of the TypedQuery. All other + // methods are not implemented. + private static class TypedQueryMock implements TypedQuery { + + private final StringBuilder log = new StringBuilder(); + + public String getLog() { + return log.toString(); + } + + @Override + public TypedQuery setParameter(Parameter parameter, T t) { + log.append(parameter.getName()).append(", ") // + .append(t.getClass().getSimpleName()).append("=").append(t) // + .append("\n"); + return null; + } + + @Override + public TypedQuery setParameter(Parameter parameter, Calendar calendar, + TemporalType temporalType) { + log.append(parameter.getName()).append(", ") // + .append(calendar.getTime()).append(", ") // + .append(temporalType.name()) // + .append("\n"); + return null; + } + + @Override + public TypedQuery setParameter(Parameter parameter, Date date, TemporalType temporalType) { + log.append(parameter.getName()).append(", ") // + .append(date).append(", ") // + .append(temporalType.name()) // + .append("\n"); + return null; + } + + @Override + public TypedQuery setParameter(String s, Object o) { + log.append(s).append(", ") // + .append(o.getClass().getSimpleName()).append("=").append(o) // + .append("\n"); + return null; + } + + @Override + public TypedQuery setParameter(String s, Calendar calendar, TemporalType temporalType) { + log.append(s).append(", ") // + .append(calendar.getTime()).append(", ") // + .append(temporalType.name()) // + .append("\n"); + return null; + } + + @Override + public TypedQuery setParameter(String s, Date date, TemporalType temporalType) { + log.append(s).append(", ") // + .append(date).append(", ") // + .append(temporalType.name()) // + .append("\n"); + return null; + } + + @Override + public TypedQuery setParameter(int i, Object o) { + log.append(i).append(", ") // + .append(o.getClass().getSimpleName()).append("=").append(o) // + .append("\n"); + return null; + } + + @Override + public TypedQuery setParameter(int i, Calendar calendar, TemporalType temporalType) { + log.append(i).append(", ") // + .append(calendar.getTime()).append(", ") // + .append(temporalType.name()) // + .append("\n"); + return null; + } + + @Override + public TypedQuery setParameter(int i, Date date, TemporalType temporalType) { + log.append(i).append(", ") // + .append(date).append(", ") // + .append(temporalType.name()) // + .append("\n"); + return null; + } + + @Override + public List getResultList() { + throw new NotImplementedException(); + } + + @Override + public Objective getSingleResult() { + throw new NotImplementedException(); + } + + @Override + public int executeUpdate() { + throw new NotImplementedException(); + } + + @Override + public TypedQuery setMaxResults(int i) { + throw new NotImplementedException(); + } + + @Override + public int getMaxResults() { + throw new NotImplementedException(); + } + + @Override + public TypedQuery setFirstResult(int i) { + throw new NotImplementedException(); + } + + @Override + public int getFirstResult() { + throw new NotImplementedException(); + } + + @Override + public TypedQuery setHint(String s, Object o) { + throw new NotImplementedException(); + } + + @Override + public Map getHints() { + throw new NotImplementedException(); + } + + @Override + public Set> getParameters() { + throw new NotImplementedException(); + } + + @Override + public Parameter getParameter(String s) { + throw new NotImplementedException(); + } + + @Override + public Parameter getParameter(String s, Class aClass) { + throw new NotImplementedException(); + } + + @Override + public Parameter getParameter(int i) { + throw new NotImplementedException(); + } + + @Override + public Parameter getParameter(int i, Class aClass) { + throw new NotImplementedException(); + } + + @Override + public boolean isBound(Parameter parameter) { + throw new NotImplementedException(); + } + + @Override + public T getParameterValue(Parameter parameter) { + throw new NotImplementedException(); + } + + @Override + public Object getParameterValue(String s) { + throw new NotImplementedException(); + } + + @Override + public Object getParameterValue(int i) { + throw new NotImplementedException(); + } + + @Override + public TypedQuery setFlushMode(FlushModeType flushModeType) { + throw new NotImplementedException(); + } + + @Override + public FlushModeType getFlushMode() { + throw new NotImplementedException(); + } + + @Override + public TypedQuery setLockMode(LockModeType lockModeType) { + throw new NotImplementedException(); + } + + @Override + public LockModeType getLockMode() { + throw new NotImplementedException(); + } + + @Override + public T unwrap(Class aClass) { + throw new NotImplementedException(); + } + } + +} diff --git a/backend/src/test/java/ch/puzzle/okr/service/persistence/AuthorizationCriteriaTest.java b/backend/src/test/java/ch/puzzle/okr/service/persistence/AuthorizationCriteriaTest.java new file mode 100644 index 0000000000..bdb382edaf --- /dev/null +++ b/backend/src/test/java/ch/puzzle/okr/service/persistence/AuthorizationCriteriaTest.java @@ -0,0 +1,104 @@ +package ch.puzzle.okr.service.persistence; + +import ch.puzzle.okr.models.Objective; +import ch.puzzle.okr.models.User; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.stream.Stream; + +import static ch.puzzle.okr.test.TestHelper.defaultAuthorizationUser; +import static ch.puzzle.okr.test.TestHelper.mockAuthorizationUser; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class AuthorizationCriteriaTest { + + @DisplayName("appendObjective() should be successful with default authorization user") + @Test + void appendObjectiveShouldBeSuccessfulWithDefaultAuthorizationUser() { + // arrange + var criteria = new AuthorizationCriteria(); + + // act + var current = criteria.appendObjective(defaultAuthorizationUser()); + + // assert + var expected = " and ((o.state=:teamDraftState and o.team.id IN (:userTeamIds)) or o.state IN (:publishedStates))"; + assertEquals(expected, current); + } + + @DisplayName("appendObjective() should be successful when user is okrChampion") + @Test + void appendObjectiveShouldBeSuccessfulWhenUserIsOkrChampion() { + // arrange + var user = User.Builder.builder() // + .withId(23L) // + .withFirstname("Hanna") // + .withLastname("muster") // + .withEmail("hanna.muster@example.com") // + .withOkrChampion(true) // + .build(); + var criteria = new AuthorizationCriteria(); + + // act + var current = criteria.appendObjective(mockAuthorizationUser(user)); + + // assert + var expected = " and (o.state=:allDraftState or o.state IN (:publishedStates))"; + assertEquals(expected, current); + } + + @DisplayName("appendOverview() should be successful when team ids or objective query are empty") + @ParameterizedTest + @MethodSource("provideListAndString") + void appendOverviewShouldBeSuccessfulWhenTeamIdsOrObjectiveQueryAreEmpty(List teamIds, + String objectiveQuery) { + // arrange + var criteria = new AuthorizationCriteria(); + + // act + var current = criteria.appendOverview(teamIds, objectiveQuery, defaultAuthorizationUser()); + + // assert + var expected = "\n and ((o.objectiveState=:teamDraftState and o.overviewId.teamId IN (:userTeamIds)) or o.objectiveState IN (:publishedStates) or o.overviewId.objectiveId = -1)"; + assertEquals(expected, current); + } + + private static Stream provideListAndString() { + return Stream.of( // + Arguments.of(List.of(), null), // + Arguments.of(List.of(), ""), // + Arguments.of(null, null), // + Arguments.of(null, "")); + } + + @DisplayName("appendOverview() should be successful when team ids and objective query are not empty") + @Test + void appendOverviewShouldBeSuccessfulWhenTeamIdsAndObjectiveQueryAreNotEmpty() { + // arrange + var criteria = new AuthorizationCriteria(); + var anyTeamIds = List.of(99L); + var anyNonEmptyString = "OBJECTIVEQUERY"; + var startingNewLine = "\n"; + var singleSpace = " "; + + // act + var current = criteria.appendOverview(anyTeamIds, anyNonEmptyString, defaultAuthorizationUser()); + + // assert + var expected = startingNewLine + singleSpace + + """ + and o.overviewId.teamId in (:teamIds) + and lower(coalesce(o.objectiveTitle, '')) like lower(concat('%',:objectiveQuery,'%')) + and ((o.objectiveState=:teamDraftState and o.overviewId.teamId IN (:userTeamIds)) or o.objectiveState IN (:publishedStates) or o.overviewId.objectiveId = -1)"""; + + assertEquals(expected, current); + assertFalse(current.contains(anyNonEmptyString)); + } + +} diff --git a/backend/src/test/java/ch/puzzle/okr/service/persistence/CheckInPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/okr/service/persistence/CheckInPersistenceServiceIT.java index 419971e392..0837b5f996 100644 --- a/backend/src/test/java/ch/puzzle/okr/service/persistence/CheckInPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/okr/service/persistence/CheckInPersistenceServiceIT.java @@ -1,52 +1,32 @@ package ch.puzzle.okr.service.persistence; -import ch.puzzle.okr.test.TestHelper; -import ch.puzzle.okr.dto.ErrorDto; -import ch.puzzle.okr.exception.OkrResponseStatusException; -import ch.puzzle.okr.models.Objective; -import ch.puzzle.okr.models.User; import ch.puzzle.okr.models.checkin.CheckIn; -import ch.puzzle.okr.models.checkin.CheckInMetric; -import ch.puzzle.okr.models.keyresult.KeyResultMetric; import ch.puzzle.okr.multitenancy.TenantContext; import ch.puzzle.okr.test.SpringIntegrationTest; +import ch.puzzle.okr.test.TestHelper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.server.ResponseStatusException; -import java.time.LocalDateTime; import java.util.List; import java.util.Objects; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; +import static ch.puzzle.okr.Constants.CHECK_IN; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; @SpringIntegrationTest class CheckInPersistenceServiceIT { - CheckIn createdCheckIn; + + private static final long KEY_RESULT_ID = 7L; @Autowired private CheckInPersistenceService checkInPersistenceService; - private static CheckIn createCheckIn(Long id) { - return createCheckIn(id, 1); - } - - private static final String UPDATED_CHECKIN = "Updated CheckIn"; - - private static CheckIn createCheckIn(Long id, int version) { - return CheckInMetric.Builder.builder().withValue(30D).withId(id).withVersion(version) - .withCreatedBy(User.Builder.builder().withId(1L).withFirstname("Frank").build()) - .withCreatedOn(LocalDateTime.MAX) - .withKeyResult(KeyResultMetric.Builder.builder().withBaseline(1.0).withStretchGoal(13.0).withId(8L) - .withObjective(Objective.Builder.builder().withId(1L).build()).build()) - .withChangeInfo("ChangeInfo").withInitiatives("Initiatives").withModifiedOn(LocalDateTime.MAX) - .withConfidence(5).build(); - } - @BeforeEach void setUp() { TenantContext.setCurrentTenant(TestHelper.SCHEMA_PITC); @@ -54,97 +34,53 @@ void setUp() { @AfterEach void tearDown() { - try { - if (createdCheckIn != null) { - checkInPersistenceService.findById(createdCheckIn.getId()); - checkInPersistenceService.deleteById(createdCheckIn.getId()); - } - } catch (ResponseStatusException ex) { - // created CheckIn already deleted - } finally { - createdCheckIn = null; - } TenantContext.setCurrentTenant(null); } + // uses data from V100_0_0__TestData.sql + @DisplayName("getCheckInsByKeyResultIdOrderByCheckInDate() should get checkIns by keyResultId and order them by date desc") @Test - void saveCheckInShouldSaveNewCheckIn() { - CheckIn checkIn = createCheckIn(null); - - createdCheckIn = checkInPersistenceService.save(checkIn); - - assertNotNull(createdCheckIn.getId()); - assertEquals(checkIn.getModifiedOn(), createdCheckIn.getModifiedOn()); - assertEquals(((CheckInMetric) checkIn).getValue(), ((CheckInMetric) createdCheckIn).getValue()); - assertEquals(checkIn.getCreatedBy(), createdCheckIn.getCreatedBy()); - assertEquals(checkIn.getCreatedOn(), createdCheckIn.getCreatedOn()); - assertEquals(checkIn.getInitiatives(), createdCheckIn.getInitiatives()); - assertEquals(checkIn.getChangeInfo(), createdCheckIn.getChangeInfo()); + void getCheckInsByKeyResultIdOrderByCheckInDateShouldGetCheckInsByKeyResultIdAndOrderThemByDateDesc() { + // act + List checkIns = checkInPersistenceService + .getCheckInsByKeyResultIdOrderByCheckInDateDesc(KEY_RESULT_ID); + + // assert + assertThat(2, greaterThanOrEqualTo(checkIns.size())); + CheckIn firstCheckIn = checkIns.get(0); + CheckIn lastCheckIn = checkIns.get(checkIns.size() - 1); + assertFirstIsCreatedAfterSecond(firstCheckIn, lastCheckIn); } - @Test - void updateKeyResultShouldUpdateKeyResult() { - createdCheckIn = checkInPersistenceService.save(createCheckIn(null)); - CheckIn updateCheckIn = createCheckIn(createdCheckIn.getId(), createdCheckIn.getVersion()); - updateCheckIn.setChangeInfo(UPDATED_CHECKIN); - - CheckIn updatedCheckIn = checkInPersistenceService.save(updateCheckIn); - - assertEquals(createdCheckIn.getId(), updatedCheckIn.getId()); - assertEquals(createdCheckIn.getVersion() + 1, updatedCheckIn.getVersion()); - assertEquals(UPDATED_CHECKIN, updatedCheckIn.getChangeInfo()); + private void assertFirstIsCreatedAfterSecond(CheckIn first, CheckIn second) { + assertTrue(first.getCreatedOn().isAfter(second.getCreatedOn())); } + // uses data from V100_0_0__TestData.sql + @DisplayName("getLastCheckInOfKeyResult() should get last checkIn of keyResult") @Test - void updateKeyResultShouldThrowExceptionWhenAlreadyUpdated() { - createdCheckIn = checkInPersistenceService.save(createCheckIn(null)); - CheckIn updateCheckIn = createCheckIn(createdCheckIn.getId(), 0); - updateCheckIn.setChangeInfo(UPDATED_CHECKIN); + void getLastCheckInOfKeyResultShouldGetLastCheckInOfKeyResult() { + // act + var lastCheckIn = checkInPersistenceService.getLastCheckInOfKeyResult(KEY_RESULT_ID); - OkrResponseStatusException exception = assertThrows(OkrResponseStatusException.class, - () -> checkInPersistenceService.save(updateCheckIn)); - - List expectedErrors = List.of(new ErrorDto("DATA_HAS_BEEN_UPDATED", List.of("Check-in"))); - - assertEquals(UNPROCESSABLE_ENTITY, exception.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(exception.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(exception.getReason())); + // assert + var allCheckins = checkInPersistenceService.getCheckInsByKeyResultIdOrderByCheckInDateDesc(KEY_RESULT_ID); + assertLastIsCreatedAfterAllOtherCheckIns(lastCheckIn, allCheckins); } - @Test - void getAllCheckInShouldReturnListOfAllCheckIns() { - List checkIns = checkInPersistenceService.findAll(); - - assertEquals(19, checkIns.size()); - } - - @Test - void getCheckInByIdShouldReturnCheckInProperly() { - CheckIn checkIn = checkInPersistenceService.findById(20L); - - assertEquals(20L, checkIn.getId()); - assertEquals(0.5, ((CheckInMetric) checkIn).getValue(), 0.01); - assertEquals( - "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore " - + "magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ", - checkIn.getChangeInfo()); + private void assertLastIsCreatedAfterAllOtherCheckIns(CheckIn last, List allCheckIns) { + for (CheckIn checkInLoop : allCheckIns) { + if (!Objects.equals(checkInLoop.getId(), last.getId())) { + assertTrue(last.getCreatedOn().isAfter(checkInLoop.getCreatedOn())); + } + } } + @DisplayName("getModelName() should return checkIn") @Test - void shouldGetCheckInsByKeyResultIdAndOrderThemByDateDesc() { - List checkIns = checkInPersistenceService.getCheckInsByKeyResultIdOrderByCheckInDateDesc(7L); - assertTrue(checkIns.get(0).getCreatedOn().isAfter(checkIns.get(checkIns.size() - 1).getCreatedOn())); + void getModelNameShouldReturnCheckIn() { + assertEquals(CHECK_IN, checkInPersistenceService.getModelName()); } - @Test - void shouldGetLastCheckInOfKeyResult() { - CheckIn checkIn = checkInPersistenceService.getLastCheckInOfKeyResult(7L); - List checkInList = checkInPersistenceService.getCheckInsByKeyResultIdOrderByCheckInDateDesc(7L); - for (CheckIn checkInLoop : checkInList) { - if (!Objects.equals(checkInLoop.getId(), checkIn.getId())) { - assertTrue(checkIn.getCreatedOn().isAfter(checkInLoop.getCreatedOn())); - } - } - } } diff --git a/backend/src/test/java/ch/puzzle/okr/service/persistence/ObjectivePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/okr/service/persistence/ObjectivePersistenceServiceIT.java index ccc6c3a8a0..9708e9d779 100644 --- a/backend/src/test/java/ch/puzzle/okr/service/persistence/ObjectivePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/okr/service/persistence/ObjectivePersistenceServiceIT.java @@ -2,56 +2,68 @@ import ch.puzzle.okr.dto.ErrorDto; import ch.puzzle.okr.exception.OkrResponseStatusException; -import ch.puzzle.okr.models.*; +import ch.puzzle.okr.models.Objective; +import ch.puzzle.okr.models.Quarter; +import ch.puzzle.okr.models.Team; import ch.puzzle.okr.models.authorization.AuthorizationUser; import ch.puzzle.okr.multitenancy.TenantContext; import ch.puzzle.okr.test.SpringIntegrationTest; import ch.puzzle.okr.test.TestHelper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.server.ResponseStatusException; +import org.springframework.http.HttpStatus; -import java.time.LocalDateTime; import java.util.List; +import java.util.stream.Stream; -import static ch.puzzle.okr.test.TestConstants.GJ_FOR_TESTS_QUARTER_ID; +import static ch.puzzle.okr.exception.OkrResponseStatusException.of; import static ch.puzzle.okr.test.TestHelper.defaultAuthorizationUser; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; -import static org.springframework.http.HttpStatus.*; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.UNAUTHORIZED; +// tests are using data from V100_0_0__TestData.sql @SpringIntegrationTest class ObjectivePersistenceServiceIT { - private static final String REASON = "not authorized to read objective"; - private static final OkrResponseStatusException exception = OkrResponseStatusException.of(REASON); - private static final String HIGHER_CUSTOMER_HAPPINESS = "Wir wollen die Kundenzufriedenheit steigern"; - private static final String MODEL_WITH_ID_NOT_FOUND = "MODEL_WITH_ID_NOT_FOUND"; + private static final long INVALID_OBJECTIVE_ID = 321L; + private static final long INVALID_KEY_RESULT_ID = 321L; + private static final long INVALID_CHECK_IN_ID = 321L; + private static final long INVALID_TEAM_ID = 321L; + private static final long INVALID_QUARTER_ID = 12L; + + private static final long ID_OF_OBJECTIVE_3 = 3L; + private static final long ID_OF_OBJECTIVE_8 = 8L; + private static final long ID_OF_OBJECTIVE_9 = 9L; + private static final long ID_OF_OBJECTIVE_10 = 10L; + + private static final String TITLE_OF_OBJECTIVE_3 = "Wir wollen die Kundenzufriedenheit steigern"; + private static final String TITLE_OF_OBJECTIVE_8 = "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua"; + private static final String TITLE_OF_OBJECTIVE_9 = "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."; + private static final String TITLE_OF_OBJECTIVE_10 = "should not appear on staging, no sea takimata sanctus est Lorem ipsum dolor sit amet."; + + private static final long ID_OF_KEY_RESULT_5 = 5L; + private static final long ID_OF_CHECK_IN_7 = 7L; + private static final long ID_OF_TEAM_6 = 6L; + + private static final String REASON_UNAUTHORIZED = "not authorized to read objective"; + private static final OkrResponseStatusException NO_RESULT_EXCEPTION = of(REASON_UNAUTHORIZED); + private static final String OBJECTIVE = "Objective"; private static final String ATTRIBUTE_NULL = "ATTRIBUTE_NULL"; + private static final long CURRENT_QUARTER_ID = 2L; + private final AuthorizationUser authorizationUser = defaultAuthorizationUser(); - private Objective createdObjective; @Autowired private ObjectivePersistenceService objectivePersistenceService; - @Autowired - private TeamPersistenceService teamPersistenceService; - @Autowired - private QuarterPersistenceService quarterPersistenceService; - - private static Objective createObjective(Long id) { - return createObjective(id, 1); - } - - private static Objective createObjective(Long id, int version) { - return Objective.Builder.builder().withId(id).withVersion(version).withTitle("title") - .withCreatedBy(User.Builder.builder().withId(1L).build()) - .withTeam(Team.Builder.builder().withId(5L).build()) - .withQuarter(Quarter.Builder.builder().withId(GJ_FOR_TESTS_QUARTER_ID).build()) - .withDescription("This is our description").withState(State.DRAFT).withCreatedOn(LocalDateTime.MAX) - .withModifiedOn(LocalDateTime.MAX).withModifiedBy(User.Builder.builder().withId(1L).build()).build(); - } @BeforeEach void setUp() { @@ -60,206 +72,209 @@ void setUp() { @AfterEach void tearDown() { - try { - if (createdObjective != null) { - objectivePersistenceService.findById(createdObjective.getId()); - objectivePersistenceService.deleteById(createdObjective.getId()); - } - } catch (ResponseStatusException ex) { - // created key result already deleted - } finally { - createdObjective = null; - } TenantContext.setCurrentTenant(null); } - @Test - void findAllShouldReturnListOfObjectives() { - List objectives = objectivePersistenceService.findAll(); - - assertEquals(7, objectives.size()); - } - + @DisplayName("findObjectiveById() should return objective properly") @Test void findObjectiveByIdShouldReturnObjectiveProperly() { - Objective objective = objectivePersistenceService.findObjectiveById(3L, authorizationUser, exception); + // act + var objective = objectivePersistenceService.findObjectiveById(ID_OF_OBJECTIVE_3, authorizationUser, + NO_RESULT_EXCEPTION); - assertEquals(3L, objective.getId()); - assertEquals(HIGHER_CUSTOMER_HAPPINESS, objective.getTitle()); + // assert + assertObjective(ID_OF_OBJECTIVE_3, TITLE_OF_OBJECTIVE_3, objective); } + @DisplayName("findObjectiveById() should throw exception when objective not found") @Test void findObjectiveByIdShouldThrowExceptionWhenObjectiveNotFound() { - ResponseStatusException findObjectiveException = assertThrows(OkrResponseStatusException.class, - () -> objectivePersistenceService.findObjectiveById(321L, authorizationUser, - ObjectivePersistenceServiceIT.exception)); + // act + var exception = assertThrows(OkrResponseStatusException.class, () -> objectivePersistenceService + .findObjectiveById(INVALID_OBJECTIVE_ID, authorizationUser, NO_RESULT_EXCEPTION)); - assertEquals(UNAUTHORIZED, findObjectiveException.getStatusCode()); - assertEquals(REASON, findObjectiveException.getReason()); + // assert + var expectedErrors = List.of(new ErrorDto(REASON_UNAUTHORIZED, List.of())); + assertResponseStatusException(UNAUTHORIZED, expectedErrors, exception); } + @DisplayName("findObjectiveById() should throw exception when objective id is null") @Test void findObjectiveByIdShouldThrowExceptionWhenObjectiveIdIsNull() { - OkrResponseStatusException findObjectiveException = assertThrows(OkrResponseStatusException.class, - () -> objectivePersistenceService.findObjectiveById(null, authorizationUser, - ObjectivePersistenceServiceIT.exception)); - - List expectedErrors = List.of(new ErrorDto(ATTRIBUTE_NULL, List.of("ID", OBJECTIVE))); + // act + var exception = assertThrows(OkrResponseStatusException.class, + () -> objectivePersistenceService.findObjectiveById(null, authorizationUser, NO_RESULT_EXCEPTION)); - assertEquals(BAD_REQUEST, findObjectiveException.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(findObjectiveException.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(findObjectiveException.getReason())); + // assert + var expectedErrors = List.of(new ErrorDto(ATTRIBUTE_NULL, List.of("ID", OBJECTIVE))); + assertResponseStatusException(BAD_REQUEST, expectedErrors, exception); } + @DisplayName("findObjectiveByKeyResultId() should return objective properly") @Test void findObjectiveByKeyResultIdShouldReturnObjectiveProperly() { - Objective objective = objectivePersistenceService.findObjectiveByKeyResultId(5L, authorizationUser, exception); + // act + var objective = objectivePersistenceService.findObjectiveByKeyResultId(ID_OF_KEY_RESULT_5, authorizationUser, + NO_RESULT_EXCEPTION); - assertEquals(3L, objective.getId()); - assertEquals(HIGHER_CUSTOMER_HAPPINESS, objective.getTitle()); + // assert + assertObjective(ID_OF_OBJECTIVE_3, TITLE_OF_OBJECTIVE_3, objective); } + @DisplayName("findObjectiveByKeyResultId() should throw exception when objective not found") @Test void findObjectiveByKeyResultIdShouldThrowExceptionWhenObjectiveNotFound() { - ResponseStatusException objectiveByKeyResultException = assertThrows(ResponseStatusException.class, - () -> objectivePersistenceService.findObjectiveByKeyResultId(321L, authorizationUser, - ObjectivePersistenceServiceIT.exception)); + // act + var exception = assertThrows(OkrResponseStatusException.class, () -> objectivePersistenceService + .findObjectiveByKeyResultId(INVALID_KEY_RESULT_ID, authorizationUser, NO_RESULT_EXCEPTION)); - assertEquals(UNAUTHORIZED, objectiveByKeyResultException.getStatusCode()); - assertEquals(REASON, objectiveByKeyResultException.getReason()); + // assert + var expectedErrors = List.of(new ErrorDto(REASON_UNAUTHORIZED, List.of())); + assertResponseStatusException(UNAUTHORIZED, expectedErrors, exception); } + @DisplayName("findObjectiveByKeyResultId() should throw exception when objective id is null") @Test void findObjectiveByKeyResultIdShouldThrowExceptionWhenObjectiveIdIsNull() { - OkrResponseStatusException objectiveByKeyResultException = assertThrows(OkrResponseStatusException.class, - () -> objectivePersistenceService.findObjectiveByKeyResultId(null, authorizationUser, - ObjectivePersistenceServiceIT.exception)); + // act + var exception = assertThrows(OkrResponseStatusException.class, () -> objectivePersistenceService + .findObjectiveByKeyResultId(null, authorizationUser, NO_RESULT_EXCEPTION)); - List expectedErrors = List.of(new ErrorDto(ATTRIBUTE_NULL, List.of("ID", OBJECTIVE))); - - assertEquals(BAD_REQUEST, objectiveByKeyResultException.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(objectiveByKeyResultException.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(objectiveByKeyResultException.getReason())); + // assert + var expectedErrors = List.of(new ErrorDto(ATTRIBUTE_NULL, List.of("ID", OBJECTIVE))); + assertResponseStatusException(BAD_REQUEST, expectedErrors, exception); } + @DisplayName("findObjectiveByCheckInId() should return objective properly") @Test void findObjectiveByCheckInIdShouldReturnObjectiveProperly() { - Objective objective = objectivePersistenceService.findObjectiveByCheckInId(7L, authorizationUser, exception); + // act + var objective = objectivePersistenceService.findObjectiveByCheckInId(ID_OF_CHECK_IN_7, authorizationUser, + NO_RESULT_EXCEPTION); - assertEquals(3L, objective.getId()); - assertEquals(HIGHER_CUSTOMER_HAPPINESS, objective.getTitle()); + // assert + assertObjective(ID_OF_OBJECTIVE_3, TITLE_OF_OBJECTIVE_3, objective); } + @DisplayName("findObjectiveByCheckInId() should throw exception when objective not found") @Test void findObjectiveByCheckInIdShouldThrowExceptionWhenObjectiveNotFound() { - ResponseStatusException objectiveByCheckInException = assertThrows(ResponseStatusException.class, - () -> objectivePersistenceService.findObjectiveByCheckInId(321L, authorizationUser, - ObjectivePersistenceServiceIT.exception)); + // act + var exception = assertThrows(OkrResponseStatusException.class, () -> objectivePersistenceService + .findObjectiveByCheckInId(INVALID_CHECK_IN_ID, authorizationUser, NO_RESULT_EXCEPTION)); - assertEquals(UNAUTHORIZED, objectiveByCheckInException.getStatusCode()); - assertEquals(REASON, objectiveByCheckInException.getReason()); + // assert + var expectedErrors = List.of(new ErrorDto(REASON_UNAUTHORIZED, List.of())); + assertResponseStatusException(UNAUTHORIZED, expectedErrors, exception); } + @DisplayName("findObjectiveByCheckInId() should throw exception when objective id is null") @Test void findObjectiveByCheckInIdShouldThrowExceptionWhenObjectiveIdIsNull() { - OkrResponseStatusException objectiveByCheckInException = assertThrows(OkrResponseStatusException.class, - () -> objectivePersistenceService.findObjectiveByCheckInId(null, authorizationUser, - ObjectivePersistenceServiceIT.exception)); - - List expectedErrors = List.of(new ErrorDto(ATTRIBUTE_NULL, List.of("ID", OBJECTIVE))); + // act + var exception = assertThrows(OkrResponseStatusException.class, () -> objectivePersistenceService + .findObjectiveByCheckInId(null, authorizationUser, ObjectivePersistenceServiceIT.NO_RESULT_EXCEPTION)); - assertEquals(BAD_REQUEST, objectiveByCheckInException.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(objectiveByCheckInException.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(objectiveByCheckInException.getReason())); + // assert + var expectedErrors = List.of(new ErrorDto(ATTRIBUTE_NULL, List.of("ID", OBJECTIVE))); + assertResponseStatusException(BAD_REQUEST, expectedErrors, exception); } + @DisplayName("findObjectiveByTeamId() should return objectives of team properly") @Test - void saveObjectiveShouldSaveNewObjective() { - Objective objective = createObjective(null); - - createdObjective = objectivePersistenceService.save(objective); - - assertNotNull(createdObjective.getId()); - assertEquals(objective.getDescription(), createdObjective.getDescription()); - assertEquals(objective.getDescription(), createdObjective.getDescription()); - assertEquals(objective.getModifiedOn(), createdObjective.getModifiedOn()); + void findObjectiveByTeamIdShouldReturnObjectivesOfTeamProperly() { + // act + var objectives = objectivePersistenceService.findObjectiveByTeamId(ID_OF_TEAM_6); + + // assert + assertEquals(3, objectives.size()); + assertObjective(ID_OF_OBJECTIVE_8, TITLE_OF_OBJECTIVE_8, objectives.get(0)); + assertObjective(ID_OF_OBJECTIVE_9, TITLE_OF_OBJECTIVE_9, objectives.get(1)); + assertObjective(ID_OF_OBJECTIVE_10, TITLE_OF_OBJECTIVE_10, objectives.get(2)); } + @DisplayName("findObjectiveByTeamId() should return empty list when objective not found") @Test - void updateObjectiveShouldUpdateObjective() { - createdObjective = objectivePersistenceService.save(createObjective(null)); - Objective updateObjective = createObjective(createdObjective.getId(), createdObjective.getVersion()); - updateObjective.setState(State.ONGOING); - - Objective updatedObjective = objectivePersistenceService.save(updateObjective); + void findObjectiveByTeamIdShouldReturnEmptyListWhenObjectiveNotFound() { + // act + var objectives = objectivePersistenceService.findObjectiveByTeamId(INVALID_TEAM_ID); - assertEquals(createdObjective.getId(), updatedObjective.getId()); - assertEquals(State.ONGOING, updatedObjective.getState()); + // assert + assertTrue(objectives.isEmpty()); } + @DisplayName("findObjectiveByTeamId() should return empty list when objective id is null") @Test - void updateObjectiveShouldThrowExceptionWhenAlreadyUpdated() { - createdObjective = objectivePersistenceService.save(createObjective(null)); - Objective updateObjective = createObjective(createdObjective.getId(), 0); - updateObjective.setState(State.ONGOING); - - OkrResponseStatusException objectiveSaveException = assertThrows(OkrResponseStatusException.class, - () -> objectivePersistenceService.save(updateObjective)); - List expectedErrors = List.of(new ErrorDto("DATA_HAS_BEEN_UPDATED", List.of(OBJECTIVE))); - - assertEquals(UNPROCESSABLE_ENTITY, objectiveSaveException.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(objectiveSaveException.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(objectiveSaveException.getReason())); - } - - @Test - void deleteObjectiveShouldThrowExceptionWhenKeyResultNotFound() { - Objective objective = createObjective(321L); - createdObjective = objectivePersistenceService.save(objective); - objectivePersistenceService.deleteById(createdObjective.getId()); - - Long objectiveId = createdObjective.getId(); - OkrResponseStatusException findObjectiveException = assertThrows(OkrResponseStatusException.class, - () -> objectivePersistenceService.findById(objectiveId)); + void findObjectiveByTeamIdShouldReturnEmptyListWhenObjectiveIdIsNull() { + // act + var objectives = objectivePersistenceService.findObjectiveByTeamId(null); - List expectedErrors = List.of(new ErrorDto(MODEL_WITH_ID_NOT_FOUND, List.of(OBJECTIVE, "200"))); - - assertEquals(NOT_FOUND, findObjectiveException.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(findObjectiveException.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(findObjectiveException.getReason())); + // assert + assertTrue(objectives.isEmpty()); } + @DisplayName("countByTeamAndQuarter() should return number of objectives for current quarter") @Test - void countByTeamAndQuarterShouldThrowErrorIfQuarterDoesNotExist() { - Team teamId5 = teamPersistenceService.findById(5L); - OkrResponseStatusException countByTeamException = assertThrows(OkrResponseStatusException.class, - () -> objectivePersistenceService.countByTeamAndQuarter(teamId5, - quarterPersistenceService.findById(12L))); - - List expectedErrors = List.of(new ErrorDto(MODEL_WITH_ID_NOT_FOUND, List.of("Quarter", "12"))); + void countByTeamAndQuarterShouldReturnNumberOfObjectivesForCurrentQuarter() { + // arrange: there are 3 objectives for the current quarter (id 2) for team with id 6 + var team = Team.Builder.builder().withId(ID_OF_TEAM_6).build(); + var quarter = Quarter.Builder.builder().withId(CURRENT_QUARTER_ID).build(); - assertEquals(NOT_FOUND, countByTeamException.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(countByTeamException.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(countByTeamException.getReason())); + // act + var count = objectivePersistenceService.countByTeamAndQuarter(team, quarter); - Quarter quarterId2 = quarterPersistenceService.findById(2L); - OkrResponseStatusException exceptionTeam = assertThrows(OkrResponseStatusException.class, - () -> objectivePersistenceService.countByTeamAndQuarter(teamPersistenceService.findById(500L), - quarterId2)); + // assert + assertEquals(3, count); + } - List expectedErrorsTeam = List.of(new ErrorDto(MODEL_WITH_ID_NOT_FOUND, List.of("Team", "500"))); + @DisplayName("countByTeamAndQuarter() should return zero when team or quarter is not valid or null") + @ParameterizedTest + @MethodSource("invalidTeamsAndQuarters") + void countByTeamAndQuarterShouldReturnZeroWhenTeamOrQuarterIsNotValidOrNull(Team team, Quarter quarter) { + // act + var count = objectivePersistenceService.countByTeamAndQuarter(team, quarter); - assertEquals(NOT_FOUND, exceptionTeam.getStatusCode()); - assertThat(expectedErrorsTeam).hasSameElementsAs(exceptionTeam.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrorsTeam).contains(exceptionTeam.getReason())); + // assert + assertEquals(0, count); + } + private static Stream invalidTeamsAndQuarters() { + var validTeam = Team.Builder.builder().withId(ID_OF_TEAM_6).build(); + var invalidTeam = Team.Builder.builder().withId(INVALID_TEAM_ID).build(); + var validQuarter = Quarter.Builder.builder().withId(CURRENT_QUARTER_ID).build(); + var invalidQuarter = Quarter.Builder.builder().withId(INVALID_QUARTER_ID).build(); + + return Stream.of( + // valid team + invalid quarter + arguments(validTeam, invalidQuarter), + // valid team + null quarter + arguments(validTeam, null), + // invalid team + valid quarter + arguments(invalidTeam, validQuarter), + // invalid team + null quarter + arguments(null, validQuarter), + // invalid team + invalid quarter + arguments(invalidTeam, invalidQuarter), + // null team + null quarter + arguments(null, null)); } + @DisplayName("getModelName() should return Objective") @Test - void countByTeamAndQuarterShouldReturnCountValue() { - Integer count = objectivePersistenceService.countByTeamAndQuarter(Team.Builder.builder().withId(5L).build(), - Quarter.Builder.builder().withId(2L).build()); + void getModelNameShouldReturnObjective() { + assertEquals(OBJECTIVE, objectivePersistenceService.getModelName()); + } + + private void assertResponseStatusException(HttpStatus expectedStatus, List expectedErrors, + OkrResponseStatusException currentException) { + assertEquals(expectedStatus, currentException.getStatusCode()); + assertThat(expectedErrors).hasSameElementsAs(currentException.getErrors()); + assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(currentException.getReason())); + } - assertEquals(2, count); + private void assertObjective(Long expectedId, String expectedTitle, Objective currentObjective) { + assertEquals(expectedId, currentObjective.getId()); + assertEquals(expectedTitle, currentObjective.getTitle()); } + } diff --git a/backend/src/test/java/ch/puzzle/okr/service/persistence/PersistenceBaseTestIT.java b/backend/src/test/java/ch/puzzle/okr/service/persistence/PersistenceBaseTestIT.java new file mode 100644 index 0000000000..7f5a60a5e5 --- /dev/null +++ b/backend/src/test/java/ch/puzzle/okr/service/persistence/PersistenceBaseTestIT.java @@ -0,0 +1,182 @@ +package ch.puzzle.okr.service.persistence; + +import ch.puzzle.okr.dto.ErrorDto; +import ch.puzzle.okr.models.User; +import ch.puzzle.okr.multitenancy.TenantContext; +import ch.puzzle.okr.repository.UserRepository; +import ch.puzzle.okr.test.SpringIntegrationTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.OptimisticLockingFailureException; +import org.springframework.web.server.ResponseStatusException; + +import java.util.List; + +import static ch.puzzle.okr.test.TestHelper.getAllErrorKeys; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.springframework.http.HttpStatus.*; + +/** + * Testing the functionality of the abstract PersistenceBase and use UserRepository as example of a CrudRepository + * implementation. + *

+ * Tests depending on data from V100_0_0__TestData.sql + */ +@SpringIntegrationTest +public class PersistenceBaseTestIT { + + private User createdUser; + + private static final long NON_EXISTING_USER_ID = 321L; + private static final long USER_PACO_ID = 1L; + private static final User USER_WITHOUT_CONSTRAINTS = User.Builder.builder() // + .withFirstname("Hans") // + .withLastname("Muster") // + .withEmail("hans.muster@puzzle.ch") // + .build(); + + @Autowired + private PersistenceBase persistenceBase; + + @BeforeEach + void setUp() { + TenantContext.setCurrentTenant("pitc"); + } + + @AfterEach + void tearDown() { + if (createdUser != null) { + persistenceBase.deleteById(createdUser.getId()); + createdUser = null; + } + TenantContext.setCurrentTenant(null); + } + + @DisplayName("findById() should return single entity if entity with id exists") + @Test + void findByIdShouldReturnSingleEntityIfEntityWithIdExists() { + var foundUser = persistenceBase.findById(USER_PACO_ID); + + assertEquals(USER_PACO_ID, foundUser.getId()); + assertUser("Paco", "Eggimann", "peggimann@puzzle.ch", foundUser); + } + + @DisplayName("findById() should throw exception if entity with id does not exist") + @Test + void findByIdShouldThrowExceptionIfEntityWithIdDoesNotExist() { + var exception = assertThrows(ResponseStatusException.class, + () -> persistenceBase.findById(NON_EXISTING_USER_ID)); + + assertEquals(NOT_FOUND, exception.getStatusCode()); + assertErrorKey("MODEL_WITH_ID_NOT_FOUND", exception); + } + + @DisplayName("findById() should throw exception if id is null") + @Test + void findByIdShouldThrowExceptionIfIdIsNull() { + var exception = assertThrows(ResponseStatusException.class, () -> persistenceBase.findById(null)); + + assertEquals(BAD_REQUEST, exception.getStatusCode()); + assertErrorKey("ATTRIBUTE_NULL", exception); + } + + @DisplayName("findAll() should return all entities as list") + @Test + void findAllShouldReturnAllEntitiesAsList() throws ResponseStatusException { + var userList = persistenceBase.findAll(); + + assertThat(userList.size()).isGreaterThanOrEqualTo(7); + } + + @DisplayName("save() should add new entity") + @Test + void saveShouldAddNewEntity() throws ResponseStatusException { + createdUser = persistenceBase.save(USER_WITHOUT_CONSTRAINTS); + + assertNotNull(createdUser); + assertUser("Hans", "Muster", "hans.muster@puzzle.ch", createdUser); + } + + @DisplayName("save() should throw exception in the case of optimistic locking failure") + @Test + void saveShouldThrowExceptionInTheCaseOfOptimisticLockingFailure() throws ResponseStatusException { + // arrange + var testRepository = mock(UserRepository.class); + when(testRepository.save(any())).thenThrow(OptimisticLockingFailureException.class); + + var persistenceBaseForTest = new PersistenceBase<>(testRepository) { + @Override + public String getModelName() { + return "for_test"; + } + }; + + // act + assert + var exception = assertThrows(ResponseStatusException.class, () -> persistenceBaseForTest.save(createdUser)); + + // assert + assertEquals(UNPROCESSABLE_ENTITY, exception.getStatusCode()); + assertErrorKey("DATA_HAS_BEEN_UPDATED", exception); + } + + @DisplayName("save() existing entity with different data should update existing entity") + @Test + void saveExistingEntityWithDifferentDataShouldUpdateExistingEntity() throws ResponseStatusException { + // arrange + createdUser = persistenceBase.save(USER_WITHOUT_CONSTRAINTS); + var createdUserId = createdUser.getId(); + var foundUser = persistenceBase.findById(createdUserId); + + // pro-condition + assertEquals("Hans", createdUser.getFirstname()); + + // act + foundUser.setFirstname("Pekka"); + persistenceBase.save(foundUser); + foundUser = persistenceBase.findById(createdUserId); + + // assert + assertEquals(createdUserId, foundUser.getId()); + assertEquals("Pekka", foundUser.getFirstname()); + } + + @DisplayName("deleteById() should delete entity") + @Test + void deleteByIdShouldDeleteEntity() throws ResponseStatusException { + // arrange + createdUser = persistenceBase.save(USER_WITHOUT_CONSTRAINTS); + var createdUserId = createdUser.getId(); + assertNotNull(persistenceBase.findById(createdUserId)); + + // act + persistenceBase.deleteById(createdUserId); + + // assert + assertEntityNotFound(createdUserId); + } + + private static void assertUser(String expectedFirstName, String expectedLastName, String expectedEmail, + User currentUser) { + assertEquals(expectedFirstName, currentUser.getFirstname()); + assertEquals(expectedLastName, currentUser.getLastname()); + assertEquals(expectedEmail, currentUser.getEmail()); + } + + private void assertErrorKey(String errorKey, ResponseStatusException exception) { + var errorKeys = getAllErrorKeys(List.of(new ErrorDto(errorKey, List.of("User")))); + assertTrue(errorKeys.contains(exception.getReason())); + } + + private void assertEntityNotFound(long entityId) { + var exception = assertThrows(ResponseStatusException.class, () -> persistenceBase.findById(entityId)); + assertEquals(NOT_FOUND, exception.getStatusCode()); + assertErrorKey("MODEL_WITH_ID_NOT_FOUND", exception); + } +} \ No newline at end of file diff --git a/backend/src/test/java/ch/puzzle/okr/service/persistence/QuarterPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/okr/service/persistence/QuarterPersistenceServiceIT.java index 23a1ce5faf..1eee877cc1 100644 --- a/backend/src/test/java/ch/puzzle/okr/service/persistence/QuarterPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/okr/service/persistence/QuarterPersistenceServiceIT.java @@ -1,7 +1,5 @@ package ch.puzzle.okr.service.persistence; -import ch.puzzle.okr.dto.ErrorDto; -import ch.puzzle.okr.exception.OkrResponseStatusException; import ch.puzzle.okr.models.Quarter; import ch.puzzle.okr.multitenancy.TenantContext; import ch.puzzle.okr.test.SpringIntegrationTest; @@ -16,12 +14,10 @@ import java.time.LocalDate; import java.util.List; +import static ch.puzzle.okr.Constants.QUARTER; import static ch.puzzle.okr.test.TestConstants.GJ_FOR_TESTS_QUARTER_ID; import static ch.puzzle.okr.test.TestConstants.GJ_FOR_TEST_QUARTER_LABEL; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.NOT_FOUND; @SpringIntegrationTest class QuarterPersistenceServiceIT { @@ -39,40 +35,6 @@ void tearDown() { TenantContext.setCurrentTenant(null); } - @Test - void shouldReturnSingleQuarterWhenFindingByValidId() { - Quarter returnedQuarter = quarterPersistenceService.findById(GJ_FOR_TESTS_QUARTER_ID); - - assertEquals(GJ_FOR_TESTS_QUARTER_ID, returnedQuarter.getId()); - assertEquals(GJ_FOR_TEST_QUARTER_LABEL, returnedQuarter.getLabel()); - assertEquals(LocalDate.of(2000, 7, 1), returnedQuarter.getStartDate()); - assertEquals(LocalDate.of(2000, 9, 30), returnedQuarter.getEndDate()); - } - - @Test - void shouldThrowExceptionWhenFindingQuarterNotFound() { - OkrResponseStatusException exception = assertThrows(OkrResponseStatusException.class, - () -> quarterPersistenceService.findById(321L)); - - List expectedErrors = List.of(new ErrorDto("MODEL_WITH_ID_NOT_FOUND", List.of("Quarter", "321"))); - - assertEquals(NOT_FOUND, exception.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(exception.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(exception.getReason())); - } - - @Test - void shouldThrowExceptionWhenFindingQuarterWithIdNull() { - OkrResponseStatusException exception = assertThrows(OkrResponseStatusException.class, - () -> quarterPersistenceService.findById(null)); - - List expectedErrors = List.of(new ErrorDto("ATTRIBUTE_NULL", List.of("ID", "Quarter"))); - - assertEquals(BAD_REQUEST, exception.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(exception.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(exception.getReason())); - } - @DisplayName("getMostCurrentQuarters() should return current quarter and future quarter and GJForTests quarter") @Test void getMostCurrentQuartersShouldReturnCurrentQuarterAndFutureQuarterAndGJForTestsQuarter() { @@ -141,4 +103,10 @@ void findByLabelShouldReturnNullWhenLabelIsNull() { // assert assertNull(returnedQuarter); } + + @DisplayName("getModelName() should return Quarter") + @Test + void getModelNameShouldReturnQuarter() { + assertEquals(QUARTER, quarterPersistenceService.getModelName()); + } } diff --git a/backend/src/test/java/ch/puzzle/okr/service/persistence/TeamPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/okr/service/persistence/TeamPersistenceServiceIT.java index f71c9daa79..f37812cb21 100644 --- a/backend/src/test/java/ch/puzzle/okr/service/persistence/TeamPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/okr/service/persistence/TeamPersistenceServiceIT.java @@ -1,29 +1,22 @@ package ch.puzzle.okr.service.persistence; -import ch.puzzle.okr.test.TestHelper; -import ch.puzzle.okr.dto.ErrorDto; -import ch.puzzle.okr.exception.OkrResponseStatusException; import ch.puzzle.okr.models.Team; import ch.puzzle.okr.multitenancy.TenantContext; import ch.puzzle.okr.test.SpringIntegrationTest; -import org.junit.jupiter.api.AfterEach; +import ch.puzzle.okr.test.TestHelper; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.server.ResponseStatusException; import java.util.List; -import static ch.puzzle.okr.test.TestConstants.TEAM_PUZZLE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; -import static org.springframework.http.HttpStatus.*; +import static ch.puzzle.okr.Constants.TEAM; +import static org.junit.jupiter.api.Assertions.assertEquals; @SpringIntegrationTest class TeamPersistenceServiceIT { - private static final String NEW_TEAM = "New Team"; - private Team createdTeam; @Autowired private TeamPersistenceService teamPersistenceService; @@ -32,112 +25,20 @@ void setUp() { TenantContext.setCurrentTenant(TestHelper.SCHEMA_PITC); } - @AfterEach - void tearDown() { - try { - if (createdTeam != null) { - teamPersistenceService.findById(createdTeam.getId()); - teamPersistenceService.deleteById(createdTeam.getId()); - } - } catch (ResponseStatusException ex) { - // created team already deleted - } finally { - createdTeam = null; - } - TenantContext.setCurrentTenant(null); - } - - @Test - void getTeamByIdShouldReturnTeam() throws ResponseStatusException { - Team team = teamPersistenceService.findById(5L); - - assertEquals(5L, team.getId()); - assertEquals(TEAM_PUZZLE, team.getName()); - } - - @Test - void getTeamByIdShouldThrowExceptionWhenTeamNotFound() { - OkrResponseStatusException exception = assertThrows(OkrResponseStatusException.class, - () -> teamPersistenceService.findById(321L)); - - List expectedErrors = List.of(new ErrorDto("MODEL_WITH_ID_NOT_FOUND", List.of("Team", "321"))); - - assertEquals(NOT_FOUND, exception.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(exception.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(exception.getReason())); - } - - @Test - void getTeamByIdShouldThrowExceptionWhenTeamIdIsNull() { - OkrResponseStatusException exception = assertThrows(OkrResponseStatusException.class, - () -> teamPersistenceService.findById(null)); - - List expectedErrors = List.of(new ErrorDto("ATTRIBUTE_NULL", List.of("ID", "Team"))); - - assertEquals(BAD_REQUEST, exception.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(exception.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(exception.getReason())); - } - - @Test - void shouldSaveANewTeam() { - Team team = Team.Builder.builder().withName("TestTeam").build(); - - createdTeam = teamPersistenceService.save(team); - assertNotNull(createdTeam.getId()); - assertEquals("TestTeam", createdTeam.getName()); - } - + // uses data from V100_0_0__TestData.sql + @DisplayName("findTeamsByName() should return teams with matching name") @Test - void shouldUpdateTeamProperly() { - Team team = Team.Builder.builder().withName(NEW_TEAM).build(); - createdTeam = teamPersistenceService.save(team); - createdTeam.setName("Updated Team"); - - Team returnedTeam = teamPersistenceService.save(createdTeam); - - assertEquals(createdTeam.getId(), returnedTeam.getId()); - assertEquals("Updated Team", returnedTeam.getName()); - } - - @Test - void updateTeamShouldThrowExceptionWhenAlreadyUpdated() { - Team team = Team.Builder.builder().withVersion(1).withName(NEW_TEAM).build(); - createdTeam = teamPersistenceService.save(team); - Team changedTeam = Team.Builder.builder().withId(createdTeam.getId()).withVersion(0).withName("Changed Team") - .build(); - - OkrResponseStatusException exception = assertThrows(OkrResponseStatusException.class, - () -> teamPersistenceService.save(changedTeam)); - List expectedErrors = List.of(new ErrorDto("DATA_HAS_BEEN_UPDATED", List.of("Team"))); - - assertEquals(UNPROCESSABLE_ENTITY, exception.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(exception.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(exception.getReason())); - } - - @Test - void shouldDeleteTeam() { - Team team = Team.Builder.builder().withName(NEW_TEAM).build(); - createdTeam = teamPersistenceService.save(team); - teamPersistenceService.deleteById(createdTeam.getId()); - - OkrResponseStatusException exception = assertThrows(OkrResponseStatusException.class, - () -> teamPersistenceService.findById(createdTeam.getId())); - - List expectedErrors = List - .of(ErrorDto.of("MODEL_WITH_ID_NOT_FOUND", List.of("Team", createdTeam.getId()))); + void findTeamsByNameShouldReturnTeamsWithMatchingName() { + List teams = teamPersistenceService.findTeamsByName("LoremIpsum"); - assertEquals(NOT_FOUND, exception.getStatusCode()); - assertThat(expectedErrors).hasSameElementsAs(exception.getErrors()); - assertTrue(TestHelper.getAllErrorKeys(expectedErrors).contains(exception.getReason())); + assertEquals(1, teams.size()); + assertEquals(6, teams.get(0).getId()); + assertEquals("LoremIpsum", teams.get(0).getName()); } + @DisplayName("getModelName() should return Team") @Test - void shouldFindTeamsByName() { - Team team = Team.Builder.builder().withName(NEW_TEAM).build(); - createdTeam = teamPersistenceService.save(team); - List teams = teamPersistenceService.findTeamsByName(NEW_TEAM); - assertThat(teams).contains(createdTeam); + void getModelNameShouldReturnTeam() { + assertEquals(TEAM, teamPersistenceService.getModelName()); } } \ No newline at end of file diff --git a/backend/src/test/java/ch/puzzle/okr/service/persistence/UserPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/okr/service/persistence/UserPersistenceServiceIT.java index ca1dc10624..0104060b48 100644 --- a/backend/src/test/java/ch/puzzle/okr/service/persistence/UserPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/okr/service/persistence/UserPersistenceServiceIT.java @@ -3,24 +3,23 @@ import ch.puzzle.okr.models.User; import ch.puzzle.okr.multitenancy.TenantContext; import ch.puzzle.okr.test.SpringIntegrationTest; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.web.server.ResponseStatusException; import java.util.List; +import java.util.Optional; +import static ch.puzzle.okr.Constants.USER; +import static ch.puzzle.okr.util.CollectionUtils.iterableToList; import static org.junit.jupiter.api.Assertions.*; @SpringIntegrationTest class UserPersistenceServiceIT { - private static final String EMAIL_ALICE = "wunderland@puzzle.ch"; - - User createdUser; + private User createdUser; @Autowired private UserPersistenceService userPersistenceService; @@ -39,63 +38,144 @@ void tearDown() { TenantContext.setCurrentTenant(null); } + @DisplayName("save() should save user with empty user team list") @Test - void shouldReturnAllUsersCorrect() throws ResponseStatusException { - List userList = userPersistenceService.findAll(); + void saveShouldSaveUserWithEmptyUserTeamList() { + // arrange + var newUser = User.Builder.builder() // + .withFirstname("Hans") // + .withLastname("Muster") // + .withEmail("muster@puzzle.ch") // + .withUserTeamList(List.of()).build(); + + // act + createdUser = userPersistenceService.save(newUser); + + // assert + assertNotNull(createdUser.getId()); + assertUser("Hans", "Muster", "muster@puzzle.ch", createdUser); + } - Assertions.assertThat(userList.size()).isGreaterThanOrEqualTo(7); + @DisplayName("save() should save user with null value for user team list") + @Test + void saveShouldSaveUserWithNullUserTeamList() { + // arrange + var newUser = User.Builder.builder() // + .withFirstname("Hans") // + .withLastname("Muster") // + .withEmail("muster@puzzle.ch") // + .withUserTeamList(null).build(); + + // act + createdUser = userPersistenceService.save(newUser); + + // assert + assertNotNull(createdUser.getId()); + assertUser("Hans", "Muster", "muster@puzzle.ch", createdUser); } + @DisplayName("saveAll() should save all users in the input list") @Test - void shouldReturnSingleUserWhenFindingOwnerByValidId() { - User returnedUser = userPersistenceService.findById(1L); + void saveAllShouldSaveAllUsersInTheInputList() { + // arrange + var newUser = User.Builder.builder() // + .withFirstname("Hans") // + .withLastname("Muster") // + .withEmail("muster@puzzle.ch") // + .build(); + + // act + var createdUsers = iterableToList(userPersistenceService.saveAll(List.of(newUser))); + + // assert + assertEquals(1, createdUsers.size()); + createdUser = createdUsers.get(0); - assertEquals(1L, returnedUser.getId()); - assertEquals("Paco", returnedUser.getFirstname()); - assertEquals("Eggimann", returnedUser.getLastname()); - assertEquals("peggimann@puzzle.ch", returnedUser.getEmail()); + assertNotNull(createdUser.getId()); + assertUser("Hans", "Muster", "muster@puzzle.ch", createdUser); } + @DisplayName("getOrCreateUser() should return single user when user found") @Test - void shouldThrowExceptionWhenFindingOwnerNotFound() { - ResponseStatusException exception = assertThrows(ResponseStatusException.class, - () -> userPersistenceService.findById(321L)); + void getOrCreateUserShouldReturnSingleUserWhenUserFound() { + // arrange + var existingUser = User.Builder.builder().withEmail("wunderland@puzzle.ch").build(); - assertEquals(HttpStatus.NOT_FOUND, exception.getStatusCode()); - assertEquals("MODEL_WITH_ID_NOT_FOUND", exception.getReason()); + // act + var returnedUser = userPersistenceService.getOrCreateUser(existingUser); + + // assert + assertUser(11L, "Alice", "Wunderland", "wunderland@puzzle.ch", returnedUser); } + @DisplayName("getOrCreateUser() should return saved user when user not found") @Test - void shouldThrowExceptionWhenFindingOwnerWithNullId() { - ResponseStatusException exception = assertThrows(ResponseStatusException.class, - () -> userPersistenceService.findById(null)); + void getOrCreateUserShouldReturnSavedUserWhenUserNotFound() { + // arrange + var newUser = User.Builder.builder() // + .withId(null) // + .withFirstname("firstname") // + .withLastname("lastname") // + .withEmail("lastname@puzzle.ch") // + .build(); + + // act + createdUser = userPersistenceService.getOrCreateUser(newUser); - assertEquals(HttpStatus.BAD_REQUEST, exception.getStatusCode()); - assertEquals("ATTRIBUTE_NULL", exception.getReason()); + // assert + assertNotNull(createdUser.getId()); + assertUser("firstname", "lastname", "lastname@puzzle.ch", createdUser); } + // uses data from V100_0_0__TestData.sql + @DisplayName("findByEmail() should return user if email is found") @Test - void getOrCreateUserShouldReturnSingleUserWhenUserFound() { - User existingUser = User.Builder.builder().withEmail(EMAIL_ALICE).build(); + void findByEmailShouldReturnUserIfEmailIsFound() { + Optional user = userPersistenceService.findByEmail("gl@gl.com"); - User returnedUser = userPersistenceService.getOrCreateUser(existingUser); + assertTrue(user.isPresent()); + assertEquals("Jaya", user.get().getFirstname()); + assertEquals("Norris", user.get().getLastname()); + } - assertEquals(11L, returnedUser.getId()); - assertEquals("Alice", returnedUser.getFirstname()); - assertEquals("Wunderland", returnedUser.getLastname()); - assertEquals("wunderland@puzzle.ch", returnedUser.getEmail()); + @DisplayName("findByEmail() should return empty optional if email is not found") + @Test + void findByEmailShouldReturnEmptyOptionalIfEmailIsNotFound() { + assertTrue(userPersistenceService.findByEmail("not_valid@gl.com").isEmpty()); } + @DisplayName("findByEmail() should return empty optional if email is null") @Test - void getOrCreateUserShouldReturnSavedUserWhenUserNotFound() { - User newUser = User.Builder.builder().withId(null).withFirstname("firstname").withLastname("lastname") - .withEmail("lastname@puzzle.ch").build(); + void findByEmailShouldReturnEmptyOptionalIfEmailIsNull() { + assertTrue(userPersistenceService.findByEmail(null).isEmpty()); + } - createdUser = userPersistenceService.getOrCreateUser(newUser); + // uses data from V100_0_0__TestData.sql + @DisplayName("findAllOkrChampions() should return all okr champions") + @Test + void findAllOkrChampionsShouldReturnAllOkrChampions() { + // act + var allOkrChampions = userPersistenceService.findAllOkrChampions(); - assertNotNull(createdUser.getId()); - assertEquals("firstname", createdUser.getFirstname()); - assertEquals("lastname", createdUser.getLastname()); - assertEquals("lastname@puzzle.ch", createdUser.getEmail()); + // assert + assertEquals(1, allOkrChampions.size()); + assertUser(61L, "Jaya", "Norris", "gl@gl.com", allOkrChampions.get(0)); + } + + @DisplayName("getModelName() should return user") + @Test + void getModelNameShouldReturnUser() { + assertEquals(USER, userPersistenceService.getModelName()); + } + + private void assertUser(Long id, String firstName, String lastName, String email, User currentUser) { + assertEquals(id, currentUser.getId()); + assertUser(firstName, lastName, email, currentUser); + } + + private void assertUser(String firstName, String lastName, String email, User currentUser) { + assertEquals(firstName, currentUser.getFirstname()); + assertEquals(lastName, currentUser.getLastname()); + assertEquals(email, currentUser.getEmail()); } } \ No newline at end of file diff --git a/backend/src/test/java/ch/puzzle/okr/test/TestHelper.java b/backend/src/test/java/ch/puzzle/okr/test/TestHelper.java index 75d53c1db4..86bb30b3f7 100644 --- a/backend/src/test/java/ch/puzzle/okr/test/TestHelper.java +++ b/backend/src/test/java/ch/puzzle/okr/test/TestHelper.java @@ -56,19 +56,22 @@ public static UserTeam defaultUserTeam(Long id, User user) { } public static AuthorizationUser defaultAuthorizationUser() { - return mockAuthorizationUser(1L, FIRSTNAME, LASTNAME, EMAIL); - } - - public static AuthorizationUser userWithoutWriteAllRole() { - return mockAuthorizationUser(1L, FIRSTNAME, LASTNAME, EMAIL); + return mockAuthorizationUser(1L, FIRSTNAME, LASTNAME, EMAIL, false); } public static AuthorizationUser mockAuthorizationUser(User user) { - return mockAuthorizationUser(user.getId(), user.getFirstname(), user.getLastname(), user.getEmail()); + return mockAuthorizationUser(user.getId(), user.getFirstname(), user.getLastname(), user.getEmail(), + user.isOkrChampion()); } - public static AuthorizationUser mockAuthorizationUser(Long id, String firstname, String lastname, String email) { - User user = User.Builder.builder().withId(id).withFirstname(firstname).withLastname(lastname).withEmail(email) + public static AuthorizationUser mockAuthorizationUser(Long id, String firstname, String lastname, String email, + boolean isOkrChampion) { + User user = User.Builder.builder() // + .withId(id) // + .withFirstname(firstname) // + .withLastname(lastname) // + .withEmail(email) // + .withOkrChampion(isOkrChampion) // .build(); user.setUserTeamList(List.of(defaultUserTeam(1L, user))); return new AuthorizationUser(user);