Merge branch 'eugenp:master' into JAVA-18149
This commit is contained in:
commit
3eb4c1f078
|
@ -67,6 +67,8 @@ ethereum/logs/
|
|||
jmeter/src/main/resources/*-JMeter.csv
|
||||
jmeter/src/main/resources/*-Basic*.csv
|
||||
jmeter/src/main/resources/*-JMeter*.csv
|
||||
jmeter/src/main/resources/*ReportsDashboard*.csv
|
||||
jmeter/src/main/resources/dashboard/*ReportsDashboard*.csv
|
||||
|
||||
ninja/devDb.mv.db
|
||||
|
||||
|
|
|
@ -80,16 +80,20 @@ public class BaeldungIntegrationTest {
|
|||
private void marshalCourseRepo(CourseRepo courseRepo) throws Exception {
|
||||
AegisWriter<XMLStreamWriter> writer = context.createXMLStreamWriter();
|
||||
AegisType aegisType = context.getTypeMapping().getType(CourseRepo.class);
|
||||
XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(new FileOutputStream(fileName));
|
||||
final FileOutputStream stream = new FileOutputStream(fileName);
|
||||
XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(stream);
|
||||
writer.write(courseRepo, new QName("http://aegis.cxf.baeldung.com", "baeldung"), false, xmlWriter, aegisType);
|
||||
xmlWriter.close();
|
||||
stream.close();
|
||||
}
|
||||
|
||||
private CourseRepo unmarshalCourseRepo() throws Exception {
|
||||
AegisReader<XMLStreamReader> reader = context.createXMLStreamReader();
|
||||
XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(new FileInputStream(fileName));
|
||||
final FileInputStream stream = new FileInputStream(fileName);
|
||||
XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(stream);
|
||||
CourseRepo courseRepo = (CourseRepo) reader.read(xmlReader, context.getTypeMapping().getType(CourseRepo.class));
|
||||
xmlReader.close();
|
||||
stream.close();
|
||||
return courseRepo;
|
||||
}
|
||||
|
||||
|
@ -97,7 +101,7 @@ public class BaeldungIntegrationTest {
|
|||
public void cleanup(){
|
||||
File testFile = new File(fileName);
|
||||
if (testFile.exists()) {
|
||||
testFile.delete();
|
||||
testFile.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -63,6 +63,7 @@
|
|||
<org.apache.httpcomponents.version>4.5.2</org.apache.httpcomponents.version>
|
||||
<velocity-version>1.7</velocity-version>
|
||||
<velocity-tools-version>2.0</velocity-tools-version>
|
||||
<maven-war-plugin.version>3.3.2</maven-war-plugin.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -62,10 +62,10 @@
|
|||
</build>
|
||||
|
||||
<properties>
|
||||
<asciidoctor-maven-plugin.version>1.5.6</asciidoctor-maven-plugin.version>
|
||||
<asciidoctorj.version>1.5.6</asciidoctorj.version>
|
||||
<asciidoctorj-pdf.version>1.5.0-alpha.15</asciidoctorj-pdf.version>
|
||||
<asciidoctorj-pdf.plugin.version>1.5.0-alpha.15</asciidoctorj-pdf.plugin.version>
|
||||
<asciidoctor-maven-plugin.version>2.2.2</asciidoctor-maven-plugin.version>
|
||||
<asciidoctorj.version>2.5.7</asciidoctorj.version>
|
||||
<asciidoctorj-pdf.version>2.3.4</asciidoctorj-pdf.version>
|
||||
<asciidoctorj-pdf.plugin.version>2.3.4</asciidoctorj-pdf.plugin.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -77,6 +77,7 @@
|
|||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -92,6 +93,7 @@
|
|||
<properties>
|
||||
<spring.version>2.2.1.RELEASE</spring.version>
|
||||
<awssdk.version>2.17.283</awssdk.version>
|
||||
<lombok.version>1.18.20</lombok.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,19 @@
|
|||
package com.baeldung.argsVsvarargs;
|
||||
|
||||
public class StringArrayAndVarargs {
|
||||
public static void capitalizeNames(String[] args) {
|
||||
for(int i = 0; i < args.length; i++){
|
||||
args[i] = args[i].toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String[] firstLetterOfWords(String... args) {
|
||||
String[] firstLetter = new String[args.length];
|
||||
for(int i = 0; i < args.length; i++){
|
||||
firstLetter[i] = String.valueOf(args[i].charAt(0));
|
||||
}
|
||||
return firstLetter;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.baeldung.argsVsvarargs;
|
||||
|
||||
import static com.baeldung.argsVsvarargs.StringArrayAndVarargs.capitalizeNames;
|
||||
import static com.baeldung.argsVsvarargs.StringArrayAndVarargs.firstLetterOfWords;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class StringArrayAndVarargsUnitTest {
|
||||
|
||||
@Test
|
||||
void whenCheckingArgumentClassName_thenNameShouldBeStringArray() {
|
||||
String[] names = {"john", "ade", "kofi", "imo"};
|
||||
assertNotNull(names);
|
||||
assertEquals("java.lang.String[]", names.getClass().getTypeName());
|
||||
capitalizeNames(names);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenCheckingReturnedObjectClass_thenClassShouldBeStringArray() {
|
||||
assertEquals(String[].class, firstLetterOfWords("football", "basketball", "volleyball").getClass());
|
||||
assertEquals(3, firstLetterOfWords("football", "basketball", "volleyball").length);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package com.baeldung.array.isobjectarray;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class CheckObjectIsArrayUnitTest {
|
||||
private static final Object ARRAY_INT = new int[] { 1, 2, 3, 4, 5 };
|
||||
private static final Object ARRAY_PERSON = new Person[] { new Person("Jackie Chan", "Hong Kong"), new Person("Tom Hanks", "United States") };
|
||||
|
||||
boolean isArray(Object obj) {
|
||||
return obj instanceof Object[] || obj instanceof boolean[] || obj instanceof byte[] || obj instanceof short[] || obj instanceof char[] || obj instanceof int[] || obj instanceof long[] || obj instanceof float[] || obj instanceof double[];
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAnArrayObject_whenUsingInstanceof_getExpectedResult() {
|
||||
assertTrue(ARRAY_PERSON instanceof Object[]);
|
||||
assertFalse(ARRAY_INT instanceof Object[]);
|
||||
assertTrue(ARRAY_INT instanceof int[]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAnArrayObject_whenUsingOurIsArray_getExpectedResult() {
|
||||
assertTrue(isArray(ARRAY_PERSON));
|
||||
assertTrue(isArray(ARRAY_INT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAnArrayObject_whenUsingClassIsArray_getExpectedResult() {
|
||||
assertTrue(ARRAY_INT.getClass()
|
||||
.isArray());
|
||||
assertTrue(ARRAY_PERSON.getClass()
|
||||
.isArray());
|
||||
|
||||
assertEquals(Person.class, ARRAY_PERSON.getClass()
|
||||
.getComponentType());
|
||||
assertEquals(int.class, ARRAY_INT.getClass()
|
||||
.getComponentType());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAnArrayObject_whenUsingArrayGet_getExpectedElement() {
|
||||
if (ARRAY_PERSON.getClass()
|
||||
.isArray() && ARRAY_PERSON.getClass()
|
||||
.getComponentType() == Person.class) {
|
||||
Person person = (Person) Array.get(ARRAY_PERSON, 1);
|
||||
assertEquals("Tom Hanks", person.getName());
|
||||
}
|
||||
if (ARRAY_INT.getClass()
|
||||
.isArray() && ARRAY_INT.getClass()
|
||||
.getComponentType() == int.class) {
|
||||
assertEquals(2, ((int) Array.get(ARRAY_INT, 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Person {
|
||||
private String name;
|
||||
private String Location;
|
||||
|
||||
public Person(String name, String location) {
|
||||
this.name = name;
|
||||
this.Location = location;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return Location;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package com.baeldung.concurrent.atomic;
|
||||
|
||||
public class SafeCounterWithLock {
|
||||
private volatile int counter;
|
||||
private int counter;
|
||||
|
||||
int getValue() {
|
||||
return counter;
|
||||
|
|
|
@ -10,12 +10,6 @@ public class SafeCounterWithoutLock {
|
|||
}
|
||||
|
||||
void increment() {
|
||||
while(true) {
|
||||
int existingValue = getValue();
|
||||
int newValue = existingValue + 1;
|
||||
if(counter.compareAndSet(existingValue, newValue)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ public class ThreadSafeCounterIntegrationTest {
|
|||
SafeCounterWithLock safeCounter = new SafeCounterWithLock();
|
||||
|
||||
IntStream.range(0, 1000)
|
||||
.forEach(count -> service.submit(safeCounter::increment));
|
||||
service.awaitTermination(100, TimeUnit.MILLISECONDS);
|
||||
.forEach(count -> service.execute(safeCounter::increment));
|
||||
shutdownAndAwaitTermination(service);
|
||||
|
||||
assertEquals(1000, safeCounter.getValue());
|
||||
}
|
||||
|
@ -29,10 +29,30 @@ public class ThreadSafeCounterIntegrationTest {
|
|||
SafeCounterWithoutLock safeCounter = new SafeCounterWithoutLock();
|
||||
|
||||
IntStream.range(0, 1000)
|
||||
.forEach(count -> service.submit(safeCounter::increment));
|
||||
service.awaitTermination(100, TimeUnit.MILLISECONDS);
|
||||
.forEach(count -> service.execute(safeCounter::increment));
|
||||
shutdownAndAwaitTermination(service);
|
||||
|
||||
assertEquals(1000, safeCounter.getValue());
|
||||
}
|
||||
|
||||
private void shutdownAndAwaitTermination(ExecutorService pool) {
|
||||
// Disable new tasks from being submitted
|
||||
pool.shutdown();
|
||||
try {
|
||||
// Wait a while for existing tasks to terminate
|
||||
if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS)) {
|
||||
// Cancel currently executing tasks forcefully
|
||||
pool.shutdownNow();
|
||||
// Wait a while for tasks to respond to being cancelled
|
||||
if (!pool.awaitTermination(100, TimeUnit.MILLISECONDS))
|
||||
System.err.println("Pool did not terminate");
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
// (Re-)Cancel if current thread also interrupted
|
||||
pool.shutdownNow();
|
||||
// Preserve interrupt status
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>core-java-hex</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<name>core-java-hex</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.core-java-modules</groupId>
|
||||
<artifactId>core-java-modules</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,25 @@
|
|||
package com.baeldung.hextorgb;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class HexToRgbUnitTest {
|
||||
|
||||
@Test
|
||||
public void givenHexCode_whenConvertedToRgb_thenCorrectRgbValuesAreReturned() {
|
||||
String hexCode = "FF9933";
|
||||
int red = 255;
|
||||
int green = 153;
|
||||
int blue = 51;
|
||||
|
||||
int resultRed = Integer.valueOf(hexCode.substring(0, 2), 16);
|
||||
int resultGreen = Integer.valueOf(hexCode.substring(2, 4), 16);
|
||||
int resultBlue = Integer.valueOf(hexCode.substring(4, 6), 16);
|
||||
|
||||
assertEquals(red, resultRed);
|
||||
assertEquals(green, resultGreen);
|
||||
assertEquals(blue, resultBlue);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.baeldung.firstocurrenceofaninteger;
|
||||
|
||||
public class FirstOccurrenceOfAnInteger {
|
||||
|
||||
static Integer findFirstInteger(String s) {
|
||||
int i = 0;
|
||||
while (i < s.length() && !Character.isDigit(s.charAt(i))) {
|
||||
i++;
|
||||
}
|
||||
int j = i;
|
||||
while (j < s.length() && Character.isDigit(s.charAt(j))) {
|
||||
j++;
|
||||
}
|
||||
return Integer.parseInt(s.substring(i, j));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.baeldung.firstocurrenceofaninteger;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class FirstOccurrenceOfAnIntegerUnitTest {
|
||||
|
||||
@Test
|
||||
void whenUsingPatternMatcher_findFirstInteger() {
|
||||
String s = "ba31dung123";
|
||||
Matcher matcher = Pattern.compile("\\d+").matcher(s);
|
||||
matcher.find();
|
||||
int i = Integer.parseInt(matcher.group());
|
||||
Assertions.assertEquals(31, i);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenUsingScanner_findFirstInteger() {
|
||||
int i = new Scanner("ba31dung123").useDelimiter("\\D+").nextInt();
|
||||
Assertions.assertEquals(31, i);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenUsingSplit_findFirstInteger() {
|
||||
String str = "ba31dung123";
|
||||
List<String> tokens = Arrays.stream(str.split("\\D+"))
|
||||
.filter(s -> s.length() > 0).collect(Collectors.toList());
|
||||
Assertions.assertEquals(31, Integer.parseInt(tokens.get(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenUsingCustomMethod_findFirstInteger() {
|
||||
String str = "ba31dung123";
|
||||
Integer i = FirstOccurrenceOfAnInteger.findFirstInteger(str);
|
||||
Assertions.assertEquals(31, i);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -62,6 +62,7 @@
|
|||
<module>core-java-exceptions-4</module>
|
||||
<module>core-java-function</module>
|
||||
<module>core-java-functional</module>
|
||||
<module>core-java-hex</module>
|
||||
<module>core-java-io</module>
|
||||
<module>core-java-io-2</module>
|
||||
<module>core-java-io-3</module>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<guice.version>4.1.0</guice.version>
|
||||
<guice.version>5.0.1</guice.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -136,7 +136,7 @@
|
|||
<resultsDirectory>${project.basedir}/src/main/resources/dashboard</resultsDirectory>
|
||||
<generateReports>true</generateReports>
|
||||
<ignoreResultFailures>true</ignoreResultFailures>
|
||||
<testResultsTimestamp>false</testResultsTimestamp>
|
||||
<testResultsTimestamp>true</testResultsTimestamp>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>rethinkdb</artifactId>
|
||||
<name>rethinkdb</name>
|
||||
<description>Code snippets for RethinkDB articles</description>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.rethinkdb</groupId>
|
||||
<artifactId>rethinkdb-driver</artifactId>
|
||||
<version>2.4.4</version>
|
||||
</dependency>
|
||||
<!-- utils -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,62 @@
|
|||
package com.baeldung.rethinkdb;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.rethinkdb.RethinkDB.r;
|
||||
|
||||
/**
|
||||
* Some tests demonstrating inserting data.
|
||||
*/
|
||||
public class InsertIntegrationTest extends TestBase {
|
||||
/**
|
||||
* Create a table for the tests.
|
||||
*/
|
||||
@BeforeEach
|
||||
public void createTable() {
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a single simple record into the database.
|
||||
*/
|
||||
@Test
|
||||
public void insertSimpleRecord() {
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.insert(
|
||||
r.hashMap()
|
||||
.with("name", "Baeldung")
|
||||
)
|
||||
.run(conn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertMap() {
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.insert(
|
||||
Map.of("name", "Baeldung")
|
||||
)
|
||||
.run(conn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertComplex() {
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.insert(
|
||||
r.hashMap()
|
||||
.with("name", "Baeldung")
|
||||
.with("articles", r.array(
|
||||
r.hashMap()
|
||||
.with("name", "String Interpolation in Java")
|
||||
.with("url", "https://www.baeldung.com/java-string-interpolation"),
|
||||
r.hashMap()
|
||||
.with("name", "Access HTTPS REST Service Using Spring RestTemplate")
|
||||
.with("url", "https://www.baeldung.com/spring-resttemplate-secure-https-service")
|
||||
)
|
||||
)
|
||||
)
|
||||
.run(conn);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package com.baeldung.rethinkdb;
|
||||
|
||||
import com.rethinkdb.net.Result;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.rethinkdb.RethinkDB.r;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Some tests demonstrating querying data.
|
||||
*/
|
||||
public class QueryIntegrationTest extends TestBase {
|
||||
/**
|
||||
* Create a table for the tests.
|
||||
*/
|
||||
@BeforeEach
|
||||
public void createTable() {
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.insert(
|
||||
r.hashMap()
|
||||
.with("id", "article1")
|
||||
.with("name", "String Interpolation in Java")
|
||||
.with("url", "https://www.baeldung.com/java-string-interpolation")
|
||||
).run(conn);
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.insert(
|
||||
r.hashMap()
|
||||
.with("id", "article2")
|
||||
.with("name", "Access HTTPS REST Service Using Spring RestTemplate")
|
||||
.with("url", "https://www.baeldung.com/spring-resttemplate-secure-https-service")
|
||||
).run(conn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listAll() {
|
||||
Result<Map> results = r.db(DB_NAME).table(tableName).run(conn, Map.class);
|
||||
|
||||
// We can't ensure the order the results come back in.
|
||||
Set<String> expected = new HashSet<>(Set.of(
|
||||
"String Interpolation in Java",
|
||||
"Access HTTPS REST Service Using Spring RestTemplate"
|
||||
));
|
||||
|
||||
for (Map result : results) {
|
||||
assertTrue(expected.remove(result.get("name")));
|
||||
}
|
||||
|
||||
assertTrue(expected.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listSome() {
|
||||
Result<Map> results = r.db(DB_NAME)
|
||||
.table(tableName)
|
||||
.filter(r -> r.g("name").eq("String Interpolation in Java"))
|
||||
.run(conn, Map.class);
|
||||
|
||||
// We can't ensure the order the results come back in.
|
||||
Set<String> expected = Set.of("https://www.baeldung.com/java-string-interpolation");
|
||||
|
||||
assertEquals(expected, results.stream()
|
||||
.map(r -> r.get("url"))
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getByKey() {
|
||||
Result<Map> results = r.db(DB_NAME).table(tableName).get("article1").run(conn, Map.class);
|
||||
|
||||
assertEquals("String Interpolation in Java", results.first().get("name"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package com.baeldung.rethinkdb;
|
||||
|
||||
import com.rethinkdb.net.Result;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.rethinkdb.RethinkDB.r;
|
||||
|
||||
/**
|
||||
* Some tests demonstrating streaming live changes to data.
|
||||
*/
|
||||
public class StreamingIntegrationTest extends TestBase {
|
||||
@Test
|
||||
public void getLiveInserts() throws InterruptedException {
|
||||
ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
|
||||
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", 0)).run(conn);
|
||||
|
||||
executorService.submit(() -> {
|
||||
Result<Map> cursor = r.db(DB_NAME).table(tableName).changes().run(conn, Map.class);
|
||||
|
||||
cursor.stream().forEach(record -> System.out.println("Record: " + record));
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", i)).run(conn);
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
}
|
||||
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSomeLiveInserts() throws InterruptedException {
|
||||
ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
|
||||
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", 0)).run(conn);
|
||||
|
||||
executorService.submit(() -> {
|
||||
Result<Map> cursor = r.db(DB_NAME).table(tableName)
|
||||
.filter(r -> r.g("index").eq(5))
|
||||
.changes()
|
||||
.run(conn, Map.class);
|
||||
|
||||
cursor.stream().forEach(record -> System.out.println("Record: " + record));
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", i)).run(conn);
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
}
|
||||
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLiveUpdates() throws InterruptedException {
|
||||
ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
|
||||
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", 0)).run(conn);
|
||||
|
||||
executorService.submit(() -> {
|
||||
Result<Map> cursor = r.db(DB_NAME).table(tableName).changes().run(conn, Map.class);
|
||||
|
||||
cursor.stream().forEach(record -> System.out.println("Record: " + record));
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
r.db(DB_NAME).table(tableName).update(r.hashMap().with("index", i)).run(conn);
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
}
|
||||
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLiveDeletes() throws InterruptedException {
|
||||
ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
r.db(DB_NAME).table(tableName).insert(r.hashMap().with("index", i)).run(conn);
|
||||
}
|
||||
|
||||
executorService.submit(() -> {
|
||||
Result<Map> cursor = r.db(DB_NAME).table(tableName).changes().run(conn, Map.class);
|
||||
|
||||
cursor.stream().forEach(record -> System.out.println("Record: " + record));
|
||||
});
|
||||
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.filter(r -> r.g("index").eq(1))
|
||||
.delete()
|
||||
.run(conn);
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.filter(r -> r.g("index").eq(3))
|
||||
.delete()
|
||||
.run(conn);
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.filter(r -> r.g("index").eq(5))
|
||||
.delete()
|
||||
.run(conn);
|
||||
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package com.baeldung.rethinkdb;
|
||||
|
||||
import com.rethinkdb.gen.exc.ReqlOpFailedError;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.rethinkdb.RethinkDB.r;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Some tests demonstrating working with tables.
|
||||
*/
|
||||
public class TablesIntegrationTest extends TestBase {
|
||||
|
||||
@Test
|
||||
public void createTable() {
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTableTwice() {
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
|
||||
Assertions.assertThrows(ReqlOpFailedError.class, () -> {
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listTables() {
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
|
||||
List<String> tables = r.db(DB_NAME).tableList().run(conn, List.class).first();
|
||||
|
||||
assertTrue(tables.contains(tableName));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.baeldung.rethinkdb;
|
||||
|
||||
import com.rethinkdb.net.Connection;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.rethinkdb.RethinkDB.r;
|
||||
|
||||
/**
|
||||
* Base class for RethinkDB tests.
|
||||
*/
|
||||
public class TestBase {
|
||||
/** The database name to work with */
|
||||
protected static final String DB_NAME = "test";
|
||||
|
||||
/** A randomly generated table name so they never collide */
|
||||
protected final String tableName = UUID.randomUUID().toString().replaceAll("-","");
|
||||
|
||||
/** A database connection */
|
||||
protected Connection conn;
|
||||
|
||||
/**
|
||||
* Connect to the database for each test
|
||||
*/
|
||||
@BeforeEach
|
||||
public void connect() {
|
||||
conn = r.connection()
|
||||
.hostname("localhost")
|
||||
.port(28015)
|
||||
.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the database after each test
|
||||
*/
|
||||
@AfterEach
|
||||
public void disconnect() {
|
||||
if (this.conn != null) {
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package com.baeldung.rethinkdb;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.rethinkdb.RethinkDB.r;
|
||||
|
||||
/**
|
||||
* Some tests demonstrating updating data.
|
||||
*/
|
||||
public class UpdateIntegrationTest extends TestBase {
|
||||
/**
|
||||
* Create a table for the tests.
|
||||
*/
|
||||
@BeforeEach
|
||||
public void createTable() {
|
||||
r.db(DB_NAME).tableCreate(tableName).run(conn);
|
||||
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.insert(
|
||||
r.hashMap()
|
||||
.with("id", "article1")
|
||||
.with("name", "String Interpolation in Java")
|
||||
.with("url", "https://www.baeldung.com/java-string-interpolation")
|
||||
).run(conn);
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.insert(
|
||||
r.hashMap()
|
||||
.with("id", "article2")
|
||||
.with("name", "Access HTTPS REST Service Using Spring RestTemplate")
|
||||
.with("url", "https://www.baeldung.com/spring-resttemplate-secure-https-service")
|
||||
).run(conn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateAll() {
|
||||
r.db(DB_NAME).table(tableName).update(r.hashMap().with("site", "Baeldung")).run(conn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSome() {
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.filter(r -> r.g("name").eq("String Interpolation in Java"))
|
||||
.update(r.hashMap().with("category", "java"))
|
||||
.run(conn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete() {
|
||||
r.db(DB_NAME).table(tableName)
|
||||
.filter(r -> r.g("name").eq("String Interpolation in Java"))
|
||||
.delete()
|
||||
.run(conn);
|
||||
}
|
||||
}
|
|
@ -4,8 +4,6 @@ import org.bson.BsonBinary;
|
|||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.mongodb.client.vault.ClientEncryption;
|
||||
|
||||
@Configuration
|
||||
public class EncryptionConfig {
|
||||
|
||||
|
@ -21,18 +19,8 @@ public class EncryptionConfig {
|
|||
@Value("${com.baeldung.csfle.auto-decryption:false}")
|
||||
private Boolean autoDecryption;
|
||||
|
||||
private ClientEncryption encryption;
|
||||
|
||||
private BsonBinary dataKeyId;
|
||||
|
||||
public void setEncryption(ClientEncryption encryption) {
|
||||
this.encryption = encryption;
|
||||
}
|
||||
|
||||
public ClientEncryption getEncryption() {
|
||||
return encryption;
|
||||
}
|
||||
|
||||
public void setDataKeyId(BsonBinary dataKeyId) {
|
||||
this.dataKeyId = dataKeyId;
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@ import org.bson.Document;
|
|||
import org.bson.conversions.Bson;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
|
||||
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
|
||||
|
||||
import com.baeldung.boot.csfle.config.converter.IntegerConverter;
|
||||
import com.baeldung.boot.csfle.config.converter.StringConverter;
|
||||
import com.baeldung.boot.csfle.config.converter.BinaryConverter;
|
||||
import com.mongodb.AutoEncryptionSettings;
|
||||
import com.mongodb.ClientEncryptionSettings;
|
||||
import com.mongodb.ConnectionString;
|
||||
|
@ -52,16 +52,17 @@ public class MongoClientConfig extends AbstractMongoClientConfiguration {
|
|||
|
||||
@Override
|
||||
public MongoCustomConversions customConversions() {
|
||||
return new MongoCustomConversions(Arrays.asList(new StringConverter(encryptionConfig), new IntegerConverter(encryptionConfig)));
|
||||
return new MongoCustomConversions(Arrays.asList(new BinaryConverter()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public MongoClient mongoClient() {
|
||||
MongoClient client;
|
||||
try {
|
||||
client = MongoClients.create(clientSettings());
|
||||
|
||||
ClientEncryption encryption = createClientEncryption();
|
||||
ClientEncryption encryption = clientEncryption();
|
||||
encryptionConfig.setDataKeyId(createOrRetrieveDataKey(client, encryption));
|
||||
|
||||
return client;
|
||||
|
@ -70,6 +71,19 @@ public class MongoClientConfig extends AbstractMongoClientConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ClientEncryption clientEncryption() throws FileNotFoundException, IOException {
|
||||
Map<String, Map<String, Object>> kmsProviders = LocalKmsUtils.providersMap(encryptionConfig.getMasterKeyPath());
|
||||
|
||||
ClientEncryptionSettings encryptionSettings = ClientEncryptionSettings.builder()
|
||||
.keyVaultMongoClientSettings(clientSettings())
|
||||
.keyVaultNamespace(encryptionConfig.getKeyVaultNamespace())
|
||||
.kmsProviders(kmsProviders)
|
||||
.build();
|
||||
|
||||
return ClientEncryptions.create(encryptionSettings);
|
||||
}
|
||||
|
||||
private BsonBinary createOrRetrieveDataKey(MongoClient client, ClientEncryption encryption) {
|
||||
MongoNamespace namespace = new MongoNamespace(encryptionConfig.getKeyVaultNamespace());
|
||||
MongoCollection<Document> keyVault = client.getDatabase(namespace.getDatabaseName())
|
||||
|
@ -92,19 +106,6 @@ public class MongoClientConfig extends AbstractMongoClientConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
private ClientEncryption createClientEncryption() throws FileNotFoundException, IOException {
|
||||
Map<String, Map<String, Object>> kmsProviders = LocalKmsUtils.providersMap(encryptionConfig.getMasterKeyPath());
|
||||
|
||||
ClientEncryptionSettings encryptionSettings = ClientEncryptionSettings.builder()
|
||||
.keyVaultMongoClientSettings(clientSettings())
|
||||
.keyVaultNamespace(encryptionConfig.getKeyVaultNamespace())
|
||||
.kmsProviders(kmsProviders)
|
||||
.build();
|
||||
|
||||
encryptionConfig.setEncryption(ClientEncryptions.create(encryptionSettings));
|
||||
return encryptionConfig.getEncryption();
|
||||
}
|
||||
|
||||
private MongoClientSettings clientSettings() throws FileNotFoundException, IOException {
|
||||
Builder settings = MongoClientSettings.builder()
|
||||
.applyConnectionString(new ConnectionString(uri));
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package com.baeldung.boot.csfle.config.converter;
|
||||
|
||||
import org.bson.BsonBinary;
|
||||
import org.bson.types.Binary;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class BinaryConverter implements Converter<Binary, BsonBinary> {
|
||||
|
||||
@Override
|
||||
public BsonBinary convert(Binary source) {
|
||||
return new BsonBinary(source.getType(), source.getData());
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package com.baeldung.boot.csfle.config.converter;
|
||||
|
||||
import org.bson.BsonBinary;
|
||||
import org.bson.BsonValue;
|
||||
import org.bson.types.Binary;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import com.baeldung.boot.csfle.config.EncryptionConfig;
|
||||
|
||||
public class IntegerConverter implements Converter<Binary, Integer> {
|
||||
|
||||
private EncryptionConfig encryptionConfig;
|
||||
|
||||
public IntegerConverter(EncryptionConfig config) {
|
||||
this.encryptionConfig = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer convert(Binary source) {
|
||||
BsonBinary bin = new BsonBinary(source.getType(), source.getData());
|
||||
BsonValue value = encryptionConfig.getEncryption()
|
||||
.decrypt(bin);
|
||||
|
||||
return value.asInt32()
|
||||
.getValue();
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package com.baeldung.boot.csfle.config.converter;
|
||||
|
||||
import org.bson.BsonBinary;
|
||||
import org.bson.BsonValue;
|
||||
import org.bson.types.Binary;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import com.baeldung.boot.csfle.config.EncryptionConfig;
|
||||
|
||||
public class StringConverter implements Converter<Binary, String> {
|
||||
|
||||
private EncryptionConfig encryptionConfig;
|
||||
|
||||
public StringConverter(EncryptionConfig config) {
|
||||
this.encryptionConfig = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convert(Binary source) {
|
||||
BsonBinary bin = new BsonBinary(source.getType(), source.getData());
|
||||
BsonValue value = encryptionConfig.getEncryption()
|
||||
.decrypt(bin);
|
||||
|
||||
return value.asString()
|
||||
.getValue();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package com.baeldung.boot.csfle.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bson.BsonBinary;
|
||||
import org.bson.BsonInt32;
|
||||
|
@ -16,6 +17,7 @@ import com.baeldung.boot.csfle.config.EncryptionConfig;
|
|||
import com.baeldung.boot.csfle.data.Citizen;
|
||||
import com.baeldung.boot.csfle.data.EncryptedCitizen;
|
||||
import com.mongodb.client.model.vault.EncryptOptions;
|
||||
import com.mongodb.client.vault.ClientEncryption;
|
||||
|
||||
@Service
|
||||
public class CitizenService {
|
||||
|
@ -29,6 +31,9 @@ public class CitizenService {
|
|||
@Autowired
|
||||
private EncryptionConfig encryptionConfig;
|
||||
|
||||
@Autowired
|
||||
private ClientEncryption clientEncryption;
|
||||
|
||||
public EncryptedCitizen save(Citizen citizen) {
|
||||
EncryptedCitizen encryptedCitizen = new EncryptedCitizen(citizen);
|
||||
encryptedCitizen.setEmail(encrypt(citizen.getEmail(), DETERMINISTIC_ALGORITHM));
|
||||
|
@ -38,26 +43,68 @@ public class CitizenService {
|
|||
}
|
||||
|
||||
public List<Citizen> findAll() {
|
||||
if (!encryptionConfig.getAutoDecryption()) {
|
||||
List<EncryptedCitizen> allEncrypted = mongo.findAll(EncryptedCitizen.class);
|
||||
|
||||
return allEncrypted.stream()
|
||||
.map(this::decrypt)
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
return mongo.findAll(Citizen.class);
|
||||
}
|
||||
}
|
||||
|
||||
public Citizen findByEmail(String email) {
|
||||
Query byEmail = new Query(Criteria.where("email")
|
||||
.is(encrypt(email, DETERMINISTIC_ALGORITHM)));
|
||||
if (!encryptionConfig.getAutoDecryption()) {
|
||||
EncryptedCitizen encryptedCitizen = mongo.findOne(byEmail, EncryptedCitizen.class);
|
||||
return decrypt(encryptedCitizen);
|
||||
} else {
|
||||
return mongo.findOne(byEmail, Citizen.class);
|
||||
}
|
||||
}
|
||||
|
||||
public BsonBinary encrypt(Object value, String algorithm) {
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
BsonValue bsonValue = value instanceof Integer
|
||||
? new BsonInt32((Integer) value)
|
||||
: new BsonString(value.toString());
|
||||
BsonValue bsonValue;
|
||||
if (value instanceof Integer) {
|
||||
bsonValue = new BsonInt32((Integer) value);
|
||||
} else if (value instanceof String) {
|
||||
bsonValue = new BsonString((String) value);
|
||||
} else {
|
||||
throw new IllegalArgumentException("unsupported type: " + value.getClass());
|
||||
}
|
||||
|
||||
EncryptOptions options = new EncryptOptions(algorithm);
|
||||
options.keyId(encryptionConfig.getDataKeyId());
|
||||
return encryptionConfig.getEncryption()
|
||||
.encrypt(bsonValue, options);
|
||||
return clientEncryption.encrypt(bsonValue, options);
|
||||
}
|
||||
|
||||
public BsonValue decryptProperty(BsonBinary value) {
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
return clientEncryption.decrypt(value);
|
||||
}
|
||||
|
||||
private Citizen decrypt(EncryptedCitizen encrypted) {
|
||||
Citizen citizen = new Citizen(encrypted);
|
||||
|
||||
BsonValue decryptedBirthYear = decryptProperty(encrypted.getBirthYear());
|
||||
if (decryptedBirthYear != null) {
|
||||
citizen.setBirthYear(decryptedBirthYear.asInt32()
|
||||
.intValue());
|
||||
}
|
||||
|
||||
BsonValue decryptedEmail = decryptProperty(encrypted.getEmail());
|
||||
if (decryptedEmail != null) {
|
||||
citizen.setEmail(decryptedEmail.asString()
|
||||
.getValue());
|
||||
}
|
||||
|
||||
return citizen;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-data-jpa-query-3</artifactId>
|
||||
<name>spring-data-jpa-query-3</name>
|
||||
<properties>
|
||||
<javafaker.version>0.15</javafaker.version>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
|
@ -22,6 +25,11 @@
|
|||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.javafaker</groupId>
|
||||
<artifactId>javafaker</artifactId>
|
||||
<version>${javafaker.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package com.baeldung.spring.data.jpa.collectionsvsstream;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ListVsStreamQueryApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ListVsStreamQueryApplication.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.baeldung.spring.data.jpa.collectionsvsstream;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "_user")
|
||||
public class User {
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private int age;
|
||||
@Id
|
||||
private int id;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(String firstName, String lastName, int age) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public User(String firstName, String lastName, int age, int id) {
|
||||
this(firstName, lastName, age);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.baeldung.spring.data.jpa.collectionsvsstream;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, String> {
|
||||
Stream<User> findAllByAgeGreaterThan(int age);
|
||||
|
||||
List<User> findByAgeGreaterThan(int age);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.baeldung.spring.data.jpa.collectionsvsstream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
|
||||
import com.github.javafaker.Faker;
|
||||
|
||||
@DataJpaTest
|
||||
class UserRepositoryUnitTest {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
Faker faker = new Faker();
|
||||
List<User> people = IntStream.range(1, 100)
|
||||
.parallel()
|
||||
.mapToObj(i -> new User(faker.name()
|
||||
.firstName(), faker.name()
|
||||
.lastName(), faker.number()
|
||||
.numberBetween(1, 100), i))
|
||||
.collect(Collectors.toList());
|
||||
userRepository.saveAll(people);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
userRepository.deleteAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAgeIs20_thenItShouldReturnAllUsersWhoseAgeIsGreaterThan20InAList() {
|
||||
List<User> users = userRepository.findByAgeGreaterThan(20);
|
||||
assertThat(users).isNotEmpty();
|
||||
assertThat(users.stream()
|
||||
.map(User::getAge)
|
||||
.allMatch(age -> age > 20)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAgeIs20_thenItShouldReturnAllUsersWhoseAgeIsGreaterThan20InAStream() {
|
||||
Stream<User> users = userRepository.findAllByAgeGreaterThan(20);
|
||||
assertThat(users).isNotNull();
|
||||
assertThat(users.map(User::getAge)
|
||||
.allMatch(age -> age > 20)).isTrue();
|
||||
}
|
||||
}
|
25
pom.xml
25
pom.xml
|
@ -332,12 +332,6 @@
|
|||
|
||||
<module>apache-cxf-modules</module>
|
||||
<module>apache-libraries</module>
|
||||
<module>apache-poi</module>
|
||||
<module>apache-velocity</module>
|
||||
<module>di-modules</module>
|
||||
<module>asciidoctor</module>
|
||||
|
||||
<module>aws-modules</module>
|
||||
|
||||
<module>azure</module>
|
||||
<module>checker-plugin</module>
|
||||
|
@ -614,12 +608,6 @@
|
|||
|
||||
<module>apache-cxf-modules</module>
|
||||
<module>apache-libraries</module>
|
||||
<module>apache-poi</module>
|
||||
<module>apache-velocity</module>
|
||||
<module>di-modules</module>
|
||||
<module>asciidoctor</module>
|
||||
|
||||
<module>aws-modules</module>
|
||||
|
||||
<module>azure</module>
|
||||
<module>checker-plugin</module>
|
||||
|
@ -907,6 +895,11 @@
|
|||
|
||||
<modules>
|
||||
<module>algorithms-modules</module>
|
||||
<module>apache-poi</module>
|
||||
<module>apache-velocity</module>
|
||||
<module>di-modules</module>
|
||||
<module>asciidoctor</module>
|
||||
<module>aws-modules</module>
|
||||
<module>core-java-modules/core-java-9</module>
|
||||
<module>core-java-modules/core-java-9-improvements</module>
|
||||
<module>core-java-modules/core-java-9-jigsaw</module>
|
||||
|
@ -963,6 +956,7 @@
|
|||
<module>spring-boot-modules/spring-boot-3</module>
|
||||
<module>spring-boot-modules/spring-boot-3-native</module>
|
||||
<module>spring-boot-modules/spring-boot-3-observation</module>
|
||||
<module>spring-boot-modules/spring-boot-3-test-pitfalls</module>
|
||||
<module>spring-swagger-codegen/custom-validations-opeanpi-codegen</module>
|
||||
<module>testing-modules/testing-assertions</module>
|
||||
<module>persistence-modules/fauna</module>
|
||||
|
@ -1106,6 +1100,12 @@
|
|||
|
||||
<modules>
|
||||
<module>algorithms-modules</module>
|
||||
<module>apache-poi</module>
|
||||
<module>apache-velocity</module>
|
||||
<module>di-modules</module>
|
||||
<module>asciidoctor</module>
|
||||
<module>aws-modules</module>
|
||||
|
||||
<module>core-java-modules/core-java-9</module>
|
||||
<module>core-java-modules/core-java-9-improvements</module>
|
||||
<module>core-java-modules/core-java-9-jigsaw</module>
|
||||
|
@ -1162,6 +1162,7 @@
|
|||
<module>spring-boot-modules/spring-boot-3</module>
|
||||
<module>spring-boot-modules/spring-boot-3-native</module>
|
||||
<module>spring-boot-modules/spring-boot-3-observation</module>
|
||||
<module>spring-boot-modules/spring-boot-3-test-pitfalls</module>
|
||||
<module>spring-swagger-codegen/custom-validations-opeanpi-codegen</module>
|
||||
<module>testing-modules/testing-assertions</module>
|
||||
<module>persistence-modules/fauna</module>
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
<quarkus.platform.version>2.16.0.Final</quarkus.platform.version>
|
||||
<surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
|
||||
</properties>
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>quarkus-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-boot-3-test-pitfalls</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>spring-boot-3-test-pitfalls</name>
|
||||
<description>Demo project for Spring Boot Testing Pitfalls</description>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-3</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-boot-3</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>0.2.0</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
|
||||
</properties>
|
||||
|
||||
|
||||
</project>
|
|
@ -0,0 +1,13 @@
|
|||
package com.baeldung.sample.pets;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class PetsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PetsApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PetDto {
|
||||
|
||||
private String name;
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import com.baeldung.sample.pets.domain.Pet;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface PetDtoMapper {
|
||||
|
||||
PetDto map(Pet source);
|
||||
|
||||
Pet map(PetDto source);
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import com.baeldung.sample.pets.domain.PetService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/pets")
|
||||
@RequiredArgsConstructor
|
||||
public class PetsController {
|
||||
|
||||
private final PetService service;
|
||||
private final PetDtoMapper mapper;
|
||||
|
||||
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Collection<PetDto> readAll() {
|
||||
return service.getPets().stream()
|
||||
.map(mapper::map)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
public record Pet(String name) {
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.Delegate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PetService {
|
||||
|
||||
@Delegate
|
||||
private final PetServiceRepository repo;
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface PetServiceRepository {
|
||||
|
||||
boolean add(Pet pet);
|
||||
|
||||
void clear();
|
||||
|
||||
Collection<Pet> getPets();
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
public class PetServiceRepositoryImpl implements PetServiceRepository {
|
||||
|
||||
private final Set<Pet> pets = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public Set<Pet> getPets() {
|
||||
return Collections.unmodifiableSet(pets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Pet pet) {
|
||||
return this.pets.add(pet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.pets.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import com.baeldung.sample.pets.domain.PetService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.mock.mockito.MockReset;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
public class PetDtoMapperIntegrationTest {
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = PetDtoMapper.class)
|
||||
static class PetDtoMapperTestConfig {
|
||||
|
||||
/*
|
||||
* This would be necessary because the controller is also initialized
|
||||
* and needs the service, although we do not want to test it here.
|
||||
*
|
||||
* Solutions:
|
||||
* - place the mapper into a separate sub package
|
||||
* - do not test the mapper separately, test it integrated within the controller
|
||||
* (recommended)
|
||||
*/
|
||||
@Bean
|
||||
PetService createServiceMock() {
|
||||
return mock(PetService.class, MockReset.withSettings(MockReset.AFTER));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Autowired
|
||||
PetDtoMapper mapper;
|
||||
|
||||
@Test
|
||||
void shouldExist() { // simply test correct test setup
|
||||
assertThat(mapper).isNotNull();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* Just an interface to use for compiler-checked component scanning during tests.
|
||||
* @see ComponentScan#basePackageClasses()
|
||||
*/
|
||||
public interface PetsBoundaryLayer {
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import com.baeldung.sample.pets.domain.PetService;
|
||||
import com.baeldung.sample.test.slices.PetsBoundaryTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@PetsBoundaryTest
|
||||
class PetsControllerMvcIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
@Autowired
|
||||
PetService service;
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyArrayWhenGetPets() throws Exception {
|
||||
when(service.getPets()).thenReturn(Collections.emptyList());
|
||||
mvc.perform(
|
||||
get("/pets")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("[]"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import com.baeldung.sample.test.slices.PetsDomainTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@PetsDomainTest
|
||||
class PetServiceIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
PetService service;
|
||||
@Autowired // Mock
|
||||
PetServiceRepository repository;
|
||||
|
||||
@Test
|
||||
void shouldAddPetWhenNotAlreadyExisting() {
|
||||
var pet = new Pet("Dog");
|
||||
when(repository.add(pet)).thenReturn(true);
|
||||
var result = service.add(pet);
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class PetServiceUnitTest {
|
||||
|
||||
PetService service = new PetService(new PetServiceRepositoryImpl());
|
||||
|
||||
@Test
|
||||
void shouldAddPetWhenNotAlreadyExisting() {
|
||||
var pet = new Pet("Dog");
|
||||
var result = service.add(pet);
|
||||
assertThat(result).isTrue();
|
||||
assertThat(service.getPets()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotAddPetWhenAlreadyExisting() {
|
||||
var pet = new Pet("Cat");
|
||||
var result = service.add(pet);
|
||||
assertThat(result).isTrue();
|
||||
// try a second time
|
||||
result = service.add(pet);
|
||||
assertThat(result).isFalse();
|
||||
assertThat(service.getPets()).hasSize(1);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* Just an interface to use for compiler-checked component scanning during tests.
|
||||
* @see ComponentScan#basePackageClasses()
|
||||
*/
|
||||
public interface PetsDomainLayer {
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.baeldung.sample.test.slices;
|
||||
|
||||
import com.baeldung.sample.pets.boundary.PetsBoundaryLayer;
|
||||
import com.baeldung.sample.pets.boundary.PetsController;
|
||||
import com.baeldung.sample.pets.domain.PetService;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.test.mock.mockito.MockReset;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@WebMvcTest(controllers = PetsController.class)
|
||||
@ComponentScan(basePackageClasses = PetsBoundaryLayer.class)
|
||||
@Import(PetsBoundaryTest.PetBoundaryTestConfiguration.class)
|
||||
// further features that can help to configure and execute tests
|
||||
@ActiveProfiles({ "test", "boundary-test" })
|
||||
@Tag("integration-test")
|
||||
@Tag("boundary-test")
|
||||
public @interface PetsBoundaryTest {
|
||||
|
||||
@TestConfiguration
|
||||
class PetBoundaryTestConfiguration {
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
PetService createPetServiceMock() {
|
||||
return mock(
|
||||
PetService.class,
|
||||
MockReset.withSettings(MockReset.AFTER)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.baeldung.sample.test.slices;
|
||||
|
||||
import com.baeldung.sample.pets.domain.PetServiceRepository;
|
||||
import com.baeldung.sample.pets.domain.PetsDomainLayer;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.test.mock.mockito.MockReset;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ComponentScan(basePackageClasses = PetsDomainLayer.class)
|
||||
@Import(PetsDomainTest.PetServiceTestConfiguration.class)
|
||||
// further features that can help to configure and execute tests
|
||||
@ActiveProfiles({"test", "domain-test"})
|
||||
@Tag("integration-test")
|
||||
@Tag("domain-test")
|
||||
public @interface PetsDomainTest {
|
||||
|
||||
@TestConfiguration
|
||||
class PetServiceTestConfiguration {
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
PetServiceRepository createPetsRepositoryMock() {
|
||||
return mock(
|
||||
PetServiceRepository.class,
|
||||
MockReset.withSettings(MockReset.AFTER)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
logging:
|
||||
level:
|
||||
root: info
|
||||
org:
|
||||
springframework:
|
||||
test:
|
||||
context:
|
||||
cache: DEBUG
|
||||
spring:
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
|
@ -0,0 +1,29 @@
|
|||
package com.baeldung.sample.singleton;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
@Configuration
|
||||
public class SingletonBeanConfig {
|
||||
|
||||
@Bean
|
||||
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
|
||||
public SingletonBean singletonBean() {
|
||||
return new SingletonBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
|
||||
public SingletonBean anotherSingletonBean() {
|
||||
return new SingletonBean();
|
||||
}
|
||||
|
||||
static class SingletonBean {
|
||||
public String getValue() {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.baeldung.sample.singleton;
|
||||
|
||||
public final class ThreadSafeSingleInstance {
|
||||
|
||||
private static volatile ThreadSafeSingleInstance instance = null;
|
||||
|
||||
private ThreadSafeSingleInstance() {}
|
||||
|
||||
public static ThreadSafeSingleInstance getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized(ThreadSafeSingleInstance.class) {
|
||||
if (instance == null) {
|
||||
instance = new ThreadSafeSingleInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.baeldung.sample.singleton;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class SingletonBeanUnitTest {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("singletonBean")
|
||||
private SingletonBeanConfig.SingletonBean beanOne;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("singletonBean")
|
||||
private SingletonBeanConfig.SingletonBean beanTwo;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("anotherSingletonBean")
|
||||
private SingletonBeanConfig.SingletonBean beanThree;
|
||||
|
||||
@Test
|
||||
void givenTwoBeansWithSameId_whenInjectingThem_thenSameInstancesAreReturned() {
|
||||
assertSame(beanOne, beanTwo);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenTwoBeansWithDifferentId_whenInjectingThem_thenDifferentInstancesAreReturned() {
|
||||
assertNotSame(beanOne, beanThree);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.baeldung.sample.singleton;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
class ThreadSafeSingleInstanceUnitTest {
|
||||
|
||||
@Test
|
||||
void givenTwoSingletonInstances_whenGettingThem_thenSameInstancesAreReturned() {
|
||||
ThreadSafeSingleInstance instanceOne = ThreadSafeSingleInstance.getInstance();
|
||||
ThreadSafeSingleInstance instanceTwo = ThreadSafeSingleInstance.getInstance();
|
||||
assertSame(instanceOne, instanceTwo);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
### Spring Reactive Articles that are also part of the e-book
|
||||
|
||||
This module contains articles about Spring Reactive that are also part of an Ebook.
|
||||
|
||||
## Spring Reactive
|
||||
|
||||
This module contains articles describing reactive processing in Spring.
|
||||
|
@ -14,3 +18,7 @@ This module contains articles describing reactive processing in Spring.
|
|||
- [Handling Errors in Spring WebFlux](https://www.baeldung.com/spring-webflux-errors)
|
||||
- [Spring Security 5 for Reactive Applications](https://www.baeldung.com/spring-security-5-reactive)
|
||||
- [Concurrency in Spring WebFlux](https://www.baeldung.com/spring-webflux-concurrency)
|
||||
|
||||
### NOTE:
|
||||
|
||||
Since this is a module tied to an e-book, it should **not** be moved or used to store the code for any further article.
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.baeldung.junit5.nested;
|
||||
|
||||
public class Article {
|
||||
private String name;
|
||||
private Membership articleLevel;
|
||||
|
||||
public Article(String name, Membership articleLevel) {
|
||||
this.name = name;
|
||||
this.articleLevel = articleLevel;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Membership getArticleLevel() {
|
||||
return articleLevel;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.baeldung.junit5.nested;
|
||||
|
||||
public enum Membership {
|
||||
FREE(0), SILVER(10), GOLD(20);
|
||||
|
||||
private final int level;
|
||||
|
||||
Membership(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public int compare(Membership other) {
|
||||
return level - other.level;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.baeldung.junit5.nested;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Publication {
|
||||
private final List<Article> articles;
|
||||
|
||||
public Publication(List<Article> articles) {
|
||||
this.articles = articles;
|
||||
}
|
||||
|
||||
public List<String> getReadableArticles(User user) {
|
||||
return articles.stream()
|
||||
.filter(a -> a.getArticleLevel()
|
||||
.compare(user.getMembership()) <= 0)
|
||||
.map(Article::getName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<String> getLockedArticles(User user) {
|
||||
return articles.stream()
|
||||
.filter(a -> a.getArticleLevel()
|
||||
.compare(user.getMembership()) > 0)
|
||||
.map(Article::getName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Article> getArticles() {
|
||||
return articles;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.baeldung.junit5.nested;
|
||||
|
||||
public class User {
|
||||
private String name;
|
||||
private Membership membership;
|
||||
|
||||
public User(String name, Membership membership) {
|
||||
this.name = name;
|
||||
this.membership = membership;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Membership getMembership() {
|
||||
return membership;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.baeldung.junit5.nested;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class NestedUnitTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
System.out.println("NestedUnitTest.beforeEach()");
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FirstNestedClass {
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
System.out.println("FirstNestedClass.beforeEach()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
System.out.println("FirstNestedClass.test()");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class SecondNestedClass {
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
System.out.println("SecondNestedClass.beforeEach()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
System.out.println("SecondNestedClass.test()");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package com.baeldung.junit5.nested;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@DisplayName("given a article publication with three articles")
|
||||
class OnlinePublicationUnitTest {
|
||||
private Publication publication;
|
||||
|
||||
@BeforeEach
|
||||
void setupArticlesAndPublication() {
|
||||
Article freeArticle = new Article("free article", Membership.FREE);
|
||||
Article silverArticle = new Article("silver level article", Membership.SILVER);
|
||||
Article goldArticle = new Article("gold level article", Membership.GOLD);
|
||||
publication = new Publication(Arrays.asList(freeArticle, silverArticle, goldArticle));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("then 3 articles are available")
|
||||
void shouldHaveThreeArticlesInTotal() {
|
||||
List<Article> allArticles = publication.getArticles();
|
||||
assertThat(allArticles).hasSize(3);
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("when a user with a 'free' membership logs in")
|
||||
class UserWithAFreeMembership {
|
||||
User freeFreya = new User("Freya", Membership.FREE);
|
||||
|
||||
@Test
|
||||
@DisplayName("then he should be able to read the 'free' articles")
|
||||
void shouldOnlyReadFreeArticles() {
|
||||
List<String> articles = publication.getReadableArticles(freeFreya);
|
||||
assertThat(articles).containsExactly("free article");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("then he shouldn't be able to read the 'silver' and 'gold' articles")
|
||||
void shouldSeeSilverAndGoldLevelArticlesAsLocked() {
|
||||
List<String> articles = publication.getLockedArticles(freeFreya);
|
||||
assertThat(articles).containsExactlyInAnyOrder("silver level article", "gold level article");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("when a user with a 'silver' membership logs in")
|
||||
class UserWithSilverMembership {
|
||||
User silverSilvester = new User("Silvester", Membership.SILVER);
|
||||
|
||||
@Test
|
||||
@DisplayName("then he should be able to read the 'free' and 'silver' level articles")
|
||||
void shouldOnlyReadFreeAndSilverLevelArticles() {
|
||||
List<String> articles = publication.getReadableArticles(silverSilvester);
|
||||
assertThat(articles).containsExactlyInAnyOrder("free article", "silver level article");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("then he should see the 'gold' level articles as locked")
|
||||
void shouldSeeGoldLevelArticlesAsLocked() {
|
||||
List<String> articles = publication.getLockedArticles(silverSilvester);
|
||||
assertThat(articles).containsExactlyInAnyOrder("gold level article");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("when a user with a 'gold' membership logs in")
|
||||
class UserWithGoldMembership {
|
||||
User goldenGeorge = new User("George", Membership.GOLD);
|
||||
|
||||
@Test
|
||||
@DisplayName("then he should be able to read all the articles")
|
||||
void shouldSeeAllArticles() {
|
||||
List<String> articles = publication.getReadableArticles(goldenGeorge);
|
||||
assertThat(articles).containsExactlyInAnyOrder("free article", "silver level article", "gold level article");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("then he should not see any article as locked")
|
||||
void shouldNotHaveHiddenArticles() {
|
||||
List<String> articles = publication.getLockedArticles(goldenGeorge);
|
||||
assertThat(articles).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue