diff --git a/pom.xml b/pom.xml index 543c1aa50..c01098191 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ org.junit.jupiter junit-jupiter - 5.11.4 + 6.0.1 test @@ -28,9 +28,14 @@ org.mockito mockito-junit-jupiter - 5.15.2 + 5.21.0 test + + org.awaitility + awaitility + 4.3.0 + @@ -57,6 +62,19 @@ + + org.pitest + pitest-maven + 1.22.0 + + + org.pitest + pitest-junit5-plugin + 1.2.2 + + + + org.jacoco jacoco-maven-plugin diff --git a/src/main/java/com/example/payment/DatabaseConnection.java b/src/main/java/com/example/payment/DatabaseConnection.java new file mode 100644 index 000000000..22b856e38 --- /dev/null +++ b/src/main/java/com/example/payment/DatabaseConnection.java @@ -0,0 +1,5 @@ +package com.example.payment; + +public interface DatabaseConnection { + void executeUpdate(String query); +} diff --git a/src/main/java/com/example/payment/EmailService.java b/src/main/java/com/example/payment/EmailService.java new file mode 100644 index 000000000..e748c2fa2 --- /dev/null +++ b/src/main/java/com/example/payment/EmailService.java @@ -0,0 +1,5 @@ +package com.example.payment; + +public interface EmailService { + void sendPaymentConfirmation(String email, double amount); +} diff --git a/src/main/java/com/example/payment/PaymentApi.java b/src/main/java/com/example/payment/PaymentApi.java new file mode 100644 index 000000000..5f741d58c --- /dev/null +++ b/src/main/java/com/example/payment/PaymentApi.java @@ -0,0 +1,5 @@ +package com.example.payment; + +public interface PaymentApi { + PaymentApiResponse charge(String apiKey, double amount); +} diff --git a/src/main/java/com/example/payment/PaymentApiResponse.java b/src/main/java/com/example/payment/PaymentApiResponse.java new file mode 100644 index 000000000..208d2b111 --- /dev/null +++ b/src/main/java/com/example/payment/PaymentApiResponse.java @@ -0,0 +1,4 @@ +package com.example.payment; + +public record PaymentApiResponse(boolean success) { +} diff --git a/src/main/java/com/example/payment/PaymentProcessor.java b/src/main/java/com/example/payment/PaymentProcessor.java index 137bab7d9..de22a6562 100644 --- a/src/main/java/com/example/payment/PaymentProcessor.java +++ b/src/main/java/com/example/payment/PaymentProcessor.java @@ -1,23 +1,32 @@ package com.example.payment; -//public class PaymentProcessor { -// private static final String API_KEY = "sk_test_123456"; -// -// public boolean processPayment(double amount) { -// // Anropar extern betaltjänst direkt med statisk API-nyckel -// PaymentApiResponse response = PaymentApi.charge(API_KEY, amount); -// -// // Skriver till databas direkt -// if (response.isSuccess()) { -// DatabaseConnection.getInstance() -// .executeUpdate("INSERT INTO payments (amount, status) VALUES (" + amount + ", 'SUCCESS')"); -// } -// -// // Skickar e-post direkt -// if (response.isSuccess()) { -// EmailService.sendPaymentConfirmation("user@example.com", amount); -// } -// -// return response.isSuccess(); -// } -//} +public class PaymentProcessor { + private static final String API_KEY = "sk_test_123456"; + private final DatabaseConnection databaseConnection; + private final EmailService emailService; + private final PaymentApi paymentApi; + + public PaymentProcessor(DatabaseConnection databaseConnection, EmailService emailService, PaymentApi paymentApi) { + this.databaseConnection = databaseConnection; + this.emailService = emailService; + this.paymentApi = paymentApi; + } + + public boolean processPayment(double amount) { + // Anropar extern betaltjänst direkt med statisk API-nyckel + PaymentApiResponse response = this.paymentApi.charge(API_KEY, amount); + + // Skriver till databas direkt + if (response.success()) { + this.databaseConnection + .executeUpdate("INSERT INTO payments (amount, status) VALUES (" + amount + ", 'SUCCESS')"); + } + + // Skickar e-post direkt + if (response.success()) { + emailService.sendPaymentConfirmation("user@example.com", amount); + } + + return response.success(); + } +} diff --git a/src/test/java/com/example/PaymentProcessorTest.java b/src/test/java/com/example/PaymentProcessorTest.java new file mode 100644 index 000000000..f81bfbeac --- /dev/null +++ b/src/test/java/com/example/PaymentProcessorTest.java @@ -0,0 +1,77 @@ +package com.example; + +import com.example.payment.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.sql.SQLException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class PaymentProcessorTest { + + @Mock + private PaymentApi paymentApi; + + @Mock + private DatabaseConnection databaseConnection; + + @Mock + private EmailService emailService; + + @InjectMocks + private PaymentProcessor paymentProcessor; + + @Test + @DisplayName("Should return true and save to database when payment is successful") + void paymentSuccess() throws SQLException { + PaymentApiResponse response = new PaymentApiResponse(true); + double amount = 100.0; + + Mockito.when(paymentApi.charge("sk_test_123456", amount)).thenReturn(response); + boolean result = paymentProcessor.processPayment(amount); + + assertThat(result).isTrue(); + verify(databaseConnection).executeUpdate(anyString()); + } + + @Test + @DisplayName("Should send email notification with correct details on success") + void sendEmailNotificationWhenPaymentIsSuccessful() throws SQLException { + PaymentApiResponse response = new PaymentApiResponse(true); + double amount = 100.0; + String email = "user@example.com"; + + Mockito.when(paymentApi.charge("sk_test_123456", amount)).thenReturn(response); + boolean result = paymentProcessor.processPayment(amount); + + assertThat(result).isTrue(); + verify(databaseConnection).executeUpdate(anyString()); + verify(emailService).sendPaymentConfirmation(email, amount); + } + + @Test + @DisplayName("Should return false and perform no side effects when payment fails") + void paymentFails() throws SQLException { + PaymentApiResponse response = new PaymentApiResponse(false); + double amount = 100.0; + + Mockito.when(paymentApi.charge("sk_test_123456", amount)).thenReturn(response); + boolean result = paymentProcessor.processPayment(amount); + + assertThat(result).isFalse(); + verify(databaseConnection, never()).executeUpdate(anyString()); + verify(emailService, never()).sendPaymentConfirmation(anyString(), anyDouble()); + } + +}