This commit is contained in:
2026-06-25 09:36:05 +07:00
parent d3fef38796
commit 5c644545b8
364 changed files with 78377 additions and 0 deletions
@@ -0,0 +1,26 @@
package com.sisvietnamvn.web;
import com.sisvietnamvn.web.config.AsyncSyncConfiguration;
import com.sisvietnamvn.web.config.EmbeddedSQL;
import com.sisvietnamvn.web.config.JacksonConfiguration;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.test.context.SpringBootTest;
/**
* Base composite annotation for integration tests.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@SpringBootTest(
classes = {
SisvietnamvnApp.class,
JacksonConfiguration.class,
AsyncSyncConfiguration.class,
com.sisvietnamvn.web.config.JacksonHibernateConfiguration.class,
}
)
@EmbeddedSQL
public @interface IntegrationTest {}
@@ -0,0 +1,13 @@
package com.sisvietnamvn.web;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SisvietnamvnApplicationTests {
@Test
void contextLoads() {
}
}
@@ -0,0 +1,38 @@
package com.sisvietnamvn.web;
import static com.tngtech.archunit.base.DescribedPredicate.alwaysTrue;
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.belongToAnyOf;
import static com.tngtech.archunit.library.Architectures.layeredArchitecture;
import com.tngtech.archunit.core.importer.ImportOption.DoNotIncludeTests;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
@AnalyzeClasses(packagesOf = SisvietnamvnApp.class, importOptions = DoNotIncludeTests.class)
class TechnicalStructureTest {
// prettier-ignore
@ArchTest
static final ArchRule respectsTechnicalArchitectureLayers = layeredArchitecture()
.consideringAllDependencies()
.layer("Config").definedBy("..config..")
.layer("Web").definedBy("..web..")
.optionalLayer("Service").definedBy("..service..")
.layer("Security").definedBy("..security..")
.optionalLayer("Persistence").definedBy("..repository..")
.layer("Domain").definedBy("..domain..")
.whereLayer("Config").mayNotBeAccessedByAnyLayer()
.whereLayer("Web").mayOnlyBeAccessedByLayers("Config")
.whereLayer("Service").mayOnlyBeAccessedByLayers("Web", "Config")
.whereLayer("Security").mayOnlyBeAccessedByLayers("Config", "Service", "Web")
.whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service", "Security", "Web", "Config")
.whereLayer("Domain").mayOnlyBeAccessedByLayers("Persistence", "Service", "Security", "Web", "Config")
.ignoreDependency(belongToAnyOf(SisvietnamvnApp.class), alwaysTrue())
.ignoreDependency(alwaysTrue(), belongToAnyOf(
com.sisvietnamvn.web.config.Constants.class,
com.sisvietnamvn.web.config.ApplicationProperties.class
));
}
@@ -0,0 +1,15 @@
package com.sisvietnamvn.web.config;
import java.util.concurrent.Executor;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.SyncTaskExecutor;
@TestConfiguration(proxyBeanMethods = false)
public class AsyncSyncConfiguration {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
return new SyncTaskExecutor();
}
}
@@ -0,0 +1,132 @@
package com.sisvietnamvn.web.config;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import ch.qos.logback.classic.spi.ILoggingEvent;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.springframework.boot.ansi.AnsiColor;
import org.springframework.boot.ansi.AnsiElement;
class CRLFLogConverterTest {
@Test
void transformShouldReturnInputStringWhenMarkerListIsEmpty() {
ILoggingEvent event = mock(ILoggingEvent.class);
when(event.getMarkerList()).thenReturn(null);
when(event.getLoggerName()).thenReturn("org.hibernate.example.Logger");
String input = "Test input string";
CRLFLogConverter converter = new CRLFLogConverter();
String result = converter.transform(event, input);
assertEquals(input, result);
}
@Test
void transformShouldReturnInputStringWhenMarkersContainCRLFSafeMarker() {
ILoggingEvent event = mock(ILoggingEvent.class);
Marker marker = MarkerFactory.getMarker("CRLF_SAFE");
List<Marker> markers = List.of(marker);
when(event.getMarkerList()).thenReturn(markers);
String input = "Test input string";
CRLFLogConverter converter = new CRLFLogConverter();
String result = converter.transform(event, input);
assertEquals(input, result);
}
@Test
void transformShouldReturnInputStringWhenMarkersNotContainCRLFSafeMarker() {
ILoggingEvent event = mock(ILoggingEvent.class);
Marker marker = MarkerFactory.getMarker("CRLF_NOT_SAFE");
List<Marker> markers = List.of(marker);
when(event.getMarkerList()).thenReturn(markers);
when(event.getLoggerName()).thenReturn("org.hibernate.example.Logger");
String input = "Test input string";
CRLFLogConverter converter = new CRLFLogConverter();
String result = converter.transform(event, input);
assertEquals(input, result);
}
@Test
void transformShouldReturnInputStringWhenLoggerIsSafe() {
ILoggingEvent event = mock(ILoggingEvent.class);
when(event.getLoggerName()).thenReturn("org.hibernate.example.Logger");
String input = "Test input string";
CRLFLogConverter converter = new CRLFLogConverter();
String result = converter.transform(event, input);
assertEquals(input, result);
}
@Test
void transformShouldReplaceNewlinesAndCarriageReturnsWithUnderscoreWhenMarkersDoNotContainCRLFSafeMarkerAndLoggerIsNotSafe() {
ILoggingEvent event = mock(ILoggingEvent.class);
List<Marker> markers = List.of();
when(event.getMarkerList()).thenReturn(markers);
when(event.getLoggerName()).thenReturn("com.mycompany.myapp.example.Logger");
String input = "Test\ninput\rstring";
CRLFLogConverter converter = new CRLFLogConverter();
String result = converter.transform(event, input);
assertEquals("Test_input_string", result);
}
@Test
void transformShouldReplaceNewlinesAndCarriageReturnsWithAnsiStringWhenMarkersDoNotContainCRLFSafeMarkerAndLoggerIsNotSafeAndAnsiElementIsNotNull() {
ILoggingEvent event = mock(ILoggingEvent.class);
List<Marker> markers = List.of();
when(event.getMarkerList()).thenReturn(markers);
when(event.getLoggerName()).thenReturn("com.mycompany.myapp.example.Logger");
String input = "Test\ninput\rstring";
CRLFLogConverter converter = new CRLFLogConverter();
converter.setOptionList(List.of("red"));
String result = converter.transform(event, input);
assertEquals("Test_input_string", result);
}
@Test
void isLoggerSafeShouldReturnTrueWhenLoggerNameStartsWithSafeLogger() {
ILoggingEvent event = mock(ILoggingEvent.class);
when(event.getLoggerName()).thenReturn("org.springframework.boot.autoconfigure.example.Logger");
CRLFLogConverter converter = new CRLFLogConverter();
boolean result = converter.isLoggerSafe(event);
assertTrue(result);
}
@Test
void isLoggerSafeShouldReturnFalseWhenLoggerNameDoesNotStartWithSafeLogger() {
ILoggingEvent event = mock(ILoggingEvent.class);
when(event.getLoggerName()).thenReturn("com.mycompany.myapp.example.Logger");
CRLFLogConverter converter = new CRLFLogConverter();
boolean result = converter.isLoggerSafe(event);
assertFalse(result);
}
@Test
void testToAnsiString() {
CRLFLogConverter cut = new CRLFLogConverter();
AnsiElement ansiElement = AnsiColor.RED;
String result = cut.toAnsiString("input", ansiElement);
assertThat(result).isEqualTo("input");
}
}
@@ -0,0 +1,10 @@
package com.sisvietnamvn.web.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EmbeddedSQL {}
@@ -0,0 +1,22 @@
package com.sisvietnamvn.web.config;
import com.sisvietnamvn.web.IntegrationTest;
import java.util.Comparator;
import org.junit.jupiter.api.ClassDescriptor;
import org.junit.jupiter.api.ClassOrderer;
import org.junit.jupiter.api.ClassOrdererContext;
public class SpringBootTestClassOrderer implements ClassOrderer {
@Override
public void orderClasses(ClassOrdererContext context) {
context.getClassDescriptors().sort(Comparator.comparingInt(SpringBootTestClassOrderer::getOrder));
}
private static int getOrder(ClassDescriptor classDescriptor) {
if (classDescriptor.findAnnotation(IntegrationTest.class).isPresent()) {
return 2;
}
return 1;
}
}
@@ -0,0 +1,7 @@
package com.sisvietnamvn.web.config;
import org.testcontainers.containers.JdbcDatabaseContainer;
public interface SqlTestContainer {
JdbcDatabaseContainer<?> getTestContainer();
}
@@ -0,0 +1,68 @@
package com.sisvietnamvn.web.config;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.MergedContextConfiguration;
import tech.jhipster.config.JHipsterConstants;
public class SqlTestContainersSpringContextCustomizerFactory implements ContextCustomizerFactory {
private final Logger log = LoggerFactory.getLogger(SqlTestContainersSpringContextCustomizerFactory.class);
private static SqlTestContainer prodTestcontainer;
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass, List<ContextConfigurationAttributes> configAttributes) {
return new ContextCustomizer() {
@Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
TestPropertyValues testValues = TestPropertyValues.empty();
EmbeddedSQL sqlAnnotation = AnnotatedElementUtils.findMergedAnnotation(testClass, EmbeddedSQL.class);
boolean usingTestProdProfile = List.of(context.getEnvironment().getActiveProfiles()).contains(
"test" + JHipsterConstants.SPRING_PROFILE_PRODUCTION
);
if (null != sqlAnnotation && usingTestProdProfile) {
log.debug("detected the EmbeddedSQL annotation on class {}", testClass.getName());
log.info("Warming up the sql database");
if (null == prodTestcontainer) {
try {
Class<? extends SqlTestContainer> containerClass = Class.forName(
this.getClass().getPackageName() + ".DatabaseTestcontainer"
).asSubclass(SqlTestContainer.class);
prodTestcontainer = beanFactory.createBean(containerClass);
beanFactory.registerSingleton(containerClass.getName(), prodTestcontainer);
/**
* ((DefaultListableBeanFactory)beanFactory).registerDisposableBean(containerClass.getName(), prodTestcontainer);
*/
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
testValues = testValues.and("spring.datasource.url=" + prodTestcontainer.getTestContainer().getJdbcUrl() + "");
testValues = testValues.and("spring.datasource.username=" + prodTestcontainer.getTestContainer().getUsername());
testValues = testValues.and("spring.datasource.password=" + prodTestcontainer.getTestContainer().getPassword());
}
testValues.applyTo(context);
}
@Override
public int hashCode() {
return SqlTestContainer.class.getName().hashCode();
}
@Override
public boolean equals(Object obj) {
return this.hashCode() == obj.hashCode();
}
};
}
}
@@ -0,0 +1,113 @@
package com.sisvietnamvn.web.config;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import jakarta.servlet.*;
import java.util.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import tech.jhipster.config.JHipsterProperties;
/**
* Unit tests for the {@link WebConfigurer} class.
*/
class WebConfigurerTest {
private WebConfigurer webConfigurer;
private MockServletContext servletContext;
private MockEnvironment env;
private JHipsterProperties props;
@BeforeEach
void setup() {
servletContext = spy(new MockServletContext());
doReturn(mock(FilterRegistration.Dynamic.class)).when(servletContext).addFilter(anyString(), any(Filter.class));
doReturn(mock(ServletRegistration.Dynamic.class)).when(servletContext).addServlet(anyString(), any(Servlet.class));
env = new MockEnvironment();
props = new JHipsterProperties();
webConfigurer = new WebConfigurer(env, props);
}
@Test
void shouldCorsFilterOnApiPath() throws Exception {
props.getCors().setAllowedOrigins(List.of("other.domain.com"));
props.getCors().setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
props.getCors().setAllowedHeaders(List.of("*"));
props.getCors().setMaxAge(1800L);
props.getCors().setAllowCredentials(true);
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()).addFilters(webConfigurer.corsFilter()).build();
mockMvc
.perform(
options("/api/test-cors")
.header(HttpHeaders.ORIGIN, "other.domain.com")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST")
)
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "other.domain.com"))
.andExpect(header().string(HttpHeaders.VARY, "Origin"))
.andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET,POST,PUT,DELETE"))
.andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"))
.andExpect(header().string(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "1800"));
mockMvc
.perform(get("/api/test-cors").header(HttpHeaders.ORIGIN, "other.domain.com"))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "other.domain.com"));
}
@Test
void shouldCorsFilterOnOtherPath() throws Exception {
props.getCors().setAllowedOrigins(List.of("*"));
props.getCors().setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
props.getCors().setAllowedHeaders(List.of("*"));
props.getCors().setMaxAge(1800L);
props.getCors().setAllowCredentials(true);
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()).addFilters(webConfigurer.corsFilter()).build();
mockMvc
.perform(get("/test/test-cors").header(HttpHeaders.ORIGIN, "other.domain.com"))
.andExpect(status().isOk())
.andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
}
@Test
void shouldCorsFilterDeactivatedForNullAllowedOrigins() throws Exception {
props.getCors().setAllowedOrigins(null);
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()).addFilters(webConfigurer.corsFilter()).build();
mockMvc
.perform(get("/api/test-cors").header(HttpHeaders.ORIGIN, "other.domain.com"))
.andExpect(status().isOk())
.andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
}
@Test
void shouldCorsFilterDeactivatedForEmptyAllowedOrigins() throws Exception {
props.getCors().setAllowedOrigins(new ArrayList<>());
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()).addFilters(webConfigurer.corsFilter()).build();
mockMvc
.perform(get("/api/test-cors").header(HttpHeaders.ORIGIN, "other.domain.com"))
.andExpect(status().isOk())
.andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
}
}
@@ -0,0 +1,18 @@
package com.sisvietnamvn.web.config;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class WebConfigurerTestController {
@GetMapping("/api/test-cors")
public void testCorsOnApiPath() {
// empty method
}
@GetMapping("/test/test-cors")
public void testCorsOnOtherPath() {
// empty method
}
}
@@ -0,0 +1,171 @@
package com.sisvietnamvn.web.config.timezone;
import static org.assertj.core.api.Assertions.assertThat;
import com.sisvietnamvn.web.IntegrationTest;
import com.sisvietnamvn.web.repository.timezone.DateTimeWrapper;
import com.sisvietnamvn.web.repository.timezone.DateTimeWrapperRepository;
import java.time.*;
import java.time.format.DateTimeFormatter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests for verifying the behavior of Hibernate in the context of storing various date and time types across different databases.
* The tests focus on ensuring that the stored values are correctly transformed and stored according to the configured timezone.
* Timezone is environment specific, and can be adjusted according to your needs.
*
* For more context, refer to:
* - GitHub Issue: https://github.com/jhipster/generator-jhipster/issues/22579
* - Pull Request: https://github.com/jhipster/generator-jhipster/pull/22946
*/
@IntegrationTest
class HibernateTimeZoneIT {
@Autowired
private DateTimeWrapperRepository dateTimeWrapperRepository;
@Autowired
private JdbcTemplate jdbcTemplate;
@Value("${spring.jpa.properties.hibernate.jdbc.time_zone:UTC}")
private String zoneId;
private DateTimeWrapper dateTimeWrapper;
private DateTimeFormatter dateTimeFormatter;
private DateTimeFormatter timeFormatter;
private DateTimeFormatter offsetTimeFormatter;
private DateTimeFormatter dateFormatter;
@BeforeEach
void setup() {
dateTimeWrapper = new DateTimeWrapper();
dateTimeWrapper.setInstant(Instant.parse("2014-11-12T05:10:00.0Z"));
dateTimeWrapper.setLocalDateTime(LocalDateTime.parse("2014-11-12T07:20:00.0"));
dateTimeWrapper.setOffsetDateTime(OffsetDateTime.parse("2011-12-14T08:30:00.0Z"));
dateTimeWrapper.setZonedDateTime(ZonedDateTime.parse("2011-12-14T08:40:00.0Z"));
dateTimeWrapper.setLocalTime(LocalTime.parse("14:50:00"));
dateTimeWrapper.setOffsetTime(OffsetTime.parse("14:00:00+02:00"));
dateTimeWrapper.setLocalDate(LocalDate.parse("2016-09-10"));
dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S").withZone(ZoneId.of(zoneId));
timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(ZoneId.of(zoneId));
offsetTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
}
@Test
@Transactional
void storeInstantWithZoneIdConfigShouldBeStoredOnConfiguredTimeZone() {
dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper);
String request = generateSqlRequest("instant", dateTimeWrapper.getId());
SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request);
String expectedValue = dateTimeFormatter.format(dateTimeWrapper.getInstant());
assertThatValueFromSqlRowSetIsEqualToExpectedValue(resultSet, expectedValue);
}
@Test
@Transactional
void storeLocalDateTimeWithZoneIdConfigShouldBeStoredOnConfiguredTimeZone() {
dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper);
String request = generateSqlRequest("local_date_time", dateTimeWrapper.getId());
SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request);
String expectedValue = dateTimeWrapper.getLocalDateTime().atZone(ZoneId.systemDefault()).format(dateTimeFormatter);
assertThatValueFromSqlRowSetIsEqualToExpectedValue(resultSet, expectedValue);
}
@Test
@Transactional
void storeOffsetDateTimeWithZoneIdConfigShouldBeStoredOnConfiguredTimeZone() {
dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper);
String request = generateSqlRequest("offset_date_time", dateTimeWrapper.getId());
SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request);
String expectedValue = dateTimeWrapper.getOffsetDateTime().format(dateTimeFormatter);
assertThatValueFromSqlRowSetIsEqualToExpectedValue(resultSet, expectedValue);
}
@Test
@Transactional
void storeZoneDateTimeWithZoneIdConfigShouldBeStoredOnConfiguredTimeZone() {
dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper);
String request = generateSqlRequest("zoned_date_time", dateTimeWrapper.getId());
SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request);
String expectedValue = dateTimeWrapper.getZonedDateTime().format(dateTimeFormatter);
assertThatValueFromSqlRowSetIsEqualToExpectedValue(resultSet, expectedValue);
}
@Test
@Transactional
void storeLocalTimeWithZoneIdConfigShouldBeStoredOnConfiguredTimeZoneAccordingToHis1stJan1970Value() {
dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper);
String request = generateSqlRequest("local_time", dateTimeWrapper.getId());
SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request);
String expectedValue = dateTimeWrapper
.getLocalTime()
.atDate(LocalDate.of(1970, Month.JANUARY, 1))
.atZone(ZoneId.systemDefault())
.format(timeFormatter);
assertThatValueFromSqlRowSetIsEqualToExpectedValue(resultSet, expectedValue);
}
@Test
@Transactional
void storeOffsetTimeWithZoneIdConfigShouldBeStoredOnConfiguredTimeZoneAccordingToHis1stJan1970Value() {
dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper);
String request = generateSqlRequest("offset_time", dateTimeWrapper.getId());
SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request);
String expectedValue = dateTimeWrapper
.getOffsetTime()
// Convert to configured timezone
.withOffsetSameInstant(ZoneId.of(zoneId).getRules().getOffset(Instant.now()))
// Normalize to System TimeZone.
// this behavior looks a bug, refer to https://github.com/jhipster/generator-jhipster/issues/22579.
.withOffsetSameLocal(OffsetDateTime.ofInstant(Instant.EPOCH, ZoneId.systemDefault()).getOffset())
// Convert the normalized value to configured timezone
.withOffsetSameInstant(ZoneId.of(zoneId).getRules().getOffset(Instant.EPOCH))
.format(offsetTimeFormatter);
assertThatValueFromSqlRowSetIsEqualToExpectedValue(resultSet, expectedValue);
}
@Test
@Transactional
void storeLocalDateWithZoneIdConfigShouldBeStoredWithoutTransformation() {
dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper);
String request = generateSqlRequest("local_date", dateTimeWrapper.getId());
SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request);
String expectedValue = dateTimeWrapper.getLocalDate().format(dateFormatter);
assertThatValueFromSqlRowSetIsEqualToExpectedValue(resultSet, expectedValue);
}
private String generateSqlRequest(String fieldName, long id) {
return "SELECT %s FROM jhi_date_time_wrapper where id=%d".formatted(fieldName, id);
}
private void assertThatValueFromSqlRowSetIsEqualToExpectedValue(SqlRowSet sqlRowSet, String expectedValue) {
while (sqlRowSet.next()) {
String dbValue = sqlRowSet.getString(1);
assertThat(dbValue).isNotNull();
assertThat(dbValue).isEqualTo(expectedValue);
}
}
}
@@ -0,0 +1,15 @@
package com.sisvietnamvn.web.domain;
import java.math.BigDecimal;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Comparator;
public class AssertUtils {
public static Comparator<ZonedDateTime> zonedDataTimeSameInstant = Comparator.nullsFirst((e1, a2) ->
e1.withZoneSameInstant(ZoneOffset.UTC).compareTo(a2.withZoneSameInstant(ZoneOffset.UTC))
);
public static Comparator<BigDecimal> bigDecimalCompareTo = Comparator.nullsFirst(BigDecimal::compareTo);
}
@@ -0,0 +1,60 @@
package com.sisvietnamvn.web.domain;
import static org.assertj.core.api.Assertions.assertThat;
public class AuthorityAsserts {
/**
* Asserts that the entity has all properties (fields/relationships) set.
*
* @param expected the expected entity
* @param actual the actual entity
*/
public static void assertAuthorityAllPropertiesEquals(Authority expected, Authority actual) {
assertAuthorityAutoGeneratedPropertiesEquals(expected, actual);
assertAuthorityAllUpdatablePropertiesEquals(expected, actual);
}
/**
* Asserts that the entity has all updatable properties (fields/relationships) set.
*
* @param expected the expected entity
* @param actual the actual entity
*/
public static void assertAuthorityAllUpdatablePropertiesEquals(Authority expected, Authority actual) {
assertAuthorityUpdatableFieldsEquals(expected, actual);
assertAuthorityUpdatableRelationshipsEquals(expected, actual);
}
/**
* Asserts that the entity has all the auto generated properties (fields/relationships) set.
*
* @param expected the expected entity
* @param actual the actual entity
*/
public static void assertAuthorityAutoGeneratedPropertiesEquals(Authority expected, Authority actual) {
// empty method
}
/**
* Asserts that the entity has all the updatable fields set.
*
* @param expected the expected entity
* @param actual the actual entity
*/
public static void assertAuthorityUpdatableFieldsEquals(Authority expected, Authority actual) {
assertThat(actual)
.as("Verify Authority relevant properties")
.satisfies(a -> assertThat(a.getName()).as("check name").isEqualTo(expected.getName()));
}
/**
* Asserts that the entity has all the updatable relationships set.
*
* @param expected the expected entity
* @param actual the actual entity
*/
public static void assertAuthorityUpdatableRelationshipsEquals(Authority expected, Authority actual) {
// empty method
}
}
@@ -0,0 +1,34 @@
package com.sisvietnamvn.web.domain;
import static com.sisvietnamvn.web.domain.AuthorityTestSamples.*;
import static org.assertj.core.api.Assertions.assertThat;
import com.sisvietnamvn.web.web.rest.TestUtil;
import org.junit.jupiter.api.Test;
class AuthorityTest {
@Test
void equalsVerifier() throws Exception {
TestUtil.equalsVerifier(Authority.class);
Authority authority1 = getAuthoritySample1();
Authority authority2 = new Authority();
assertThat(authority1).isNotEqualTo(authority2);
authority2.setName(authority1.getName());
assertThat(authority1).isEqualTo(authority2);
authority2 = getAuthoritySample2();
assertThat(authority1).isNotEqualTo(authority2);
}
@Test
void hashCodeVerifier() {
Authority authority = new Authority();
assertThat(authority.hashCode()).isZero();
Authority authority1 = getAuthoritySample1();
authority.setName(authority1.getName());
assertThat(authority).hasSameHashCodeAs(authority1);
}
}
@@ -0,0 +1,18 @@
package com.sisvietnamvn.web.domain;
import java.util.UUID;
public class AuthorityTestSamples {
public static Authority getAuthoritySample1() {
return new Authority().name("name1");
}
public static Authority getAuthoritySample2() {
return new Authority().name("name2");
}
public static Authority getAuthorityRandomSampleGenerator() {
return new Authority().name(UUID.randomUUID().toString());
}
}
@@ -0,0 +1,70 @@
package com.sisvietnamvn.web.management;
import static org.assertj.core.api.Assertions.assertThat;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import java.util.Collection;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class SecurityMetersServiceTests {
private static final String INVALID_TOKENS_METER_EXPECTED_NAME = "security.authentication.invalid-tokens";
private MeterRegistry meterRegistry;
private SecurityMetersService securityMetersService;
@BeforeEach
void setup() {
meterRegistry = new SimpleMeterRegistry();
securityMetersService = new SecurityMetersService(meterRegistry);
}
@Test
void testInvalidTokensCountersByCauseAreCreated() {
meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).counter();
meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "expired").counter();
meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "unsupported").counter();
meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "invalid-signature").counter();
meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter();
Collection<Counter> counters = meterRegistry.find(INVALID_TOKENS_METER_EXPECTED_NAME).counters();
assertThat(counters).hasSize(4);
}
@Test
void testCountMethodsShouldBeBoundToCorrectCounters() {
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "expired").counter().count()).isZero();
securityMetersService.trackTokenExpired();
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "expired").counter().count()).isEqualTo(1);
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "unsupported").counter().count()).isZero();
securityMetersService.trackTokenUnsupported();
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "unsupported").counter().count()).isEqualTo(1);
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "invalid-signature").counter().count()).isZero();
securityMetersService.trackTokenInvalidSignature();
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "invalid-signature").counter().count()).isEqualTo(1);
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter().count()).isZero();
securityMetersService.trackTokenMalformed();
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter().count()).isEqualTo(1);
}
}
@@ -0,0 +1,135 @@
package com.sisvietnamvn.web.repository.timezone;
import jakarta.persistence.*;
import java.io.Serial;
import java.io.Serializable;
import java.time.*;
import java.util.Objects;
@Entity
@Table(name = "jhi_date_time_wrapper")
public class DateTimeWrapper implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
@SequenceGenerator(name = "sequenceGenerator")
private Long id;
@Column(name = "instant")
private Instant instant;
@Column(name = "local_date_time")
private LocalDateTime localDateTime;
@Column(name = "offset_date_time")
private OffsetDateTime offsetDateTime;
@Column(name = "zoned_date_time")
private ZonedDateTime zonedDateTime;
@Column(name = "local_time")
private LocalTime localTime;
@Column(name = "offset_time")
private OffsetTime offsetTime;
@Column(name = "local_date")
private LocalDate localDate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Instant getInstant() {
return instant;
}
public void setInstant(Instant instant) {
this.instant = instant;
}
public LocalDateTime getLocalDateTime() {
return localDateTime;
}
public void setLocalDateTime(LocalDateTime localDateTime) {
this.localDateTime = localDateTime;
}
public OffsetDateTime getOffsetDateTime() {
return offsetDateTime;
}
public void setOffsetDateTime(OffsetDateTime offsetDateTime) {
this.offsetDateTime = offsetDateTime;
}
public ZonedDateTime getZonedDateTime() {
return zonedDateTime;
}
public void setZonedDateTime(ZonedDateTime zonedDateTime) {
this.zonedDateTime = zonedDateTime;
}
public LocalTime getLocalTime() {
return localTime;
}
public void setLocalTime(LocalTime localTime) {
this.localTime = localTime;
}
public OffsetTime getOffsetTime() {
return offsetTime;
}
public void setOffsetTime(OffsetTime offsetTime) {
this.offsetTime = offsetTime;
}
public LocalDate getLocalDate() {
return localDate;
}
public void setLocalDate(LocalDate localDate) {
this.localDate = localDate;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DateTimeWrapper dateTimeWrapper = (DateTimeWrapper) o;
return !(dateTimeWrapper.getId() == null || getId() == null) && Objects.equals(getId(), dateTimeWrapper.getId());
}
@Override
public int hashCode() {
return Objects.hashCode(getId());
}
// prettier-ignore
@Override
public String toString() {
return "TimeZoneTest{" +
"id=" + id +
", instant=" + instant +
", localDateTime=" + localDateTime +
", offsetDateTime=" + offsetDateTime +
", zonedDateTime=" + zonedDateTime +
'}';
}
}
@@ -0,0 +1,10 @@
package com.sisvietnamvn.web.repository.timezone;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* Spring Data JPA repository for the {@link DateTimeWrapper} entity.
*/
@Repository
public interface DateTimeWrapperRepository extends JpaRepository<DateTimeWrapper, Long> {}
@@ -0,0 +1,136 @@
package com.sisvietnamvn.web.security;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import com.sisvietnamvn.web.IntegrationTest;
import com.sisvietnamvn.web.domain.User;
import com.sisvietnamvn.web.repository.UserRepository;
import com.sisvietnamvn.web.service.UserService;
import java.util.Locale;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.transaction.annotation.Transactional;
/**
* Integrations tests for {@link DomainUserDetailsService}.
*/
@Transactional
@IntegrationTest
class DomainUserDetailsServiceIT {
private static final String USER_ONE_LOGIN = "test-user-one";
private static final String USER_ONE_EMAIL = "test-user-one@localhost";
private static final String USER_TWO_LOGIN = "test-user-two";
private static final String USER_TWO_EMAIL = "test-user-two@localhost";
private static final String USER_THREE_LOGIN = "test-user-three";
private static final String USER_THREE_EMAIL = "test-user-three@localhost";
@Autowired
private UserRepository userRepository;
@Autowired
private UserService userService;
@Autowired
@Qualifier("userDetailsService")
private UserDetailsService domainUserDetailsService;
public User getUserOne() {
User userOne = new User();
userOne.setLogin(USER_ONE_LOGIN);
userOne.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
userOne.setActivated(true);
userOne.setEmail(USER_ONE_EMAIL);
userOne.setFirstName("userOne");
userOne.setLastName("doe");
userOne.setLangKey("en");
return userOne;
}
public User getUserTwo() {
User userTwo = new User();
userTwo.setLogin(USER_TWO_LOGIN);
userTwo.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
userTwo.setActivated(true);
userTwo.setEmail(USER_TWO_EMAIL);
userTwo.setFirstName("userTwo");
userTwo.setLastName("doe");
userTwo.setLangKey("en");
return userTwo;
}
public User getUserThree() {
User userThree = new User();
userThree.setLogin(USER_THREE_LOGIN);
userThree.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
userThree.setActivated(false);
userThree.setEmail(USER_THREE_EMAIL);
userThree.setFirstName("userThree");
userThree.setLastName("doe");
userThree.setLangKey("en");
return userThree;
}
@BeforeEach
void init() {
userRepository.save(getUserOne());
userRepository.save(getUserTwo());
userRepository.save(getUserThree());
}
@AfterEach
void cleanup() {
userService.deleteUser(USER_ONE_LOGIN);
userService.deleteUser(USER_TWO_LOGIN);
userService.deleteUser(USER_THREE_LOGIN);
}
@Test
void assertThatUserCanBeFoundByLogin() {
UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_ONE_LOGIN);
assertThat(userDetails).isNotNull();
assertThat(userDetails.getUsername()).isEqualTo(USER_ONE_LOGIN);
}
@Test
void assertThatUserCanBeFoundByLoginIgnoreCase() {
UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_ONE_LOGIN.toUpperCase(Locale.ENGLISH));
assertThat(userDetails).isNotNull();
assertThat(userDetails.getUsername()).isEqualTo(USER_ONE_LOGIN);
}
@Test
void assertThatUserCanBeFoundByEmail() {
UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_TWO_EMAIL);
assertThat(userDetails).isNotNull();
assertThat(userDetails.getUsername()).isEqualTo(USER_TWO_LOGIN);
}
@Test
void assertThatUserCanBeFoundByEmailIgnoreCase() {
UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_TWO_EMAIL.toUpperCase(Locale.ENGLISH));
assertThat(userDetails).isNotNull();
assertThat(userDetails.getUsername()).isEqualTo(USER_TWO_LOGIN);
}
@Test
void assertThatEmailIsPrioritizedOverLogin() {
UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_ONE_EMAIL);
assertThat(userDetails).isNotNull();
assertThat(userDetails.getUsername()).isEqualTo(USER_ONE_LOGIN);
}
@Test
void assertThatUserNotActivatedExceptionIsThrownForNotActivatedUsers() {
assertThatExceptionOfType(UserNotActivatedException.class).isThrownBy(() ->
domainUserDetailsService.loadUserByUsername(USER_THREE_LOGIN)
);
}
}
@@ -0,0 +1,126 @@
package com.sisvietnamvn.web.security;
import static com.sisvietnamvn.web.security.SecurityUtils.USER_ID_CLAIM;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
/**
* Test class for the {@link SecurityUtils} utility class.
*/
class SecurityUtilsUnitTest {
@BeforeEach
@AfterEach
void cleanup() {
SecurityContextHolder.clearContext();
}
@Test
void testGetCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin"));
SecurityContextHolder.setContext(securityContext);
Optional<String> login = SecurityUtils.getCurrentUserLogin();
assertThat(login).contains("admin");
}
@Test
void testGetCurrentUserJWT() {
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "token"));
SecurityContextHolder.setContext(securityContext);
Optional<String> jwt = SecurityUtils.getCurrentUserJWT();
assertThat(jwt).contains("token");
}
@Test
void testGetCurrentUserJWTFromJwtCredentials() {
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
var now = Instant.now();
var jwt = Jwt.withTokenValue("token").issuedAt(now).expiresAt(now.plusSeconds(60)).header("Test", "test").build();
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", jwt));
SecurityContextHolder.setContext(securityContext);
Optional<String> currentUserJwt = SecurityUtils.getCurrentUserJWT();
assertThat(currentUserJwt).contains("token");
}
@Test
void testGetCurrentUserId() {
var userId = 1L;
var securityContext = SecurityContextHolder.createEmptyContext();
var now = Instant.now();
var jwt = Jwt.withTokenValue("token")
.issuedAt(now)
.expiresAt(now.plusSeconds(60))
.claim(USER_ID_CLAIM, userId)
.header("Test", "test")
.build();
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(jwt, "token"));
SecurityContextHolder.setContext(securityContext);
var contextUserId = SecurityUtils.getCurrentUserId();
assertThat(contextUserId.orElse(null)).isEqualTo(userId);
}
@Test
void testIsAuthenticated() {
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin"));
SecurityContextHolder.setContext(securityContext);
boolean isAuthenticated = SecurityUtils.isAuthenticated();
assertThat(isAuthenticated).isTrue();
}
@Test
void testAnonymousIsNotAuthenticated() {
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
var authorities = List.of(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS));
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities));
SecurityContextHolder.setContext(securityContext);
boolean isAuthenticated = SecurityUtils.isAuthenticated();
assertThat(isAuthenticated).isFalse();
}
@Test
void testHasCurrentUserThisAuthority() {
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
var authorities = List.of(new SimpleGrantedAuthority(AuthoritiesConstants.USER));
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities));
SecurityContextHolder.setContext(securityContext);
assertThat(SecurityUtils.hasCurrentUserThisAuthority(AuthoritiesConstants.USER)).isTrue();
assertThat(SecurityUtils.hasCurrentUserThisAuthority(AuthoritiesConstants.ADMIN)).isFalse();
}
@Test
void testHasCurrentUserAnyOfAuthorities() {
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
var authorities = List.of(new SimpleGrantedAuthority(AuthoritiesConstants.USER));
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities));
SecurityContextHolder.setContext(securityContext);
assertThat(SecurityUtils.hasCurrentUserAnyOfAuthorities(AuthoritiesConstants.USER, AuthoritiesConstants.ADMIN)).isTrue();
assertThat(SecurityUtils.hasCurrentUserAnyOfAuthorities(AuthoritiesConstants.ANONYMOUS, AuthoritiesConstants.ADMIN)).isFalse();
}
@Test
void testHasCurrentUserNoneOfAuthorities() {
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
var authorities = List.of(new SimpleGrantedAuthority(AuthoritiesConstants.USER));
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities));
SecurityContextHolder.setContext(securityContext);
assertThat(SecurityUtils.hasCurrentUserNoneOfAuthorities(AuthoritiesConstants.USER, AuthoritiesConstants.ADMIN)).isFalse();
assertThat(SecurityUtils.hasCurrentUserNoneOfAuthorities(AuthoritiesConstants.ANONYMOUS, AuthoritiesConstants.ADMIN)).isTrue();
}
}
@@ -0,0 +1,32 @@
package com.sisvietnamvn.web.security.jwt;
import com.sisvietnamvn.web.config.SecurityConfiguration;
import com.sisvietnamvn.web.config.SecurityJwtConfiguration;
import com.sisvietnamvn.web.config.WebConfigurer;
import com.sisvietnamvn.web.management.SecurityMetersService;
import com.sisvietnamvn.web.web.rest.AuthenticateController;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.test.context.SpringBootTest;
import tech.jhipster.config.JHipsterProperties;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@SpringBootTest(
properties = {
"jhipster.security.authentication.jwt.base64-secret=fd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8",
"jhipster.security.authentication.jwt.token-validity-in-seconds=60000",
},
classes = {
JHipsterProperties.class,
WebConfigurer.class,
SecurityConfiguration.class,
SecurityJwtConfiguration.class,
SecurityMetersService.class,
AuthenticateController.class,
JwtAuthenticationTestUtils.class,
}
)
public @interface AuthenticationIntegrationTest {}
@@ -0,0 +1,100 @@
package com.sisvietnamvn.web.security.jwt;
import static com.sisvietnamvn.web.security.AuthoritiesConstants.ADMIN;
import static com.sisvietnamvn.web.security.SecurityUtils.AUTHORITIES_CLAIM;
import static com.sisvietnamvn.web.security.SecurityUtils.JWT_ALGORITHM;
import com.nimbusds.jose.jwk.source.ImmutableSecret;
import com.nimbusds.jose.util.Base64;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import java.time.Instant;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.oauth2.jwt.JwsHeader;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
public class JwtAuthenticationTestUtils {
public static final String BEARER = "Bearer ";
@Bean
private MeterRegistry meterRegistry() {
return new SimpleMeterRegistry();
}
public static String createValidToken(String jwtKey) {
return createValidTokenForUser(jwtKey, "anonymous");
}
public static String createValidTokenForUser(String jwtKey, String user) {
JwtEncoder encoder = jwtEncoder(jwtKey);
var now = Instant.now();
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuedAt(now)
.expiresAt(now.plusSeconds(60))
.subject(user)
.claims(customClaim -> customClaim.put(AUTHORITIES_CLAIM, List.of(ADMIN)))
.build();
JwsHeader jwsHeader = JwsHeader.with(JWT_ALGORITHM).build();
return encoder.encode(JwtEncoderParameters.from(jwsHeader, claims)).getTokenValue();
}
public static String createTokenWithDifferentSignature() {
JwtEncoder encoder = jwtEncoder("Xfd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8");
var now = Instant.now();
var past = now.plusSeconds(60);
JwtClaimsSet claims = JwtClaimsSet.builder().issuedAt(now).expiresAt(past).subject("anonymous").build();
JwsHeader jwsHeader = JwsHeader.with(JWT_ALGORITHM).build();
return encoder.encode(JwtEncoderParameters.from(jwsHeader, claims)).getTokenValue();
}
public static String createExpiredToken(String jwtKey) {
JwtEncoder encoder = jwtEncoder(jwtKey);
var now = Instant.now();
var past = now.minusSeconds(600);
JwtClaimsSet claims = JwtClaimsSet.builder().issuedAt(past).expiresAt(past.plusSeconds(1)).subject("anonymous").build();
JwsHeader jwsHeader = JwsHeader.with(JWT_ALGORITHM).build();
return encoder.encode(JwtEncoderParameters.from(jwsHeader, claims)).getTokenValue();
}
public static String createInvalidToken(String jwtKey) {
return createValidToken(jwtKey).substring(1);
}
public static String createSignedInvalidJwt(String jwtKey) throws Exception {
return calculateHMAC("foo", jwtKey);
}
private static JwtEncoder jwtEncoder(String jwtKey) {
return new NimbusJwtEncoder(new ImmutableSecret<>(getSecretKey(jwtKey)));
}
private static SecretKey getSecretKey(String jwtKey) {
byte[] keyBytes = Base64.from(jwtKey).decode();
return new SecretKeySpec(keyBytes, 0, keyBytes.length, JWT_ALGORITHM.getName());
}
private static String calculateHMAC(String data, String key) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.from(key).decode(), "HmacSHA512");
Mac mac = Mac.getInstance("HmacSHA512");
mac.init(secretKeySpec);
return String.copyValueOf(Hex.encode(mac.doFinal(data.getBytes())));
}
}
@@ -0,0 +1,55 @@
package com.sisvietnamvn.web.security.jwt;
import static com.sisvietnamvn.web.security.jwt.JwtAuthenticationTestUtils.*;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@AutoConfigureMockMvc
@AuthenticationIntegrationTest
class TokenAuthenticationIT {
@Autowired
private MockMvc mvc;
@Value("${jhipster.security.authentication.jwt.base64-secret}")
private String jwtKey;
@Test
void testLoginWithValidToken() throws Exception {
expectOk(createValidToken(jwtKey));
}
@Test
void testReturnFalseWhenJWThasInvalidSignature() throws Exception {
expectUnauthorized(createTokenWithDifferentSignature());
}
@Test
void testReturnFalseWhenJWTisMalformed() throws Exception {
expectUnauthorized(createSignedInvalidJwt(jwtKey));
}
@Test
void testReturnFalseWhenJWTisExpired() throws Exception {
expectUnauthorized(createExpiredToken(jwtKey));
}
private void expectOk(String token) throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/api/authenticate").header(AUTHORIZATION, BEARER + token)).andExpect(
status().isNoContent()
);
}
private void expectUnauthorized(String token) throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/api/authenticate").header(AUTHORIZATION, BEARER + token)).andExpect(
status().isUnauthorized()
);
}
}
@@ -0,0 +1,88 @@
package com.sisvietnamvn.web.security.jwt;
import static com.sisvietnamvn.web.security.jwt.JwtAuthenticationTestUtils.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import java.util.Collection;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@AutoConfigureMockMvc
@AuthenticationIntegrationTest
class TokenAuthenticationSecurityMetersIT {
private static final String INVALID_TOKENS_METER_EXPECTED_NAME = "security.authentication.invalid-tokens";
@Autowired
private MockMvc mvc;
@Value("${jhipster.security.authentication.jwt.base64-secret}")
private String jwtKey;
@Autowired
private MeterRegistry meterRegistry;
@Test
void testValidTokenShouldNotCountAnything() throws Exception {
Collection<Counter> counters = meterRegistry.find(INVALID_TOKENS_METER_EXPECTED_NAME).counters();
var count = aggregate(counters);
tryToAuthenticate(createValidToken(jwtKey));
assertThat(aggregate(counters)).isEqualTo(count);
}
@Test
void testTokenExpiredCount() throws Exception {
var count = meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "expired").counter().count();
tryToAuthenticate(createExpiredToken(jwtKey));
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "expired").counter().count()).isEqualTo(count + 1);
}
@Test
void testTokenSignatureInvalidCount() throws Exception {
var count = meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "invalid-signature").counter().count();
tryToAuthenticate(createTokenWithDifferentSignature());
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "invalid-signature").counter().count()).isEqualTo(
count + 1
);
}
@Test
void testTokenMalformedCount() throws Exception {
var count = meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter().count();
tryToAuthenticate(createSignedInvalidJwt(jwtKey));
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter().count()).isEqualTo(count + 1);
}
@Test
void testTokenInvalidCount() throws Exception {
var count = meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter().count();
tryToAuthenticate(createInvalidToken(jwtKey));
assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter().count()).isEqualTo(count + 1);
}
private void tryToAuthenticate(String token) throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/api/authenticate").header(AUTHORIZATION, BEARER + token));
}
private double aggregate(Collection<Counter> counters) {
return counters.stream().mapToDouble(Counter::count).sum();
}
}
@@ -0,0 +1,239 @@
package com.sisvietnamvn.web.service;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import com.sisvietnamvn.web.IntegrationTest;
import com.sisvietnamvn.web.config.Constants;
import com.sisvietnamvn.web.domain.User;
import jakarta.mail.Multipart;
import jakarta.mail.Session;
import jakarta.mail.internet.MimeBodyPart;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailSendException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import tech.jhipster.config.JHipsterProperties;
/**
* Integration tests for {@link MailService}.
*/
@ExtendWith(MockitoExtension.class)
@IntegrationTest
class MailServiceIT {
private static final String[] languages = {
// jhipster-needle-i18n-language-constant-start
// jhipster-needle-i18n-language-constant - JHipster will add/remove languages in this array
};
private static final Pattern PATTERN_LOCALE_3 = Pattern.compile("([a-z]{2})-([a-zA-Z]{4})-([a-z]{2})");
private static final Pattern PATTERN_LOCALE_2 = Pattern.compile("([a-z]{2})-([a-z]{2})");
@Autowired
private JHipsterProperties jHipsterProperties;
@MockitoBean
private JavaMailSender javaMailSender;
@Captor
private ArgumentCaptor<MimeMessage> messageCaptor;
@Autowired
private MailService mailService;
@BeforeEach
void setup() {
doNothing().when(javaMailSender).send(any(MimeMessage.class));
when(javaMailSender.createMimeMessage()).thenReturn(new MimeMessage((Session) null));
}
@Test
void testSendEmail() throws Exception {
mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", false, false);
verify(javaMailSender).send(messageCaptor.capture());
MimeMessage message = messageCaptor.getValue();
assertThat(message.getSubject()).isEqualTo("testSubject");
assertThat(message.getAllRecipients()[0]).hasToString("john.doe@example.com");
assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom());
assertThat(message.getContent()).isInstanceOf(String.class);
assertThat(message.getContent()).hasToString("testContent");
assertThat(message.getDataHandler().getContentType()).isEqualTo("text/plain; charset=UTF-8");
}
@Test
void testSendHtmlEmail() throws Exception {
mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", false, true);
verify(javaMailSender).send(messageCaptor.capture());
MimeMessage message = messageCaptor.getValue();
assertThat(message.getSubject()).isEqualTo("testSubject");
assertThat(message.getAllRecipients()[0]).hasToString("john.doe@example.com");
assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom());
assertThat(message.getContent()).isInstanceOf(String.class);
assertThat(message.getContent()).hasToString("testContent");
assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8");
}
@Test
void testSendMultipartEmail() throws Exception {
mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", true, false);
verify(javaMailSender).send(messageCaptor.capture());
MimeMessage message = messageCaptor.getValue();
MimeMultipart mp = (MimeMultipart) message.getContent();
MimeBodyPart part = (MimeBodyPart) ((MimeMultipart) mp.getBodyPart(0).getContent()).getBodyPart(0);
ByteArrayOutputStream aos = new ByteArrayOutputStream();
part.writeTo(aos);
assertThat(message.getSubject()).isEqualTo("testSubject");
assertThat(message.getAllRecipients()[0]).hasToString("john.doe@example.com");
assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom());
assertThat(message.getContent()).isInstanceOf(Multipart.class);
assertThat(aos).hasToString("\r\ntestContent");
assertThat(part.getDataHandler().getContentType()).isEqualTo("text/plain; charset=UTF-8");
}
@Test
void testSendMultipartHtmlEmail() throws Exception {
mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", true, true);
verify(javaMailSender).send(messageCaptor.capture());
MimeMessage message = messageCaptor.getValue();
MimeMultipart mp = (MimeMultipart) message.getContent();
MimeBodyPart part = (MimeBodyPart) ((MimeMultipart) mp.getBodyPart(0).getContent()).getBodyPart(0);
ByteArrayOutputStream aos = new ByteArrayOutputStream();
part.writeTo(aos);
assertThat(message.getSubject()).isEqualTo("testSubject");
assertThat(message.getAllRecipients()[0]).hasToString("john.doe@example.com");
assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom());
assertThat(message.getContent()).isInstanceOf(Multipart.class);
assertThat(aos).hasToString("\r\ntestContent");
assertThat(part.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8");
}
@Test
void testSendEmailFromTemplate() throws Exception {
User user = new User();
user.setLangKey(Constants.DEFAULT_LANGUAGE);
user.setLogin("john");
user.setEmail("john.doe@example.com");
mailService.sendEmailFromTemplate(user, "mail/testEmail", "email.test.title");
verify(javaMailSender).send(messageCaptor.capture());
MimeMessage message = messageCaptor.getValue();
assertThat(message.getSubject()).isEqualTo("test title");
assertThat(message.getAllRecipients()[0]).hasToString(user.getEmail());
assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom());
assertThat(message.getContent().toString()).isEqualToNormalizingNewlines("<html>test title, http://127.0.0.1:8080, john</html>\n");
assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8");
}
@Test
void testSendActivationEmail() throws Exception {
User user = new User();
user.setLangKey(Constants.DEFAULT_LANGUAGE);
user.setLogin("john");
user.setEmail("john.doe@example.com");
mailService.sendActivationEmail(user);
verify(javaMailSender).send(messageCaptor.capture());
MimeMessage message = messageCaptor.getValue();
assertThat(message.getAllRecipients()[0]).hasToString(user.getEmail());
assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom());
assertThat(message.getContent().toString()).isNotEmpty();
assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8");
}
@Test
void testCreationEmail() throws Exception {
User user = new User();
user.setLangKey(Constants.DEFAULT_LANGUAGE);
user.setLogin("john");
user.setEmail("john.doe@example.com");
mailService.sendCreationEmail(user);
verify(javaMailSender).send(messageCaptor.capture());
MimeMessage message = messageCaptor.getValue();
assertThat(message.getAllRecipients()[0]).hasToString(user.getEmail());
assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom());
assertThat(message.getContent().toString()).isNotEmpty();
assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8");
}
@Test
void testSendPasswordResetMail() throws Exception {
User user = new User();
user.setLangKey(Constants.DEFAULT_LANGUAGE);
user.setLogin("john");
user.setEmail("john.doe@example.com");
mailService.sendPasswordResetMail(user);
verify(javaMailSender).send(messageCaptor.capture());
MimeMessage message = messageCaptor.getValue();
assertThat(message.getAllRecipients()[0]).hasToString(user.getEmail());
assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom());
assertThat(message.getContent().toString()).isNotEmpty();
assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8");
}
@Test
void testSendEmailWithException() {
doThrow(MailSendException.class).when(javaMailSender).send(any(MimeMessage.class));
try {
mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", false, false);
} catch (Exception e) {
fail("Exception shouldn't have been thrown");
}
}
@Test
void testSendLocalizedEmailForAllSupportedLanguages() throws Exception {
User user = new User();
user.setLogin("john");
user.setEmail("john.doe@example.com");
for (String langKey : languages) {
user.setLangKey(langKey);
mailService.sendEmailFromTemplate(user, "mail/testEmail", "email.test.title");
verify(javaMailSender, atLeastOnce()).send(messageCaptor.capture());
MimeMessage message = messageCaptor.getValue();
String propertyFilePath = "i18n/messages_" + getMessageSourceSuffixForLanguage(langKey) + ".properties";
URL resource = this.getClass().getClassLoader().getResource(propertyFilePath);
Path filePath = Path.of(resource.toURI());
Properties properties = new Properties();
properties.load(new InputStreamReader(Files.newInputStream(filePath), Charset.forName("UTF-8")));
String emailTitle = (String) properties.get("email.test.title");
assertThat(message.getSubject()).isEqualTo(emailTitle);
assertThat(message.getContent().toString()).isEqualToNormalizingNewlines(
"<html>" + emailTitle + ", http://127.0.0.1:8080, john</html>\n"
);
}
}
/**
* Convert a lang key to the Java locale.
*/
private String getMessageSourceSuffixForLanguage(String langKey) {
String javaLangKey = langKey;
Matcher matcher2 = PATTERN_LOCALE_2.matcher(langKey);
if (matcher2.matches()) {
javaLangKey = matcher2.group(1) + "_" + matcher2.group(2).toUpperCase();
}
Matcher matcher3 = PATTERN_LOCALE_3.matcher(langKey);
if (matcher3.matches()) {
javaLangKey = matcher3.group(1) + "_" + matcher3.group(2) + "_" + matcher3.group(3).toUpperCase();
}
return javaLangKey;
}
}
@@ -0,0 +1,208 @@
package com.sisvietnamvn.web.service;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import com.sisvietnamvn.web.IntegrationTest;
import com.sisvietnamvn.web.domain.User;
import com.sisvietnamvn.web.repository.UserRepository;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.data.auditing.AuditingHandler;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.transaction.annotation.Transactional;
import tech.jhipster.security.RandomUtil;
/**
* Integration tests for {@link UserService}.
*/
@IntegrationTest
@Transactional
class UserServiceIT {
private static final String DEFAULT_LOGIN = "johndoe_service";
private static final String DEFAULT_EMAIL = "johndoe_service@localhost";
private static final String DEFAULT_FIRSTNAME = "john";
private static final String DEFAULT_LASTNAME = "doe";
@Autowired
private CacheManager cacheManager;
private static final String DEFAULT_IMAGEURL = "http://placehold.it/50x50";
private static final String DEFAULT_LANGKEY = "dummy";
@Autowired
private UserRepository userRepository;
@Autowired
private UserService userService;
@Autowired
private AuditingHandler auditingHandler;
@MockitoBean
private DateTimeProvider dateTimeProvider;
private User user;
private Long numberOfUsers;
@BeforeEach
void countUsers() {
numberOfUsers = userRepository.count();
}
@BeforeEach
void init() {
user = new User();
user.setLogin(DEFAULT_LOGIN);
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setActivated(true);
user.setEmail(DEFAULT_EMAIL);
user.setFirstName(DEFAULT_FIRSTNAME);
user.setLastName(DEFAULT_LASTNAME);
user.setImageUrl(DEFAULT_IMAGEURL);
user.setLangKey(DEFAULT_LANGKEY);
when(dateTimeProvider.getNow()).thenReturn(Optional.of(LocalDateTime.now()));
auditingHandler.setDateTimeProvider(dateTimeProvider);
}
@AfterEach
void cleanupAndCheck() {
cacheManager
.getCacheNames()
.stream()
.map(cacheName -> this.cacheManager.getCache(cacheName))
.filter(Objects::nonNull)
.forEach(Cache::clear);
userService.deleteUser(DEFAULT_LOGIN);
assertThat(userRepository.count()).isEqualTo(numberOfUsers);
numberOfUsers = null;
}
@Test
@Transactional
void assertThatUserMustExistToResetPassword() {
userRepository.saveAndFlush(user);
Optional<User> maybeUser = userService.requestPasswordReset("invalid.login@localhost");
assertThat(maybeUser).isNotPresent();
maybeUser = userService.requestPasswordReset(user.getEmail());
assertThat(maybeUser).isPresent();
assertThat(maybeUser.orElse(null).getEmail()).isEqualTo(user.getEmail());
assertThat(maybeUser.orElse(null).getResetDate()).isNotNull();
assertThat(maybeUser.orElse(null).getResetKey()).isNotNull();
}
@Test
@Transactional
void assertThatOnlyActivatedUserCanRequestPasswordReset() {
user.setActivated(false);
userRepository.saveAndFlush(user);
Optional<User> maybeUser = userService.requestPasswordReset(user.getLogin());
assertThat(maybeUser).isNotPresent();
userRepository.delete(user);
}
@Test
@Transactional
void assertThatResetKeyMustNotBeOlderThan24Hours() {
Instant daysAgo = Instant.now().minus(25, ChronoUnit.HOURS);
String resetKey = RandomUtil.generateResetKey();
user.setActivated(true);
user.setResetDate(daysAgo);
user.setResetKey(resetKey);
userRepository.saveAndFlush(user);
Optional<User> maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey());
assertThat(maybeUser).isNotPresent();
userRepository.delete(user);
}
@Test
@Transactional
void assertThatResetKeyMustBeValid() {
Instant daysAgo = Instant.now().minus(25, ChronoUnit.HOURS);
user.setActivated(true);
user.setResetDate(daysAgo);
user.setResetKey("1234");
userRepository.saveAndFlush(user);
Optional<User> maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey());
assertThat(maybeUser).isNotPresent();
userRepository.delete(user);
}
@Test
@Transactional
void assertThatUserCanResetPassword() {
String oldPassword = user.getPassword();
Instant daysAgo = Instant.now().minus(2, ChronoUnit.HOURS);
String resetKey = RandomUtil.generateResetKey();
user.setActivated(true);
user.setResetDate(daysAgo);
user.setResetKey(resetKey);
userRepository.saveAndFlush(user);
Optional<User> maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey());
assertThat(maybeUser).isPresent();
assertThat(maybeUser.orElse(null).getResetDate()).isNull();
assertThat(maybeUser.orElse(null).getResetKey()).isNull();
assertThat(maybeUser.orElse(null).getPassword()).isNotEqualTo(oldPassword);
userRepository.delete(user);
}
@Test
@Transactional
void assertThatNotActivatedUsersWithNotNullActivationKeyCreatedBefore3DaysAreDeleted() {
Instant now = Instant.now();
when(dateTimeProvider.getNow()).thenReturn(Optional.of(now.minus(4, ChronoUnit.DAYS)));
user.setActivated(false);
user.setActivationKey(RandomStringUtils.insecure().next(20));
User dbUser = userRepository.saveAndFlush(user);
dbUser.setCreatedDate(now.minus(4, ChronoUnit.DAYS));
userRepository.saveAndFlush(user);
Instant threeDaysAgo = now.minus(3, ChronoUnit.DAYS);
List<User> users = userRepository.findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(threeDaysAgo);
assertThat(users).isNotEmpty();
userService.removeNotActivatedUsers();
users = userRepository.findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(threeDaysAgo);
assertThat(users).isEmpty();
}
@Test
@Transactional
void assertThatNotActivatedUsersWithNullActivationKeyCreatedBefore3DaysAreNotDeleted() {
Instant now = Instant.now();
when(dateTimeProvider.getNow()).thenReturn(Optional.of(now.minus(4, ChronoUnit.DAYS)));
user.setActivated(false);
User dbUser = userRepository.saveAndFlush(user);
dbUser.setCreatedDate(now.minus(4, ChronoUnit.DAYS));
userRepository.saveAndFlush(user);
Instant threeDaysAgo = now.minus(3, ChronoUnit.DAYS);
List<User> users = userRepository.findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(threeDaysAgo);
assertThat(users).isEmpty();
userService.removeNotActivatedUsers();
Optional<User> maybeDbUser = userRepository.findById(dbUser.getId());
assertThat(maybeDbUser).contains(dbUser);
}
}
@@ -0,0 +1,179 @@
package com.sisvietnamvn.web.service.mapper;
import static org.assertj.core.api.Assertions.assertThat;
import com.sisvietnamvn.web.domain.Authority;
import com.sisvietnamvn.web.domain.User;
import com.sisvietnamvn.web.security.AuthoritiesConstants;
import com.sisvietnamvn.web.service.dto.AdminUserDTO;
import com.sisvietnamvn.web.service.dto.UserDTO;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link UserMapper}.
*/
class UserMapperTest {
private static final String DEFAULT_LOGIN = "johndoe";
private static final Long DEFAULT_ID = 1L;
private UserMapper userMapper;
private User user;
private AdminUserDTO userDto;
@BeforeEach
void init() {
userMapper = new UserMapper();
user = new User();
user.setLogin(DEFAULT_LOGIN);
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setActivated(true);
user.setEmail("johndoe@localhost");
user.setFirstName("john");
user.setLastName("doe");
user.setImageUrl("image_url");
user.setCreatedBy(DEFAULT_LOGIN);
user.setCreatedDate(Instant.now());
user.setLastModifiedBy(DEFAULT_LOGIN);
user.setLastModifiedDate(Instant.now());
user.setLangKey("en");
Set<Authority> authorities = new HashSet<>();
Authority authority = new Authority();
authority.setName(AuthoritiesConstants.USER);
authorities.add(authority);
user.setAuthorities(authorities);
userDto = new AdminUserDTO(user);
}
@Test
void testUserToUserDTO() {
AdminUserDTO convertedUserDto = userMapper.userToAdminUserDTO(user);
assertThat(convertedUserDto.getId()).isEqualTo(user.getId());
assertThat(convertedUserDto.getLogin()).isEqualTo(user.getLogin());
assertThat(convertedUserDto.getFirstName()).isEqualTo(user.getFirstName());
assertThat(convertedUserDto.getLastName()).isEqualTo(user.getLastName());
assertThat(convertedUserDto.getEmail()).isEqualTo(user.getEmail());
assertThat(convertedUserDto.isActivated()).isEqualTo(user.isActivated());
assertThat(convertedUserDto.getImageUrl()).isEqualTo(user.getImageUrl());
assertThat(convertedUserDto.getCreatedBy()).isEqualTo(user.getCreatedBy());
assertThat(convertedUserDto.getCreatedDate()).isEqualTo(user.getCreatedDate());
assertThat(convertedUserDto.getLastModifiedBy()).isEqualTo(user.getLastModifiedBy());
assertThat(convertedUserDto.getLastModifiedDate()).isEqualTo(user.getLastModifiedDate());
assertThat(convertedUserDto.getLangKey()).isEqualTo(user.getLangKey());
assertThat(convertedUserDto.getAuthorities()).containsExactly(AuthoritiesConstants.USER);
}
@Test
void testUserDTOtoUser() {
User convertedUser = userMapper.userDTOToUser(userDto);
assertThat(convertedUser.getId()).isEqualTo(userDto.getId());
assertThat(convertedUser.getLogin()).isEqualTo(userDto.getLogin());
assertThat(convertedUser.getFirstName()).isEqualTo(userDto.getFirstName());
assertThat(convertedUser.getLastName()).isEqualTo(userDto.getLastName());
assertThat(convertedUser.getEmail()).isEqualTo(userDto.getEmail());
assertThat(convertedUser.isActivated()).isEqualTo(userDto.isActivated());
assertThat(convertedUser.getImageUrl()).isEqualTo(userDto.getImageUrl());
assertThat(convertedUser.getLangKey()).isEqualTo(userDto.getLangKey());
assertThat(convertedUser.getCreatedBy()).isEqualTo(userDto.getCreatedBy());
assertThat(convertedUser.getCreatedDate()).isEqualTo(userDto.getCreatedDate());
assertThat(convertedUser.getLastModifiedBy()).isEqualTo(userDto.getLastModifiedBy());
assertThat(convertedUser.getLastModifiedDate()).isEqualTo(userDto.getLastModifiedDate());
assertThat(convertedUser.getAuthorities()).extracting("name").containsExactly(AuthoritiesConstants.USER);
}
@Test
void usersToUserDTOsShouldMapOnlyNonNullUsers() {
List<User> users = new ArrayList<>();
users.add(user);
users.add(null);
List<UserDTO> userDTOS = userMapper.usersToUserDTOs(users);
assertThat(userDTOS).isNotEmpty().size().isEqualTo(1);
}
@Test
void userDTOsToUsersShouldMapOnlyNonNullUsers() {
List<AdminUserDTO> usersDto = new ArrayList<>();
usersDto.add(userDto);
usersDto.add(null);
List<User> users = userMapper.userDTOsToUsers(usersDto);
assertThat(users).isNotEmpty().size().isEqualTo(1);
}
@Test
void userDTOsToUsersWithAuthoritiesStringShouldMapToUsersWithAuthoritiesDomain() {
Set<String> authoritiesAsString = new HashSet<>();
authoritiesAsString.add("ADMIN");
userDto.setAuthorities(authoritiesAsString);
List<AdminUserDTO> usersDto = new ArrayList<>();
usersDto.add(userDto);
List<User> users = userMapper.userDTOsToUsers(usersDto);
assertThat(users).isNotEmpty().size().isEqualTo(1);
assertThat(users.getFirst().getAuthorities()).isNotNull();
assertThat(users.getFirst().getAuthorities()).isNotEmpty();
assertThat(users.getFirst().getAuthorities().iterator().next().getName()).isEqualTo("ADMIN");
}
@Test
void userDTOsToUsersMapWithNullAuthoritiesStringShouldReturnUserWithEmptyAuthorities() {
userDto.setAuthorities(null);
List<AdminUserDTO> usersDto = new ArrayList<>();
usersDto.add(userDto);
List<User> users = userMapper.userDTOsToUsers(usersDto);
assertThat(users).isNotEmpty().size().isEqualTo(1);
assertThat(users.getFirst().getAuthorities()).isNotNull();
assertThat(users.getFirst().getAuthorities()).isEmpty();
}
@Test
void userDTOToUserMapWithAuthoritiesStringShouldReturnUserWithAuthorities() {
User convertedUser = userMapper.userDTOToUser(userDto);
assertThat(convertedUser).isNotNull();
assertThat(convertedUser.getAuthorities()).isNotNull();
assertThat(convertedUser.getAuthorities()).isNotEmpty();
assertThat(convertedUser.getAuthorities().iterator().next().getName()).isEqualTo(AuthoritiesConstants.USER);
}
@Test
void userDTOToUserMapWithNullAuthoritiesStringShouldReturnUserWithEmptyAuthorities() {
userDto.setAuthorities(null);
User persistUser = userMapper.userDTOToUser(userDto);
assertThat(persistUser).isNotNull();
assertThat(persistUser.getAuthorities()).isNotNull();
assertThat(persistUser.getAuthorities()).isEmpty();
}
@Test
void userDTOToUserMapWithNullUserShouldReturnNull() {
assertThat(userMapper.userDTOToUser(null)).isNull();
}
@Test
void testUserFromId() {
assertThat(userMapper.userFromId(DEFAULT_ID).getId()).isEqualTo(DEFAULT_ID);
assertThat(userMapper.userFromId(null)).isNull();
}
}
@@ -0,0 +1,778 @@
package com.sisvietnamvn.web.web.rest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sisvietnamvn.web.IntegrationTest;
import com.sisvietnamvn.web.config.Constants;
import com.sisvietnamvn.web.domain.User;
import com.sisvietnamvn.web.repository.AuthorityRepository;
import com.sisvietnamvn.web.repository.UserRepository;
import com.sisvietnamvn.web.security.AuthoritiesConstants;
import com.sisvietnamvn.web.service.UserService;
import com.sisvietnamvn.web.service.dto.AdminUserDTO;
import com.sisvietnamvn.web.service.dto.PasswordChangeDTO;
import com.sisvietnamvn.web.web.rest.vm.KeyAndPasswordVM;
import com.sisvietnamvn.web.web.rest.vm.ManagedUserVM;
import java.time.Instant;
import java.util.*;
import java.util.stream.Stream;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests for the {@link AccountResource} REST controller.
*/
@AutoConfigureMockMvc
@IntegrationTest
class AccountResourceIT {
static final String TEST_USER_LOGIN = "test";
@Autowired
private ObjectMapper om;
@Autowired
private UserRepository userRepository;
@Autowired
private AuthorityRepository authorityRepository;
@Autowired
private UserService userService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private MockMvc restAccountMockMvc;
private Long numberOfUsers;
@BeforeEach
void countUsers() {
numberOfUsers = userRepository.count();
}
@AfterEach
void cleanupAndCheck() {
assertThat(userRepository.count()).isEqualTo(numberOfUsers);
numberOfUsers = null;
}
@Test
@WithUnauthenticatedMockUser
void testNonAuthenticatedUser() throws Exception {
restAccountMockMvc.perform(get("/api/authenticate")).andExpect(status().isUnauthorized());
}
@Test
@WithMockUser(TEST_USER_LOGIN)
void testAuthenticatedUser() throws Exception {
restAccountMockMvc.perform(get("/api/authenticate").with(request -> request)).andExpect(status().isNoContent());
}
@Test
@WithMockUser(TEST_USER_LOGIN)
void testGetExistingAccount() throws Exception {
Set<String> authorities = new HashSet<>();
authorities.add(AuthoritiesConstants.ADMIN);
AdminUserDTO user = new AdminUserDTO();
user.setLogin(TEST_USER_LOGIN);
user.setFirstName("john");
user.setLastName("doe");
user.setEmail("john.doe@jhipster.com");
user.setImageUrl("http://placehold.it/50x50");
user.setLangKey("en");
user.setAuthorities(authorities);
userService.createUser(user);
restAccountMockMvc
.perform(get("/api/account").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.login").value(TEST_USER_LOGIN))
.andExpect(jsonPath("$.firstName").value("john"))
.andExpect(jsonPath("$.lastName").value("doe"))
.andExpect(jsonPath("$.email").value("john.doe@jhipster.com"))
.andExpect(jsonPath("$.imageUrl").value("http://placehold.it/50x50"))
.andExpect(jsonPath("$.langKey").value("en"))
.andExpect(jsonPath("$.authorities").value(AuthoritiesConstants.ADMIN));
userService.deleteUser(TEST_USER_LOGIN);
}
@Test
void testGetUnknownAccount() throws Exception {
restAccountMockMvc.perform(get("/api/account").accept(MediaType.APPLICATION_PROBLEM_JSON)).andExpect(status().isUnauthorized());
}
@Test
@Transactional
void testRegisterValid() throws Exception {
ManagedUserVM validUser = new ManagedUserVM();
validUser.setLogin("test-register-valid");
validUser.setPassword("password");
validUser.setFirstName("Alice");
validUser.setLastName("Test");
validUser.setEmail("test-register-valid@example.com");
validUser.setImageUrl("http://placehold.it/50x50");
validUser.setLangKey(Constants.DEFAULT_LANGUAGE);
validUser.setAuthorities(Set.of(AuthoritiesConstants.USER));
assertThat(userRepository.findOneByLogin("test-register-valid")).isEmpty();
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(validUser)))
.andExpect(status().isCreated());
assertThat(userRepository.findOneByLogin("test-register-valid")).isPresent();
userService.deleteUser("test-register-valid");
}
@Test
@Transactional
void testRegisterInvalidLogin() throws Exception {
ManagedUserVM invalidUser = new ManagedUserVM();
invalidUser.setLogin("funky-log(n"); // <-- invalid
invalidUser.setPassword("password");
invalidUser.setFirstName("Funky");
invalidUser.setLastName("One");
invalidUser.setEmail("funky@example.com");
invalidUser.setActivated(true);
invalidUser.setImageUrl("http://placehold.it/50x50");
invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE);
invalidUser.setAuthorities(Set.of(AuthoritiesConstants.USER));
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(invalidUser)))
.andExpect(status().isBadRequest());
Optional<User> user = userRepository.findOneByEmailIgnoreCase("funky@example.com");
assertThat(user).isEmpty();
}
static Stream<ManagedUserVM> invalidUsers() {
return Stream.of(
createInvalidUser("bob", "password", "Bob", "Green", "invalid", true), // <-- invalid
createInvalidUser("bob", "123", "Bob", "Green", "bob@example.com", true), // password with only 3 digits
createInvalidUser("bob", null, "Bob", "Green", "bob@example.com", true) // invalid null password
);
}
@ParameterizedTest
@MethodSource("invalidUsers")
@Transactional
void testRegisterInvalidUsers(ManagedUserVM invalidUser) throws Exception {
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(invalidUser)))
.andExpect(status().isBadRequest());
Optional<User> user = userRepository.findOneByLogin("bob");
assertThat(user).isEmpty();
}
private static ManagedUserVM createInvalidUser(
String login,
String password,
String firstName,
String lastName,
String email,
boolean activated
) {
ManagedUserVM invalidUser = new ManagedUserVM();
invalidUser.setLogin(login);
invalidUser.setPassword(password);
invalidUser.setFirstName(firstName);
invalidUser.setLastName(lastName);
invalidUser.setEmail(email);
invalidUser.setActivated(activated);
invalidUser.setImageUrl("http://placehold.it/50x50");
invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE);
invalidUser.setAuthorities(Set.of(AuthoritiesConstants.USER));
return invalidUser;
}
@Test
@Transactional
void testRegisterDuplicateLogin() throws Exception {
// First registration
ManagedUserVM firstUser = new ManagedUserVM();
firstUser.setLogin("alice");
firstUser.setPassword("password");
firstUser.setFirstName("Alice");
firstUser.setLastName("Something");
firstUser.setEmail("alice@example.com");
firstUser.setImageUrl("http://placehold.it/50x50");
firstUser.setLangKey(Constants.DEFAULT_LANGUAGE);
firstUser.setAuthorities(Set.of(AuthoritiesConstants.USER));
// Duplicate login, different email
ManagedUserVM secondUser = new ManagedUserVM();
secondUser.setLogin(firstUser.getLogin());
secondUser.setPassword(firstUser.getPassword());
secondUser.setFirstName(firstUser.getFirstName());
secondUser.setLastName(firstUser.getLastName());
secondUser.setEmail("alice2@example.com");
secondUser.setImageUrl(firstUser.getImageUrl());
secondUser.setLangKey(firstUser.getLangKey());
secondUser.setCreatedBy(firstUser.getCreatedBy());
secondUser.setCreatedDate(firstUser.getCreatedDate());
secondUser.setLastModifiedBy(firstUser.getLastModifiedBy());
secondUser.setLastModifiedDate(firstUser.getLastModifiedDate());
secondUser.setAuthorities(new HashSet<>(firstUser.getAuthorities()));
// First user
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(firstUser)))
.andExpect(status().isCreated());
// Second (non activated) user
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(secondUser)))
.andExpect(status().isCreated());
Optional<User> testUser = userRepository.findOneByEmailIgnoreCase("alice2@example.com");
assertThat(testUser).isPresent();
testUser.orElseThrow().setActivated(true);
userRepository.save(testUser.orElseThrow());
// Second (already activated) user
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(secondUser)))
.andExpect(status().is4xxClientError());
userService.deleteUser("alice");
}
@Test
@Transactional
void testRegisterDuplicateEmail() throws Exception {
// First user
ManagedUserVM firstUser = new ManagedUserVM();
firstUser.setLogin("test-register-duplicate-email");
firstUser.setPassword("password");
firstUser.setFirstName("Alice");
firstUser.setLastName("Test");
firstUser.setEmail("test-register-duplicate-email@example.com");
firstUser.setImageUrl("http://placehold.it/50x50");
firstUser.setLangKey(Constants.DEFAULT_LANGUAGE);
firstUser.setAuthorities(Set.of(AuthoritiesConstants.USER));
// Register first user
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(firstUser)))
.andExpect(status().isCreated());
Optional<User> testUser1 = userRepository.findOneByLogin("test-register-duplicate-email");
assertThat(testUser1).isPresent();
// Duplicate email, different login
ManagedUserVM secondUser = new ManagedUserVM();
secondUser.setLogin("test-register-duplicate-email-2");
secondUser.setPassword(firstUser.getPassword());
secondUser.setFirstName(firstUser.getFirstName());
secondUser.setLastName(firstUser.getLastName());
secondUser.setEmail(firstUser.getEmail());
secondUser.setImageUrl(firstUser.getImageUrl());
secondUser.setLangKey(firstUser.getLangKey());
secondUser.setAuthorities(new HashSet<>(firstUser.getAuthorities()));
// Register second (non activated) user
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(secondUser)))
.andExpect(status().isCreated());
Optional<User> testUser2 = userRepository.findOneByLogin("test-register-duplicate-email");
assertThat(testUser2).isEmpty();
Optional<User> testUser3 = userRepository.findOneByLogin("test-register-duplicate-email-2");
assertThat(testUser3).isPresent();
// Duplicate email - with uppercase email address
ManagedUserVM userWithUpperCaseEmail = new ManagedUserVM();
userWithUpperCaseEmail.setId(firstUser.getId());
userWithUpperCaseEmail.setLogin("test-register-duplicate-email-3");
userWithUpperCaseEmail.setPassword(firstUser.getPassword());
userWithUpperCaseEmail.setFirstName(firstUser.getFirstName());
userWithUpperCaseEmail.setLastName(firstUser.getLastName());
userWithUpperCaseEmail.setEmail("TEST-register-duplicate-email@example.com");
userWithUpperCaseEmail.setImageUrl(firstUser.getImageUrl());
userWithUpperCaseEmail.setLangKey(firstUser.getLangKey());
userWithUpperCaseEmail.setAuthorities(new HashSet<>(firstUser.getAuthorities()));
// Register third (not activated) user
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userWithUpperCaseEmail)))
.andExpect(status().isCreated());
Optional<User> testUser4 = userRepository.findOneByLogin("test-register-duplicate-email-3");
assertThat(testUser4).isPresent();
assertThat(testUser4.orElseThrow().getEmail()).isEqualTo("test-register-duplicate-email@example.com");
testUser4.orElseThrow().setActivated(true);
userService.updateUser((new AdminUserDTO(testUser4.orElseThrow())));
// Register 4th (already activated) user
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(secondUser)))
.andExpect(status().is4xxClientError());
userService.deleteUser("test-register-duplicate-email-3");
}
@Test
@Transactional
void testRegisterAdminIsIgnored() throws Exception {
ManagedUserVM validUser = new ManagedUserVM();
validUser.setLogin("badguy");
validUser.setPassword("password");
validUser.setFirstName("Bad");
validUser.setLastName("Guy");
validUser.setEmail("badguy@example.com");
validUser.setActivated(true);
validUser.setImageUrl("http://placehold.it/50x50");
validUser.setLangKey(Constants.DEFAULT_LANGUAGE);
validUser.setAuthorities(Set.of(AuthoritiesConstants.ADMIN));
restAccountMockMvc
.perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(validUser)))
.andExpect(status().isCreated());
Optional<User> userDup = userRepository.findOneWithAuthoritiesByLogin("badguy");
assertThat(userDup).isPresent();
assertThat(userDup.orElseThrow().getAuthorities())
.hasSize(1)
.containsExactly(authorityRepository.findById(AuthoritiesConstants.USER).orElseThrow());
userService.deleteUser("badguy");
}
@Test
@Transactional
void testActivateAccount() throws Exception {
final String activationKey = "some activation key";
User user = new User();
user.setLogin("activate-account");
user.setEmail("activate-account@example.com");
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setActivated(false);
user.setActivationKey(activationKey);
userRepository.saveAndFlush(user);
restAccountMockMvc.perform(get("/api/activate?key={activationKey}", activationKey)).andExpect(status().isOk());
user = userRepository.findOneByLogin(user.getLogin()).orElse(null);
assertThat(user.isActivated()).isTrue();
userService.deleteUser("activate-account");
}
@Test
@Transactional
void testActivateAccountWithWrongKey() throws Exception {
restAccountMockMvc.perform(get("/api/activate?key=wrongActivationKey")).andExpect(status().isInternalServerError());
}
@Test
@Transactional
@WithMockUser("save-account")
void testSaveAccount() throws Exception {
User user = new User();
user.setLogin("save-account");
user.setEmail("save-account@example.com");
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setActivated(true);
userRepository.saveAndFlush(user);
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setLogin("not-used");
userDTO.setFirstName("firstname");
userDTO.setLastName("lastname");
userDTO.setEmail("save-account@example.com");
userDTO.setActivated(false);
userDTO.setImageUrl("http://placehold.it/50x50");
userDTO.setLangKey(Constants.DEFAULT_LANGUAGE);
userDTO.setAuthorities(Set.of(AuthoritiesConstants.ADMIN));
restAccountMockMvc
.perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isOk());
User updatedUser = userRepository.findOneWithAuthoritiesByLogin(user.getLogin()).orElse(null);
assertThat(updatedUser.getFirstName()).isEqualTo(userDTO.getFirstName());
assertThat(updatedUser.getLastName()).isEqualTo(userDTO.getLastName());
assertThat(updatedUser.getEmail()).isEqualTo(userDTO.getEmail());
assertThat(updatedUser.getLangKey()).isEqualTo(userDTO.getLangKey());
assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword());
assertThat(updatedUser.getImageUrl()).isEqualTo(userDTO.getImageUrl());
assertThat(updatedUser.isActivated()).isTrue();
assertThat(updatedUser.getAuthorities()).isEmpty();
userService.deleteUser("save-account");
}
@Test
@Transactional
@WithMockUser("save-invalid-email")
void testSaveInvalidEmail() throws Exception {
User user = new User();
user.setLogin("save-invalid-email");
user.setEmail("save-invalid-email@example.com");
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setActivated(true);
userRepository.saveAndFlush(user);
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setLogin("not-used");
userDTO.setFirstName("firstname");
userDTO.setLastName("lastname");
userDTO.setEmail("invalid email");
userDTO.setActivated(false);
userDTO.setImageUrl("http://placehold.it/50x50");
userDTO.setLangKey(Constants.DEFAULT_LANGUAGE);
userDTO.setAuthorities(Set.of(AuthoritiesConstants.ADMIN));
restAccountMockMvc
.perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isBadRequest());
assertThat(userRepository.findOneByEmailIgnoreCase("invalid email")).isNotPresent();
userService.deleteUser("save-invalid-email");
}
@Test
@Transactional
@WithMockUser("save-existing-email")
void testSaveExistingEmail() throws Exception {
User user = new User();
user.setLogin("save-existing-email");
user.setEmail("save-existing-email@example.com");
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setActivated(true);
userRepository.saveAndFlush(user);
User anotherUser = new User();
anotherUser.setLogin("save-existing-email2");
anotherUser.setEmail("save-existing-email2@example.com");
anotherUser.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
anotherUser.setActivated(true);
userRepository.saveAndFlush(anotherUser);
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setLogin("not-used");
userDTO.setFirstName("firstname");
userDTO.setLastName("lastname");
userDTO.setEmail("save-existing-email2@example.com");
userDTO.setActivated(false);
userDTO.setImageUrl("http://placehold.it/50x50");
userDTO.setLangKey(Constants.DEFAULT_LANGUAGE);
userDTO.setAuthorities(Set.of(AuthoritiesConstants.ADMIN));
restAccountMockMvc
.perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isBadRequest());
User updatedUser = userRepository.findOneByLogin("save-existing-email").orElseThrow();
assertThat(updatedUser.getEmail()).isEqualTo("save-existing-email@example.com");
userService.deleteUser("save-existing-email");
userService.deleteUser("save-existing-email2");
}
@Test
@Transactional
@WithMockUser("save-existing-email-and-login")
void testSaveExistingEmailAndLogin() throws Exception {
User user = new User();
user.setLogin("save-existing-email-and-login");
user.setEmail("save-existing-email-and-login@example.com");
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setActivated(true);
userRepository.saveAndFlush(user);
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setLogin("not-used");
userDTO.setFirstName("firstname");
userDTO.setLastName("lastname");
userDTO.setEmail("save-existing-email-and-login@example.com");
userDTO.setActivated(false);
userDTO.setImageUrl("http://placehold.it/50x50");
userDTO.setLangKey(Constants.DEFAULT_LANGUAGE);
userDTO.setAuthorities(Set.of(AuthoritiesConstants.ADMIN));
restAccountMockMvc
.perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isOk());
User updatedUser = userRepository.findOneByLogin("save-existing-email-and-login").orElse(null);
assertThat(updatedUser.getEmail()).isEqualTo("save-existing-email-and-login@example.com");
userService.deleteUser("save-existing-email-and-login");
}
@Test
@Transactional
@WithMockUser("change-password-wrong-existing-password")
void testChangePasswordWrongExistingPassword() throws Exception {
User user = new User();
String currentPassword = RandomStringUtils.insecure().nextAlphanumeric(60);
user.setPassword(passwordEncoder.encode(currentPassword));
user.setLogin("change-password-wrong-existing-password");
user.setEmail("change-password-wrong-existing-password@example.com");
userRepository.saveAndFlush(user);
restAccountMockMvc
.perform(
post("/api/account/change-password")
.contentType(MediaType.APPLICATION_JSON)
.content(om.writeValueAsBytes(new PasswordChangeDTO("1" + currentPassword, "new password")))
)
.andExpect(status().isBadRequest());
User updatedUser = userRepository.findOneByLogin("change-password-wrong-existing-password").orElse(null);
assertThat(passwordEncoder.matches("new password", updatedUser.getPassword())).isFalse();
assertThat(passwordEncoder.matches(currentPassword, updatedUser.getPassword())).isTrue();
userService.deleteUser("change-password-wrong-existing-password");
}
@Test
@Transactional
@WithMockUser("change-password")
void testChangePassword() throws Exception {
User user = new User();
String currentPassword = RandomStringUtils.insecure().nextAlphanumeric(60);
user.setPassword(passwordEncoder.encode(currentPassword));
user.setLogin("change-password");
user.setEmail("change-password@example.com");
userRepository.saveAndFlush(user);
restAccountMockMvc
.perform(
post("/api/account/change-password")
.contentType(MediaType.APPLICATION_JSON)
.content(om.writeValueAsBytes(new PasswordChangeDTO(currentPassword, "new password")))
)
.andExpect(status().isOk());
User updatedUser = userRepository.findOneByLogin("change-password").orElse(null);
assertThat(passwordEncoder.matches("new password", updatedUser.getPassword())).isTrue();
userService.deleteUser("change-password");
}
@Test
@Transactional
@WithMockUser("change-password-too-small")
void testChangePasswordTooSmall() throws Exception {
User user = new User();
String currentPassword = RandomStringUtils.insecure().nextAlphanumeric(60);
user.setPassword(passwordEncoder.encode(currentPassword));
user.setLogin("change-password-too-small");
user.setEmail("change-password-too-small@example.com");
userRepository.saveAndFlush(user);
String newPassword = RandomStringUtils.insecure().next(ManagedUserVM.PASSWORD_MIN_LENGTH - 1);
restAccountMockMvc
.perform(
post("/api/account/change-password")
.contentType(MediaType.APPLICATION_JSON)
.content(om.writeValueAsBytes(new PasswordChangeDTO(currentPassword, newPassword)))
)
.andExpect(status().isBadRequest());
User updatedUser = userRepository.findOneByLogin("change-password-too-small").orElse(null);
assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword());
userService.deleteUser("change-password-too-small");
}
@Test
@Transactional
@WithMockUser("change-password-too-long")
void testChangePasswordTooLong() throws Exception {
User user = new User();
String currentPassword = RandomStringUtils.insecure().nextAlphanumeric(60);
user.setPassword(passwordEncoder.encode(currentPassword));
user.setLogin("change-password-too-long");
user.setEmail("change-password-too-long@example.com");
userRepository.saveAndFlush(user);
String newPassword = RandomStringUtils.insecure().next(ManagedUserVM.PASSWORD_MAX_LENGTH + 1);
restAccountMockMvc
.perform(
post("/api/account/change-password")
.contentType(MediaType.APPLICATION_JSON)
.content(om.writeValueAsBytes(new PasswordChangeDTO(currentPassword, newPassword)))
)
.andExpect(status().isBadRequest());
User updatedUser = userRepository.findOneByLogin("change-password-too-long").orElse(null);
assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword());
userService.deleteUser("change-password-too-long");
}
@Test
@Transactional
@WithMockUser("change-password-empty")
void testChangePasswordEmpty() throws Exception {
User user = new User();
String currentPassword = RandomStringUtils.insecure().nextAlphanumeric(60);
user.setPassword(passwordEncoder.encode(currentPassword));
user.setLogin("change-password-empty");
user.setEmail("change-password-empty@example.com");
userRepository.saveAndFlush(user);
restAccountMockMvc
.perform(
post("/api/account/change-password")
.contentType(MediaType.APPLICATION_JSON)
.content(om.writeValueAsBytes(new PasswordChangeDTO(currentPassword, "")))
)
.andExpect(status().isBadRequest());
User updatedUser = userRepository.findOneByLogin("change-password-empty").orElse(null);
assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword());
userService.deleteUser("change-password-empty");
}
@Test
@Transactional
void testRequestPasswordReset() throws Exception {
User user = new User();
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setActivated(true);
user.setLogin("password-reset");
user.setEmail("password-reset@example.com");
user.setLangKey("en");
userRepository.saveAndFlush(user);
restAccountMockMvc
.perform(post("/api/account/reset-password/init").content("password-reset@example.com"))
.andExpect(status().isOk());
userService.deleteUser("password-reset");
}
@Test
@Transactional
void testRequestPasswordResetUpperCaseEmail() throws Exception {
User user = new User();
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setActivated(true);
user.setLogin("password-reset-upper-case");
user.setEmail("password-reset-upper-case@example.com");
user.setLangKey("en");
userRepository.saveAndFlush(user);
restAccountMockMvc
.perform(post("/api/account/reset-password/init").content("password-reset-upper-case@EXAMPLE.COM"))
.andExpect(status().isOk());
userService.deleteUser("password-reset-upper-case");
}
@Test
void testRequestPasswordResetWrongEmail() throws Exception {
restAccountMockMvc
.perform(post("/api/account/reset-password/init").content("password-reset-wrong-email@example.com"))
.andExpect(status().isOk());
}
@Test
@Transactional
void testFinishPasswordReset() throws Exception {
User user = new User();
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setLogin("finish-password-reset");
user.setEmail("finish-password-reset@example.com");
user.setResetDate(Instant.now().plusSeconds(60));
user.setResetKey("reset key");
userRepository.saveAndFlush(user);
KeyAndPasswordVM keyAndPassword = new KeyAndPasswordVM();
keyAndPassword.setKey(user.getResetKey());
keyAndPassword.setNewPassword("new password");
restAccountMockMvc
.perform(
post("/api/account/reset-password/finish")
.contentType(MediaType.APPLICATION_JSON)
.content(om.writeValueAsBytes(keyAndPassword))
)
.andExpect(status().isOk());
User updatedUser = userRepository.findOneByLogin(user.getLogin()).orElse(null);
assertThat(passwordEncoder.matches(keyAndPassword.getNewPassword(), updatedUser.getPassword())).isTrue();
userService.deleteUser("finish-password-reset");
}
@Test
@Transactional
void testFinishPasswordResetTooSmall() throws Exception {
User user = new User();
user.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
user.setLogin("finish-password-reset-too-small");
user.setEmail("finish-password-reset-too-small@example.com");
user.setResetDate(Instant.now().plusSeconds(60));
user.setResetKey("reset key too small");
userRepository.saveAndFlush(user);
KeyAndPasswordVM keyAndPassword = new KeyAndPasswordVM();
keyAndPassword.setKey(user.getResetKey());
keyAndPassword.setNewPassword("foo");
restAccountMockMvc
.perform(
post("/api/account/reset-password/finish")
.contentType(MediaType.APPLICATION_JSON)
.content(om.writeValueAsBytes(keyAndPassword))
)
.andExpect(status().isBadRequest());
User updatedUser = userRepository.findOneByLogin(user.getLogin()).orElse(null);
assertThat(passwordEncoder.matches(keyAndPassword.getNewPassword(), updatedUser.getPassword())).isFalse();
userService.deleteUser("finish-password-reset-too-small");
}
@Test
@Transactional
void testFinishPasswordResetWrongKey() throws Exception {
KeyAndPasswordVM keyAndPassword = new KeyAndPasswordVM();
keyAndPassword.setKey("wrong reset key");
keyAndPassword.setNewPassword("new password");
restAccountMockMvc
.perform(
post("/api/account/reset-password/finish")
.contentType(MediaType.APPLICATION_JSON)
.content(om.writeValueAsBytes(keyAndPassword))
)
.andExpect(status().isInternalServerError());
}
}
@@ -0,0 +1,102 @@
package com.sisvietnamvn.web.web.rest;
import static org.hamcrest.Matchers.emptyString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sisvietnamvn.web.IntegrationTest;
import com.sisvietnamvn.web.domain.User;
import com.sisvietnamvn.web.repository.UserRepository;
import com.sisvietnamvn.web.web.rest.vm.LoginVM;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests for the {@link AuthenticateController} REST controller.
*/
@AutoConfigureMockMvc
@IntegrationTest
class AuthenticateControllerIT {
@Autowired
private ObjectMapper om;
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private MockMvc mockMvc;
@Test
@Transactional
void testAuthorize() throws Exception {
User user = new User();
user.setLogin("user-jwt-controller");
user.setEmail("user-jwt-controller@example.com");
user.setActivated(true);
user.setPassword(passwordEncoder.encode("test"));
userRepository.saveAndFlush(user);
LoginVM login = new LoginVM();
login.setUsername("user-jwt-controller");
login.setPassword("test");
mockMvc
.perform(post("/api/authenticate").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(login)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id_token").isString())
.andExpect(jsonPath("$.id_token").isNotEmpty())
.andExpect(header().string("Authorization", not(nullValue())))
.andExpect(header().string("Authorization", not(is(emptyString()))));
}
@Test
@Transactional
void testAuthorizeWithRememberMe() throws Exception {
User user = new User();
user.setLogin("user-jwt-controller-remember-me");
user.setEmail("user-jwt-controller-remember-me@example.com");
user.setActivated(true);
user.setPassword(passwordEncoder.encode("test"));
userRepository.saveAndFlush(user);
LoginVM login = new LoginVM();
login.setUsername("user-jwt-controller-remember-me");
login.setPassword("test");
login.setRememberMe(true);
mockMvc
.perform(post("/api/authenticate").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(login)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id_token").isString())
.andExpect(jsonPath("$.id_token").isNotEmpty())
.andExpect(header().string("Authorization", not(nullValue())))
.andExpect(header().string("Authorization", not(is(emptyString()))));
}
@Test
void testAuthorizeFails() throws Exception {
LoginVM login = new LoginVM();
login.setUsername("wrong-user");
login.setPassword("wrong password");
mockMvc
.perform(post("/api/authenticate").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(login)))
.andExpect(status().isUnauthorized())
.andExpect(jsonPath("$.id_token").doesNotExist())
.andExpect(header().doesNotExist("Authorization"));
}
}
@@ -0,0 +1,206 @@
package com.sisvietnamvn.web.web.rest;
import static com.sisvietnamvn.web.domain.AuthorityAsserts.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sisvietnamvn.web.IntegrationTest;
import com.sisvietnamvn.web.domain.Authority;
import com.sisvietnamvn.web.repository.AuthorityRepository;
import jakarta.persistence.EntityManager;
import java.util.UUID;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests for the {@link AuthorityResource} REST controller.
*/
@IntegrationTest
@AutoConfigureMockMvc
@WithMockUser(authorities = { "ROLE_ADMIN" })
class AuthorityResourceIT {
private static final String ENTITY_API_URL = "/api/authorities";
private static final String ENTITY_API_URL_ID = ENTITY_API_URL + "/{name}";
@Autowired
private ObjectMapper om;
@Autowired
private AuthorityRepository authorityRepository;
@Autowired
private EntityManager em;
@Autowired
private MockMvc restAuthorityMockMvc;
private Authority authority;
private Authority insertedAuthority;
/**
* Create an entity for this test.
*
* This is a static method, as tests for other entities might also need it,
* if they test an entity which requires the current entity.
*/
public static Authority createEntity() {
return new Authority().name(UUID.randomUUID().toString());
}
/**
* Create an updated entity for this test.
*
* This is a static method, as tests for other entities might also need it,
* if they test an entity which requires the current entity.
*/
public static Authority createUpdatedEntity() {
return new Authority().name(UUID.randomUUID().toString());
}
@BeforeEach
void initTest() {
authority = createEntity();
}
@AfterEach
void cleanup() {
if (insertedAuthority != null) {
authorityRepository.delete(insertedAuthority);
insertedAuthority = null;
}
}
@Test
@Transactional
void createAuthority() throws Exception {
long databaseSizeBeforeCreate = getRepositoryCount();
// Create the Authority
var returnedAuthority = om.readValue(
restAuthorityMockMvc
.perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(authority)))
.andExpect(status().isCreated())
.andReturn()
.getResponse()
.getContentAsString(),
Authority.class
);
// Validate the Authority in the database
assertIncrementedRepositoryCount(databaseSizeBeforeCreate);
assertAuthorityUpdatableFieldsEquals(returnedAuthority, getPersistedAuthority(returnedAuthority));
insertedAuthority = returnedAuthority;
}
@Test
@Transactional
void createAuthorityWithExistingId() throws Exception {
// Create the Authority with an existing ID
insertedAuthority = authorityRepository.saveAndFlush(authority);
long databaseSizeBeforeCreate = getRepositoryCount();
// An entity with an existing ID cannot be created, so this API call must fail
restAuthorityMockMvc
.perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(authority)))
.andExpect(status().isBadRequest());
// Validate the Authority in the database
assertSameRepositoryCount(databaseSizeBeforeCreate);
}
@Test
@Transactional
void getAllAuthorities() throws Exception {
// Initialize the database
authority.setName(UUID.randomUUID().toString());
insertedAuthority = authorityRepository.saveAndFlush(authority);
// Get all the authorityList
restAuthorityMockMvc
.perform(get(ENTITY_API_URL + "?sort=name,desc"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.[*].name").value(hasItem(authority.getName())));
}
@Test
@Transactional
void getAuthority() throws Exception {
// Initialize the database
authority.setName(UUID.randomUUID().toString());
insertedAuthority = authorityRepository.saveAndFlush(authority);
// Get the authority
restAuthorityMockMvc
.perform(get(ENTITY_API_URL_ID, authority.getName()))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.name").value(authority.getName()));
}
@Test
@Transactional
void getNonExistingAuthority() throws Exception {
// Get the authority
restAuthorityMockMvc.perform(get(ENTITY_API_URL_ID, Long.MAX_VALUE)).andExpect(status().isNotFound());
}
@Test
@Transactional
void deleteAuthority() throws Exception {
// Initialize the database
authority.setName(UUID.randomUUID().toString());
insertedAuthority = authorityRepository.saveAndFlush(authority);
long databaseSizeBeforeDelete = getRepositoryCount();
// Delete the authority
restAuthorityMockMvc
.perform(delete(ENTITY_API_URL_ID, authority.getName()).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent());
// Validate the database contains one less item
assertDecrementedRepositoryCount(databaseSizeBeforeDelete);
}
protected long getRepositoryCount() {
return authorityRepository.count();
}
protected void assertIncrementedRepositoryCount(long countBefore) {
assertThat(countBefore + 1).isEqualTo(getRepositoryCount());
}
protected void assertDecrementedRepositoryCount(long countBefore) {
assertThat(countBefore - 1).isEqualTo(getRepositoryCount());
}
protected void assertSameRepositoryCount(long countBefore) {
assertThat(countBefore).isEqualTo(getRepositoryCount());
}
protected Authority getPersistedAuthority(Authority authority) {
return authorityRepository.findById(authority.getName()).orElseThrow();
}
protected void assertPersistedAuthorityToMatchAllProperties(Authority expectedAuthority) {
assertAuthorityAllPropertiesEquals(expectedAuthority, getPersistedAuthority(expectedAuthority));
}
protected void assertPersistedAuthorityToMatchUpdatableProperties(Authority expectedAuthority) {
assertAuthorityAllUpdatablePropertiesEquals(expectedAuthority, getPersistedAuthority(expectedAuthority));
}
}
@@ -0,0 +1,103 @@
package com.sisvietnamvn.web.web.rest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import com.sisvietnamvn.web.IntegrationTest;
import com.sisvietnamvn.web.domain.User;
import com.sisvietnamvn.web.repository.UserRepository;
import com.sisvietnamvn.web.security.AuthoritiesConstants;
import com.sisvietnamvn.web.service.UserService;
import java.util.Objects;
import java.util.Set;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests for the {@link PublicUserResource} REST controller.
*/
@AutoConfigureMockMvc
@WithMockUser(authorities = AuthoritiesConstants.ADMIN)
@IntegrationTest
class PublicUserResourceIT {
@Autowired
private UserRepository userRepository;
@Autowired
private UserService userService;
@Autowired
private CacheManager cacheManager;
@Autowired
private MockMvc restUserMockMvc;
private User user;
private Long numberOfUsers;
@BeforeEach
void countUsers() {
numberOfUsers = userRepository.count();
}
@BeforeEach
void initTest() {
user = UserResourceIT.initTestUser();
}
@AfterEach
void cleanupAndCheck() {
cacheManager
.getCacheNames()
.stream()
.map(cacheName -> this.cacheManager.getCache(cacheName))
.filter(Objects::nonNull)
.forEach(Cache::clear);
userService.deleteUser(user.getLogin());
assertThat(userRepository.count()).isEqualTo(numberOfUsers);
numberOfUsers = null;
}
@Test
@Transactional
void getAllPublicUsers() throws Exception {
// Initialize the database
userRepository.saveAndFlush(user);
// Get all the users
restUserMockMvc
.perform(get("/api/users?sort=id,desc").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.[?(@.id == %d)].login".formatted(user.getId())).value(user.getLogin()))
.andExpect(jsonPath("$.[?(@.id == %d)].keys()".formatted(user.getId())).value(Set.of("id", "login")))
.andExpect(jsonPath("$.[*].email").doesNotHaveJsonPath())
.andExpect(jsonPath("$.[*].imageUrl").doesNotHaveJsonPath())
.andExpect(jsonPath("$.[*].langKey").doesNotHaveJsonPath());
}
@Test
@Transactional
void getAllUsersSortedByParameters() throws Exception {
// Initialize the database
userRepository.saveAndFlush(user);
restUserMockMvc.perform(get("/api/users?sort=resetKey,desc").accept(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest());
restUserMockMvc.perform(get("/api/users?sort=password,desc").accept(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest());
restUserMockMvc
.perform(get("/api/users?sort=resetKey,desc&sort=id,desc").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest());
restUserMockMvc.perform(get("/api/users?sort=id,desc").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk());
}
}
@@ -0,0 +1,190 @@
package com.sisvietnamvn.web.web.rest;
import static org.assertj.core.api.Assertions.assertThat;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.List;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import org.hamcrest.TypeSafeMatcher;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
/**
* Utility class for testing REST controllers.
*/
public final class TestUtil {
/**
* Create a byte array with a specific size filled with specified data.
*
* @param size the size of the byte array.
* @param data the data to put in the byte array.
* @return the JSON byte array.
*/
public static byte[] createByteArray(int size, String data) {
byte[] byteArray = new byte[size];
Arrays.fill(byteArray, Byte.parseByte(data, 2));
return byteArray;
}
/**
* A matcher that tests that the examined string represents the same instant as the reference datetime.
*/
public static class ZonedDateTimeMatcher extends TypeSafeDiagnosingMatcher<String> {
private final ZonedDateTime date;
public ZonedDateTimeMatcher(ZonedDateTime date) {
this.date = date;
}
@Override
protected boolean matchesSafely(String item, Description mismatchDescription) {
try {
if (!date.isEqual(ZonedDateTime.parse(item))) {
mismatchDescription.appendText("was ").appendValue(item);
return false;
}
return true;
} catch (DateTimeParseException e) {
mismatchDescription.appendText("was ").appendValue(item).appendText(", which could not be parsed as a ZonedDateTime");
return false;
}
}
@Override
public void describeTo(Description description) {
description.appendText("a String representing the same Instant as ").appendValue(date);
}
}
/**
* Creates a matcher that matches when the examined string represents the same instant as the reference datetime.
*
* @param date the reference datetime against which the examined string is checked.
*/
public static ZonedDateTimeMatcher sameInstant(ZonedDateTime date) {
return new ZonedDateTimeMatcher(date);
}
/**
* A matcher that tests that the examined number represents the same value - it can be Long, Double, etc - as the reference BigDecimal.
*/
public static class NumberMatcher extends TypeSafeMatcher<Number> {
final BigDecimal value;
public NumberMatcher(BigDecimal value) {
this.value = value;
}
@Override
public void describeTo(Description description) {
description.appendText("a numeric value is ").appendValue(value);
}
@Override
protected boolean matchesSafely(Number item) {
BigDecimal bigDecimal = asDecimal(item);
return bigDecimal != null && value.compareTo(bigDecimal) == 0;
}
private static BigDecimal asDecimal(Number item) {
return switch (item) {
case null -> null;
case BigDecimal bigDecimal -> bigDecimal;
case Long l -> BigDecimal.valueOf(l);
case Integer i -> BigDecimal.valueOf(i);
case Double v -> BigDecimal.valueOf(v);
case Float v -> BigDecimal.valueOf(v);
default -> BigDecimal.valueOf(item.doubleValue());
};
}
}
/**
* Creates a matcher that matches when the examined number represents the same value as the reference BigDecimal.
*
* @param number the reference BigDecimal against which the examined number is checked.
*/
public static NumberMatcher sameNumber(BigDecimal number) {
return new NumberMatcher(number);
}
/**
* Verifies the equals/hashcode contract on the domain object.
*/
public static <T> void equalsVerifier(Class<T> clazz) throws Exception {
T domainObject1 = clazz.getConstructor().newInstance();
assertThat(domainObject1.toString()).isNotNull();
assertThat(domainObject1).isEqualTo(domainObject1);
assertThat(domainObject1).hasSameHashCodeAs(domainObject1);
// Test with an instance of another class
Object testOtherObject = new Object();
assertThat(domainObject1).isNotEqualTo(testOtherObject);
assertThat(domainObject1).isNotEqualTo(null);
// Test with an instance of the same class
T domainObject2 = clazz.getConstructor().newInstance();
assertThat(domainObject1).isNotEqualTo(domainObject2);
// HashCodes are equals because the objects are not persisted yet
assertThat(domainObject1).hasSameHashCodeAs(domainObject2);
}
/**
* Create a {@link FormattingConversionService} which use ISO date format, instead of the localized one.
* @return the {@link FormattingConversionService}.
*/
public static FormattingConversionService createFormattingConversionService() {
DefaultFormattingConversionService dfcs = new DefaultFormattingConversionService();
var registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(dfcs);
return dfcs;
}
/**
* Executes a query on the EntityManager finding all stored objects.
* @param <T> The type of objects to be searched
* @param em The instance of the EntityManager
* @param clazz The class type to be searched
* @return A list of all found objects
*/
public static <T> List<T> findAll(EntityManager em, Class<T> clazz) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(clazz);
Root<T> rootEntry = cq.from(clazz);
CriteriaQuery<T> all = cq.select(rootEntry);
TypedQuery<T> allQuery = em.createQuery(all);
return allQuery.getResultList();
}
@SuppressWarnings("unchecked")
public static <T> T createUpdateProxyForBean(T update, T original) {
Enhancer e = new Enhancer();
e.setSuperclass(original.getClass());
e.setCallback(
(MethodInterceptor) (obj, method, args, proxy) -> {
Object val = update.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(update, args);
if (val == null) {
return original.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(original, args);
}
return val;
}
);
return (T) e.create();
}
private TestUtil() {}
}
@@ -0,0 +1,505 @@
package com.sisvietnamvn.web.web.rest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sisvietnamvn.web.IntegrationTest;
import com.sisvietnamvn.web.domain.User;
import com.sisvietnamvn.web.repository.UserRepository;
import com.sisvietnamvn.web.security.AuthoritiesConstants;
import com.sisvietnamvn.web.service.UserService;
import com.sisvietnamvn.web.service.dto.AdminUserDTO;
import com.sisvietnamvn.web.service.mapper.UserMapper;
import jakarta.persistence.EntityManager;
import java.util.*;
import java.util.function.Consumer;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests for the {@link UserResource} REST controller.
*/
@AutoConfigureMockMvc
@WithMockUser(authorities = AuthoritiesConstants.ADMIN)
@IntegrationTest
class UserResourceIT {
private static final String DEFAULT_LOGIN = "johndoe";
private static final String UPDATED_LOGIN = "jhipster";
private static final Long DEFAULT_ID = 1L;
private static final String DEFAULT_EMAIL = "johndoe@localhost";
private static final String UPDATED_EMAIL = "jhipster@localhost";
private static final String DEFAULT_FIRSTNAME = "john";
private static final String UPDATED_FIRSTNAME = "jhipsterFirstName";
private static final String DEFAULT_LASTNAME = "doe";
private static final String UPDATED_LASTNAME = "jhipsterLastName";
private static final String DEFAULT_IMAGEURL = "http://placehold.it/50x50";
private static final String UPDATED_IMAGEURL = "http://placehold.it/40x40";
private static final String DEFAULT_LANGKEY = "en";
private static final String UPDATED_LANGKEY = "fr";
@Autowired
private ObjectMapper om;
@Autowired
private UserRepository userRepository;
@Autowired
private UserService userService;
@Autowired
private UserMapper userMapper;
@Autowired
private EntityManager em;
@Autowired
private CacheManager cacheManager;
@Autowired
private MockMvc restUserMockMvc;
private User user;
private Long numberOfUsers;
@BeforeEach
void countUsers() {
numberOfUsers = userRepository.count();
}
/**
* Create a User.
*
* This is a static method, as tests for other entities might also need it,
* if they test an entity which has a required relationship to the User entity.
*/
public static User createEntity() {
User persistUser = new User();
persistUser.setLogin(DEFAULT_LOGIN + RandomStringUtils.insecure().nextAlphabetic(5));
persistUser.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
persistUser.setActivated(true);
persistUser.setEmail(RandomStringUtils.insecure().nextAlphabetic(5) + DEFAULT_EMAIL);
persistUser.setFirstName(DEFAULT_FIRSTNAME);
persistUser.setLastName(DEFAULT_LASTNAME);
persistUser.setImageUrl(DEFAULT_IMAGEURL);
persistUser.setLangKey(DEFAULT_LANGKEY);
return persistUser;
}
/**
* Setups the database with one user.
*/
public static User initTestUser() {
User persistUser = createEntity();
persistUser.setLogin(DEFAULT_LOGIN);
persistUser.setEmail(DEFAULT_EMAIL);
return persistUser;
}
@BeforeEach
void initTest() {
user = initTestUser();
}
@AfterEach
void cleanupAndCheck() {
userService.deleteUser(DEFAULT_LOGIN);
userService.deleteUser(UPDATED_LOGIN);
userService.deleteUser(user.getLogin());
userService.deleteUser("anotherlogin");
assertThat(userRepository.count()).isEqualTo(numberOfUsers);
numberOfUsers = null;
cacheManager
.getCacheNames()
.stream()
.map(cacheName -> this.cacheManager.getCache(cacheName))
.filter(Objects::nonNull)
.forEach(Cache::invalidate);
}
@Test
@Transactional
void createUser() throws Exception {
// Create the User
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setLogin(DEFAULT_LOGIN);
userDTO.setFirstName(DEFAULT_FIRSTNAME);
userDTO.setLastName(DEFAULT_LASTNAME);
userDTO.setEmail(DEFAULT_EMAIL);
userDTO.setActivated(true);
userDTO.setImageUrl(DEFAULT_IMAGEURL);
userDTO.setLangKey(DEFAULT_LANGKEY);
userDTO.setAuthorities(Set.of(AuthoritiesConstants.USER));
var returnedUserDTO = om.readValue(
restUserMockMvc
.perform(post("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isCreated())
.andReturn()
.getResponse()
.getContentAsString(),
AdminUserDTO.class
);
User convertedUser = userMapper.userDTOToUser(returnedUserDTO);
// Validate the returned User
assertThat(convertedUser.getLogin()).isEqualTo(DEFAULT_LOGIN);
assertThat(convertedUser.getFirstName()).isEqualTo(DEFAULT_FIRSTNAME);
assertThat(convertedUser.getLastName()).isEqualTo(DEFAULT_LASTNAME);
assertThat(convertedUser.getEmail()).isEqualTo(DEFAULT_EMAIL);
assertThat(convertedUser.getImageUrl()).isEqualTo(DEFAULT_IMAGEURL);
assertThat(convertedUser.getLangKey()).isEqualTo(DEFAULT_LANGKEY);
}
@Test
@Transactional
void createUserWithExistingId() throws Exception {
int databaseSizeBeforeCreate = userRepository.findAll().size();
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setId(DEFAULT_ID);
userDTO.setLogin(DEFAULT_LOGIN);
userDTO.setFirstName(DEFAULT_FIRSTNAME);
userDTO.setLastName(DEFAULT_LASTNAME);
userDTO.setEmail(DEFAULT_EMAIL);
userDTO.setActivated(true);
userDTO.setImageUrl(DEFAULT_IMAGEURL);
userDTO.setLangKey(DEFAULT_LANGKEY);
userDTO.setAuthorities(Set.of(AuthoritiesConstants.USER));
// An entity with an existing ID cannot be created, so this API call must fail
restUserMockMvc
.perform(post("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isBadRequest());
// Validate the User in the database
assertPersistedUsers(users -> assertThat(users).hasSize(databaseSizeBeforeCreate));
}
@Test
@Transactional
void createUserWithExistingLogin() throws Exception {
// Initialize the database
userRepository.saveAndFlush(user);
int databaseSizeBeforeCreate = userRepository.findAll().size();
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setLogin(DEFAULT_LOGIN); // this login should already be used
userDTO.setFirstName(DEFAULT_FIRSTNAME);
userDTO.setLastName(DEFAULT_LASTNAME);
userDTO.setEmail("anothermail@localhost");
userDTO.setActivated(true);
userDTO.setImageUrl(DEFAULT_IMAGEURL);
userDTO.setLangKey(DEFAULT_LANGKEY);
userDTO.setAuthorities(Set.of(AuthoritiesConstants.USER));
// Create the User
restUserMockMvc
.perform(post("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isBadRequest());
// Validate the User in the database
assertPersistedUsers(users -> assertThat(users).hasSize(databaseSizeBeforeCreate));
}
@Test
@Transactional
void createUserWithExistingEmail() throws Exception {
// Initialize the database
userRepository.saveAndFlush(user);
int databaseSizeBeforeCreate = userRepository.findAll().size();
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setLogin("anotherlogin");
userDTO.setFirstName(DEFAULT_FIRSTNAME);
userDTO.setLastName(DEFAULT_LASTNAME);
userDTO.setEmail(DEFAULT_EMAIL); // this email should already be used
userDTO.setActivated(true);
userDTO.setImageUrl(DEFAULT_IMAGEURL);
userDTO.setLangKey(DEFAULT_LANGKEY);
userDTO.setAuthorities(Set.of(AuthoritiesConstants.USER));
// Create the User
restUserMockMvc
.perform(post("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isBadRequest());
// Validate the User in the database
assertPersistedUsers(users -> assertThat(users).hasSize(databaseSizeBeforeCreate));
}
@Test
@Transactional
void getAllUsers() throws Exception {
// Initialize the database
userRepository.saveAndFlush(user);
// Get all the users
restUserMockMvc
.perform(get("/api/admin/users?sort=id,desc").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.[*].login").value(hasItem(DEFAULT_LOGIN)))
.andExpect(jsonPath("$.[*].firstName").value(hasItem(DEFAULT_FIRSTNAME)))
.andExpect(jsonPath("$.[*].lastName").value(hasItem(DEFAULT_LASTNAME)))
.andExpect(jsonPath("$.[*].email").value(hasItem(DEFAULT_EMAIL)))
.andExpect(jsonPath("$.[*].imageUrl").value(hasItem(DEFAULT_IMAGEURL)))
.andExpect(jsonPath("$.[*].langKey").value(hasItem(DEFAULT_LANGKEY)));
}
@Test
@Transactional
void getUser() throws Exception {
// Initialize the database
userRepository.saveAndFlush(user);
assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin(), User.class)).isNull();
// Get the user
restUserMockMvc
.perform(get("/api/admin/users/{login}", user.getLogin()))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.login").value(user.getLogin()))
.andExpect(jsonPath("$.firstName").value(DEFAULT_FIRSTNAME))
.andExpect(jsonPath("$.lastName").value(DEFAULT_LASTNAME))
.andExpect(jsonPath("$.email").value(DEFAULT_EMAIL))
.andExpect(jsonPath("$.imageUrl").value(DEFAULT_IMAGEURL))
.andExpect(jsonPath("$.langKey").value(DEFAULT_LANGKEY));
assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin(), User.class)).isNotNull();
}
@Test
@Transactional
void getNonExistingUser() throws Exception {
restUserMockMvc.perform(get("/api/admin/users/unknown")).andExpect(status().isNotFound());
}
@Test
@Transactional
void updateUser() throws Exception {
// Initialize the database
userRepository.saveAndFlush(user);
int databaseSizeBeforeUpdate = userRepository.findAll().size();
// Update the user
User updatedUser = userRepository.findById(user.getId()).orElseThrow();
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setId(updatedUser.getId());
userDTO.setLogin(updatedUser.getLogin());
userDTO.setFirstName(UPDATED_FIRSTNAME);
userDTO.setLastName(UPDATED_LASTNAME);
userDTO.setEmail(UPDATED_EMAIL);
userDTO.setActivated(updatedUser.isActivated());
userDTO.setImageUrl(UPDATED_IMAGEURL);
userDTO.setLangKey(UPDATED_LANGKEY);
userDTO.setCreatedBy(updatedUser.getCreatedBy());
userDTO.setCreatedDate(updatedUser.getCreatedDate());
userDTO.setLastModifiedBy(updatedUser.getLastModifiedBy());
userDTO.setLastModifiedDate(updatedUser.getLastModifiedDate());
userDTO.setAuthorities(Set.of(AuthoritiesConstants.USER));
restUserMockMvc
.perform(put("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isOk());
// Validate the User in the database
assertPersistedUsers(users -> {
assertThat(users).hasSize(databaseSizeBeforeUpdate);
User testUser = users
.stream()
.filter(usr -> usr.getId().equals(updatedUser.getId()))
.findFirst()
.orElseThrow();
assertThat(testUser.getFirstName()).isEqualTo(UPDATED_FIRSTNAME);
assertThat(testUser.getLastName()).isEqualTo(UPDATED_LASTNAME);
assertThat(testUser.getEmail()).isEqualTo(UPDATED_EMAIL);
assertThat(testUser.getImageUrl()).isEqualTo(UPDATED_IMAGEURL);
assertThat(testUser.getLangKey()).isEqualTo(UPDATED_LANGKEY);
});
}
@Test
@Transactional
void updateUserLogin() throws Exception {
// Initialize the database
userRepository.saveAndFlush(user);
int databaseSizeBeforeUpdate = userRepository.findAll().size();
// Update the user
User updatedUser = userRepository.findById(user.getId()).orElseThrow();
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setId(updatedUser.getId());
userDTO.setLogin(UPDATED_LOGIN);
userDTO.setFirstName(UPDATED_FIRSTNAME);
userDTO.setLastName(UPDATED_LASTNAME);
userDTO.setEmail(UPDATED_EMAIL);
userDTO.setActivated(updatedUser.isActivated());
userDTO.setImageUrl(UPDATED_IMAGEURL);
userDTO.setLangKey(UPDATED_LANGKEY);
userDTO.setCreatedBy(updatedUser.getCreatedBy());
userDTO.setCreatedDate(updatedUser.getCreatedDate());
userDTO.setLastModifiedBy(updatedUser.getLastModifiedBy());
userDTO.setLastModifiedDate(updatedUser.getLastModifiedDate());
userDTO.setAuthorities(Set.of(AuthoritiesConstants.USER));
restUserMockMvc
.perform(put("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isOk());
// Validate the User in the database
assertPersistedUsers(users -> {
assertThat(users).hasSize(databaseSizeBeforeUpdate);
User testUser = users
.stream()
.filter(usr -> usr.getId().equals(updatedUser.getId()))
.findFirst()
.orElseThrow();
assertThat(testUser.getLogin()).isEqualTo(UPDATED_LOGIN);
assertThat(testUser.getFirstName()).isEqualTo(UPDATED_FIRSTNAME);
assertThat(testUser.getLastName()).isEqualTo(UPDATED_LASTNAME);
assertThat(testUser.getEmail()).isEqualTo(UPDATED_EMAIL);
assertThat(testUser.getImageUrl()).isEqualTo(UPDATED_IMAGEURL);
assertThat(testUser.getLangKey()).isEqualTo(UPDATED_LANGKEY);
});
}
@Test
@Transactional
void updateUserExistingEmail() throws Exception {
// Initialize the database with 2 users
userRepository.saveAndFlush(user);
User anotherUser = new User();
anotherUser.setLogin("jhipster");
anotherUser.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
anotherUser.setActivated(true);
anotherUser.setEmail("jhipster@localhost");
anotherUser.setFirstName("java");
anotherUser.setLastName("hipster");
anotherUser.setImageUrl("");
anotherUser.setLangKey("en");
userRepository.saveAndFlush(anotherUser);
// Update the user
User updatedUser = userRepository.findById(user.getId()).orElseThrow();
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setId(updatedUser.getId());
userDTO.setLogin(updatedUser.getLogin());
userDTO.setFirstName(updatedUser.getFirstName());
userDTO.setLastName(updatedUser.getLastName());
userDTO.setEmail("jhipster@localhost"); // this email should already be used by anotherUser
userDTO.setActivated(updatedUser.isActivated());
userDTO.setImageUrl(updatedUser.getImageUrl());
userDTO.setLangKey(updatedUser.getLangKey());
userDTO.setCreatedBy(updatedUser.getCreatedBy());
userDTO.setCreatedDate(updatedUser.getCreatedDate());
userDTO.setLastModifiedBy(updatedUser.getLastModifiedBy());
userDTO.setLastModifiedDate(updatedUser.getLastModifiedDate());
userDTO.setAuthorities(Set.of(AuthoritiesConstants.USER));
restUserMockMvc
.perform(put("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isBadRequest());
}
@Test
@Transactional
void updateUserExistingLogin() throws Exception {
// Initialize the database
userRepository.saveAndFlush(user);
User anotherUser = new User();
anotherUser.setLogin("jhipster");
anotherUser.setPassword(RandomStringUtils.insecure().nextAlphanumeric(60));
anotherUser.setActivated(true);
anotherUser.setEmail("jhipster@localhost");
anotherUser.setFirstName("java");
anotherUser.setLastName("hipster");
anotherUser.setImageUrl("");
anotherUser.setLangKey("en");
userRepository.saveAndFlush(anotherUser);
// Update the user
User updatedUser = userRepository.findById(user.getId()).orElseThrow();
AdminUserDTO userDTO = new AdminUserDTO();
userDTO.setId(updatedUser.getId());
userDTO.setLogin("jhipster"); // this login should already be used by anotherUser
userDTO.setFirstName(updatedUser.getFirstName());
userDTO.setLastName(updatedUser.getLastName());
userDTO.setEmail(updatedUser.getEmail());
userDTO.setActivated(updatedUser.isActivated());
userDTO.setImageUrl(updatedUser.getImageUrl());
userDTO.setLangKey(updatedUser.getLangKey());
userDTO.setCreatedBy(updatedUser.getCreatedBy());
userDTO.setCreatedDate(updatedUser.getCreatedDate());
userDTO.setLastModifiedBy(updatedUser.getLastModifiedBy());
userDTO.setLastModifiedDate(updatedUser.getLastModifiedDate());
userDTO.setAuthorities(Set.of(AuthoritiesConstants.USER));
restUserMockMvc
.perform(put("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(om.writeValueAsBytes(userDTO)))
.andExpect(status().isBadRequest());
}
@Test
@Transactional
void deleteUser() throws Exception {
// Initialize the database
userRepository.saveAndFlush(user);
int databaseSizeBeforeDelete = userRepository.findAll().size();
// Delete the user
restUserMockMvc
.perform(delete("/api/admin/users/{login}", user.getLogin()).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent());
assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin(), User.class)).isNull();
// Validate the database is empty
assertPersistedUsers(users -> assertThat(users).hasSize(databaseSizeBeforeDelete - 1));
}
@Test
void testUserEquals() throws Exception {
TestUtil.equalsVerifier(User.class);
User user1 = new User();
user1.setId(DEFAULT_ID);
User user2 = new User();
user2.setId(user1.getId());
assertThat(user1).isEqualTo(user2);
user2.setId(2L);
assertThat(user1).isNotEqualTo(user2);
user1.setId(null);
assertThat(user1).isNotEqualTo(user2);
}
private void assertPersistedUsers(Consumer<List<User>> userAssertion) {
userAssertion.accept(userRepository.findAll());
}
}
@@ -0,0 +1,23 @@
package com.sisvietnamvn.web.web.rest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.context.support.WithSecurityContext;
import org.springframework.security.test.context.support.WithSecurityContextFactory;
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithUnauthenticatedMockUser.Factory.class)
public @interface WithUnauthenticatedMockUser {
class Factory implements WithSecurityContextFactory<WithUnauthenticatedMockUser> {
@Override
public SecurityContext createSecurityContext(WithUnauthenticatedMockUser annotation) {
return SecurityContextHolder.createEmptyContext();
}
}
}
@@ -0,0 +1,117 @@
package com.sisvietnamvn.web.web.rest.errors;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.sisvietnamvn.web.IntegrationTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
/**
* Integration tests {@link ExceptionTranslator} controller advice.
*/
@WithMockUser
@AutoConfigureMockMvc
@IntegrationTest
class ExceptionTranslatorIT {
@Autowired
private MockMvc mockMvc;
@Test
void testConcurrencyFailure() throws Exception {
mockMvc
.perform(get("/api/exception-translator-test/concurrency-failure"))
.andExpect(status().isConflict())
.andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
.andExpect(jsonPath("$.message").value(ErrorConstants.ERR_CONCURRENCY_FAILURE));
}
@Test
void testMethodArgumentNotValid() throws Exception {
mockMvc
.perform(post("/api/exception-translator-test/method-argument").content("{}").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
.andExpect(jsonPath("$.message").value(ErrorConstants.ERR_VALIDATION))
.andExpect(jsonPath("$.fieldErrors.[0].objectName").value("test"))
.andExpect(jsonPath("$.fieldErrors.[0].field").value("test"))
.andExpect(jsonPath("$.fieldErrors.[0].message").value("must not be null"));
}
@Test
void testMissingServletRequestPartException() throws Exception {
mockMvc
.perform(get("/api/exception-translator-test/missing-servlet-request-part"))
.andExpect(status().isBadRequest())
.andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
.andExpect(jsonPath("$.message").value("error.http.400"));
}
@Test
void testMissingServletRequestParameterException() throws Exception {
mockMvc
.perform(get("/api/exception-translator-test/missing-servlet-request-parameter"))
.andExpect(status().isBadRequest())
.andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
.andExpect(jsonPath("$.message").value("error.http.400"));
}
@Test
void testAccessDenied() throws Exception {
mockMvc
.perform(get("/api/exception-translator-test/access-denied"))
.andExpect(status().isForbidden())
.andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
.andExpect(jsonPath("$.message").value("error.http.403"))
.andExpect(jsonPath("$.detail").value("test access denied!"));
}
@Test
void testUnauthorized() throws Exception {
mockMvc
.perform(get("/api/exception-translator-test/unauthorized"))
.andExpect(status().isUnauthorized())
.andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
.andExpect(jsonPath("$.message").value("error.http.401"))
.andExpect(jsonPath("$.path").value("/api/exception-translator-test/unauthorized"))
.andExpect(jsonPath("$.detail").value("test authentication failed!"));
}
@Test
void testMethodNotSupported() throws Exception {
mockMvc
.perform(post("/api/exception-translator-test/access-denied"))
.andExpect(status().isMethodNotAllowed())
.andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
.andExpect(jsonPath("$.message").value("error.http.405"))
.andExpect(jsonPath("$.detail").value("Request method 'POST' is not supported"));
}
@Test
void testExceptionWithResponseStatus() throws Exception {
mockMvc
.perform(get("/api/exception-translator-test/response-status"))
.andExpect(status().isBadRequest())
.andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
.andExpect(jsonPath("$.message").value("error.http.400"))
.andExpect(jsonPath("$.title").value("test response status"));
}
@Test
void testInternalServerError() throws Exception {
mockMvc
.perform(get("/api/exception-translator-test/internal-server-error"))
.andExpect(status().isInternalServerError())
.andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
.andExpect(jsonPath("$.message").value("error.http.500"))
.andExpect(jsonPath("$.title").value("Internal Server Error"));
}
}
@@ -0,0 +1,72 @@
package com.sisvietnamvn.web.web.rest.errors;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/exception-translator-test")
public class ExceptionTranslatorTestController {
@GetMapping("/concurrency-failure")
public void concurrencyFailure() {
throw new ConcurrencyFailureException("test concurrency failure");
}
@PostMapping("/method-argument")
public void methodArgument(@Valid @RequestBody TestDTO testDTO) {
// empty method
}
@GetMapping("/missing-servlet-request-part")
public void missingServletRequestPartException(@RequestPart("part") String part) {
// empty method
}
@GetMapping("/missing-servlet-request-parameter")
public void missingServletRequestParameterException(@RequestParam("param") String param) {
// empty method
}
@GetMapping("/access-denied")
public void accessdenied() {
throw new AccessDeniedException("test access denied!");
}
@GetMapping("/unauthorized")
public void unauthorized() {
throw new BadCredentialsException("test authentication failed!");
}
@GetMapping("/response-status")
public void exceptionWithResponseStatus() {
throw new TestResponseStatusException();
}
@GetMapping("/internal-server-error")
public void internalServerError() {
throw new RuntimeException();
}
public static class TestDTO {
@NotNull
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "test response status")
@SuppressWarnings("serial")
public static class TestResponseStatusException extends RuntimeException {}
}
@@ -0,0 +1,2 @@
org.springframework.test.context.ContextCustomizerFactory = \
com.sisvietnamvn.web.config.SqlTestContainersSpringContextCustomizerFactory
@@ -0,0 +1,33 @@
# ===================================================================
# Spring Boot configuration.
#
# This configuration is used for unit/integration tests with testcontainers database containers.
#
# To activate this configuration launch integration tests with the 'testcontainers' profile
#
# More information on database containers: https://www.testcontainers.org/modules/databases/
# ===================================================================
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:h2:file:./build/h2db/testdb/sisvietnamvn;DB_CLOSE_DELAY=-1
username: sisvietnamvn
password:
hikari:
auto-commit: false
jpa:
open-in-view: false
hibernate:
ddl-auto: none
properties:
hibernate.id.new_generator_mappings: true
hibernate.connection.provider_disables_autocommit: true
hibernate.cache.use_second_level_cache: false
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: false
hibernate.hbm2ddl.auto: none #TODO: temp relief for integration tests, revisit required
hibernate.type.preferred_instant_jdbc_type: TIMESTAMP
hibernate.jdbc.time_zone: UTC
hibernate.timezone.default_storage: NORMALIZE
hibernate.query.fail_on_pagination_over_collection_fetch: true
@@ -0,0 +1,35 @@
# ===================================================================
# Spring Boot configuration.
#
# This configuration is used for unit/integration tests with testcontainers database containers.
#
# To activate this configuration launch integration tests with the 'testcontainers' profile
#
# More information on database containers: https://www.testcontainers.org/modules/databases/
#
# You have to specify an Oracle image name in a classpath file named testcontainers.properties.
# Follow the instructions at https://www.testcontainers.org/modules/databases/oraclexe/
# ===================================================================
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
hikari:
poolName: Hikari
auto-commit: false
maximum-pool-size: 1
jpa:
open-in-view: false
hibernate:
ddl-auto: none
properties:
hibernate.id.new_generator_mappings: true
hibernate.connection.provider_disables_autocommit: true
hibernate.cache.use_second_level_cache: false
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: false
hibernate.hbm2ddl.auto: none #TODO: temp relief for integration tests, revisit required
hibernate.type.preferred_instant_jdbc_type: TIMESTAMP
hibernate.jdbc.time_zone: UTC
hibernate.timezone.default_storage: NORMALIZE
hibernate.query.fail_on_pagination_over_collection_fetch: true
@@ -0,0 +1,94 @@
# ===================================================================
# Spring Boot configuration.
#
# This configuration is used for unit/integration tests.
#
# More information on profiles: https://www.jhipster.tech/profiles/
# More information on configuration properties: https://www.jhipster.tech/common-application-properties/
# ===================================================================
# ===================================================================
# Standard Spring Boot properties.
# Full reference is available at:
# https://docs.spring.io/spring-boot/appendix/application-properties/index.html
# ===================================================================
spring:
application:
name: sisvietnamvn
# Replace by 'prod, faker' to add the faker context and have sample data loaded in production
liquibase:
contexts: test
jackson2:
serialization:
write-durations-as-timestamps: false
mail:
host: localhost
main:
allow-bean-definition-overriding: true
messages:
basename: i18n/messages
security:
oauth2:
resourceserver:
jwt:
authority-prefix: ''
authorities-claim-name: auth
task:
execution:
thread-name-prefix: sisvietnamvn-task-
pool:
core-size: 1
max-size: 50
queue-capacity: 10000
scheduling:
thread-name-prefix: sisvietnamvn-scheduling-
pool:
size: 20
thymeleaf:
mode: HTML
server:
port: 10344
address: localhost
# ===================================================================
# JHipster specific properties
#
# Full reference is available at: https://www.jhipster.tech/common-application-properties/
# ===================================================================
jhipster:
mail:
from: sisvietnamvn@localhost.com
base-url: http://127.0.0.1:8080
logging:
# To test json console appender
use-json-format: false
logstash:
enabled: false
host: localhost
port: 5000
ring-buffer-size: 512
security:
authentication:
jwt:
# This token must be encoded using Base64 (you can type `echo 'secret-key'|base64` on your command line)
base64-secret: ZWRjOTBhYjJiYzg5N2JhMTUyMTBiYzZhNzJjMWUwNmYzOTM3MjJmZGE3Zjc3NGE0ODhjMDZlYzRjNmFiODFhMDNkNmFjNmYwYzY4NmMwNGRkMDMwY2Q0NTllNDlhYzEyMjg0NmY2NzM5MWM1M2IxOTdkMDNhZTBhZDkzYWJjMWI=
# Token is valid 24 hours
token-validity-in-seconds: 86400
token-validity-in-seconds-for-remember-me: 86400
# ===================================================================
# Application specific properties
# Add your own application properties here, see the ApplicationProperties class
# to have type-safe configuration, like in the JHipsterProperties above
#
# More documentation is available at:
# https://www.jhipster.tech/common-application-properties/
# ===================================================================
# application:
management:
health:
mail:
enabled: false
@@ -0,0 +1,4 @@
email.test.title=test title
# Value used for English locale unit test in MailServiceIT
# as this file is loaded instead of real file
email.activation.title=sisvietnamvn account activation
@@ -0,0 +1,4 @@
junit.jupiter.execution.timeout.default = 15 s
junit.jupiter.execution.timeout.testable.method.default = 15 s
junit.jupiter.execution.timeout.beforeall.method.default = 60 s
junit.jupiter.testclass.order.default=com.sisvietnamvn.web.config.SpringBootTestClassOrderer
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration scan="true">
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="com.sisvietnamvn.web" level="INFO"/>
<logger name="tech.jhipster" level="WARN"/>
<!-- https://www.testcontainers.org/supported_docker_environment/logging_config/ -->
<logger name="org.testcontainers" level="INFO"/>
<logger name="com.github.dockerjava" level="WARN"/>
<logger name="jakarta.activation" level="WARN"/>
<logger name="jakarta.mail" level="WARN"/>
<logger name="jakarta.xml.bind" level="WARN"/>
<logger name="ch.qos.logback" level="WARN"/>
<logger name="com.jayway.jsonpath" level="WARN"/>
<logger name="com.ryantenney" level="WARN"/>
<logger name="com.sun" level="WARN"/>
<logger name="com.zaxxer" level="WARN"/>
<logger name="org.ehcache" level="WARN"/>
<logger name="org.apache" level="WARN"/>
<logger name="org.apache.catalina.startup.DigesterFactory" level="OFF"/>
<logger name="org.bson" level="WARN"/>
<logger name="org.hibernate.validator" level="WARN"/>
<logger name="org.hibernate" level="WARN"/>
<logger name="org.hibernate.ejb.HibernatePersistence" level="OFF"/>
<logger name="org.springframework" level="WARN"/>
<logger name="org.springframework.web" level="WARN"/>
<logger name="org.springframework.security" level="WARN"/>
<logger name="org.springframework.cache" level="WARN"/>
<logger name="org.thymeleaf" level="WARN"/>
<logger name="org.xnio" level="WARN"/>
<logger name="io.swagger.v3" level="INFO"/>
<logger name="sun.rmi" level="WARN"/>
<logger name="sun.rmi.transport" level="WARN"/>
<logger name="com.tngtech.archunit.core.importer" level="ERROR"/>
<logger name="org.hibernate.orm.incubating" level="ERROR"/>
<logger name="liquibase" level="WARN"/>
<logger name="LiquibaseSchemaResolver" level="INFO"/>
<!-- jhipster-needle-logback-add-log - JHipster will add a new log with level -->
</configuration>
@@ -0,0 +1,19 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org" th:lang="${#locale.language}" lang="en">
<head>
<title th:text="#{email.activation.title}">JHipster activation</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="#{email.activation.greeting(${user.login})}">Dear</p>
<p th:text="#{email.activation.text1}">Your JHipster account has been created, please click on the URL below to activate it:</p>
<p>
<a th:with="url=(@{|${baseUrl}/account/activate?key=${user.activationKey}|})" th:href="${url}" th:text="${url}">Activation link</a>
</p>
<p>
<span th:text="#{email.activation.text2}">Regards, </span>
<br />
<em th:text="#{email.signature}">JHipster.</em>
</p>
</body>
</html>
@@ -0,0 +1,19 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org" th:lang="${#locale.language}" lang="en">
<head>
<title th:text="#{email.activation.title}">JHipster creation</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="#{email.activation.greeting(${user.login})}">Dear</p>
<p th:text="#{email.creation.text1}">Your JHipster account has been created, please click on the URL below to access it:</p>
<p>
<a th:with="url=(@{|${baseUrl}/account/reset/finish?key=${user.resetKey}|})" th:href="${url}" th:text="${url}">Login link</a>
</p>
<p>
<span th:text="#{email.activation.text2}">Regards, </span>
<br />
<em th:text="#{email.signature}">JHipster.</em>
</p>
</body>
</html>
@@ -0,0 +1,21 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org" th:lang="${#locale.language}" lang="en">
<head>
<title th:text="#{email.reset.title}">JHipster password reset</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="#{email.reset.greeting(${user.login})}">Dear</p>
<p th:text="#{email.reset.text1}">
For your JHipster account a password reset was requested, please click on the URL below to reset it:
</p>
<p>
<a th:with="url=(@{|${baseUrl}/account/reset/finish?key=${user.resetKey}|})" th:href="${url}" th:text="${url}">Login link</a>
</p>
<p>
<span th:text="#{email.reset.text2}">Regards, </span>
<br />
<em th:text="#{email.signature}">JHipster.</em>
</p>
</body>
</html>
@@ -0,0 +1 @@
<html xmlns:th="http://www.thymeleaf.org" th:text="|#{email.test.title}, ${baseUrl}, ${user.login}|"></html>