Skip to content

Commit 838c47d

Browse files
committed
Update dependencies and add support for Rest Assured and Jackson 3.x
1 parent 6174487 commit 838c47d

14 files changed

Lines changed: 684 additions & 424 deletions

File tree

mvnw

100644100755
File mode changed.

pom.xml

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>org.springframework.boot</groupId>
88
<artifactId>spring-boot-starter-parent</artifactId>
9-
<version>3.5.4</version>
9+
<version>4.0.2</version>
1010
<relativePath/>
1111
</parent>
1212

@@ -25,17 +25,20 @@
2525
</modules>
2626

2727
<properties>
28-
<revision>1.0.0</revision>
28+
<revision>1.0.2</revision>
2929
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
3030
<java.version>21</java.version>
31-
<spring-cloud.version>2025.0.0</spring-cloud.version>
32-
<lombok.version>1.18.38</lombok.version>
31+
<spring-cloud.version>2025.1.0</spring-cloud.version>
32+
<lombok.version>1.18.42</lombok.version>
3333
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
3434
<mapstruct.version>1.6.3</mapstruct.version>
35-
<commons-lang3.version>3.18.0</commons-lang3.version>
35+
<commons-lang3.version>3.20.0</commons-lang3.version>
3636
<slf4j-api.version>2.0.17</slf4j-api.version>
37-
<micrometer-registry-otlp.version>1.15.3</micrometer-registry-otlp.version>
38-
<maven-enforcer-plugin.version>3.6.1</maven-enforcer-plugin.version>
37+
<micrometer-registry-otlp.version>1.16.2</micrometer-registry-otlp.version>
38+
<maven-enforcer-plugin.version>3.6.2</maven-enforcer-plugin.version>
39+
<rest-assured.version>6.0.0</rest-assured.version>
40+
<tools-jackson.version>3.0.4</tools-jackson.version>
41+
<assertj-core.version>3.27.7</assertj-core.version>
3942
</properties>
4043

4144
<!--
@@ -53,6 +56,18 @@
5356
<scope>import</scope>
5457
</dependency>
5558

59+
<!-- Rest Assured -->
60+
<dependency>
61+
<groupId>io.rest-assured</groupId>
62+
<artifactId>spring-mock-mvc</artifactId>
63+
<version>${rest-assured.version}</version>
64+
</dependency>
65+
<dependency>
66+
<groupId>io.rest-assured</groupId>
67+
<artifactId>json-schema-validator</artifactId>
68+
<version>${rest-assured.version}</version>
69+
</dependency>
70+
5671
<!-- Dependências dos Módulos Internos -->
5772
<dependency>
5873
<groupId>com.br</groupId>
@@ -118,9 +133,19 @@
118133
<artifactId>commons-lang3</artifactId>
119134
<version>${commons-lang3.version}</version>
120135
</dependency>
136+
137+
<!-- Jackson 3.x - Nova estrutura (tools.jackson.core) suporte para Java 8 Date/Time API (LocalDateTime, etc.) -->
138+
<!-- ⚠️ OLD: import com.fasterxml.jackson.* -->
139+
<!-- ✅ NEW: import tools.jackson.* -->
121140
<dependency>
122-
<groupId>com.fasterxml.jackson.core</groupId>
141+
<groupId>tools.jackson.core</groupId>
142+
<artifactId>jackson-core</artifactId>
143+
<version>${tools-jackson.version}</version>
144+
</dependency>
145+
<dependency>
146+
<groupId>tools.jackson.core</groupId>
123147
<artifactId>jackson-databind</artifactId>
148+
<version>${tools-jackson.version}</version>
124149
</dependency>
125150

126151
<!-- Testes -->
@@ -132,6 +157,17 @@
132157
<dependency>
133158
<groupId>org.assertj</groupId>
134159
<artifactId>assertj-core</artifactId>
160+
<version>${assertj-core.version}</version>
161+
<scope>test</scope>
162+
</dependency>
163+
<dependency>
164+
<groupId>org.mockito</groupId>
165+
<artifactId>mockito-core</artifactId>
166+
<scope>test</scope>
167+
</dependency>
168+
<dependency>
169+
<groupId>org.mockito</groupId>
170+
<artifactId>mockito-junit-jupiter</artifactId>
135171
<scope>test</scope>
136172
</dependency>
137173

