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
63 changes: 58 additions & 5 deletions unicorn_approvals/ApprovalsService/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
<aws.java.powertool.version>2.9.0</aws.java.powertool.version>
<aws-lambda-java-events.version>3.16.1</aws-lambda-java-events.version>
<mockito-core.version>5.23.0</mockito-core.version>
<junit.version>4.13.2</junit.version>
<junit-jupiter.version>5.12.2</junit-jupiter.version>
<mockito-junit-jupiter.version>5.23.0</mockito-junit-jupiter.version>
<aws-lambda-java-tests.version>1.1.2</aws-lambda-java-tests.version>
<aws-lambda-java-core.version>1.4.0</aws-lambda-java-core.version>
<netty-nio-client.version>2.42.13</netty-nio-client.version>
Expand Down Expand Up @@ -135,9 +136,15 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito-junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -166,7 +173,7 @@
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit4</artifactId>
<artifactId>surefire-junit-platform</artifactId>
<version>3.5.5</version>
</dependency>
</dependencies>
Expand Down Expand Up @@ -238,6 +245,52 @@
<target>21</target>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.13</version>
<configuration>
<excludes>
<exclude>approvals/dao/**</exclude>
<exclude>schema/**</exclude>
<exclude>approvals/ContractStatusNotFoundException*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"TaskToken": "test-task-token-abc123",
"Input": {
"property_id": "usa/anytown/main-street/123"
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
package approvals;

import com.amazonaws.services.lambda.runtime.Context;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.jupiter.MockitoExtension;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
@ExtendWith(MockitoExtension.class)
public class ContractStatusTests {

@Mock
private Context context;

@Mock
private DynamoDbClient dynamoDbClient;

private ContractStatusChangedHandlerFunction contractStatusChangedHandler;

@Before
@BeforeEach
public void setUp() {
contractStatusChangedHandler = new ContractStatusChangedHandlerFunction();
contractStatusChangedHandler.setDynamodbClient(dynamoDbClient);
Expand All @@ -36,16 +43,44 @@ public void setUp() {
public void shouldProcessValidContractStatusChangeEvent() throws IOException {
// Given
Path testEventPath = Paths.get("src/test/events/lambda/contract_status_changed.json");

// When
try (InputStream inputStream = Files.newInputStream(testEventPath);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

contractStatusChangedHandler.handleRequest(inputStream, outputStream, context);

// Then
String response = outputStream.toString();
assertTrue("Response should contain contract_id", response.contains("contract_id"));
assertTrue(response.contains("contract_id"));
}
}

@Test
public void shouldHandleMalformedEvent() {
// Given
String malformedJson = "{ not valid json }";
InputStream inputStream = new ByteArrayInputStream(malformedJson.getBytes(StandardCharsets.UTF_8));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

// When / Then
assertThrows(Exception.class, () ->
contractStatusChangedHandler.handleRequest(inputStream, outputStream, context));
}

@Test
public void shouldHandleDynamoDbFailure() throws IOException {
// Given
Path testEventPath = Paths.get("src/test/events/lambda/contract_status_changed.json");
when(dynamoDbClient.updateItem(any(UpdateItemRequest.class)))
.thenThrow(DynamoDbException.builder().message("DynamoDB failure").build());

// When / Then
try (InputStream inputStream = Files.newInputStream(testEventPath);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

assertThrows(DynamoDbException.class, () ->
contractStatusChangedHandler.handleRequest(inputStream, outputStream, context));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package approvals;

import approvals.helpers.TestHelpers;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;
import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import software.amazon.awssdk.services.sfn.SfnAsyncClient;
import software.amazon.awssdk.services.sfn.model.SendTaskSuccessRequest;
import software.amazon.awssdk.services.sfn.model.SendTaskSuccessResponse;

import java.lang.reflect.Field;
import java.util.concurrent.CompletableFuture;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class PropertiesApprovalSyncFunctionTests {

@Mock
private Context context;

@Mock
private SfnAsyncClient sfnAsyncClient;

private PropertiesApprovalSyncFunction handler;

@BeforeEach
public void setUp() throws Exception {
handler = new PropertiesApprovalSyncFunction();
// Inject the mock SFN client via reflection
Field sfnField = PropertiesApprovalSyncFunction.class.getDeclaredField("sfnClient");
sfnField.setAccessible(true);
sfnField.set(handler, sfnAsyncClient);
}

@Test
public void shouldSendTaskSuccessWhenApprovedWithToken() {
// Given
DynamodbEvent event = TestHelpers.createDynamoDbStreamEvent(
"usa/anytown/main-street/111",
"contract-001",
"APPROVED",
"DRAFT",
"test-task-token-123",
null
);

when(sfnAsyncClient.sendTaskSuccess(any(SendTaskSuccessRequest.class)))
.thenReturn(CompletableFuture.completedFuture(SendTaskSuccessResponse.builder().build()));

// When
StreamsEventResponse response = handler.handleRequest(event, context);

// Then
assertTrue(response.getBatchItemFailures() == null || response.getBatchItemFailures().isEmpty());
verify(sfnAsyncClient, times(1)).sendTaskSuccess(any(SendTaskSuccessRequest.class));
}

@Test
public void shouldSkipRecordWithoutTaskToken() {
// Given - no task token in either NewImage or OldImage
DynamodbEvent event = TestHelpers.createDynamoDbStreamEvent(
"usa/anytown/main-street/111",
"contract-001",
"APPROVED",
null,
null,
null
);

// When
StreamsEventResponse response = handler.handleRequest(event, context);

// Then
assertTrue(response.getBatchItemFailures() == null || response.getBatchItemFailures().isEmpty());
verifyNoInteractions(sfnAsyncClient);
}

@Test
public void shouldSkipRecordWithNonApprovedStatus() {
// Given - status is DRAFT, not APPROVED
DynamodbEvent event = TestHelpers.createDynamoDbStreamEvent(
"usa/anytown/main-street/111",
"contract-001",
"DRAFT",
null,
"test-task-token-123",
null
);

// When
StreamsEventResponse response = handler.handleRequest(event, context);

// Then
assertTrue(response.getBatchItemFailures() == null || response.getBatchItemFailures().isEmpty());
verifyNoInteractions(sfnAsyncClient);
}

@Test
public void shouldSkipRecordWithMissingNewImage() {
// Given
DynamodbEvent event = TestHelpers.createDynamoDbStreamEventWithNullNewImage();

// When
StreamsEventResponse response = handler.handleRequest(event, context);

// Then
assertTrue(response.getBatchItemFailures() == null || response.getBatchItemFailures().isEmpty());
verifyNoInteractions(sfnAsyncClient);
}
}
Loading
Loading