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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public enum ErrorCodes {
FUND_LOCATION_RESTRICTION_VIOLATION("fundLocationRestrictionViolation", "One of the locations is restricted to be used by all funds."),
ENCUMBRANCES_FOR_RE_ENCUMBER_NOT_FOUND("encumbrancesForReEncumberNotFound", "The encumbrances were correctly created during the rollover or have already been updated."),
CLAIMING_CONFIG_INVALID("claimingConfigInvalid", "Claiming interval should be set and greater than 0 if claiming is active"),
POL_NUMBER_INVALID_OR_TOO_LONG("polNumberInvalidOrTooLong", "POL number is invalid or bigger than 26 symbols"),
TEMPLATE_NAME_ALREADY_EXISTS("templateNameNotUnique", "Template name already exists"),
BARCODE_IS_NOT_UNIQUE("barcodeIsNotUnique", "The barcode already exists. The barcode must be unique"),
DELETE_WITH_EXPENDED_AMOUNT("deleteWithExpendedAmount", "Cannot delete an encumbrance with an expended amount"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
package org.folio.service.titles;

import org.apache.commons.lang3.StringUtils;
import org.folio.rest.core.exceptions.ErrorCodes;
import org.folio.rest.jaxrs.model.Error;
import org.folio.rest.jaxrs.model.Title;
import org.folio.service.orders.BaseValidationService;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class TitleValidationService extends BaseValidationService {
// Pattern matches the JSON schema definition in acq-models/mod-orders-storage/schemas/po_line.json
private static final Pattern POL_NUMBER_PATTERN = Pattern.compile("^[a-zA-Z0-9]{1,22}-[0-9]{1,3}$");

/**
* Validates title.
*
* @param title the incoming title
* @return list of errors or empty list in case if title is valid
*/
public List<Error> validateTitle(Title title) {
List<ErrorCodes> errorCodes = checkClaimingConfig(title.getClaimingActive(), title.getClaimingInterval());
List<ErrorCodes> errorCodes = new ArrayList<>(checkClaimingConfig(title.getClaimingActive(), title.getClaimingInterval()));
errorCodes.addAll(validatePoLineNumber(title.getPoLineNumber()));
return convertErrorCodesToErrors(errorCodes);
}

private List<ErrorCodes> validatePoLineNumber(String poLineNumber) {
List<ErrorCodes> errors = new ArrayList<>();

if (StringUtils.isNotEmpty(poLineNumber) && !POL_NUMBER_PATTERN.matcher(poLineNumber).matches()) {
errors.add(ErrorCodes.POL_NUMBER_INVALID_OR_TOO_LONG);
}

return errors;
}

private List<Error> convertErrorCodesToErrors(List<ErrorCodes> errorCodes) {
return errorCodes.stream().map(ErrorCodes::toError).toList();
}
Expand Down
5 changes: 2 additions & 3 deletions src/test/java/org/folio/rest/impl/PoNumberApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,9 @@ void testPoNumberValidateWithUniquePONumber()
}

@Test
void testPoNumberValidateWithInvalidPattern()
{
void testPoNumberValidateWithInvalidPattern() {
JsonObject poNumber=new JsonObject();
poNumber.put(PO_NUMBER, "1111111111111111111111");
poNumber.put(PO_NUMBER, "12345678901234567890123"); // 23 characters - exceeds limit of 22
verifyPostResponse(PONUMBER_VALIDATE_PATH, poNumber.encodePrettily(), prepareHeaders(EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10), APPLICATION_JSON, 422);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package org.folio.service.titles;

import static org.folio.rest.core.exceptions.ErrorCodes.POL_NUMBER_INVALID_OR_TOO_LONG;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.List;

import org.folio.rest.jaxrs.model.Error;
import org.folio.rest.jaxrs.model.Title;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("Title Validation Service Unit Tests")
class TitleValidationServiceTest {

private TitleValidationService titleValidationService;

@BeforeEach
void setUp() {
titleValidationService = new TitleValidationService();
}

@Test
@DisplayName("Should pass validation for valid POL number")
void testValidPoLineNumber() {
Title title = new Title().withPoLineNumber("ABC123-1");
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, is(empty()));
}

@Test
@DisplayName("Should pass validation for POL number with 22 character prefix")
void testPoLineNumberWith22CharPrefix() {
Title title = new Title().withPoLineNumber("1234567890123456789012-999");
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, is(empty()));
}

@Test
@DisplayName("Should fail validation when POL number prefix exceeds 22 characters")
void testPoLineNumberPrefixTooLong() {
// Given - 23 character prefix
Title title = new Title().withPoLineNumber("12345678901234567890123-1");
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, hasSize(1));
assertEquals(POL_NUMBER_INVALID_OR_TOO_LONG.toError().getCode(), errors.get(0).getCode());
assertEquals("POL number is invalid or bigger than 26 symbols", errors.get(0).getMessage());
}

@Test
@DisplayName("Should fail validation for POL number with invalid format (no dash)")
void testPoLineNumberNoDash() {
Title title = new Title().withPoLineNumber("ABC1231");
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, hasSize(1));
assertEquals(POL_NUMBER_INVALID_OR_TOO_LONG.toError().getCode(), errors.get(0).getCode());
}

@Test
@DisplayName("Should fail validation for POL number with special characters")
void testPoLineNumberWithSpecialCharacters() {
Title title = new Title().withPoLineNumber("ABC!@#-1");
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, hasSize(1));
assertEquals(POL_NUMBER_INVALID_OR_TOO_LONG.toError().getCode(), errors.get(0).getCode());
}

@Test
@DisplayName("Should fail validation when POL suffix is non-numeric")
void testPoLineNumberNonNumericSuffix() {
Title title = new Title().withPoLineNumber("ABC123-ABC");
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, hasSize(1));
assertEquals(POL_NUMBER_INVALID_OR_TOO_LONG.toError().getCode(), errors.get(0).getCode());
}

@Test
@DisplayName("Should fail validation when POL suffix exceeds 3 digits")
void testPoLineNumberSuffixTooLong() {
Title title = new Title().withPoLineNumber("ABC123-1234");
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, hasSize(1));
assertEquals(POL_NUMBER_INVALID_OR_TOO_LONG.toError().getCode(), errors.get(0).getCode());
}

@Test
@DisplayName("Should pass validation for null POL number")
void testNullPoLineNumber() {
Title title = new Title().withPoLineNumber(null);
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, is(empty()));
}

@Test
@DisplayName("Should pass validation for empty POL number")
void testEmptyPoLineNumber() {
Title title = new Title().withPoLineNumber("");
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, is(empty()));
}

@Test
@DisplayName("Should pass validation for POL number with minimum length")
void testPoLineNumberMinimumLength() {
Title title = new Title().withPoLineNumber("A-1");
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, is(empty()));
}

@Test
@DisplayName("Should pass validation for POL number with alphanumeric prefix")
void testPoLineNumberAlphanumericPrefix() {
Title title = new Title().withPoLineNumber("Mon123Ogr456-999");
List<Error> errors = titleValidationService.validateTitle(title);
assertThat(errors, is(empty()));
}
}