@@ -203,15 +239,6 @@
203239
</executions>
204240
</plugin>
205241

206-
<!--
207-
Executa as regras de qualidade do build definidas no pluginManagement.
208-
Garante consistência de versões Java/Maven e resolve conflitos de dependências transitivas.
209-
-->
210-
<plugin>
211-
<groupId>org.apache.maven.plugins</groupId>
212-
<artifactId>maven-enforcer-plugin</artifactId>
213-
</plugin>
214-
215242
</plugins>
216243
</pluginManagement>
217244

wallet-config/src/main/java/com/br/walletconfig/kafka/EventPublisherConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import com.br.walletdataprovider.kafka.KafkaWalletEventPublisher;
66
import com.br.walletdataprovider.kafka.OutboxWalletEventPublisher;
77
import com.br.walletdataprovider.mongodb.repository.OutboxEventMongoRepository;
8-
import com.fasterxml.jackson.databind.ObjectMapper;
8+
import tools.jackson.databind.ObjectMapper;
99
import org.springframework.context.annotation.Bean;
1010
import org.springframework.context.annotation.Configuration;
1111
import org.springframework.kafka.core.KafkaTemplate;

wallet-config/src/main/java/com/br/walletconfig/kafka/KafkaConfig.java

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
11
package com.br.walletconfig.kafka;
22

3+
import com.fasterxml.jackson.databind.JsonSerializer;
34
import org.apache.kafka.clients.admin.NewTopic;
5+
import org.apache.kafka.clients.producer.ProducerConfig;
46
import org.springframework.beans.factory.annotation.Value;
57
import org.springframework.context.annotation.Bean;
68
import org.springframework.context.annotation.Configuration;
79
import org.springframework.kafka.annotation.EnableKafka;
810
import org.springframework.kafka.config.TopicBuilder;
11+
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
12+
import org.springframework.kafka.core.KafkaTemplate;
13+
import org.springframework.kafka.core.ProducerFactory;
14+
import tools.jackson.databind.ser.jdk.StringSerializer;
15+
16+
import java.util.HashMap;
17+
import java.util.Map;
918

1019
/**
11-
* Configuração simplificada do Kafka usando apenas @Value.
12-
* As demais configurações (producer, consumer, listener) estão todas no application.yml
13-
* e são aplicadas automaticamente pela autoconfiguração do Spring Boot.
20+
* <p>Configuração do Kafka para Spring Boot 4.x</p>
21+
* <p>No Spring Boot 4.x, o KafkaTemplate<String, Object> precisa ser criado explicitamente</p>
22+
* <p>Demais configurações (producer, consumer, listener) estão todas no application.yml</p>
1423
*/
1524
@Configuration
1625
@EnableKafka
1726
public class KafkaConfig {
1827

28+
@Value("${spring.kafka.bootstrap-servers}")
29+
private String bootstrapServers;
30+
1931
@Value("${wallet.kafka.topics.wallet-events.name}")
2032
private String walletEventsTopicName;
2133

@@ -57,4 +69,33 @@ public NewTopic walletOutboxTopic() {
5769
.replicas(walletOutboxReplicas)
5870
.build();
5971
}
72+
73+
/**
74+
* Configuração do ProducerFactory para KafkaTemplate<String, Object>
75+
* Necessário no Spring Boot 4.x para registrar o bean corretamente
76+
*/
77+
@Bean
78+
public ProducerFactory<String, Object> producerFactory() {
79+
Map<String, Object> configProps = new HashMap<>();
80+
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
81+
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
82+
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
83+
84+
// Configurações adicionais do application.yml serão mescladas automaticamente
85+
configProps.put(ProducerConfig.ACKS_CONFIG, "all"); // Garante que todas as réplicas confirmem
86+
configProps.put(ProducerConfig.RETRIES_CONFIG, 3); // Tente 3 vezes
87+
configProps.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true); //Evita duplicatas
88+
89+
return new DefaultKafkaProducerFactory<>(configProps);
90+
}
91+
92+
/**
93+
* Bean do KafkaTemplate<String, Object>
94+
* Necessário no Spring Boot 4.x - a autoconfiguração não cria mais este bean genérico
95+
*/
96+
@Bean
97+
public KafkaTemplate<String, Object> kafkaTemplate(ProducerFactory<String, Object> producerFactory) {
98+
return new KafkaTemplate<>(producerFactory);
99+
}
100+
60101
}

