package software.amazon.logs.loggroup; import org.mockito.ArgumentCaptor; import software.amazon.awssdk.services.cloudwatchlogs.model.CloudWatchLogsRequest; import software.amazon.awssdk.services.cloudwatchlogs.model.CreateLogGroupRequest; import software.amazon.awssdk.services.cloudwatchlogs.model.PutRetentionPolicyRequest; import software.amazon.cloudformation.exceptions.CfnAlreadyExistsException; import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; import software.amazon.cloudformation.proxy.Logger; import software.amazon.cloudformation.proxy.OperationStatus; import software.amazon.cloudformation.proxy.ProgressEvent; import software.amazon.cloudformation.proxy.ResourceHandlerRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import software.amazon.awssdk.services.cloudwatchlogs.model.CreateLogGroupResponse; import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogGroupsResponse; import software.amazon.awssdk.services.cloudwatchlogs.model.LogGroup; import software.amazon.awssdk.services.cloudwatchlogs.model.PutRetentionPolicyResponse; import software.amazon.awssdk.services.cloudwatchlogs.model.ResourceAlreadyExistsException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class CreateHandlerTest { private CreateHandler handler; @Mock private AmazonWebServicesClientProxy proxy; @Mock private Logger logger; @BeforeEach public void setup() { handler = new CreateHandler(); proxy = mock(AmazonWebServicesClientProxy.class); logger = mock(Logger.class); } @Test public void handleRequest_Success() { final DescribeLogGroupsResponse describeResponseInitial = DescribeLogGroupsResponse.builder() .logGroups(Collections.emptyList()) .build(); final CreateLogGroupResponse createLogGroupResponse = CreateLogGroupResponse.builder().build(); final PutRetentionPolicyResponse putRetentionPolicyResponse = PutRetentionPolicyResponse.builder().build(); final LogGroup logGroup = LogGroup.builder() .logGroupName("LogGroup") .retentionInDays(1) .kmsKeyId("arn:aws:kms:us-east-1:$123456789012:key/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") .build(); final Map tags = new HashMap() {{ put("key-1", "value-1"); put("key-2", "value-2"); }}; doReturn(describeResponseInitial, createLogGroupResponse, putRetentionPolicyResponse) .when(proxy) .injectCredentialsAndInvokeV2( ArgumentMatchers.any(), ArgumentMatchers.any() ); final ResourceModel model = ResourceModel.builder() .logGroupName("LogGroup") .retentionInDays(1) .kmsKeyId("arn:aws:kms:us-east-1:$123456789012:key/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") .build(); final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(model) .desiredResourceTags(tags) .build(); final ProgressEvent response = handler.handleRequest(proxy, request, null, logger); ArgumentCaptor requests = ArgumentCaptor.forClass(CloudWatchLogsRequest.class); verify(proxy, times(2)).injectCredentialsAndInvokeV2(requests.capture(), any()); assertThat(requests.getAllValues().get(0)).isEqualTo(CreateLogGroupRequest.builder() .logGroupName("LogGroup") .kmsKeyId("arn:aws:kms:us-east-1:$123456789012:key/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") .tags(tags) .build()); assertThat(requests.getAllValues().get(1)).isEqualTo(PutRetentionPolicyRequest.builder() .logGroupName("LogGroup") .retentionInDays(1) .build()); assertThat(response).isNotNull(); assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); assertThat(response.getCallbackContext()).isNull(); assertThat(response.getCallbackDelaySeconds()).isEqualTo(0); assertThat(response.getResourceModels()).isNull(); assertThat(response.getResourceModel()).isEqualToComparingOnlyGivenFields(logGroup); assertThat(response.getMessage()).isNull(); assertThat(response.getErrorCode()).isNull(); } @Test public void handleRequest_SuccessGeneratedLogGroupName_ModelIsNull() { final DescribeLogGroupsResponse describeResponseInitial = DescribeLogGroupsResponse.builder() .logGroups(Collections.emptyList()) .build(); final CreateLogGroupResponse createLogGroupResponse = CreateLogGroupResponse.builder().build(); doReturn(describeResponseInitial, createLogGroupResponse) .when(proxy) .injectCredentialsAndInvokeV2( ArgumentMatchers.any(), ArgumentMatchers.any() ); final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .clientRequestToken("token") .build(); final ProgressEvent response = handler.handleRequest(proxy, request, null, logger); assertThat(response).isNotNull(); assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); assertThat(response.getCallbackContext()).isNull(); assertThat(response.getCallbackDelaySeconds()).isEqualTo(0); assertThat(response.getResourceModels()).isNull(); // There isn't an easy way to check the generated value of the name assertThat(response.getResourceModel()).isNotNull(); assertThat(response.getMessage()).isNull(); assertThat(response.getErrorCode()).isNull(); } @Test public void handleRequest_SuccessGeneratedLogGroupName() { final DescribeLogGroupsResponse describeResponseInitial = DescribeLogGroupsResponse.builder() .logGroups(Collections.emptyList()) .build(); final CreateLogGroupResponse createLogGroupResponse = CreateLogGroupResponse.builder().build(); final PutRetentionPolicyResponse putRetentionPolicyResponse = PutRetentionPolicyResponse.builder().build(); doReturn(describeResponseInitial, createLogGroupResponse, putRetentionPolicyResponse) .when(proxy) .injectCredentialsAndInvokeV2( ArgumentMatchers.any(), ArgumentMatchers.any() ); final ResourceModel model = ResourceModel.builder() .retentionInDays(1) .kmsKeyId("arn:aws:kms:us-east-1:$123456789012:key/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") .build(); final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .logicalResourceIdentifier("id") .clientRequestToken("token") .desiredResourceState(model) .build(); final Map systemTags = new HashMap<>(); systemTags.put("aws:cloudformation:stack-name", "unit_test_Stack"); request.setSystemTags(systemTags); request.setClientRequestToken("4b90a7e4-b790-456b-a937-0cfdfa212fed"); request.setLogicalResourceIdentifier("taskDefinition"); final ProgressEvent response = handler.handleRequest(proxy, request, null, logger); assertThat(response).isNotNull(); assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); assertThat(response.getCallbackContext()).isNull(); assertThat(response.getCallbackDelaySeconds()).isEqualTo(0); assertThat(response.getResourceModel().getLogGroupName()).startsWith("unit_test_Stack"); assertThat(response.getResourceModels()).isNull(); // There isn't an easy way to check the generated value of the name assertThat(response.getResourceModel()).isNotNull(); assertThat(response.getMessage()).isNull(); assertThat(response.getErrorCode()).isNull(); } @Test public void handleRequest_FailureAlreadyExists() { doThrow(ResourceAlreadyExistsException.class) .when(proxy) .injectCredentialsAndInvokeV2( ArgumentMatchers.any(), ArgumentMatchers.any() ); final ResourceModel model = ResourceModel.builder() .logGroupName("LogGroup") .retentionInDays(1) .kmsKeyId("arn:aws:kms:us-east-1:$123456789012:key/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") .build(); final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(model) .build(); assertThrows(CfnAlreadyExistsException.class, () -> handler.handleRequest(proxy, request, null, logger)); } }