diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 98e5207f..74f661c8 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -116,6 +116,19 @@ "calendar.endpoint.calendars.calendarId.put" ] }, + { + "methods": [ + "PUT" + ], + "pathPattern": "/transactions/{dcbTransactionId}/renew", + "permissionsRequired": [ + "dcb.transactions.renew.put" + ], + "modulePermissions": [ + "circulation.renew-by-id.post", + "circulation-storage.loan-policies.item.get" + ] + }, { "methods": [ "GET" @@ -267,7 +280,8 @@ "dcb.transactions.status.get", "dcb.transactions.collection.get", "dcb.ecs-request.transactions.post", - "dcb.transactions.item.put" + "dcb.transactions.item.put", + "dcb.transactions.renew.put" ] }, { @@ -275,6 +289,11 @@ "displayName": "creates new transaction", "description": "creates new transaction" }, + { + "permissionName": "dcb.transactions.renew.put", + "displayName": "renew loan by transaction id", + "description": "renew loan by transaction id" + }, { "permissionName": "dcb.transactions.status.get", "displayName": "get transaction details", diff --git a/src/main/java/org/folio/dcb/client/feign/CirculationClient.java b/src/main/java/org/folio/dcb/client/feign/CirculationClient.java index 74c4a89c..1e60f69c 100644 --- a/src/main/java/org/folio/dcb/client/feign/CirculationClient.java +++ b/src/main/java/org/folio/dcb/client/feign/CirculationClient.java @@ -4,6 +4,8 @@ import org.folio.dcb.domain.dto.CheckOutRequest; import org.folio.dcb.domain.dto.CirculationRequest; import org.folio.dcb.domain.dto.LoanCollection; +import org.folio.dcb.domain.dto.RenewByIdRequest; +import org.folio.dcb.domain.dto.RenewByIdResponse; import org.folio.spring.config.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @@ -30,4 +32,7 @@ CirculationRequest updateRequest(@PathVariable("requestId") String requestId, @GetMapping("/loans") LoanCollection fetchLoanByQuery(@RequestParam("query") String query); + + @PostMapping("/renew-by-id") + RenewByIdResponse renewById(@RequestBody RenewByIdRequest renewByIdRequest); } diff --git a/src/main/java/org/folio/dcb/client/feign/CirculationLoanPolicyStorageClient.java b/src/main/java/org/folio/dcb/client/feign/CirculationLoanPolicyStorageClient.java index ec96923e..2ff6669a 100644 --- a/src/main/java/org/folio/dcb/client/feign/CirculationLoanPolicyStorageClient.java +++ b/src/main/java/org/folio/dcb/client/feign/CirculationLoanPolicyStorageClient.java @@ -1,5 +1,6 @@ package org.folio.dcb.client.feign; +import org.folio.dcb.domain.dto.LoanPolicy; import org.folio.dcb.domain.dto.LoanPolicyCollection; import org.folio.spring.config.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClient; @@ -10,4 +11,6 @@ public interface CirculationLoanPolicyStorageClient { @GetMapping("/loan-policies") LoanPolicyCollection fetchLoanPolicyByQuery(@RequestParam("query") String query); + @GetMapping("/loan-policies/{id}") + LoanPolicy fetchLoanPolicyById(@RequestParam("id") String id); } diff --git a/src/main/java/org/folio/dcb/controller/TransactionApiController.java b/src/main/java/org/folio/dcb/controller/TransactionApiController.java index dd569b76..0ca8bcc1 100644 --- a/src/main/java/org/folio/dcb/controller/TransactionApiController.java +++ b/src/main/java/org/folio/dcb/controller/TransactionApiController.java @@ -22,6 +22,13 @@ public class TransactionApiController implements TransactionsApi { private final TransactionsService transactionsService; private final TransactionAuditService transactionAuditService; + + @Override + public ResponseEntity renewItemLoanByTransactionId(String dcbTransactionId) { + return ResponseEntity.status(HttpStatus.OK) + .body(transactionsService.renewLoanByTransactionId(dcbTransactionId)); + } + @Override public ResponseEntity getTransactionStatusById(String dcbTransactionId) { log.info("getTransactionStatus:: by id {} ", dcbTransactionId); diff --git a/src/main/java/org/folio/dcb/service/TransactionsService.java b/src/main/java/org/folio/dcb/service/TransactionsService.java index 549db7d7..39831746 100644 --- a/src/main/java/org/folio/dcb/service/TransactionsService.java +++ b/src/main/java/org/folio/dcb/service/TransactionsService.java @@ -19,5 +19,5 @@ public interface TransactionsService { TransactionStatusResponse getTransactionStatusById(String dcbTransactionId); TransactionStatusResponseCollection getTransactionStatusList(OffsetDateTime fromDate, OffsetDateTime toDate, Integer pageNumber, Integer pageSize); void updateTransactionDetails(String dcbTransactionId, DcbUpdateTransaction dcbUpdateTransaction); - + TransactionStatusResponse renewLoanByTransactionId(String dcbTransactionId); } diff --git a/src/main/java/org/folio/dcb/service/impl/TransactionsServiceImpl.java b/src/main/java/org/folio/dcb/service/impl/TransactionsServiceImpl.java index 4a91738e..7ae58ceb 100644 --- a/src/main/java/org/folio/dcb/service/impl/TransactionsServiceImpl.java +++ b/src/main/java/org/folio/dcb/service/impl/TransactionsServiceImpl.java @@ -1,5 +1,10 @@ package org.folio.dcb.service.impl; +import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.LENDER; +import static org.folio.dcb.domain.dto.TransactionStatus.StatusEnum.ITEM_CHECKED_OUT; +import static org.folio.dcb.utils.TransactionDetailsUtil.rolesNotEqual; +import static org.folio.dcb.utils.TransactionDetailsUtil.statusesNotEqual; + import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.folio.dcb.client.feign.CirculationClient; @@ -11,6 +16,8 @@ import org.folio.dcb.domain.dto.LoanCollection; import org.folio.dcb.domain.dto.LoanPolicy; import org.folio.dcb.domain.dto.LoanPolicyCollection; +import org.folio.dcb.domain.dto.RenewByIdRequest; +import org.folio.dcb.domain.dto.RenewByIdResponse; import org.folio.dcb.domain.dto.RenewalInfo; import org.folio.dcb.domain.dto.TransactionStatus; import org.folio.dcb.domain.dto.TransactionStatusResponse; @@ -34,6 +41,7 @@ import org.springframework.stereotype.Service; import java.time.OffsetDateTime; +import java.util.Objects; import java.util.Optional; @Service @@ -143,7 +151,7 @@ private Optional getLoanRenewalDetails(TransactionEntity tra } private static boolean isTxnItemCheckoutAndRoleIsBorrowerOrBorrowingPickup(TransactionEntity transactionEntity) { - return transactionEntity.getStatus() == TransactionStatus.StatusEnum.ITEM_CHECKED_OUT + return transactionEntity.getStatus() == ITEM_CHECKED_OUT && (transactionEntity.getRole() == DcbTransaction.RoleEnum.BORROWING_PICKUP || transactionEntity.getRole() == DcbTransaction.RoleEnum.BORROWER); } @@ -177,6 +185,65 @@ public TransactionStatusResponseCollection getTransactionStatusList(OffsetDateTi .build(); } + @Override + public TransactionStatusResponse renewLoanByTransactionId(String dcbTransactionId) { + log.info("renewLoanByTransactionId:: getting transaction by id {} ", dcbTransactionId); + var transaction = getTransactionEntityOrThrow(dcbTransactionId); + validateTransactionForRenewal(transaction); + var itemId = transaction.getItemId(); + var patronId = transaction.getPatronId(); + var renewalResponse = circulationClient.renewById(buildRenewRequest(itemId, patronId)); + log.debug("renewLoanByTransactionId:: Renew response {}", renewalResponse); + validateRenewalResponse(dcbTransactionId, renewalResponse, itemId); + var loanPolicy = circulationLoanPolicyStorageClient.fetchLoanPolicyById( + renewalResponse.getLoanPolicyId()); + log.info("renewLoanByTransactionId:: Loan policy response {}", loanPolicy); + validateLoanPolicy(renewalResponse.getLoanPolicyId(), loanPolicy); + var loanRenewalDetails = Optional.of(new LoanRenewalDetails(renewalResponse.getRenewalCount(), + loanPolicy.getRenewalsPolicy().getNumberAllowed(), loanPolicy.getRenewable())); + return generateTransactionStatusResponseFromTransactionEntity(transaction, loanRenewalDetails); + } + + private void validateLoanPolicy(String loanPolicyId, LoanPolicy loanPolicy) { + if (Objects.isNull(loanPolicy)) { + log.debug("validateLoanPolicy:: Loan policy is null"); + throw new NotFoundException(String.format("Loan policy not found for loan id: %s", loanPolicyId)); + } + } + + private static void validateRenewalResponse(String dcbTransactionId, RenewByIdResponse response, + String itemId) { + if(Objects.isNull(response)) { + log.debug("validateRenewalResponse:: renewal response is null"); + throw new NotFoundException(String.format("Renew failed. Transaction id:%s, Item id: %s", + dcbTransactionId, itemId)); + } + } + + private static RenewByIdRequest buildRenewRequest(String itemId, String patronId) { + return RenewByIdRequest.builder() + .itemId(itemId) + .userId(patronId) + .build(); + } + + private static void validateTransactionForRenewal(TransactionEntity transaction) { + TransactionStatus.StatusEnum status = transaction.getStatus(); + DcbTransaction.RoleEnum role = transaction.getRole(); + if (statusesNotEqual(ITEM_CHECKED_OUT, status)) { + log.debug("validateTransactionForRenewal:: Transaction status is {}", status); + throw new StatusException(String.format( + "Loan couldn't be renewed with transaction status %s, it could be renewed only with " + + "ITEM_CHECKED_OUT status", status)); + } + if (rolesNotEqual(LENDER, role)) { + log.debug("validateTransactionForRenewal:: Transaction role is {}", role); + throw new IllegalArgumentException( + String.format("Loan couldn't be renewed with role %s, it could be renewed only with role" + + " LENDER", role)); + } + } + @Override public void updateTransactionDetails(String dcbTransactionId, DcbUpdateTransaction dcbUpdateTransaction) { var transactionEntity = getTransactionEntityOrThrow(dcbTransactionId); @@ -184,7 +251,7 @@ public void updateTransactionDetails(String dcbTransactionId, DcbUpdateTransacti throw new StatusException(String.format( "Transaction details should not be updated from %s status, it can be updated only from CREATED status", transactionEntity.getStatus())); } - if (DcbTransaction.RoleEnum.LENDER.equals(transactionEntity.getRole())) { + if (LENDER.equals(transactionEntity.getRole())) { throw new IllegalArgumentException("Item details cannot be updated for lender role"); } baseLibraryService.updateTransactionDetails(transactionEntity, dcbUpdateTransaction.getItem()); diff --git a/src/main/java/org/folio/dcb/utils/TransactionDetailsUtil.java b/src/main/java/org/folio/dcb/utils/TransactionDetailsUtil.java new file mode 100644 index 00000000..e6229a24 --- /dev/null +++ b/src/main/java/org/folio/dcb/utils/TransactionDetailsUtil.java @@ -0,0 +1,20 @@ +package org.folio.dcb.utils; + +import java.util.Objects; + +import org.folio.dcb.domain.dto.DcbTransaction; +import org.folio.dcb.domain.dto.TransactionStatus; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class TransactionDetailsUtil { + + public static boolean statusesNotEqual(TransactionStatus.StatusEnum statusOne, TransactionStatus.StatusEnum statusTwo) { + return !Objects.equals(statusOne, statusTwo); + } + + public static boolean rolesNotEqual(DcbTransaction.RoleEnum roleOne, DcbTransaction.RoleEnum roleTwo) { + return !Objects.equals(roleOne, roleTwo); + } +} diff --git a/src/main/resources/swagger.api/dcb_transaction.yaml b/src/main/resources/swagger.api/dcb_transaction.yaml index e0a1db27..67fb773a 100644 --- a/src/main/resources/swagger.api/dcb_transaction.yaml +++ b/src/main/resources/swagger.api/dcb_transaction.yaml @@ -111,6 +111,23 @@ paths: $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalServerError' + /transactions/{dcbTransactionId}/renew: + parameters: + - $ref: '#/components/parameters/dcbTransactionId' + put: + description: Increment the renew loan count and returned transaction status with renewal details + operationId: renewItemLoanByTransactionId + tags: + - circulation + responses: + '200': + $ref: '#/components/responses/TransactionStatusResponse' + '404': + $ref: '#/components/responses/NotFound' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalServerError' components: requestBodies: TransactionStatusBody: @@ -237,6 +254,10 @@ components: maximum: 2147483647 required: false schemas: + RenewByIdRequest: + $ref: 'schemas/RenewByIdRequest.yaml#/RenewByIdRequest' + RenewByIdResponse: + $ref: 'schemas/RenewByIdResponse.yaml#/RenewByIdResponse' InventoryItem: $ref: 'schemas/InventoryItem.yaml#/InventoryItem' InventoryHolding: diff --git a/src/main/resources/swagger.api/schemas/RenewByIdRequest.yaml b/src/main/resources/swagger.api/schemas/RenewByIdRequest.yaml new file mode 100644 index 00000000..f5293f04 --- /dev/null +++ b/src/main/resources/swagger.api/schemas/RenewByIdRequest.yaml @@ -0,0 +1,16 @@ +RenewByIdRequest: + type: object + description: Request to renew an existing loan, found by the IDs of item and user + properties: + itemId: + description: ID of the item to be renewed + type: string + $ref: "./uuid.yaml" + userId: + description: ID of the user (representing the patron) the item has been loaned to + type: string + $ref: "./uuid.yaml" + required: + - itemId + - userId + additionalProperties: false diff --git a/src/main/resources/swagger.api/schemas/RenewByIdResponse.yaml b/src/main/resources/swagger.api/schemas/RenewByIdResponse.yaml new file mode 100644 index 00000000..c49e0634 --- /dev/null +++ b/src/main/resources/swagger.api/schemas/RenewByIdResponse.yaml @@ -0,0 +1,131 @@ +RenewByIdResponse: + type: object + title: Loan + description: Links the item with the patron and applies certain conditions based on policies + properties: + id: + description: Unique ID (generated UUID) of the loan + type: string + $ref: "uuid.yaml" + userId: + description: ID of the patron the item was lent to. Required for open loans, not required for closed loans (for anonymization). + type: string + $ref: "uuid.yaml" + borrower: + description: Additional information about the borrower of the item, taken from the user referred to by the userId + readonly: true + type: object + properties: + firstName: + description: first name of the borrower (read only, defined by the server) + type: string + readonly: true + lastName: + description: last name of the borrower (read only, defined by the server) + type: string + readonly: true + middleName: + description: middle name of the borrower (read only, defined by the server) + type: string + readonly: true + barcode: + description: barcode used to identify the borrower (read only, defined by the server) + type: string + readonly: true + preferredFirstName: + description: preferred first name of the borrower (read only, defined by the server) + type: string + readonly: true + patronGroup: + description: current patron group of the borrower (read only, defined by the server) + type: string + readonly: true + additionalProperties: false + proxyUserId: + description: ID of the user representing a proxy for the patron + type: string + $ref: "uuid.yaml" + itemId: + description: ID of the item lent to the patron + type: string + $ref: "uuid.yaml" + loanPolicyId: + description: ID of last policy used in relation to this loan + type: string + $ref: "uuid.yaml" + loanPolicy: + description: Additional information about the loan policy of the item, taken from the loan loanPolicyId + readonly: true + type: object + properties: + name: + readonly: true + description: Name of last policy used in relation to this loan (read only, defined by the server) + type: string + overdueFinePolicyId: + description: ID of last overdue fine policy used in relation to this loan + type: string + $ref: "uuid.yaml" + overdueFinePolicy: + description: Additional information about the overdue fine policy of the item, taken from the loan overdueFinePolicyId + readonly: true + type: object + properties: + name: + readonly: true + description: Name of last overdue fine policy used in relation to this loan (read only, defined by the server) + type: string + lostItemPolicyId: + description: ID of last lost item policy used in relation to this loan + type: string + $ref: "uuid.yaml" + lostItemPolicy: + description: Additional information about the lost item policy of the item, taken from the loan lostItemPolicyId + readonly: true + type: object + properties: + name: + readonly: true + description: Name of last lost item policy used in relation to this loan (read only, defined by the server) + type: string + itemEffectiveLocationIdAtCheckOut: + description: The effective location, at the time of checkout, of the item loaned to the patron. + type: string + $ref: "uuid.yaml" + status: + description: Overall status of the loan + type: object + properties: + name: + description: Name of the status (currently can be any value, values commonly used are Open and Closed) + type: string + loanDate: + description: Date and time when the loan began + type: string + format: date-time + dueDate: + description: Date and time when the item is due to be returned + type: string + format: date-time + returnDate: + description: Date and time when the item was returned + type: string + format: date-time + systemReturnDate: + description: Date and time when return was processed + type: string + format: date-time + action: + description: Last action performed on a loan (currently can be any value, values commonly used are checkedout and checkedin) + type: string + actionComment: + description: Last action performed on a loan comments + type: string + renewalCount: + description: Count of how many times a loan has been renewed (incremented by the client) + type: integer + minimum: 0 + dueDateChangedByRecall: + description: Is due date changed by recall request + type: boolean + readonly: true diff --git a/src/test/java/org/folio/dcb/controller/TransactionApiControllerTest.java b/src/test/java/org/folio/dcb/controller/TransactionApiControllerTest.java index 8ba2fc8f..eaa03424 100644 --- a/src/test/java/org/folio/dcb/controller/TransactionApiControllerTest.java +++ b/src/test/java/org/folio/dcb/controller/TransactionApiControllerTest.java @@ -1,43 +1,9 @@ package org.folio.dcb.controller; -import com.jayway.jsonpath.JsonPath; -import org.folio.dcb.client.feign.CirculationClient; -import org.folio.dcb.client.feign.CirculationLoanPolicyStorageClient; -import org.folio.dcb.domain.dto.DcbItem; -import org.folio.dcb.domain.dto.DcbTransaction; -import org.folio.dcb.domain.dto.Loan; -import org.folio.dcb.domain.dto.LoanCollection; -import org.folio.dcb.domain.dto.LoanPolicy; -import org.folio.dcb.domain.dto.LoanPolicyCollection; -import org.folio.dcb.domain.dto.RenewalsPolicy; -import org.folio.dcb.domain.dto.Status; -import org.folio.dcb.domain.dto.TransactionStatus; -import org.folio.dcb.domain.entity.TransactionAuditEntity; -import org.folio.dcb.repository.TransactionAuditRepository; -import org.folio.dcb.repository.TransactionRepository; -import org.folio.spring.service.SystemUserScopedExecutionService; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MvcResult; -import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; - -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import java.util.stream.Stream; - import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.BORROWER; import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.BORROWING_PICKUP; -import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.LENDER; import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.PICKUP; +import static org.folio.dcb.domain.dto.TransactionStatusResponse.StatusEnum.ITEM_CHECKED_OUT; import static org.folio.dcb.utils.EntityUtils.DCB_NEW_BARCODE; import static org.folio.dcb.utils.EntityUtils.DCB_TRANSACTION_ID; import static org.folio.dcb.utils.EntityUtils.DCB_TYPE_USER_ID; @@ -60,12 +26,60 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; + +import org.folio.dcb.client.feign.CirculationClient; +import org.folio.dcb.client.feign.CirculationLoanPolicyStorageClient; +import org.folio.dcb.domain.dto.DcbItem; +import org.folio.dcb.domain.dto.DcbTransaction; +import org.folio.dcb.domain.dto.Loan; +import org.folio.dcb.domain.dto.LoanCollection; +import org.folio.dcb.domain.dto.LoanPolicy; +import org.folio.dcb.domain.dto.LoanPolicyCollection; +import org.folio.dcb.domain.dto.RenewByIdRequest; +import org.folio.dcb.domain.dto.RenewByIdResponse; +import org.folio.dcb.domain.dto.RenewByIdResponseBorrower; +import org.folio.dcb.domain.dto.RenewByIdResponseLoanPolicy; +import org.folio.dcb.domain.dto.RenewByIdResponseLostItemPolicy; +import org.folio.dcb.domain.dto.RenewByIdResponseOverdueFinePolicy; +import org.folio.dcb.domain.dto.RenewByIdResponseStatus; +import org.folio.dcb.domain.dto.RenewalInfo; +import org.folio.dcb.domain.dto.RenewalsPolicy; +import org.folio.dcb.domain.dto.Status; +import org.folio.dcb.domain.dto.TransactionStatus; +import org.folio.dcb.domain.dto.TransactionStatusResponse; +import org.folio.dcb.domain.entity.TransactionAuditEntity; +import org.folio.dcb.domain.entity.TransactionEntity; +import org.folio.dcb.repository.TransactionAuditRepository; +import org.folio.dcb.repository.TransactionRepository; +import org.folio.spring.service.SystemUserScopedExecutionService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; + +import com.jayway.jsonpath.JsonPath; + +import lombok.SneakyThrows; + class TransactionApiControllerTest extends BaseIT { private static final String TRANSACTION_AUDIT_ERROR_ACTION = "ERROR"; @@ -84,8 +98,6 @@ class TransactionApiControllerTest extends BaseIT { @SpyBean private CirculationLoanPolicyStorageClient circulationLoanPolicyStorageClient; - ObjectMapper objectMapper = new ObjectMapper(); - @Test void createLendingCirculationRequestTest() throws Exception { removeExistedTransactionFromDbIfSoExists(); @@ -93,7 +105,7 @@ void createLendingCirculationRequestTest() throws Exception { this.mockMvc.perform( post("/transactions/" + DCB_TRANSACTION_ID) - .content(asJsonString(createDcbTransactionByRole(LENDER))) + .content(asJsonString(createDcbTransactionByRole(DcbTransaction.RoleEnum.LENDER))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) @@ -105,7 +117,7 @@ void createLendingCirculationRequestTest() throws Exception { //Trying to create another transaction with same transaction id this.mockMvc.perform( post("/transactions/" + DCB_TRANSACTION_ID) - .content(asJsonString(createDcbTransactionByRole(LENDER))) + .content(asJsonString(createDcbTransactionByRole(DcbTransaction.RoleEnum.LENDER))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) @@ -116,18 +128,20 @@ void createLendingCirculationRequestTest() throws Exception { systemUserScopedExecutionService.executeAsyncSystemUserScoped( TENANT, () -> { - TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(DCB_TRANSACTION_ID) + TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId( + DCB_TRANSACTION_ID) .orElse(null); assertNotNull(auditExisting); assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction()); - assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); } + assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); + } ); } @Test void createLendingCirculationRequestWithValidIdAndInvalidBarcode() throws Exception { removeExistedTransactionFromDbIfSoExists(); - var dcbTransaction = createDcbTransactionByRole(LENDER); + var dcbTransaction = createDcbTransactionByRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.getItem().setBarcode("DCB_ITEM1"); this.mockMvc.perform( @@ -144,7 +158,7 @@ void createLendingCirculationRequestWithValidIdAndInvalidBarcode() throws Except @Test void createCirculationRequestWithInvalidUUID() throws Exception { removeExistedTransactionFromDbIfSoExists(); - var dcbTransaction = createDcbTransactionByRole(LENDER); + var dcbTransaction = createDcbTransactionByRole(DcbTransaction.RoleEnum.LENDER); //Setting a non UUID for itemId dcbTransaction.getItem().setId("1234"); @@ -164,7 +178,7 @@ void createBorrowingPickupCirculationRequestTest() throws Exception { removeExistingTransactionsByItemId(ITEM_ID); DcbItem expected = createDcbItem(); -// expected.setPickupLocation("3a40852d-49fd-4df2-a1f9-6e2641a6e91f"); // temporary stub + // expected.setPickupLocation("3a40852d-49fd-4df2-a1f9-6e2641a6e91f"); // temporary stub this.mockMvc.perform( post("/transactions/" + DCB_TRANSACTION_ID) @@ -191,17 +205,19 @@ void createBorrowingPickupCirculationRequestTest() throws Exception { systemUserScopedExecutionService.executeAsyncSystemUserScoped( TENANT, () -> { - TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(DCB_TRANSACTION_ID) + TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId( + DCB_TRANSACTION_ID) .orElse(null); assertNotNull(auditExisting); assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction()); - assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); } + assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); + } ); } @Test void createLendingCirculationRequestWithInvalidItemId() throws Exception { - var dcbTransaction = createDcbTransactionByRole(LENDER); + var dcbTransaction = createDcbTransactionByRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.getItem().setId("5b95877d-86c0-4cb7-a0cd-7660b348ae5b"); String trnId = UUID.randomUUID().toString(); @@ -219,15 +235,18 @@ void createLendingCirculationRequestWithInvalidItemId() throws Exception { systemUserScopedExecutionService.executeAsyncSystemUserScoped( TENANT, () -> { - TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(trnId) + TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId( + trnId) .orElse(null); assertNotNull(auditExisting); - assertEquals(TRANSACTION_AUDIT_ERROR_ACTION, auditExisting.getAction()); } + assertEquals(TRANSACTION_AUDIT_ERROR_ACTION, auditExisting.getAction()); + } ); } @Test - void createBorrowingPickupCirculationRequestWithInvalidDefaultNotExistedPatronId() throws Exception { + void createBorrowingPickupCirculationRequestWithInvalidDefaultNotExistedPatronId() + throws Exception { var dcbTransaction = createDcbTransactionByRole(BORROWING_PICKUP); dcbTransaction.getPatron().setId(NOT_EXISTED_PATRON_ID); @@ -245,30 +264,34 @@ void createBorrowingPickupCirculationRequestWithInvalidDefaultNotExistedPatronId systemUserScopedExecutionService.executeAsyncSystemUserScoped( TENANT, () -> { - TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(trnId) + TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId( + trnId) .orElse(null); assertNotNull(auditExisting); - assertEquals(TRANSACTION_AUDIT_ERROR_ACTION, auditExisting.getAction()); } + assertEquals(TRANSACTION_AUDIT_ERROR_ACTION, auditExisting.getAction()); + } ); } - /** - * The test at the put endpoint invocation stage initiates stage verification from OPEN to AWAITING_PICKUP - * */ + /** + * The test at the put endpoint invocation stage initiates stage verification from OPEN to AWAITING_PICKUP + */ @Test void transactionStatusUpdateFromOpenToAwaitingTest() throws Exception { var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.OPEN); - dcbTransaction.setRole(LENDER); + dcbTransaction.setRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") - .content(asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.AWAITING_PICKUP))) + .content( + asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.AWAITING_PICKUP))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) @@ -282,14 +305,16 @@ void transactionStatusUpdateFromOpenToCheckedInTest() throws Exception { var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.OPEN); - dcbTransaction.setRole(LENDER); + dcbTransaction.setRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") - .content(asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_IN))) + .content( + asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_IN))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) @@ -306,7 +331,8 @@ void transactionStatusCancelledFromClosedTest() throws Exception { dcbTransaction.setRole(BORROWER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") @@ -337,7 +363,8 @@ void transactionStatusUpdateFromCreatedToOpenForPickupLibTest() throws Exception dcbTransaction.setRole(PICKUP); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") @@ -351,21 +378,23 @@ void transactionStatusUpdateFromCreatedToOpenForPickupLibTest() throws Exception /** * The test at the put endpoint invocation stage initiates stage verification from AWAITING_PICKUP to CHECKED_OUT - * */ + */ @Test void transactionStatusUpdateFromAwaitingToCheckedOutTest() throws Exception { var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.AWAITING_PICKUP); - dcbTransaction.setRole(LENDER); + dcbTransaction.setRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") - .content(asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT))) + .content( + asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) @@ -375,27 +404,30 @@ void transactionStatusUpdateFromAwaitingToCheckedOutTest() throws Exception { /** * The test at the put endpoint invocation stage initiates stage verification from CHECKED_OUT to CHECKED_IN - * */ + */ @Test void transactionStatusUpdateFromCheckOutToCheckInTest() throws Exception { var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT); - dcbTransaction.setRole(LENDER); + dcbTransaction.setRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") - .content(asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_IN))) + .content( + asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_IN))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.status").value("ITEM_CHECKED_IN")); } + @Test void transactionStatusUpdateFromCheckedInToClosedTest() throws Exception { @@ -405,7 +437,8 @@ void transactionStatusUpdateFromCheckedInToClosedTest() throws Exception { dcbTransaction.setRole(BORROWING_PICKUP); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") @@ -417,18 +450,17 @@ void transactionStatusUpdateFromCheckedInToClosedTest() throws Exception { .andExpect(jsonPath("$.status").value("CLOSED")); } - /** * Initiates the data generation via the post endpoint invocation stage * then get stage verifies, the data exists. * For LENDER role - * */ + */ @Test void getLendingTransactionStatusSuccessTest() throws Exception { var id = UUID.randomUUID().toString(); this.mockMvc.perform( post("/transactions/" + id) - .content(asJsonString(createDcbTransactionByRole(LENDER))) + .content(asJsonString(createDcbTransactionByRole(DcbTransaction.RoleEnum.LENDER))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) @@ -438,7 +470,7 @@ void getLendingTransactionStatusSuccessTest() throws Exception { .andExpect(jsonPath("$.patron").value(createDefaultDcbPatron())); mockMvc.perform( - get("/transactions/"+id+"/status") + get("/transactions/" + id + "/status") .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); @@ -446,201 +478,328 @@ void getLendingTransactionStatusSuccessTest() throws Exception { private static Stream transactionRoles() { return Stream.of( - Arguments.of(BORROWER), - Arguments.of(BORROWING_PICKUP) + Arguments.of(BORROWER), + Arguments.of(BORROWING_PICKUP) ); } @ParameterizedTest @MethodSource("transactionRoles") - void getLendingTransactionStatusSuccessTestRenewalCountUnlimitedFalse(DcbTransaction.RoleEnum role) throws Exception { + void getLendingTransactionStatusSuccessTestRenewalCountUnlimitedFalse( + DcbTransaction.RoleEnum role) throws Exception { var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT); dcbTransaction.setRole(role); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); String randomUUID = UUID.randomUUID().toString(); LoanCollection loanCollection = LoanCollection.builder() - .loans(List.of(Loan.builder() - .id(randomUUID) - .loanPolicyId(randomUUID) - .renewalCount("8") - .status(Status.builder().name("OPEN").build()).build())) - .totalRecords(1) - .build(); + .loans(List.of(Loan.builder() + .id(randomUUID) + .loanPolicyId(randomUUID) + .renewalCount("8") + .status(Status.builder().name("OPEN").build()).build())) + .totalRecords(1) + .build(); Mockito.doReturn(loanCollection).when(circulationClient).fetchLoanByQuery(anyString()); LoanPolicyCollection loanPolicyCollection = LoanPolicyCollection.builder() - .loanPolicies(List.of(LoanPolicy.builder() - .id(randomUUID) - .renewable(true) - .renewalsPolicy(RenewalsPolicy.builder() - .unlimited(false) - .numberAllowed(22) - .build()).build())) - .totalRecords(1) - .build(); - Mockito.doReturn(loanPolicyCollection).when(circulationLoanPolicyStorageClient).fetchLoanPolicyByQuery(anyString()); + .loanPolicies(List.of(LoanPolicy.builder() + .id(randomUUID) + .renewable(true) + .renewalsPolicy(RenewalsPolicy.builder() + .unlimited(false) + .numberAllowed(22) + .build()).build())) + .totalRecords(1) + .build(); + Mockito.doReturn(loanPolicyCollection) + .when(circulationLoanPolicyStorageClient) + .fetchLoanPolicyByQuery(anyString()); mockMvc.perform( - get("/transactions/" + transactionID + "/status") - .headers(defaultHeaders()) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.item.renewalInfo.renewalCount").value(8)) - .andExpect(jsonPath("$.item.renewalInfo.renewable").value(true)) - .andExpect(jsonPath("$.item.renewalInfo.renewalMaxCount").value(22)); + get("/transactions/" + transactionID + "/status") + .headers(defaultHeaders()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.item.renewalInfo.renewalCount").value(8)) + .andExpect(jsonPath("$.item.renewalInfo.renewable").value(true)) + .andExpect(jsonPath("$.item.renewalInfo.renewalMaxCount").value(22)); + } + + @Test + @SneakyThrows + void renewItemLoanByTransactionId() { + String transactionId = UUID.randomUUID().toString(); + String itemId = UUID.randomUUID().toString(); + String userId = UUID.randomUUID().toString(); + String loanPolicyId = UUID.randomUUID().toString(); + + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(buildTransactionFoRenew(transactionId, itemId, userId))); + + RenewByIdResponse renewalResponse = createTestInstance(loanPolicyId); + RenewByIdRequest renewByIdRequest = buildRenewByIdResponse(itemId, userId); + + doReturn(renewalResponse).when(circulationClient).renewById(renewByIdRequest); + doReturn(buildTestLoanPolicy()).when(circulationLoanPolicyStorageClient) + .fetchLoanPolicyById(loanPolicyId); + + TransactionStatusResponse actual = new ObjectMapper().readValue(performRenewById(transactionId), + TransactionStatusResponse.class); + + assertEquals(buildExpectedResponse(), actual); + } + + private static TransactionStatusResponse buildExpectedResponse() { + return TransactionStatusResponse.builder() + .item( + DcbItem.builder() + .renewalInfo( + RenewalInfo.builder() + .renewable(true) + .renewalMaxCount(10) + .renewalCount(1) + .build() + ) + .build() + ) + .status(ITEM_CHECKED_OUT) + .role(TransactionStatusResponse.RoleEnum.LENDER) + .build(); + } + + private String performRenewById(String transactionId) throws Exception { + return mockMvc.perform( + put("/transactions/" + transactionId + "/renew") + .headers(defaultHeaders()) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + } + + private static TransactionEntity buildTransactionFoRenew(String transactionId, String itemId, + String userId) { + TransactionEntity transaction = createTransactionEntity(); + transaction.setId(transactionId); + transaction.setStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT); + transaction.setRole(DcbTransaction.RoleEnum.LENDER); + transaction.setItemId(itemId); + transaction.setPatronId(userId); + return transaction; + } + + private static LoanPolicy buildTestLoanPolicy() { + return LoanPolicy.builder() + .renewable(true) + .renewalsPolicy( + RenewalsPolicy.builder() + .numberAllowed(10) + .unlimited(false) + .build() + ).build(); + } + + private static RenewByIdRequest buildRenewByIdResponse(String itemId, String userId) { + return RenewByIdRequest.builder() + .itemId(itemId) + .userId(userId) + .build(); + } + + public static RenewByIdResponse createTestInstance(String loanPolicyId) { + return RenewByIdResponse.builder() + .id("test-id") + .userId("test-user-id") + .borrower(new RenewByIdResponseBorrower()) + .proxyUserId("test-proxy-user-id") + .itemId("test-item-id") + .loanPolicyId(loanPolicyId) + .loanPolicy(new RenewByIdResponseLoanPolicy()) + .overdueFinePolicyId("test-overdue-fine-policy-id") + .overdueFinePolicy(new RenewByIdResponseOverdueFinePolicy()) + .lostItemPolicyId("test-lost-item-policy-id") + .lostItemPolicy(new RenewByIdResponseLostItemPolicy()) + .itemEffectiveLocationIdAtCheckOut("test-location-id") + .status(new RenewByIdResponseStatus()) + .loanDate(OffsetDateTime.now()) + .dueDate(OffsetDateTime.now().plusDays(14)) + .returnDate(OffsetDateTime.now().plusDays(21)) + .systemReturnDate(OffsetDateTime.now().plusDays(21)) + .action("test-action") + .actionComment("test-action-comment") + .renewalCount(1) + .dueDateChangedByRecall(false) + .build(); } @ParameterizedTest @MethodSource("transactionRoles") - void getLendingTransactionStatusSuccessTestRenewalCountUnlimitedTrue(DcbTransaction.RoleEnum role) throws Exception { + void getLendingTransactionStatusSuccessTestRenewalCountUnlimitedTrue(DcbTransaction.RoleEnum role) + throws Exception { var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT); dcbTransaction.setRole(role); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); String randomUUID = UUID.randomUUID().toString(); LoanCollection loanCollection = LoanCollection.builder() - .loans(List.of(Loan.builder() - .id(randomUUID) - .loanPolicyId(randomUUID) - .renewalCount("8") - .status(Status.builder().name("OPEN").build()).build())) - .totalRecords(1) - .build(); + .loans(List.of(Loan.builder() + .id(randomUUID) + .loanPolicyId(randomUUID) + .renewalCount("8") + .status(Status.builder().name("OPEN").build()).build())) + .totalRecords(1) + .build(); Mockito.doReturn(loanCollection).when(circulationClient).fetchLoanByQuery(anyString()); LoanPolicyCollection loanPolicyCollection = LoanPolicyCollection.builder() - .loanPolicies(List.of(LoanPolicy.builder() - .id(randomUUID) - .renewable(true) - .renewalsPolicy(RenewalsPolicy.builder().unlimited(true).build()).build())) - .totalRecords(1) - .build(); - Mockito.doReturn(loanPolicyCollection).when(circulationLoanPolicyStorageClient).fetchLoanPolicyByQuery(anyString()); + .loanPolicies(List.of(LoanPolicy.builder() + .id(randomUUID) + .renewable(true) + .renewalsPolicy(RenewalsPolicy.builder().unlimited(true).build()).build())) + .totalRecords(1) + .build(); + Mockito.doReturn(loanPolicyCollection) + .when(circulationLoanPolicyStorageClient) + .fetchLoanPolicyByQuery(anyString()); mockMvc.perform( - get("/transactions/" + transactionID + "/status") - .headers(defaultHeaders()) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.item.renewalInfo.renewalCount").value(8)) - .andExpect(jsonPath("$.item.renewalInfo.renewable").value(true)) - .andExpect(jsonPath("$.item.renewalInfo.renewalMaxCount").value(-1)); + get("/transactions/" + transactionID + "/status") + .headers(defaultHeaders()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.item.renewalInfo.renewalCount").value(8)) + .andExpect(jsonPath("$.item.renewalInfo.renewable").value(true)) + .andExpect(jsonPath("$.item.renewalInfo.renewalMaxCount").value(-1)); } @ParameterizedTest @MethodSource("transactionRoles") - void getLendingTransactionStatusSuccessTestRenewalCountFalseRenewableLoanPolicy(DcbTransaction.RoleEnum role) throws Exception { + void getLendingTransactionStatusSuccessTestRenewalCountFalseRenewableLoanPolicy( + DcbTransaction.RoleEnum role) throws Exception { var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT); dcbTransaction.setRole(role); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); String randomUUID = UUID.randomUUID().toString(); LoanCollection loanCollection = LoanCollection.builder() - .loans(List.of(Loan.builder() - .id(randomUUID) - .loanPolicyId(randomUUID) - .renewalCount("8") - .status(Status.builder().name("OPEN").build()).build())) - .totalRecords(1) - .build(); + .loans(List.of(Loan.builder() + .id(randomUUID) + .loanPolicyId(randomUUID) + .renewalCount("8") + .status(Status.builder().name("OPEN").build()).build())) + .totalRecords(1) + .build(); Mockito.doReturn(loanCollection).when(circulationClient).fetchLoanByQuery(anyString()); LoanPolicyCollection loanPolicyCollection = LoanPolicyCollection.builder() - .loanPolicies(List.of(LoanPolicy.builder() - .id(randomUUID) - .renewable(false).build())) - .totalRecords(1) - .build(); - Mockito.doReturn(loanPolicyCollection).when(circulationLoanPolicyStorageClient).fetchLoanPolicyByQuery(anyString()); + .loanPolicies(List.of(LoanPolicy.builder() + .id(randomUUID) + .renewable(false).build())) + .totalRecords(1) + .build(); + Mockito.doReturn(loanPolicyCollection) + .when(circulationLoanPolicyStorageClient) + .fetchLoanPolicyByQuery(anyString()); mockMvc.perform( - get("/transactions/" + transactionID + "/status") - .headers(defaultHeaders()) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.item.renewalInfo.renewalCount").value(8)) - .andExpect(jsonPath("$.item.renewalInfo.renewable").value(false)) - .andExpect(jsonPath("$.item.renewalInfo.renewalMaxCount").doesNotExist()); + get("/transactions/" + transactionID + "/status") + .headers(defaultHeaders()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.item.renewalInfo.renewalCount").value(8)) + .andExpect(jsonPath("$.item.renewalInfo.renewable").value(false)) + .andExpect(jsonPath("$.item.renewalInfo.renewalMaxCount").doesNotExist()); } @ParameterizedTest @MethodSource("transactionRoles") - void getLendingTransactionStatusSuccessTestRenewalCountZeroWhenNullRenewalCountInLoan(DcbTransaction.RoleEnum role) throws Exception { + void getLendingTransactionStatusSuccessTestRenewalCountZeroWhenNullRenewalCountInLoan( + DcbTransaction.RoleEnum role) throws Exception { var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT); dcbTransaction.setRole(role); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); String randomUUID = UUID.randomUUID().toString(); LoanCollection loanCollection = LoanCollection.builder() - .loans(List.of(Loan.builder() - .id(randomUUID) - .loanPolicyId(randomUUID) - .renewalCount(null) - .status(Status.builder().name("OPEN").build()).build())) - .totalRecords(1) - .build(); + .loans(List.of(Loan.builder() + .id(randomUUID) + .loanPolicyId(randomUUID) + .renewalCount(null) + .status(Status.builder().name("OPEN").build()).build())) + .totalRecords(1) + .build(); Mockito.doReturn(loanCollection).when(circulationClient).fetchLoanByQuery(anyString()); LoanPolicyCollection loanPolicyCollection = LoanPolicyCollection.builder() - .loanPolicies(List.of(LoanPolicy.builder() - .id(randomUUID) - .renewable(true) - .renewalsPolicy(RenewalsPolicy.builder() - .unlimited(false) - .numberAllowed(22) - .build()).build())) - .totalRecords(1) - .build(); - Mockito.doReturn(loanPolicyCollection).when(circulationLoanPolicyStorageClient).fetchLoanPolicyByQuery(anyString()); + .loanPolicies(List.of(LoanPolicy.builder() + .id(randomUUID) + .renewable(true) + .renewalsPolicy(RenewalsPolicy.builder() + .unlimited(false) + .numberAllowed(22) + .build()).build())) + .totalRecords(1) + .build(); + Mockito.doReturn(loanPolicyCollection) + .when(circulationLoanPolicyStorageClient) + .fetchLoanPolicyByQuery(anyString()); mockMvc.perform( - get("/transactions/" + transactionID + "/status") - .headers(defaultHeaders()) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.item.renewalInfo.renewalCount").value(0)) - .andExpect(jsonPath("$.item.renewalInfo.renewable").value(true)) - .andExpect(jsonPath("$.item.renewalInfo.renewalMaxCount").value(22)); + get("/transactions/" + transactionID + "/status") + .headers(defaultHeaders()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.item.renewalInfo.renewalCount").value(0)) + .andExpect(jsonPath("$.item.renewalInfo.renewable").value(true)) + .andExpect(jsonPath("$.item.renewalInfo.renewalMaxCount").value(22)); } @ParameterizedTest @MethodSource("transactionRoles") - void getLendingTransactionStatusSuccessTestRenewalCountLoanNotExist(DcbTransaction.RoleEnum role) throws Exception { + void getLendingTransactionStatusSuccessTestRenewalCountLoanNotExist(DcbTransaction.RoleEnum role) + throws Exception { var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT); dcbTransaction.setRole(role); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); LoanCollection loanCollection = LoanCollection.builder() - .loans(Collections.emptyList()) - .totalRecords(0) - .build(); + .loans(Collections.emptyList()) + .totalRecords(0) + .build(); Mockito.doReturn(loanCollection).when(circulationClient).fetchLoanByQuery(anyString()); mockMvc.perform( - get("/transactions/" + transactionID + "/status") - .headers(defaultHeaders()) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.item.renewalInfo").doesNotExist()); + get("/transactions/" + transactionID + "/status") + .headers(defaultHeaders()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.item.renewalInfo").doesNotExist()); } @Test @@ -648,24 +807,25 @@ void getLendingTransactionStatusSuccessTestRenewalCountNegativeTest() throws Exc var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.AWAITING_PICKUP); - dcbTransaction.setRole(LENDER); + dcbTransaction.setRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); mockMvc.perform( - get("/transactions/" + transactionID + "/status") - .headers(defaultHeaders()) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.item.renewalInfo").doesNotExist()); + get("/transactions/" + transactionID + "/status") + .headers(defaultHeaders()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.item.renewalInfo").doesNotExist()); } @Test void getTransactionStatusNotFoundTest() throws Exception { var id = UUID.randomUUID().toString(); mockMvc.perform( - get("/transactions/"+id+"/status") + get("/transactions/" + id + "/status") .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()); @@ -690,7 +850,7 @@ void createTransactionForPickupLibrary() throws Exception { //Trying to create another transaction with same transaction id this.mockMvc.perform( post("/transactions/" + DCB_TRANSACTION_ID) - .content(asJsonString(createDcbTransactionByRole(LENDER))) + .content(asJsonString(createDcbTransactionByRole(DcbTransaction.RoleEnum.LENDER))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) @@ -701,11 +861,13 @@ void createTransactionForPickupLibrary() throws Exception { systemUserScopedExecutionService.executeAsyncSystemUserScoped( TENANT, () -> { - TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(DCB_TRANSACTION_ID) + TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId( + DCB_TRANSACTION_ID) .orElse(null); assertNotNull(auditExisting); assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction()); - assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); } + assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); + } ); } @@ -717,11 +879,13 @@ void transactionStatusUpdateFromOpenToAwaitingPickup() throws Exception { dcbTransaction.setRole(BORROWER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") - .content(asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.AWAITING_PICKUP))) + .content( + asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.AWAITING_PICKUP))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) @@ -759,11 +923,13 @@ void createBorrowerCirculationRequestTest() throws Exception { systemUserScopedExecutionService.executeAsyncSystemUserScoped( TENANT, () -> { - TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId(DCB_TRANSACTION_ID) + TransactionAuditEntity auditExisting = transactionAuditRepository.findLatestTransactionAuditEntityByDcbTransactionId( + DCB_TRANSACTION_ID) .orElse(null); assertNotNull(auditExisting); assertNotEquals(TRANSACTION_AUDIT_DUPLICATE_ERROR_ACTION, auditExisting.getAction()); - assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); } + assertNotEquals(DUPLICATE_ERROR_TRANSACTION_ID, auditExisting.getTransactionId()); + } ); } @@ -810,11 +976,13 @@ void transactionStatusUpdateFromAwaitingPickupToItemCheckedOut() throws Exceptio dcbTransaction.setRole(BORROWER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") - .content(asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT))) + .content( + asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) @@ -830,7 +998,8 @@ void transactionStatusUpdateFromCreatedToItemClosed() throws Exception { dcbTransaction.setRole(BORROWER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") @@ -847,17 +1016,18 @@ void lenderTransactionStatusUpdateFromCreatedToOpen() throws Exception { var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.CREATED); - dcbTransaction.setRole(LENDER); + dcbTransaction.setRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( - put("/transactions/" + transactionID + "/status") - .content(asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.OPEN))) - .headers(defaultHeaders()) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON)) + put("/transactions/" + transactionID + "/status") + .content(asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.OPEN))) + .headers(defaultHeaders()) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.status").value("OPEN")); } @@ -870,11 +1040,13 @@ void transactionStatusUpdateFromItemCheckedOutToItemCheckedIn() throws Exception dcbTransaction.setRole(BORROWER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); this.mockMvc.perform( put("/transactions/" + transactionID + "/status") - .content(asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_IN))) + .content( + asJsonString(createTransactionStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_IN))) .headers(defaultHeaders()) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) @@ -887,10 +1059,11 @@ void transactionStatusUpdateFromCreatedToCancelledAsLenderTest() throws Exceptio var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.CREATED); - dcbTransaction.setRole(LENDER); + dcbTransaction.setRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); mockMvc.perform( put("/transactions/" + transactionID + "/status") @@ -907,10 +1080,11 @@ void transactionStatusUpdateFromCheckedInToCancelledAsLenderTest() throws Except var transactionID = UUID.randomUUID().toString(); var dcbTransaction = createTransactionEntity(); dcbTransaction.setStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_IN); - dcbTransaction.setRole(LENDER); + dcbTransaction.setRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); mockMvc.perform( put("/transactions/" + transactionID + "/status") @@ -926,7 +1100,7 @@ void createLendingTransactionWithDifferentUserType() throws Exception { removeExistedTransactionFromDbIfSoExists(); removeExistingTransactionsByItemId(ITEM_ID); - var transaction = createDcbTransactionByRole(LENDER); + var transaction = createDcbTransactionByRole(DcbTransaction.RoleEnum.LENDER); transaction.getPatron().setId(PATRON_TYPE_USER_ID); this.mockMvc.perform( post("/transactions/" + DCB_TRANSACTION_ID) @@ -936,7 +1110,7 @@ void createLendingTransactionWithDifferentUserType() throws Exception { .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); - transaction = createDcbTransactionByRole(LENDER); + transaction = createDcbTransactionByRole(DcbTransaction.RoleEnum.LENDER); transaction.getPatron().setId(DCB_TYPE_USER_ID); this.mockMvc.perform( post("/transactions/" + DCB_TRANSACTION_ID) @@ -1094,7 +1268,8 @@ void transactionStatusUpdateFromCheckedInToCancelledAsBorrowerPickupTest() throw dcbTransaction.setRole(BORROWING_PICKUP); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); mockMvc.perform( put("/transactions/" + transactionID + "/status") @@ -1113,7 +1288,8 @@ void transactionStatusUpdateFromCheckedInToCancelledAsPickupTest() throws Except dcbTransaction.setRole(PICKUP); dcbTransaction.setId(transactionID); - systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> transactionRepository.save(dcbTransaction)); + systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, + () -> transactionRepository.save(dcbTransaction)); mockMvc.perform( put("/transactions/" + transactionID + "/status") @@ -1199,7 +1375,8 @@ void getTransactionStatusUpdateListTest() throws Exception { .andExpect(jsonPath("$.currentPageSize", is(1000))) .andExpect(jsonPath("$.maximumPageNumber", is(0))) .andExpect(jsonPath("$.transactions[*].status", - containsInRelativeOrder("AWAITING_PICKUP", "ITEM_CHECKED_OUT", "ITEM_CHECKED_IN", "CLOSED"))); + containsInRelativeOrder("AWAITING_PICKUP", "ITEM_CHECKED_OUT", "ITEM_CHECKED_IN", + "CLOSED"))); // Now try to get all the records from startDate1 to endDate2 without pageSize(default pageSize) this.mockMvc.perform( @@ -1215,7 +1392,8 @@ void getTransactionStatusUpdateListTest() throws Exception { .andExpect(jsonPath("$.currentPageSize", is(1000))) .andExpect(jsonPath("$.maximumPageNumber", is(0))) .andExpect(jsonPath("$.transactions[*].status", - containsInRelativeOrder("OPEN", "AWAITING_PICKUP", "ITEM_CHECKED_OUT", "ITEM_CHECKED_IN", "CLOSED"))); + containsInRelativeOrder("OPEN", "AWAITING_PICKUP", "ITEM_CHECKED_OUT", "ITEM_CHECKED_IN", + "CLOSED"))); // Now try to get the records based on pagination from startDate1 to endDate2. this.mockMvc.perform( @@ -1224,7 +1402,7 @@ void getTransactionStatusUpdateListTest() throws Exception { .param("fromDate", String.valueOf(startDate1)) .param("toDate", String.valueOf(endDate2)) .param("pageNumber", String.valueOf(1)) - .param("pageSize",String.valueOf(2)) + .param("pageSize", String.valueOf(2)) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().is(200)) @@ -1256,7 +1434,8 @@ void createAndUpdateBorrowerTransactionTest() throws Exception { String responseContent = result.getResponse().getContentAsString(); String itemId = JsonPath.parse(responseContent).read("$.item.id", String.class); String itemBarcode = JsonPath.parse(responseContent).read("$.item.barcode", String.class); - String lendingLibraryCode = JsonPath.parse(responseContent).read("$.item.lendingLibraryCode", String.class); + String lendingLibraryCode = JsonPath.parse(responseContent) + .read("$.item.lendingLibraryCode", String.class); String materialType = JsonPath.parse(responseContent).read("$.item.materialType", String.class); //Trying to update the transaction with same transaction id @@ -1285,10 +1464,9 @@ void createAndUpdateBorrowerTransactionTest() throws Exception { }); } - private void removeExistedTransactionFromDbIfSoExists() { systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> { - if (transactionRepository.existsById(DCB_TRANSACTION_ID)){ + if (transactionRepository.existsById(DCB_TRANSACTION_ID)) { transactionRepository.deleteById(DCB_TRANSACTION_ID); } }); @@ -1296,8 +1474,8 @@ private void removeExistedTransactionFromDbIfSoExists() { private void removeExistingTransactionsByItemId(String itemId) { systemUserScopedExecutionService.executeAsyncSystemUserScoped(TENANT, () -> - transactionRepository.findTransactionsByItemIdAndStatusNotInClosed(UUID.fromString(itemId)) - .forEach(transactionEntity -> transactionRepository.deleteById(transactionEntity.getId())) + transactionRepository.findTransactionsByItemIdAndStatusNotInClosed(UUID.fromString(itemId)) + .forEach(transactionEntity -> transactionRepository.deleteById(transactionEntity.getId())) ); } @@ -1306,18 +1484,18 @@ void createLendingCirculationRequestTestWithSpecialCharBarcode() throws Exceptio removeExistedTransactionFromDbIfSoExists(); removeExistingTransactionsByItemId(ITEM_ID); String specialCharBarCode = "!@#$%^^&&*()_=+`~|\\]{}DCB_ITEM/'''"; - DcbTransaction dcbTransaction = createDcbTransactionByRole(LENDER); + DcbTransaction dcbTransaction = createDcbTransactionByRole(DcbTransaction.RoleEnum.LENDER); dcbTransaction.getItem().barcode(specialCharBarCode); DcbItem expectedDCBItem = createDcbItem().barcode(specialCharBarCode); this.mockMvc.perform( - post("/transactions/" + DCB_TRANSACTION_ID) - .content(asJsonString(dcbTransaction)) - .headers(defaultHeaders()) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isCreated()) - .andExpect(jsonPath("$.status").value("CREATED")) - .andExpect(jsonPath("$.item").value(expectedDCBItem)) - .andExpect(jsonPath("$.patron").value(createDefaultDcbPatron())); + post("/transactions/" + DCB_TRANSACTION_ID) + .content(asJsonString(dcbTransaction)) + .headers(defaultHeaders()) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.status").value("CREATED")) + .andExpect(jsonPath("$.item").value(expectedDCBItem)) + .andExpect(jsonPath("$.patron").value(createDefaultDcbPatron())); } } diff --git a/src/test/java/org/folio/dcb/service/TransactionServiceTest.java b/src/test/java/org/folio/dcb/service/TransactionServiceTest.java index b462bdb0..f1f77d54 100644 --- a/src/test/java/org/folio/dcb/service/TransactionServiceTest.java +++ b/src/test/java/org/folio/dcb/service/TransactionServiceTest.java @@ -1,6 +1,43 @@ package org.folio.dcb.service; +import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.BORROWER; +import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.BORROWING_PICKUP; +import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.LENDER; +import static org.folio.dcb.domain.dto.TransactionStatus.StatusEnum.AWAITING_PICKUP; +import static org.folio.dcb.domain.dto.TransactionStatus.StatusEnum.CLOSED; +import static org.folio.dcb.domain.dto.TransactionStatus.StatusEnum.ITEM_CHECKED_IN; +import static org.folio.dcb.domain.dto.TransactionStatus.StatusEnum.ITEM_CHECKED_OUT; +import static org.folio.dcb.domain.dto.TransactionStatus.StatusEnum.OPEN; +import static org.folio.dcb.utils.EntityUtils.DCB_TRANSACTION_ID; +import static org.folio.dcb.utils.EntityUtils.createDcbTransactionByRole; +import static org.folio.dcb.utils.EntityUtils.createTransactionEntity; +import static org.folio.dcb.utils.EntityUtils.createTransactionResponse; +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Stream; + +import org.folio.dcb.client.feign.CirculationClient; +import org.folio.dcb.client.feign.CirculationLoanPolicyStorageClient; +import org.folio.dcb.domain.dto.DcbTransaction.RoleEnum; +import org.folio.dcb.domain.dto.LoanPolicy; +import org.folio.dcb.domain.dto.RenewByIdRequest; +import org.folio.dcb.domain.dto.RenewByIdResponse; +import org.folio.dcb.domain.dto.RenewalsPolicy; import org.folio.dcb.domain.dto.TransactionStatus; +import org.folio.dcb.domain.dto.TransactionStatus.StatusEnum; import org.folio.dcb.domain.dto.TransactionStatusResponse; import org.folio.dcb.domain.dto.TransactionStatusResponseList; import org.folio.dcb.domain.entity.TransactionAuditEntity; @@ -16,36 +53,20 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.domain.Page; -import java.time.OffsetDateTime; -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -import static org.folio.dcb.domain.dto.DcbTransaction.RoleEnum.LENDER; -import static org.folio.dcb.utils.EntityUtils.DCB_TRANSACTION_ID; -import static org.folio.dcb.utils.EntityUtils.createDcbTransactionByRole; -import static org.folio.dcb.utils.EntityUtils.createTransactionEntity; -import static org.folio.dcb.utils.EntityUtils.createTransactionResponse; -import static org.junit.Assert.assertThrows; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - @ExtendWith(MockitoExtension.class) class TransactionServiceTest { @InjectMocks private TransactionsServiceImpl transactionsService; - @Mock(name="lendingLibraryService") + @Mock(name = "lendingLibraryService") private LendingLibraryServiceImpl lendingLibraryService; @Mock private TransactionRepository transactionRepository; @@ -55,21 +76,106 @@ class TransactionServiceTest { private TransactionAuditRepository transactionAuditRepository; @Mock private TransactionMapper transactionMapper; + @Mock + private CirculationClient circulationClient; + @Mock + private CirculationLoanPolicyStorageClient circulationLoanPolicyStorageClient; + + @Test + void renewLoanByTransactionIdTest() { + when(transactionRepository.findById(anyString())).thenReturn(Optional.ofNullable( + buildTransactionToRenew(ITEM_CHECKED_OUT, LENDER))); + when(circulationClient.renewById(any())).thenReturn(buildTestRenewBleResponse()); + when(circulationLoanPolicyStorageClient.fetchLoanPolicyById(anyString())).thenReturn( + buildTestLoanPolicy()); + when(circulationClient.renewById(any())).thenReturn(buildTestRenewBleResponse()); + when(circulationLoanPolicyStorageClient.fetchLoanPolicyById(anyString())).thenReturn( + buildTestLoanPolicy()); + + transactionsService.renewLoanByTransactionId(DCB_TRANSACTION_ID); + verify(circulationClient, times(1)).renewById(any()); + verify(circulationLoanPolicyStorageClient, times(1)).fetchLoanPolicyById(any()); + } + + @Test + void renewLoanByTransactionIdShouldThrowNotFoundExceptionWhenRenewResponseNull() { + when(circulationClient.renewById(any(RenewByIdRequest.class))).thenReturn(null); + when(transactionRepository.findById(anyString())).thenReturn(Optional.of( + buildTransactionToRenew(ITEM_CHECKED_OUT, LENDER))); + assertThrows(NotFoundException.class, () -> transactionsService.renewLoanByTransactionId( + DCB_TRANSACTION_ID)); + } + + @Test + void renewLoanByTransactionIdShouldThrowNotFoundExceptionWhenLoanPolicyResponseNull() { + when(transactionRepository.findById(anyString())).thenReturn(Optional.ofNullable( + buildTransactionToRenew(ITEM_CHECKED_OUT, LENDER))); + when(circulationClient.renewById(any())).thenReturn(buildTestRenewBleResponse()); + when(circulationLoanPolicyStorageClient.fetchLoanPolicyById(anyString())).thenReturn(null); + assertThrows(NotFoundException.class, () -> transactionsService.renewLoanByTransactionId( + DCB_TRANSACTION_ID)); + } + + @ParameterizedTest + @MethodSource + void renewLoanByTransactionIdShouldThrowExceptionTest(TransactionEntity transaction, + Class exception) { + when(transactionRepository.findById(anyString())).thenReturn(Optional.of(transaction)); + assertThrows(exception, () -> transactionsService.renewLoanByTransactionId(DCB_TRANSACTION_ID)); + } + + private static Stream renewLoanByTransactionIdShouldThrowExceptionTest() { + return Stream.of( + Arguments.of(buildTransactionToRenew(ITEM_CHECKED_IN, LENDER), StatusException.class), + Arguments.of(buildTransactionToRenew(OPEN, LENDER), StatusException.class), + Arguments.of(buildTransactionToRenew(CLOSED, LENDER), StatusException.class), + Arguments.of(buildTransactionToRenew(AWAITING_PICKUP, LENDER), StatusException.class), + Arguments.of(buildTransactionToRenew(AWAITING_PICKUP, LENDER), StatusException.class), + Arguments.of(buildTransactionToRenew(ITEM_CHECKED_OUT, BORROWER), + IllegalArgumentException.class), + Arguments.of(buildTransactionToRenew(ITEM_CHECKED_OUT, BORROWING_PICKUP), + IllegalArgumentException.class) + ); + } + + private static TransactionEntity buildTransactionToRenew(StatusEnum status, RoleEnum role) { + return TransactionEntity.builder() + .id(DCB_TRANSACTION_ID) + .status(status) + .role(role) + .build(); + } + + private static LoanPolicy buildTestLoanPolicy() { + return new LoanPolicy() + .renewalsPolicy( + new RenewalsPolicy() + .numberAllowed(10) + ); + } + + private static RenewByIdResponse buildTestRenewBleResponse() { + return new RenewByIdResponse() + .loanPolicyId("123") + .renewalCount(2); + } @Test void createLendingCirculationRequestTest() { when(lendingLibraryService.createCirculation(any(), any())) .thenReturn(createTransactionResponse()); - transactionsService.createCirculationRequest(DCB_TRANSACTION_ID, createDcbTransactionByRole(LENDER)); - verify(lendingLibraryService).createCirculation(DCB_TRANSACTION_ID, createDcbTransactionByRole(LENDER)); + transactionsService.createCirculationRequest(DCB_TRANSACTION_ID, + createDcbTransactionByRole(LENDER)); + verify(lendingLibraryService).createCirculation(DCB_TRANSACTION_ID, + createDcbTransactionByRole(LENDER)); } @Test - void shouldReturnAnyTransactionStatusById(){ + void shouldReturnAnyTransactionStatusById() { var transactionIdUnique = UUID.randomUUID().toString(); when(transactionRepository.findById(transactionIdUnique)) .thenReturn(Optional.ofNullable(TransactionEntity.builder() - .status(TransactionStatus.StatusEnum.CREATED) + .status(StatusEnum.CREATED) .role(LENDER) .build())); @@ -79,21 +185,24 @@ void shouldReturnAnyTransactionStatusById(){ } @Test - void getTransactionStatusByIdNotFoundExceptionTest(){ + void getTransactionStatusByIdNotFoundExceptionTest() { var transactionIdUnique = UUID.randomUUID().toString(); when(transactionRepository.findById(transactionIdUnique)) .thenReturn(Optional.empty()); Throwable exception = assertThrows( - NotFoundException.class, () -> transactionsService.getTransactionStatusById(transactionIdUnique) + NotFoundException.class, + () -> transactionsService.getTransactionStatusById(transactionIdUnique) ); - Assertions.assertEquals(String.format("DCB Transaction was not found by id= %s ", transactionIdUnique), exception.getMessage()); + Assertions.assertEquals( + String.format("DCB Transaction was not found by id= %s ", transactionIdUnique), + exception.getMessage()); } /** * For any kind of role: LENDER/BORROWER/PICKUP/BORROWING_PICKUP - * */ + */ @Test void createTransactionWithExistingTransactionIdTest() { var dcbTransaction = createDcbTransactionByRole(LENDER); @@ -105,35 +214,50 @@ void createTransactionWithExistingTransactionIdTest() { @Test void updateTransactionEntityLenderTest() { var dcbTransactionEntity = createTransactionEntity(); - dcbTransactionEntity.setStatus(TransactionStatus.StatusEnum.OPEN); + dcbTransactionEntity.setStatus(StatusEnum.OPEN); dcbTransactionEntity.setRole(LENDER); - when(transactionRepository.findById(DCB_TRANSACTION_ID)).thenReturn(Optional.of(dcbTransactionEntity)); - when(statusProcessorService.lendingChainProcessor(TransactionStatus.StatusEnum.OPEN, TransactionStatus.StatusEnum.ITEM_CHECKED_OUT)) - .thenReturn(List.of(TransactionStatus.StatusEnum.AWAITING_PICKUP, TransactionStatus.StatusEnum.ITEM_CHECKED_OUT)); - doNothing().when(lendingLibraryService).updateTransactionStatus(dcbTransactionEntity, TransactionStatus.builder().status(TransactionStatus.StatusEnum.AWAITING_PICKUP).build()); - doNothing().when(lendingLibraryService).updateTransactionStatus(dcbTransactionEntity, TransactionStatus.builder().status(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT).build()); + when(transactionRepository.findById(DCB_TRANSACTION_ID)).thenReturn( + Optional.of(dcbTransactionEntity)); + when(statusProcessorService.lendingChainProcessor(StatusEnum.OPEN, ITEM_CHECKED_OUT)) + .thenReturn(List.of(StatusEnum.AWAITING_PICKUP, ITEM_CHECKED_OUT)); + doNothing().when(lendingLibraryService) + .updateTransactionStatus(dcbTransactionEntity, TransactionStatus.builder().status( + StatusEnum.AWAITING_PICKUP).build()); + doNothing().when(lendingLibraryService) + .updateTransactionStatus(dcbTransactionEntity, TransactionStatus.builder().status( + ITEM_CHECKED_OUT).build()); - transactionsService.updateTransactionStatus(DCB_TRANSACTION_ID, TransactionStatus.builder().status(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT).build()); + transactionsService.updateTransactionStatus(DCB_TRANSACTION_ID, + TransactionStatus.builder().status( + ITEM_CHECKED_OUT).build()); - verify(statusProcessorService).lendingChainProcessor(TransactionStatus.StatusEnum.OPEN, TransactionStatus.StatusEnum.ITEM_CHECKED_OUT); + verify(statusProcessorService).lendingChainProcessor(StatusEnum.OPEN, ITEM_CHECKED_OUT); } @Test void updateTransactionEntityErrorTest() { var dcbTransactionEntity = createTransactionEntity(); - dcbTransactionEntity.setStatus(TransactionStatus.StatusEnum.OPEN); - when(transactionRepository.findById(DCB_TRANSACTION_ID)).thenReturn(Optional.of(dcbTransactionEntity)); - var openTransactionStatus = TransactionStatus.builder().status(TransactionStatus.StatusEnum.OPEN).build(); + dcbTransactionEntity.setStatus(StatusEnum.OPEN); + when(transactionRepository.findById(DCB_TRANSACTION_ID)).thenReturn( + Optional.of(dcbTransactionEntity)); + var openTransactionStatus = TransactionStatus.builder().status(StatusEnum.OPEN).build(); - Assertions.assertThrows(StatusException.class, () -> transactionsService.updateTransactionStatus(DCB_TRANSACTION_ID, openTransactionStatus)); + Assertions.assertThrows(StatusException.class, + () -> transactionsService.updateTransactionStatus(DCB_TRANSACTION_ID, openTransactionStatus)); - dcbTransactionEntity.setStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_IN); - var cancelledTransactionStatus = TransactionStatus.builder().status(TransactionStatus.StatusEnum.CANCELLED).build(); - Assertions.assertThrows(StatusException.class, () -> transactionsService.updateTransactionStatus(DCB_TRANSACTION_ID, cancelledTransactionStatus)); + dcbTransactionEntity.setStatus(StatusEnum.ITEM_CHECKED_IN); + var cancelledTransactionStatus = TransactionStatus.builder() + .status(StatusEnum.CANCELLED) + .build(); + Assertions.assertThrows(StatusException.class, + () -> transactionsService.updateTransactionStatus(DCB_TRANSACTION_ID, + cancelledTransactionStatus)); - dcbTransactionEntity.setStatus(TransactionStatus.StatusEnum.ITEM_CHECKED_OUT); - Assertions.assertThrows(StatusException.class, () -> transactionsService.updateTransactionStatus(DCB_TRANSACTION_ID, cancelledTransactionStatus)); + dcbTransactionEntity.setStatus(ITEM_CHECKED_OUT); + Assertions.assertThrows(StatusException.class, + () -> transactionsService.updateTransactionStatus(DCB_TRANSACTION_ID, + cancelledTransactionStatus)); } @Test