Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PUB-2576 - Add functional tests for audit logs #467

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
86e4180
Replace constructor with lombok annotation
NatashaAlker Dec 19, 2024
b9b2e7d
Add delete method to clean up after audit functional test
NatashaAlker Dec 19, 2024
8aff3eb
Add delete method to clean up after audit functional test
NatashaAlker Dec 19, 2024
dab2f88
Replace constructor with lombok annotation
NatashaAlker Dec 19, 2024
bc081cc
Add get method for audit functional tests
NatashaAlker Dec 19, 2024
f57ebf6
Add get method for audit functional tests
NatashaAlker Dec 19, 2024
6dd6539
Add functional tests for audit endpoints
NatashaAlker Dec 19, 2024
e980853
Merge branch 'master' into PUB-2576
NatashaAlker Dec 19, 2024
c605cbc
Fix PMD issue
NatashaAlker Dec 19, 2024
27825b8
Add unit test for testing support delete audit method
NatashaAlker Dec 19, 2024
205d3d2
Remove whitespace
NatashaAlker Dec 19, 2024
ff77fa9
Add unit test for functional test helper method
NatashaAlker Dec 19, 2024
39a6b75
Merge branch 'freeze' into PUB-2576
NatashaAlker Jan 2, 2025
a67f2b2
Remove unused method
NatashaAlker Jan 6, 2025
97c3089
Filter returned audits for those created within the test only
NatashaAlker Jan 6, 2025
6bab7eb
Update error message to also apply to audit logs
NatashaAlker Jan 6, 2025
a08bf94
Add integration tests for testing support delete endpoint
NatashaAlker Jan 6, 2025
8ebaf69
Fix checkstyle and pmd fails
NatashaAlker Jan 6, 2025
33ac555
Merge branch 'master' into PUB-2576
NatashaAlker Jan 6, 2025
b28c068
test commits being picked up by jenkins
NatashaAlker Jan 7, 2025
3ef2f98
Update get request to include test generated email
NatashaAlker Jan 9, 2025
3106714
Add helper functionality to update the timestamp on an audit log
NatashaAlker Jan 9, 2025
6a2faad
Add tests for helper functionality
NatashaAlker Jan 9, 2025
d40d85f
Add tests for helper functions
NatashaAlker Jan 9, 2025
81f2b70
Add functional test for deletion of out of date audits
NatashaAlker Jan 9, 2025
2cdb0b7
Merge branch 'master' into PUB-2576
ChrisS1512 Jan 10, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package uk.gov.hmcts.reform.pip.account.management.controllers;

import com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.HttpHeaders;
import io.restassured.response.Response;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import uk.gov.hmcts.reform.pip.account.management.model.AuditLog;
import uk.gov.hmcts.reform.pip.account.management.utils.FunctionalTestBase;
import uk.gov.hmcts.reform.pip.account.management.utils.OAuthClient;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;