wallet-config/src/main/resources/application.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,6 @@ spring:
4848

4949
# Configurações do Producer
5050
producer:
51-
key-serializer: org.apache.kafka.common.serialization.StringSerializer
52-
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
53-
acks: all # Garante que todas as réplicas confirmem
54-
retries: 3
5551
enable-idempotence: true # Evita duplicatas
5652
batch-size: ${KAFKA_PRODUCER_BATCH_SIZE:16384}
5753
linger-ms: ${KAFKA_PRODUCER_LINGER_MS:5}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.br.walletcore.usecase;
2+
3+
import com.br.walletcore.domain.Wallet;
4+
import com.br.walletcore.port.events.OutboxEventPublisher;
5+
import com.br.walletcore.port.events.WalletEventPublisher;
6+
import com.br.walletcore.port.repositories.WalletRepository;
7+
import org.junit.jupiter.api.DisplayName;
8+
import org.junit.jupiter.api.Test;
9+
import org.junit.jupiter.api.extension.ExtendWith;
10+
import org.mockito.InjectMocks;
11+
import org.mockito.Mock;
12+
import org.mockito.junit.jupiter.MockitoExtension;
13+
14+
import java.util.Optional;
15+
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
18+
import static org.mockito.ArgumentMatchers.any;
19+
import static org.mockito.ArgumentMatchers.anyMap;
20+
import static org.mockito.ArgumentMatchers.anyString;
21+
import static org.mockito.Mockito.*;
22+
23+
@ExtendWith(MockitoExtension.class)
24+
class CreateWalletUseCaseTest {
25+
26+
@Mock
27+
private WalletRepository walletRepository;
28+
29+
@Mock
30+
private WalletEventPublisher walletEventPublisher;
31+
32+
@Mock
33+
private OutboxEventPublisher outboxEventPublisher;
34+
35+
@InjectMocks
36+
private CreateWalletUseCase createWalletUseCase;
37+
38+
@Test
39+
@DisplayName("Should create wallet successfully when user does not have one")
40+
void shouldCreateWalletSuccessfully() {
41+
// Given
42+
String userId = "user-123";
43+
String currency = "BRL";
44+
45+
when(walletRepository.findByUserId(userId)).thenReturn(Optional.empty());
46+
when(walletRepository.save(any(Wallet.class))).thenAnswer(invocation -> {
47+
Wallet wallet = invocation.getArgument(0);
48+
wallet.setId("wallet-id-123");
49+
return wallet;
50+
});
51+
52+
// When
53+
Wallet result = createWalletUseCase.execute(userId, currency);
54+
55+
// Then
56+
assertThat(result).isNotNull();
57+
assertThat(result.getUserId()).isEqualTo(userId);
58+
assertThat(result.getBalance().getAmount()).isZero();
59+
assertThat(result.getBalance().getCurrency()).isEqualTo(currency);
60+
61+
verify(walletRepository).findByUserId(userId);
62+
verify(walletRepository).save(any(Wallet.class));
63+
verify(walletEventPublisher).publishWalletEvent(anyString(), anyMap());
64+
verify(outboxEventPublisher).publishOutboxEvent(anyString(), anyMap());
65+
}
66+
67+
@Test
68+
@DisplayName("Should throw exception when user already has a wallet")
69+
void shouldThrowExceptionWhenUserAlreadyHasWallet() {
70+
// Given
71+
String userId = "user-123";
72+
String currency = "BRL";
73+
74+
when(walletRepository.findByUserId(userId)).thenReturn(Optional.of(new Wallet()));
75+
76+
// When & Then
77+
assertThatThrownBy(() -> createWalletUseCase.execute(userId, currency))
78+
.isInstanceOf(IllegalArgumentException.class)
79+
.hasMessage("User already has a wallet");
80+
81+
verify(walletRepository).findByUserId(userId);
82+
verify(walletRepository, never()).save(any(Wallet.class));
83+
verifyNoInteractions(walletEventPublisher, outboxEventPublisher);
84+
}
85+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.br.walletcore.usecase;
2+
3+
import com.br.walletcore.domain.Money;
4+
import com.br.walletcore.domain.Wallet;
5+
import com.br.walletcore.domain.WalletTransaction;
6+
import com.br.walletcore.enums.TransactionType;
7+
import com.br.walletcore.port.events.OutboxEventPublisher;
8+
import com.br.walletcore.port.events.WalletEventPublisher;
9+
import com.br.walletcore.port.repositories.WalletRepository;
10+
import org.junit.jupiter.api.DisplayName;
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.api.extension.ExtendWith;
13+
import org.mockito.InjectMocks;
14+
import org.mockito.Mock;
15+
import org.mockito.junit.jupiter.MockitoExtension;
16+
17+
import java.math.BigDecimal;
18+
import java.util.Optional;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
22+
import static org.mockito.ArgumentMatchers.any;
23+
import static org.mockito.ArgumentMatchers.anyMap;
24+
import static org.mockito.ArgumentMatchers.anyString;
25+
import static org.mockito.Mockito.*;
26+
27+
@ExtendWith(MockitoExtension.class)
28+
class DepositUseCaseTest {
29+
30+
@Mock
31+
private WalletRepository walletRepository;
32+
33+
@Mock
34+
private WalletEventPublisher walletEventPublisher;
35+
36+
@Mock
37+
private OutboxEventPublisher outboxEventPublisher;
38+
39+
@InjectMocks
40+
private DepositUseCase depositUseCase;
41+
42+
@Test
43+
@DisplayName("Should deposit successfully")
44+
void shouldDepositSuccessfully() {
45+
// Given
46+
String userId = "user-123";
47+
Money depositAmount = Money.of(new BigDecimal("100.00"), "BRL");
48+
Wallet wallet = Wallet.builder()
49+
.id("wallet-123")
50+
.userId(userId)
51+
.balance(Money.of(BigDecimal.ZERO, "BRL"))
52+
.build();
53+
54+
when(walletRepository.findByUserId(userId)).thenReturn(Optional.of(wallet));
55+
when(walletRepository.save(any(Wallet.class))).thenAnswer(invocation -> invocation.getArgument(0));
56+
57+
// When
58+
WalletTransaction result = depositUseCase.execute(userId, depositAmount);
59+
60+
// Then
61+
assertThat(result).isNotNull();
62+
assertThat(result.getAmount()).isEqualTo(depositAmount);
63+
assertThat(result.getType()).isEqualTo(TransactionType.DEPOSIT);
64+
assertThat(result.getBalanceAfter().getAmount()).isEqualTo(new BigDecimal("100.00"));
65+
66+
verify(walletRepository).findByUserId(userId);
67+
verify(walletRepository).save(any(Wallet.class));
68+
verify(walletRepository).saveTransaction(any(WalletTransaction.class));
69+
verify(walletEventPublisher).publishWalletEvent(anyString(), anyMap());
70+
verify(outboxEventPublisher).publishOutboxEvent(anyString(), anyMap());
71+
}
72+
73+
@Test
74+
@DisplayName("Should throw exception when wallet not found")
75+
void shouldThrowExceptionWhenWalletNotFound() {
76+
// Given
77+
String userId = "user-123";
78+
Money depositAmount = Money.of(new BigDecimal("100.00"), "BRL");
79+
80+
when(walletRepository.findByUserId(userId)).thenReturn(Optional.empty());
81+
82+
// When & Then
83+
assertThatThrownBy(() -> depositUseCase.execute(userId, depositAmount))
84+
.isInstanceOf(IllegalArgumentException.class)
85+
.hasMessage("Wallet not found for user: " + userId);
86+
87+
verify(walletRepository).findByUserId(userId);
88+
verify(walletRepository, never()).save(any(Wallet.class));
89+
verifyNoInteractions(walletEventPublisher, outboxEventPublisher);
90+
}
91+
}

0 commit comments

Comments
 (0)