Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.4</version>
<version>6.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -28,9 +28,14 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.15.2</version>
<version>5.21.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
Expand All @@ -57,6 +62,19 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.22.0</version>
<dependencies>
<dependency>
<groupId>org.pitest</groupId>
<artifactId>pitest-junit5-plugin</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies>
</plugin>

<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/example/payment/DatabaseConnection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.payment;

public interface DatabaseConnection {
void executeUpdate(String query);
}
5 changes: 5 additions & 0 deletions src/main/java/com/example/payment/EmailService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.payment;

public interface EmailService {
void sendPaymentConfirmation(String email, double amount);
}
5 changes: 5 additions & 0 deletions src/main/java/com/example/payment/PaymentApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.payment;

public interface PaymentApi {
PaymentApiResponse charge(String apiKey, double amount);
}
4 changes: 4 additions & 0 deletions src/main/java/com/example/payment/PaymentApiResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.payment;

public record PaymentApiResponse(boolean success) {
}
51 changes: 30 additions & 21 deletions src/main/java/com/example/payment/PaymentProcessor.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
77 changes: 77 additions & 0 deletions src/test/java/com/example/PaymentProcessorTest.java
Original file line number Diff line number Diff line change
@@ -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());
}

}