@ExtendWith(SpringExtension.class)
@ActiveProfiles(profiles = "functional")
@SpringBootTest(classes = {OAuthClient.class})
class AuditCreationTest extends FunctionalTestBase {
private static final UUID USER_ID = UUID.randomUUID();
private static final String TEST_EMAIL_PREFIX = String.format(
"pip-am-test-email-%s", ThreadLocalRandom.current().nextInt(1000, 9999));
private static final String TEST_EMAIL = TEST_EMAIL_PREFIX + "@justice.gov.uk";
private static final String ROLES = "SYSTEM_ADMIN";
private static final String USER_PROVENANCE = "PI_AAD";
private static final String ACTION = "PUBLICATION_UPLOAD";
private static final String DETAILS = "Publication with artefact id %s successfully uploaded";
private static final Integer PAGE_NUMBER = 0;
private static final Integer PAGE_SIZE = 2;

private static final String AUDIT_URL = "/audit";
private static final String GET_AUDIT_URL = "/audit/%s";
private static final String TESTING_SUPPORT_AUDIT_URL = "/testing-support/audit/";
private static final String BEARER = "Bearer ";
private static final String CONTENT = "content";

private Map<String, String> bearer;

@BeforeAll
public void startUp() {
bearer = Map.of(HttpHeaders.AUTHORIZATION, BEARER + accessToken);
}

@AfterAll
public void teardown() {
doDeleteRequest(TESTING_SUPPORT_AUDIT_URL + TEST_EMAIL_PREFIX, bearer);
}

private AuditLog createAuditLog() {

UUID artefactId = UUID.randomUUID();
String responseBody = """
{
"userId": "%s",
"userEmail": "%s",
"roles": "%s",
"userProvenance": "%s",
"action": "%s",
"details": "%s"
}
""".formatted(USER_ID, TEST_EMAIL, ROLES, USER_PROVENANCE, ACTION, String.format(DETAILS, artefactId));

Response response = doPostRequest(AUDIT_URL, bearer, responseBody);

assertThat(response.getStatusCode()).isEqualTo(OK.value());

return response.getBody().as(AuditLog.class);
}

private void deleteAuditLog() {
Response response = doDeleteRequest(AUDIT_URL, bearer);

assertThat(response.getStatusCode()).isEqualTo(OK.value());
}

@Test
void shouldBeAbleToCreateAndGetAnAuditRecord() {
AuditLog auditLog = createAuditLog();

Response getResponse = doGetRequest(String.format(GET_AUDIT_URL, auditLog.getId()), bearer);
assertThat(getResponse.getStatusCode()).isEqualTo(OK.value());

AuditLog retrievedAuditLog = getResponse.getBody().as(AuditLog.class);
assertThat(retrievedAuditLog.getId()).isEqualTo(auditLog.getId());
assertThat(retrievedAuditLog.getUserEmail()).isEqualTo(auditLog.getUserEmail());
}

@Test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll run into the issues with the below when it runs on the master pipeline due to master linking up to the Staging DB and the numbers below not matching.

To fix this, I'd suggest using the functionality in the recently merged PUB-1909, to filter based on email alongside the page sizes below to ensure it will always return a consistent result

void shouldBeAbleToGetAllAuditLogsUsingDefaultDisplayParameters() {
AuditLog auditLog = createAuditLog();

Response getResponse = doGetRequestWithQueryParameters(String.format(AUDIT_URL), bearer,
"", "", TEST_EMAIL_PREFIX);

assertThat(getResponse.getStatusCode()).isEqualTo(OK.value());
assertThat(getResponse.jsonPath().getInt("pageable.pageNumber")).isEqualTo(0);
assertThat(getResponse.jsonPath().getInt("pageable.pageSize")).isEqualTo(25);

List<AuditLog> retrievedAuditLogs = getResponse.jsonPath().getList(CONTENT, AuditLog.class);

assertThat(retrievedAuditLogs).isNotNull();
assertThat(retrievedAuditLogs.size()).isEqualTo(2);
assertThat(retrievedAuditLogs.getFirst().getId()).isEqualTo(auditLog.getId());
}

@Test
void shouldBeAbleToGetAllAuditLogsUsingCustomDisplayParameters() {
AuditLog auditLog = createAuditLog();

Response getResponse = doGetRequestWithQueryParameters(String.format(AUDIT_URL), bearer,
PAGE_NUMBER.toString(), PAGE_SIZE.toString(), TEST_EMAIL_PREFIX);

assertThat(getResponse.getStatusCode()).isEqualTo(OK.value());
assertThat(getResponse.jsonPath().getInt("pageable.pageNumber")).isEqualTo(0);
assertThat(getResponse.jsonPath().getInt("pageable.pageSize")).isEqualTo(2);
assertThat(getResponse.jsonPath().getInt("totalPages")).isEqualTo(2);
assertThat(getResponse.jsonPath().getInt("totalElements")).isEqualTo(4);

List<AuditLog> retrievedAuditLogs = getResponse.jsonPath().getList(CONTENT, AuditLog.class);

assertThat(retrievedAuditLogs).isNotNull();
assertThat(retrievedAuditLogs.getFirst().getId()).isEqualTo(auditLog.getId());
assertThat(retrievedAuditLogs.size()).isEqualTo(2);
}

@Test
void shouldReturnErrorWhenAuditRecordDoesNotExist() {
Response getResponse = doGetRequest(String.format(GET_AUDIT_URL, USER_ID), bearer);

assertThat(getResponse.getStatusCode()).isEqualTo(NOT_FOUND.value());
}

@Test
void shouldReturnOkForDeleteAuditLogsIfAllInDate() {
AuditLog auditLog = createAuditLog();

deleteAuditLog();

Response getResponse = doGetRequestWithQueryParameters(String.format(AUDIT_URL), bearer,
"", "", TEST_EMAIL_PREFIX);

List<AuditLog> retrievedAuditLogs = getResponse.jsonPath().getList(CONTENT, AuditLog.class);

assertThat(retrievedAuditLogs).isNotNull();
assertThat(retrievedAuditLogs.getFirst().getId()).isEqualTo(auditLog.getId());
assertThat(retrievedAuditLogs.size()).isEqualTo(3);
}

@Test
void shouldBeAbleToDeleteAnOutOfDateAudit() {
Response getResponse = doGetRequestWithQueryParameters(String.format(AUDIT_URL), bearer,
"", "", TEST_EMAIL_PREFIX);
List<AuditLog> retrievedAuditLogs = getResponse.jsonPath().getList(CONTENT, AuditLog.class);
assertThat(retrievedAuditLogs.size()).isEqualTo(4);

AuditLog auditLog = createAuditLog();
doPutRequest(TESTING_SUPPORT_AUDIT_URL + auditLog.getId(), bearer);

getResponse = doGetRequestWithQueryParameters(String.format(AUDIT_URL), bearer, "", "", TEST_EMAIL_PREFIX);
retrievedAuditLogs = getResponse.jsonPath().getList(CONTENT, AuditLog.class);
assertThat(retrievedAuditLogs.size()).isEqualTo(5);

deleteAuditLog();

getResponse = doGetRequestWithQueryParameters(String.format(AUDIT_URL), bearer, "", "", TEST_EMAIL_PREFIX);
retrievedAuditLogs = getResponse.jsonPath().getList(CONTENT, AuditLog.class);
assertThat(retrievedAuditLogs.size()).isEqualTo(4);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ protected Response doGetRequest(final String path, final Map<String, String> add
.thenReturn();
}

protected Response doGetRequestWithQueryParameters(final String path, final Map<String, String> additionalHeaders,
final String pageNumber, final String pageSize, final String email) {
return given()
.relaxedHTTPSValidation()
.headers(getRequestHeaders(additionalHeaders))
.queryParam("pageNumber", pageNumber)
.queryParam("pageSize", pageSize)
.queryParam("email", email)
.when()
.get(path)
.thenReturn();
}

protected Response doGetRequestWithRequestParams(final String path, final Map<String, String> additionalHeaders,
Map<String, String> params) {
return given()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import uk.gov.hmcts.reform.pip.account.management.Application;
import uk.gov.hmcts.reform.pip.account.management.config.ClientConfiguration;
import uk.gov.hmcts.reform.pip.account.management.model.AuditLog;
import uk.gov.hmcts.reform.pip.account.management.model.AzureAccount;
import uk.gov.hmcts.reform.pip.account.management.model.CreationEnum;
import uk.gov.hmcts.reform.pip.account.management.model.MediaApplication;
Expand All @@ -33,8 +34,10 @@
import uk.gov.hmcts.reform.pip.account.management.utils.IntegrationTestBase;
import uk.gov.hmcts.reform.pip.model.account.Roles;
import uk.gov.hmcts.reform.pip.model.account.UserProvenances;
import uk.gov.hmcts.reform.pip.model.enums.AuditAction;

import java.io.InputStream;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -47,21 +50,25 @@
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static uk.gov.hmcts.reform.pip.model.enums.AuditAction.PUBLICATION_UPLOAD;

@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@ActiveProfiles("integration")
@AutoConfigureEmbeddedDatabase(type = AutoConfigureEmbeddedDatabase.DatabaseType.POSTGRES)
@WithMockUser(username = "admin", authorities = {"APPROLE_api.request.admin"})
@SuppressWarnings({"PMD.TooManyMethods", "PMD.ExcessiveImports", "PMD.UnitTestShouldIncludeAssert"})
@SuppressWarnings({"PMD.TooManyMethods", "PMD.ExcessiveImports", "PMD.UnitTestShouldIncludeAssert",
"PMD.CouplingBetweenObjects"})
class TestingSupportApiTest extends IntegrationTestBase {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

private static final String TESTING_SUPPORT_BASE_URL = "/testing-support/";
private static final String TESTING_SUPPORT_ACCOUNT_URL = TESTING_SUPPORT_BASE_URL + "account/";
private static final String TESTING_SUPPORT_APPLICATION_URL = TESTING_SUPPORT_BASE_URL + "application/";
private static final String TESTING_SUPPORT_CREATE_ACCOUNT_URL = TESTING_SUPPORT_BASE_URL + "account";
private static final String TESTING_SUPPORT_AUDIT_URL = TESTING_SUPPORT_BASE_URL + "audit/";

private static final String ACCOUNT_URL = "/account/";
private static final String ACCOUNT_ADD_USER_URL = ACCOUNT_URL + "add/pi";
Expand All @@ -75,6 +82,7 @@ class TestingSupportApiTest extends IntegrationTestBase {
private static final String EMAIL = EMAIL_PREFIX + "user123@test.com";
private static final String PASSWORD = "P@55word11";
private static final String ID = "1234";
private static final UUID USER_ID = UUID.randomUUID();

private static final String PROVENANCE_USER_ID = UUID.randomUUID().toString();
private static final UserProvenances PROVENANCE = UserProvenances.PI_AAD;
Expand All @@ -86,6 +94,10 @@ class TestingSupportApiTest extends IntegrationTestBase {
private static final String EMPLOYER = "Test employer";
private static final MediaApplicationStatus PENDING_STATUS = MediaApplicationStatus.PENDING;

private static final String AUDIT_URL = "/audit";
private static final AuditAction ACTION = PUBLICATION_UPLOAD;
private static final String DETAILS = "Publication successfully uploaded";

private static final String UNAUTHORIZED_ROLE = "APPROLE_unknown.authorized";
private static final String UNAUTHORIZED_USERNAME = "unauthorized_isAuthorized";

Expand Down Expand Up @@ -282,6 +294,42 @@ void testTestingSupportDeleteApplicationsWithEmailPrefix() throws Exception {
.andExpect(status().isNotFound());
}

@Test
void testTestingSupportDeleteAuditLogsWithEmailPrefix() throws Exception {
AuditLog auditLog = createAuditLog();

mockMvc.perform(get(AUDIT_URL + "/" + auditLog.getId()))
.andExpect(status().isOk());

MvcResult deleteResponse = mockMvc.perform(delete(TESTING_SUPPORT_AUDIT_URL + EMAIL_PREFIX))
.andExpect(status().isOk())
.andReturn();

assertThat(deleteResponse.getResponse().getContentAsString())
.as("Audit Log delete response does not match")
.isEqualTo("2 audit log(s) deleted with user email starting with " + EMAIL_PREFIX);

mockMvc.perform(get(AUDIT_URL + "/" + auditLog.getId()))
.andExpect(status().isNotFound());
}

@Test
void testTestingSupportUpdateAuditLogTimestampWithId() throws Exception {
AuditLog auditLog = createAuditLog();
LocalDate expiredDate = auditLog.getTimestamp().minusDays(200).toLocalDate();

mockMvc.perform(get(AUDIT_URL + "/" + auditLog.getId()))
.andExpect(status().isOk());

MvcResult updateAudit = mockMvc.perform(put(TESTING_SUPPORT_AUDIT_URL + auditLog.getId()))
.andExpect(status().isOk())
.andReturn();

assertThat(updateAudit.getResponse().getContentAsString())
.as("Audit Log update response does not match")
.contains("1 audit log(s) updated with timestamp " + expiredDate);
}

@Test
@WithMockUser(username = UNAUTHORIZED_USERNAME, authorities = {UNAUTHORIZED_ROLE})
void testUnauthorisedTestingSupportDeleteAccounts() throws Exception {
Expand All @@ -296,6 +344,13 @@ void testUnauthorisedTestingSupportDeleteApplications() throws Exception {
.andExpect(status().isForbidden());
}

@Test
@WithMockUser(username = UNAUTHORIZED_USERNAME, authorities = {UNAUTHORIZED_ROLE})
void testUnauthorisedTestingSupportDeleteAudits() throws Exception {
mockMvc.perform(delete(TESTING_SUPPORT_AUDIT_URL + EMAIL_PREFIX))
.andExpect(status().isForbidden());
}

private PiUser createUser() {
PiUser user = new PiUser();
user.setEmail(EMAIL);
Expand All @@ -318,7 +373,6 @@ private AzureAccount createAccount(String password) {
return newAccount;
}


@SuppressWarnings("PMD.SignatureDeclareThrowsException")
private MediaApplication createApplication() throws Exception {
MediaApplication application = new MediaApplication();
Expand Down Expand Up @@ -346,4 +400,29 @@ private MediaApplication createApplication() throws Exception {
return OBJECT_MAPPER.readValue(mvcResult.getResponse().getContentAsString(), MediaApplication.class);
}
}

@SuppressWarnings("PMD.SignatureDeclareThrowsException")
private AuditLog createAuditLog() throws Exception {
AuditLog auditLog = new AuditLog();
auditLog.setId(USER_ID);
auditLog.setUserId(ID);
auditLog.setUserEmail(EMAIL);
auditLog.setRoles(ROLE);
auditLog.setUserProvenance(PROVENANCE);
auditLog.setAction(ACTION);
auditLog.setDetails(DETAILS);

MockHttpServletRequestBuilder postRequest = MockMvcRequestBuilders
.post(AUDIT_URL)
.content(OBJECT_MAPPER.writeValueAsString(auditLog))
.header(ISSUER_HEADER, ISSUER_ID)
.contentType(MediaType.APPLICATION_JSON);

MvcResult mvcResult = mockMvc.perform(postRequest)
.andExpect(status().isOk())
.andReturn();

return OBJECT_MAPPER.readValue(mvcResult.getResponse().getContentAsString(), AuditLog.class);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
Expand Down Expand Up @@ -34,6 +34,7 @@
@ApiResponse(responseCode = "401", description = "Invalid access credential")
@ApiResponse(responseCode = "403", description = "User has not been authorized")
@IsAdmin
@AllArgsConstructor
@SecurityRequirement(name = "bearerAuth")
public class AuditController {

Expand All @@ -42,11 +43,6 @@ public class AuditController {
private static final String OK_ERROR_CODE = "200";
private static final String NOT_FOUND_ERROR_CODE = "404";

@Autowired
public AuditController(AuditService auditService) {
this.auditService = auditService;
}

@ApiResponse(responseCode = OK_ERROR_CODE, description = "All audit logs returned as a page with filtering.")
@Operation(summary = "Get all audit logs returned as a page")
@GetMapping
Expand Down
Loading
Loading