diff --git a/backend/Dockerfile b/backend/Dockerfile index b6c2953df3..c2a086b80d 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -12,8 +12,8 @@ RUN microdnf update --nodocs -y && \ microdnf clean all # Add Maven to the PATH environment variable -ENV MAVEN_HOME /opt/maven -ENV PATH $MAVEN_HOME/bin:$PATH +ENV MAVEN_HOME=/opt/maven +ENV PATH=$MAVEN_HOME/bin:$PATH # Receiving app version ARG APP_VERSION=0.0.1 diff --git a/backend/src/main/java/ca/bc/gov/app/service/client/ClientLegacyService.java b/backend/src/main/java/ca/bc/gov/app/service/client/ClientLegacyService.java index 7276bf15f2..c9b3ba2b73 100644 --- a/backend/src/main/java/ca/bc/gov/app/service/client/ClientLegacyService.java +++ b/backend/src/main/java/ca/bc/gov/app/service/client/ClientLegacyService.java @@ -209,15 +209,54 @@ public Flux searchDocument( } + /** + * Searches for a list of {@link ForestClientDto} in the legacy API based on the given search type + * and value. This method constructs a query parameter map using the provided search type and + * value, sends a GET request to the legacy API, and converts the response into a Flux of + * ForestClientDto objects. It also logs the search parameters and the results for debugging + * purposes. + * + * @param searchType The type of search to perform (e.g., "registrationNumber", "companyName"). + * @param value The value to search for. + * @return A Flux of ForestClientDto objects matching the search criteria. + */ + public Flux searchGeneric( + String searchType, + String value + ) { + return searchGeneric(searchType, searchType, value); + } + public Flux searchGeneric( String searchType, + String paramName, String value ) { - if (StringUtils.isBlank(value)) + if (StringUtils.isAnyBlank(searchType, paramName, value)) { return Flux.empty(); + } - Map> parameters = Map.of(searchType, List.of(value)); + Map> parameters = Map.of(paramName, List.of(value)); + + return searchGeneric(searchType, parameters); + } + + public Flux searchGeneric( + String searchType, + Map> parameters + ) { + + if ( + StringUtils.isBlank(searchType) + || parameters == null + || parameters.isEmpty() + || parameters.values().stream().anyMatch(CollectionUtils::isEmpty) + || parameters.values().stream().flatMap(List::stream).anyMatch(StringUtils::isBlank) + || parameters.keySet().stream().anyMatch(StringUtils::isBlank) + ) { + return Flux.empty(); + } return legacyApi @@ -251,7 +290,8 @@ public Flux searchLocation(AddressSearchDto dto) { .body(BodyInserters.fromValue(dto)) .exchangeToFlux(response -> response.bodyToFlux(ForestClientDto.class)) .doOnNext( - client -> log.info("Found Legacy data for location search with client number {}", client.clientNumber()) + client -> log.info("Found Legacy data for location search with client number {}", + client.clientNumber()) ); } @@ -263,8 +303,10 @@ public Flux searchContact(ContactSearchDto dto) { .body(BodyInserters.fromValue(dto)) .exchangeToFlux(response -> response.bodyToFlux(ForestClientDto.class)) .doOnNext( - client -> log.info("Found Legacy data for contact search with client number {}", client.clientNumber()) + client -> log.info("Found Legacy data for contact search with client number {}", + client.clientNumber()) ); } + } diff --git a/backend/src/main/java/ca/bc/gov/app/service/client/ClientMatchService.java b/backend/src/main/java/ca/bc/gov/app/service/client/ClientMatchService.java index 242f17dc2b..e3aa6c2830 100644 --- a/backend/src/main/java/ca/bc/gov/app/service/client/ClientMatchService.java +++ b/backend/src/main/java/ca/bc/gov/app/service/client/ClientMatchService.java @@ -84,7 +84,7 @@ public Mono matchClients( private Mono matchStep1(ClientSubmissionDto dto) { switch (dto.businessInformation().clientType()) { - case "BCR" -> { + case "C", "RSP", "S", "A", "P", "L" -> { return findAndRunMatcher(dto, StepMatchEnum.STEP1REGISTERED); } case "R" -> { diff --git a/backend/src/main/java/ca/bc/gov/app/service/client/matches/RegisteredStepMatcher.java b/backend/src/main/java/ca/bc/gov/app/service/client/matches/RegisteredStepMatcher.java new file mode 100644 index 0000000000..b2cffcc4d0 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/app/service/client/matches/RegisteredStepMatcher.java @@ -0,0 +1,203 @@ +package ca.bc.gov.app.service.client.matches; + +import ca.bc.gov.app.dto.client.ClientSubmissionDto; +import ca.bc.gov.app.dto.client.StepMatchEnum; +import ca.bc.gov.app.dto.legacy.ForestClientDto; +import ca.bc.gov.app.service.client.ClientLegacyService; +import io.micrometer.observation.annotation.Observed; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Component +@Slf4j +@Observed +@RequiredArgsConstructor +public class RegisteredStepMatcher implements StepMatcher { + + private static final String BUSINESS_FIELD_NAME = "businessInformation.businessName"; + + /** + * The ClientLegacyService used to search for registered companies and other data. + */ + private final ClientLegacyService legacyService; + + /** + * This method is used to get the logger for this class. This is just to allow the default methods + * to access the logger. + * + * @return The Logger object used for logging in this class. + */ + public Logger getLogger() { + return log; + } + + /** + * This method returns the step matcher enumeration value for registered steps. + * + * @return The StepMatchEnum value representing the registered step. + */ + public StepMatchEnum getStepMatcher() { + return StepMatchEnum.STEP1REGISTERED; + } + + /** + *

This method matches the client submission data to the registered step. It performs three + * searches:

+ *
    + *
  1. A fuzzy match should happen for the Client name
  2. + *
  3. A full match should happen for the Incorporation number
  4. + *
  5. A fuzzy match should happen for the Doing Business as
  6. + *
  7. A full match should happen for the Client name
  8. + *
  9. A full match should happen for the Doing Business as
  10. + *
  11. A full match should happen for the Acronym
  12. + *
  13. A full match should happen for the combination of First name, Last name and date of birth if the user is a Sole Proprietor
  14. + *
+ *

The results of these searches are then processed and reduced to a single result.

+ * + * @param dto The ClientSubmissionDto object containing the client data to be matched. + * @return A Mono indicating when the matching process is complete. + */ + @Override + public Mono matchStep(ClientSubmissionDto dto) { + + // Search for individual without document id + Flux individualFuzzyMatch = + Mono + .just(dto.businessInformation()) + .filter(businessInformation -> businessInformation.clientType().equals("RSP")) + .flatMapMany(businessInformation -> + legacyService.searchIndividual( + businessInformation.firstName(), + businessInformation.lastName(), + businessInformation.birthdate(), + null + ) + ) + .doOnNext(client -> log.info("Match found for sole proprietor fuzzy match: {}", + client.clientNumber()) + ); + + Flux clientRegistrationFullMatch = + legacyService + .searchLegacy( + dto.businessInformation().registrationNumber(), + null, + null, + null + ).doOnNext(client -> log.info("Match found for registration number full match: {}", + client.clientNumber()) + ); + + Flux clientNameFullMatch = + legacyService + .searchGeneric( + "clientName", + dto.businessInformation().businessName() + ).doOnNext(client -> log.info("Match found for client name full match: {}", + client.clientNumber()) + ); + + Flux clientNameFuzzyMatch = + legacyService + .searchGeneric( + "match", + "companyName", + dto.businessInformation().businessName() + ).doOnNext(client -> log.info("Match found for client name fuzzy match: {}", + client.clientNumber()) + ); + + Flux clientAcronymFullMatch = + legacyService + .searchGeneric( + "acronym", + dto.businessInformation().clientAcronym() + ).doOnNext(client -> log.info("Match found for client acronym full match: {}", + client.clientNumber()) + ); + + Flux dbaFuzzyMatch = + legacyService + .searchGeneric( + "doingBusinessAs", + "dbaName", + dto.businessInformation().doingBusinessAs() + ).doOnNext(client -> log.info("Match found for doing business as fuzzy match: {}", + client.clientNumber()) + ); + + Flux dbaFullMatch = + legacyService + .searchGeneric( + "doingBusinessAs", + Map.of( + "dbaName", List.of(dto.businessInformation().doingBusinessAs()), + "isFuzzy", List.of("false") + ) + ).doOnNext(client -> log.info("Match found for doing business as full match: {}", + client.clientNumber()) + ); + + return reduceMatchResults( + Flux.concat( + + //A fuzzy match should happen for the Client name + processResult( + clientNameFuzzyMatch, + BUSINESS_FIELD_NAME, + true + ), + + //A full match should happen for the Client name + processResult( + clientNameFullMatch, + BUSINESS_FIELD_NAME, + false + ), + + //A full match should happen for the Incorporation number + //We point to the businessName as this is the only field the user has access to + processResult( + clientRegistrationFullMatch, + BUSINESS_FIELD_NAME, + false + ), + + //A fuzzy match should happen for the Doing Business as + processResult( + dbaFuzzyMatch, + "businessInformation.doingBusinessAs", + true + ), + + //A full match should happen for the Doing Business as + processResult( + dbaFullMatch, + "businessInformation.doingBusinessAs", + false + ), + + //A full match should happen for the Acronym + processResult( + clientAcronymFullMatch, + "businessInformation.clientAcronym", + false + ), + + //A full match should happen for the combination of First name, Last name and date of birth if the user is a Sole Proprietor + //We point to the businessName as this is the only field the user has access to + processResult( + individualFuzzyMatch, + BUSINESS_FIELD_NAME, + true + ) + ) + ); + } +} diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 90033a5dbd..ebf64042ba 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -169,8 +169,8 @@ ca: - X-Total-Count - x-client-id - X-Client-Id - - X-Step - x-step + - X-Step methods: - OPTIONS - GET diff --git a/backend/src/test/java/ca/bc/gov/app/extensions/ClientMatchDataGenerator.java b/backend/src/test/java/ca/bc/gov/app/extensions/ClientMatchDataGenerator.java index b2edfc8a97..7b9b7bafa8 100644 --- a/backend/src/test/java/ca/bc/gov/app/extensions/ClientMatchDataGenerator.java +++ b/backend/src/test/java/ca/bc/gov/app/extensions/ClientMatchDataGenerator.java @@ -9,8 +9,10 @@ import ca.bc.gov.app.dto.legacy.ForestClientDto; import java.time.LocalDate; import java.util.List; +import java.util.UUID; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ClientMatchDataGenerator { @@ -33,14 +35,170 @@ public static ClientSubmissionDto getIndividualDto( .businessInformation() .withBusinessName(lastName) .withFirstName(firstName) + .withBusinessType("U") .withBirthdate(birthdate) .withIdentificationType(new ClientValueTextDto(idType, idType)) .withIdentificationProvince(idProvince) .withClientIdentification(idValue) + ); + } + + public static ClientSubmissionDto getIndividualDto( + String firstName, + String lastName, + LocalDate birthdate, + String idType, + String idProvince, + String idValue, + ClientAddressDto addressDto, + ClientContactDto contactDto + ) { + + ClientSubmissionDto dto = getDto(addressDto, contactDto); + + return + dto + .withBusinessInformation( + dto + .businessInformation() + .withBusinessName(lastName) + .withFirstName(firstName) + .withBusinessType("U") .withClientType("I") + .withBirthdate(birthdate) + .withIdentificationType(new ClientValueTextDto(idType, idType)) + .withIdentificationProvince(idProvince) + .withClientIdentification(idValue) + ); + } + + public static ClientSubmissionDto getRegistered( + String registrationNumber, + String businessName, + String legalType, + String workSafeBcNumber, + String doingBusinessAs, + String clientAcronym, + String clientType + ) { + + ClientSubmissionDto dto = getDtoType(clientType); + + return + dto + .withBusinessInformation( + dto + .businessInformation() + .withRegistrationNumber(registrationNumber) + .withBusinessName(businessName) + .withBusinessType("R") + .withLegalType(legalType) + .withWorkSafeBcNumber(workSafeBcNumber) + .withDoingBusinessAs(doingBusinessAs) + .withClientAcronym(clientAcronym) + ); } + public static ClientSubmissionDto getRegisteredSP( + String registrationNumber, + String businessName, + String workSafeBcNumber, + String doingBusinessAs, + String clientAcronym, + String firstName, + String lastName, + LocalDate birthdate + ) { + + ClientSubmissionDto dto = getDtoType("RSP"); + + return + dto + .withBusinessInformation( + dto + .businessInformation() + .withRegistrationNumber(registrationNumber) + .withBusinessName(businessName) + .withBusinessType("R") + .withLegalType("SP") + .withWorkSafeBcNumber(workSafeBcNumber) + .withDoingBusinessAs(doingBusinessAs) + .withClientAcronym(clientAcronym) + .withFirstName(firstName) + .withLastName(lastName) + .withBirthdate(birthdate) + ); + } + + public static ClientSubmissionDto getOther( + String businessName, + String legalType, + String workSafeBcNumber, + String clientAcronym, + String clientType + ) { + + ClientSubmissionDto dto = getDtoType(clientType); + + return + dto + .withBusinessInformation( + dto + .businessInformation() + .withBusinessName(businessName) + .withBusinessType("U") + .withLegalType(legalType) + .withWorkSafeBcNumber(workSafeBcNumber) + .withClientAcronym(clientAcronym) + + ); + } + + public static ClientSubmissionDto getRandomData(String type) { + return getRandomData(type,null,null); + } + + public static ClientSubmissionDto getRandomData( + String type, + ClientAddressDto addressDto, + ClientContactDto contactDto + ) { + + return switch (type) { + case "I" -> getIndividualDto( + UUID.randomUUID().toString(), + UUID.randomUUID().toString(), + LocalDate.now(), + UUID.randomUUID().toString(), + UUID.randomUUID().toString(), + UUID.randomUUID().toString(), + addressDto, + contactDto + ); + case "C", "RSP", "S", "A", "P", "L" -> getRegistered( + "C1234567", + UUID.randomUUID().toString(), + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + type + ) + .withLocation(getLocationDto(addressDto, contactDto)); + case "G", "F", "U", "R" -> getOther( + UUID.randomUUID().toString(), + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + type + ) + .withLocation(getLocationDto(addressDto, contactDto)); + default -> getDtoType(type); + }; + + } + public static ClientSubmissionDto getAddress() { ClientSubmissionDto dto = getDto( new ClientAddressDto( @@ -151,10 +309,7 @@ public static ClientSubmissionDto getDto( null, idProvince ), - new ClientLocationDto( - addressDto == null ? List.of() : List.of(addressDto), - contactDto == null ? List.of() : List.of(contactDto) - ), + getLocationDto(addressDto, contactDto), null, null ); @@ -179,4 +334,14 @@ public static ForestClientDto getForestClientDto(String clientNumber) { ); } + public static ClientLocationDto getLocationDto( + ClientAddressDto addressDto, + ClientContactDto contactDto + ) { + return new ClientLocationDto( + addressDto == null ? List.of() : List.of(addressDto), + contactDto == null ? List.of() : List.of(contactDto) + ); + } + } diff --git a/backend/src/test/java/ca/bc/gov/app/service/client/ClientLegacyServiceTest.java b/backend/src/test/java/ca/bc/gov/app/service/client/ClientLegacyServiceIntegrationTest.java similarity index 55% rename from backend/src/test/java/ca/bc/gov/app/service/client/ClientLegacyServiceTest.java rename to backend/src/test/java/ca/bc/gov/app/service/client/ClientLegacyServiceIntegrationTest.java index 2e97707d62..181d984d18 100644 --- a/backend/src/test/java/ca/bc/gov/app/service/client/ClientLegacyServiceTest.java +++ b/backend/src/test/java/ca/bc/gov/app/service/client/ClientLegacyServiceIntegrationTest.java @@ -3,13 +3,18 @@ import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.okJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.junit.jupiter.api.Assertions.*; +import ca.bc.gov.app.dto.legacy.AddressSearchDto; +import ca.bc.gov.app.dto.legacy.ContactSearchDto; import ca.bc.gov.app.extensions.AbstractTestContainerIntegrationTest; import ca.bc.gov.app.extensions.WiremockLogNotifier; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; @@ -22,7 +27,8 @@ import org.springframework.beans.factory.annotation.Autowired; import reactor.test.StepVerifier; -class ClientLegacyServiceTest extends AbstractTestContainerIntegrationTest { +@DisplayName("Integration Test | Client Legacy Service Test") +class ClientLegacyServiceIntegrationTest extends AbstractTestContainerIntegrationTest { @RegisterExtension static WireMockExtension legacyStub = WireMockExtension @@ -81,8 +87,63 @@ void shouldSearchGenericWithInvalidValues(String values) { } + @ParameterizedTest + @MethodSource("invalidValuesForMap") + @DisplayName("searching legacy with invalid values for map") + void shouldNotSearchWhenInvalidCasesHitGeneric(Map> parameters){ + service.searchGeneric("generic",parameters) + .as(StepVerifier::create) + .verifyComplete(); + } + + @Test + @DisplayName("searching legacy for location") + void shouldSearchALocation(){ + + legacyStub + .stubFor( + post(urlPathEqualTo("/api/search/address")) + .willReturn(okJson("[{\"clientNumber\":\"00000001\"}]")) + ); + + service.searchLocation(new AddressSearchDto("2975 Jutland Rd","Victoria","BC","V8T5J9","Canada")) + .as(StepVerifier::create) + .assertNext(results -> assertEquals("00000001", results.clientNumber())) + .verifyComplete(); + } + + @Test + @DisplayName("searching legacy for contact") + void shouldSearchAContact(){ + legacyStub + .stubFor( + post(urlPathEqualTo("/api/search/contact")) + .willReturn(okJson("[{\"clientNumber\":\"00000001\"}]")) + ); + + service.searchContact(new ContactSearchDto("John",null,"Smith","mail@mail.ca","2505555555","2505555555","2505555555")) + .as(StepVerifier::create) + .assertNext(results -> assertEquals("00000001", results.clientNumber())) + .verifyComplete(); + } + + private static Stream invalidValues() { - return Stream.of(null,StringUtils.EMPTY," "); + return Stream.of( + null, + StringUtils.EMPTY, + " " + ); + } + + private static Stream>> invalidValuesForMap(){ + return Stream.of( + Map.of("email",List.of("")), + Map.of("email",List.of(" ")), + Map.of("email",List.of()), + Map.of("",List.of()), + Map.of(" ",List.of()) + ); } } \ No newline at end of file diff --git a/backend/src/test/java/ca/bc/gov/app/service/client/ClientMatchServiceIntegrationTest.java b/backend/src/test/java/ca/bc/gov/app/service/client/ClientMatchServiceIntegrationTest.java index c4fe981c7f..ad51cc5e3b 100644 --- a/backend/src/test/java/ca/bc/gov/app/service/client/ClientMatchServiceIntegrationTest.java +++ b/backend/src/test/java/ca/bc/gov/app/service/client/ClientMatchServiceIntegrationTest.java @@ -1,30 +1,33 @@ package ca.bc.gov.app.service.client; +import static ca.bc.gov.app.extensions.ClientMatchDataGenerator.getDto; +import static ca.bc.gov.app.extensions.ClientMatchDataGenerator.getIndividualDto; +import static ca.bc.gov.app.extensions.ClientMatchDataGenerator.getRandomData; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.okJson; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Named.named; import ca.bc.gov.app.dto.client.ClientAddressDto; -import ca.bc.gov.app.dto.client.ClientBusinessInformationDto; import ca.bc.gov.app.dto.client.ClientContactDto; -import ca.bc.gov.app.dto.client.ClientLocationDto; import ca.bc.gov.app.dto.client.ClientSubmissionDto; -import ca.bc.gov.app.dto.client.ClientValueTextDto; import ca.bc.gov.app.dto.client.MatchResult; +import ca.bc.gov.app.dto.legacy.ForestClientDto; import ca.bc.gov.app.exception.DataMatchException; import ca.bc.gov.app.exception.InvalidRequestObjectException; import ca.bc.gov.app.extensions.AbstractTestContainerIntegrationTest; +import ca.bc.gov.app.extensions.ClientMatchDataGenerator; import ca.bc.gov.app.extensions.WiremockLogNotifier; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.List; -import java.util.UUID; import java.util.stream.Stream; import org.apache.commons.lang3.NotImplementedException; +import org.apache.commons.lang3.StringUtils; import org.assertj.core.api.Condition; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.extension.RegisterExtension; @@ -32,6 +35,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; +import reactor.core.publisher.Flux; import reactor.test.StepVerifier; @DisplayName("Integrated Test | Client Match Service") @@ -54,7 +58,7 @@ class ClientMatchServiceIntegrationTest extends AbstractTestContainerIntegration private ClientMatchService service; @DisplayName("Should fail for invalid cases") - @ParameterizedTest + @ParameterizedTest(name = "Case Step {1}: {0} will throw {2}") @MethodSource("invalidCases") void shouldFailForInvalidCases( ClientSubmissionDto dto, @@ -103,7 +107,8 @@ void shouldMatchIndividuals( .withQueryParam("dob", equalTo(dto.businessInformation().birthdate().format( DateTimeFormatter.ISO_DATE)) ) - .withQueryParam("identification", equalTo(dto.businessInformation().clientIdentification())) + .withQueryParam("identification", + equalTo(dto.businessInformation().clientIdentification())) .willReturn(okJson(individualFullMatch)) ); @@ -148,193 +153,236 @@ void shouldMatchIndividuals( } + @DisplayName("Matching registered clients") + @ParameterizedTest + @MethodSource("registeredMatch") + void shouldMatchRegistered( + ClientSubmissionDto dto, + String individualFuzzyMatch, + String clientNameFuzzyMatch, + String clientRegistrationFullMatch, + String fullNameMatch, + String acronymMatch, + String dbaFuzzyMatch, + String dbaFullMatch, + boolean error, + boolean fuzzy + ) { + + legacyStub.resetAll(); + + if(dto.businessInformation().clientType().equalsIgnoreCase("RSP")) { + legacyStub + .stubFor( + get(urlPathEqualTo("/api/search/individual")) + .withQueryParam("firstName", equalTo(dto.businessInformation().firstName())) + .withQueryParam("lastName", equalTo(dto.businessInformation().lastName())) + .withQueryParam("dob", equalTo(dto.businessInformation().birthdate().format( + DateTimeFormatter.ISO_DATE)) + ) + .willReturn(okJson(individualFuzzyMatch)) + ); + } + + legacyStub + .stubFor( + get(urlPathEqualTo("/api/search/registrationOrName")) + .withQueryParam("registrationNumber", equalTo(dto.businessInformation().registrationNumber())) + .willReturn(okJson(clientRegistrationFullMatch)) + ); + + legacyStub + .stubFor( + get(urlPathEqualTo("/api/search/clientName")) + .withQueryParam("clientName", equalTo(dto.businessInformation().businessName())) + .willReturn(okJson(fullNameMatch)) + ); + + legacyStub + .stubFor( + get(urlPathEqualTo("/api/search/match")) + .withQueryParam("companyName", equalTo(dto.businessInformation().businessName())) + .willReturn(okJson(clientNameFuzzyMatch)) + ); + + if(StringUtils.isNotBlank(dto.businessInformation().clientAcronym())) { + legacyStub + .stubFor( + get(urlPathEqualTo("/api/search/acronym")) + .withQueryParam("acronym", equalTo(dto.businessInformation().clientAcronym())) + .willReturn(okJson(acronymMatch)) + ); + } + + if(StringUtils.isNotBlank(dto.businessInformation().doingBusinessAs())) { + legacyStub + .stubFor( + get(urlPathEqualTo("/api/search/doingBusinessAs")) + .withQueryParam("dbaName", equalTo(dto.businessInformation().doingBusinessAs())) + .willReturn(okJson(dbaFuzzyMatch)) + ); + + legacyStub + .stubFor( + get(urlPathEqualTo("/api/search/doingBusinessAs")) + .withQueryParam("dbaName", equalTo(dto.businessInformation().doingBusinessAs())) + .withQueryParam("isFuzzy", equalTo("false")) + .willReturn(okJson(dbaFullMatch)) + ); + } + + StepVerifier.FirstStep matcher = + service + .matchClients(dto, 1) + .as(StepVerifier::create); + + if (error) { + matcher + .consumeErrorWith(errorContent -> + assertThat(errorContent) + .isInstanceOf(DataMatchException.class) + .hasMessage("409 CONFLICT \"Match found on existing data.\"") + .extracting("matches") + .isInstanceOf(List.class) + .asList() + .has( + new Condition<>( + matchResult -> + matchResult + .stream() + .map(m -> (MatchResult) m) + .anyMatch(m -> m.fuzzy() == fuzzy), + "MatchResult with fuzzy value %s", + fuzzy + ) + ) + + ) + .verify(); + } else { + matcher.verifyComplete(); + } + + } + private static Stream invalidCases() { return Stream .of( Arguments.of( - null, + named("Null ClientSubmissionDto", null), 1, - InvalidRequestObjectException.class + named("Invalid Request", InvalidRequestObjectException.class) ), + Arguments.of( - new ClientSubmissionDto( - null, - null, - null, - null + named("Null content of ClientSubmissionDto", + new ClientSubmissionDto( + null, + null, + null, + null + ) ), 1, - InvalidRequestObjectException.class + named("Invalid Request", InvalidRequestObjectException.class) ), + Arguments.of( - getDto(), + named("getDto()", getDto(null, null)), 1, - InvalidRequestObjectException.class + named("Invalid Request", InvalidRequestObjectException.class) ), + Arguments.of( - getRandomData().withLocation(null), + named("Individual random data and null location", + getRandomData("I").withLocation(null)), 1, - InvalidRequestObjectException.class - ), - Arguments.of( - getRandomData() - .withLocation( - new ClientLocationDto( - null, - null - ) - ), - 2, - InvalidRequestObjectException.class + named("Invalid Request", InvalidRequestObjectException.class) ), + Arguments.of( - getRandomData() - .withLocation( - new ClientLocationDto( - List.of(), - null - ) - ), + named("Individual random data and null content location", getRandomData("I")), 2, - InvalidRequestObjectException.class + named("Invalid Request", InvalidRequestObjectException.class) ), Arguments.of( - getRandomData() - .withLocation( - new ClientLocationDto( - List.of( - new ClientAddressDto( - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - 0, - null - ) - ), + named("Individual random with invalid address on list", + getRandomData("I", + new ClientAddressDto( + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 0, null - ) - ), + ), + null + ) + ), 2, - InvalidRequestObjectException.class + named("Invalid Request", InvalidRequestObjectException.class) ), Arguments.of( - getRandomData() - .withLocation( - new ClientLocationDto( - null, - null - ) - ), + named("Individual random data and null content location", getRandomData("I")), 3, - InvalidRequestObjectException.class + named("Invalid Request", InvalidRequestObjectException.class) ), Arguments.of( - getRandomData() - .withLocation( - new ClientLocationDto( + named("Individual random with invalid contact on list", + getRandomData("I", null, + new ClientContactDto( + null, + null, null, - List.of() - ) - ), - 3, - InvalidRequestObjectException.class - ), - Arguments.of( - getRandomData() - .withLocation( - new ClientLocationDto( null, - List.of( - new ClientContactDto( - null, - null, - null, - null, - null, - null, - null, - 0, - null - ) - ) + null, + null, + null, + 0, + null ) - ), + ) + ), 3, - InvalidRequestObjectException.class + named("Invalid Request", InvalidRequestObjectException.class) ), Arguments.of( - getRandomData(), + named("Random individual with wrong step", getRandomData("I")), 4, - InvalidRequestObjectException.class + named("Invalid Request", InvalidRequestObjectException.class) ), Arguments.of( - getRandomData() - .withBusinessInformation( - getRandomData() - .businessInformation() - .withClientType("BCR") - ), + named("Random First Nation", getRandomData("R")), 1, - NotImplementedException.class + named("Not Implemented", NotImplementedException.class) ), Arguments.of( - getRandomData() - .withBusinessInformation( - getRandomData() - .businessInformation() - .withClientType("R") - ), + named("Random Government", getRandomData("G")), 1, - NotImplementedException.class + named("Not Implemented", NotImplementedException.class) ), Arguments.of( - getRandomData() - .withBusinessInformation( - getRandomData() - .businessInformation() - .withClientType("G") - ), + named("Random Forest", getRandomData("F")), 1, - NotImplementedException.class + named("Not Implemented", NotImplementedException.class) ), Arguments.of( - getRandomData() - .withBusinessInformation( - getRandomData() - .businessInformation() - .withClientType("F") - ), + named("Random Unregistered", getRandomData("U")), 1, - NotImplementedException.class + named("Not Implemented", NotImplementedException.class) ), Arguments.of( - getRandomData() - .withBusinessInformation( - getRandomData() - .businessInformation() - .withClientType("U") - ), + named("Random Invalid type", getRandomData("J")), 1, - NotImplementedException.class - ), - Arguments.of( - getRandomData() - .withBusinessInformation( - getRandomData() - .businessInformation() - .withClientType("J") - ), - 1, - InvalidRequestObjectException.class + named("Invalid Request", InvalidRequestObjectException.class) ) ); } @@ -435,82 +483,222 @@ private static Stream individualMatch() { ); } - private static ClientSubmissionDto getIndividualDto( - String firstName, - String lastName, - LocalDate birthdate, - String idType, - String idProvince, - String idValue - ) { - - ClientSubmissionDto dto = getDtoType("I"); - - return - dto - .withBusinessInformation( - dto - .businessInformation() - .withBusinessName(lastName) - .withFirstName(firstName) - .withBirthdate(birthdate) - .withIdentificationType(new ClientValueTextDto(idType,idType)) - .withIdentificationProvince(idProvince) - .withClientIdentification(idValue) - .withClientType("I") - ); - } - - private static ClientSubmissionDto getRandomData() { - return getIndividualDto( - UUID.randomUUID().toString(), - UUID.randomUUID().toString(), - LocalDate.now(), - UUID.randomUUID().toString(), - UUID.randomUUID().toString(), - UUID.randomUUID().toString() - ); - } - - private static ClientSubmissionDto getDtoType(String type) { - ClientSubmissionDto dto = getDto(); - return dto.withBusinessInformation( - dto - .businessInformation() - .withClientType(type) - ); + private static Stream registeredMatch() { + return Stream.of( + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "C" + ), + named("no individual", "[]"), + named("no fuzzy name", "[]"), + named("no registration", "[]"), + named("no full name", "[]"), + named("no acronym", "[]"), + named("no dba fuzzy", "[]"), + named("no dba full", "[]"), + false, + false + ), + Arguments.of( + ClientMatchDataGenerator + .getRegisteredSP( + "C123456", + "Fake Corp", + StringUtils.EMPTY, + "Fake Corp", + StringUtils.EMPTY, + "Johnathan", + "Wick", + LocalDate.of(1970, 1, 12) + ), + named("individual matched", "[{\"clientNumber\":\"00000001\"}]"), + named("no fuzzy name", "[]"), + named("no registration", "[]"), + named("no full name", "[]"), + named("no acronym", "[]"), + named("no dba fuzzy", "[]"), + named("no dba full", "[]"), + true, + true + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "S123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "S" + ), + named("no individual", "[]"), + named("no fuzzy name", "[]"), + named("registration matched", "[{\"clientNumber\":\"00000001\"}]"), + named("no full name", "[]"), + named("no acronym", "[]"), + named("no dba fuzzy", "[]"), + named("no dba full", "[]"), + true, + false + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "A123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + "FAKE", + "A" + ), + named("no individual", "[]"), + named("no fuzzy name", "[]"), + named("no registration", "[]"), + named("no full name", "[]"), + named("acronym matched","[{\"clientNumber\":\"00000001\"}]"), + named("no dba fuzzy", "[]"), + named("no dba full", "[]"), + true, + false + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "P123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "P" + ), + named("no individual", "[]"), + named("fuzzy name matched", "[{\"clientNumber\":\"00000001\"}]"), + named("no registration", "[]"), + named("no full name", "[]"), + named("no acronym", "[]"), + named("no dba fuzzy", "[]"), + named("no dba full", "[]"), + true, + true + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "L123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "L" + ), + named("no individual", "[]"), + named("fuzzy name matched", "[{\"clientNumber\":\"00000001\"}]"), + named("no registration", "[]"), + named("full name matched", "[{\"clientNumber\":\"00000002\"}]"), + named("no acronym", "[]"), + named("no dba fuzzy", "[]"), + named("no dba full", "[]"), + true, + false + ), - } - private static ClientSubmissionDto getDto() { - return new ClientSubmissionDto( - new ClientBusinessInformationDto( - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + "Fake Corp", + StringUtils.EMPTY, + "C" + ), + named("no individual", "[]"), + named("no fuzzy name", "[]"), + named("no registration", "[]"), + named("no full name", "[]"), + named("no acronym", "[]"), + named("no dba fuzzy", "[{\"clientNumber\":\"00000001\"}]"), + named("no dba full", "[]"), + true, + true + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + "Fake Corp", + StringUtils.EMPTY, + "C" + ), + named("no individual", "[]"), + named("no fuzzy name", "[]"), + named("no registration", "[]"), + named("no full name", "[]"), + named("no acronym", "[]"), + named("dba fuzzy matched", "[{\"clientNumber\":\"00000001\"}]"), + named("dba full matched", "[{\"clientNumber\":\"00000001\"}]"), + true, + false ), - new ClientLocationDto( - null, - null + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + "Fake Corp", + StringUtils.EMPTY, + "C" + ), + named("no individual", "[]"), + named("fuzzy name matched", "[{\"clientNumber\":\"00000001\"}]"), + named("no registration", "[]"), + named("full name matched", "[{\"clientNumber\":\"00000002\"}]"), + named("no acronym", "[]"), + named("dba fuzzy matched", "[{\"clientNumber\":\"00000003\"}]"), + named("dba full matched", "[{\"clientNumber\":\"00000004\"}]"), + true, + false ), - null, - null + Arguments.of( + ClientMatchDataGenerator + .getRegisteredSP( + "C123456", + "Fake Corp", + StringUtils.EMPTY, + "Fake Corp", + "FAKE", + "Johnathan", + "Wick", + LocalDate.of(1970, 1, 12) + ), + named("individual matched", "[{\"clientNumber\":\"00000001\"}]"), + named("fuzzy name matched", "[{\"clientNumber\":\"00000002\"}]"), + named("registration matched", "[{\"clientNumber\":\"00000003\"}]"), + named("full name matched", "[{\"clientNumber\":\"00000004\"}]"), + named("acronym matched","[{\"clientNumber\":\"00000005\"}]"), + named("dba fuzzy matched", "[{\"clientNumber\":\"00000006\"}]"), + named("dba full matched", "[{\"clientNumber\":\"00000007\"}]"), + true, + false + ) ); } diff --git a/backend/src/test/java/ca/bc/gov/app/service/client/matches/ContactStepMatcherTest.java b/backend/src/test/java/ca/bc/gov/app/service/client/matches/ContactStepMatcherTest.java index f94fd19ba9..db33a79115 100644 --- a/backend/src/test/java/ca/bc/gov/app/service/client/matches/ContactStepMatcherTest.java +++ b/backend/src/test/java/ca/bc/gov/app/service/client/matches/ContactStepMatcherTest.java @@ -47,7 +47,7 @@ void shouldMatchStep( boolean error, boolean fuzzy ) { - when(legacyService.searchGeneric(anyString(), any())) + when(legacyService.searchGeneric(anyString(), anyString())) .thenReturn(emailMatch, businessPhoneMatch, secondaryPhoneMatch, faxMatch); when(legacyService.searchContact(any(ContactSearchDto.class))) diff --git a/backend/src/test/java/ca/bc/gov/app/service/client/matches/LocationStepMatcherTest.java b/backend/src/test/java/ca/bc/gov/app/service/client/matches/LocationStepMatcherTest.java index 3d0490b3d0..55360c9916 100644 --- a/backend/src/test/java/ca/bc/gov/app/service/client/matches/LocationStepMatcherTest.java +++ b/backend/src/test/java/ca/bc/gov/app/service/client/matches/LocationStepMatcherTest.java @@ -46,7 +46,7 @@ void shouldMatchStep( boolean error, boolean fuzzy ) { - when(legacyService.searchGeneric(anyString(), any())) + when(legacyService.searchGeneric(anyString(), anyString())) .thenReturn(emailMatch, businessPhoneMatch, secondaryPhoneMatch, faxMatch); when(legacyService.searchLocation(any(AddressSearchDto.class))) diff --git a/backend/src/test/java/ca/bc/gov/app/service/client/matches/RegisteredStepMatcherTest.java b/backend/src/test/java/ca/bc/gov/app/service/client/matches/RegisteredStepMatcherTest.java new file mode 100644 index 0000000000..0437b9c01e --- /dev/null +++ b/backend/src/test/java/ca/bc/gov/app/service/client/matches/RegisteredStepMatcherTest.java @@ -0,0 +1,347 @@ +package ca.bc.gov.app.service.client.matches; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Named.named; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.bc.gov.app.dto.client.ClientSubmissionDto; +import ca.bc.gov.app.dto.client.MatchResult; +import ca.bc.gov.app.dto.client.StepMatchEnum; +import ca.bc.gov.app.dto.legacy.ForestClientDto; +import ca.bc.gov.app.exception.DataMatchException; +import ca.bc.gov.app.extensions.ClientMatchDataGenerator; +import ca.bc.gov.app.service.client.ClientLegacyService; +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Stream; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.assertj.core.api.Condition; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +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 reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +@DisplayName("Unit Test | Registered Step Matcher") +@Slf4j +class RegisteredStepMatcherTest { + + private final ClientLegacyService legacyService = mock(ClientLegacyService.class); + private final RegisteredStepMatcher registeredStepMatcher = new RegisteredStepMatcher( + legacyService); + + @DisplayName("Should return step matcher enum value for individual steps") + @Test + void shouldReturnStepMatcherEnumValueForIndividualSteps() { + Assertions.assertEquals(StepMatchEnum.STEP1REGISTERED, registeredStepMatcher.getStepMatcher()); + } + + @DisplayName("Should match step") + @ParameterizedTest(name = "error {8} fuzzy {9} when provided with {1}, {2}, {3}, {4}, {5}, {6}, {7}") + @MethodSource("matchStep") + void shouldMatchStep( + ClientSubmissionDto dto, + Flux individualFuzzyMatch, + Flux clientNameFuzzyMatch, + Flux clientRegistrationFullMatch, + Flux fullNameMatch, + Flux acronymMatch, + Flux dbaFuzzyMatch, + Flux dbaFullMatch, + boolean error, + boolean fuzzy + ) { + when( + legacyService + .searchIndividual( + anyString(), + anyString(), + any(LocalDate.class), + eq(null) + ) + ) + .thenReturn(individualFuzzyMatch); + + when( + legacyService + .searchLegacy( + anyString(), + eq(null), + eq(null), + eq(null) + ) + ) + .thenReturn(clientRegistrationFullMatch); + + when(legacyService.searchGeneric(anyString(), anyString())) + .thenReturn(fullNameMatch, acronymMatch); + + when(legacyService.searchGeneric(anyString(), anyString(), anyString())) + .thenReturn(clientNameFuzzyMatch, dbaFuzzyMatch); + + when(legacyService.searchGeneric(anyString(), anyMap())) + .thenReturn(dbaFullMatch); + + StepVerifier.FirstStep matcher = + registeredStepMatcher + .matchStep(dto) + .as(StepVerifier::create); + + if (error) { + matcher + .consumeErrorWith(errorContent -> + assertThat(errorContent) + .isInstanceOf(DataMatchException.class) + .hasMessage("409 CONFLICT \"Match found on existing data.\"") + .extracting("matches") + .isInstanceOf(List.class) + .asList() + .has( + new Condition<>( + matchResult -> + matchResult + .stream() + .map(m -> (MatchResult) m) + .anyMatch(m -> m.fuzzy() == fuzzy), + "MatchResult with fuzzy value %s", + fuzzy + ) + ) + + ) + .verify(); + } else { + matcher.verifyComplete(); + } + + } + + private static Stream matchStep() { + return Stream.of( + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "C" + ), + named("no individual", Flux.empty()), + named("no fuzzy name", Flux.empty()), + named("no registration", Flux.empty()), + named("no full name", Flux.empty()), + named("no acronym", Flux.empty()), + named("no dba fuzzy", Flux.empty()), + named("no dba full", Flux.empty()), + false, + false + ), + Arguments.of( + ClientMatchDataGenerator + .getRegisteredSP( + "C123456", + "Fake Corp", + StringUtils.EMPTY, + "Fake Corp", + StringUtils.EMPTY, + "Johnathan", + "Wick", + LocalDate.of(1970, 1, 12) + ), + named("individual matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000001"))), + named("no fuzzy name", Flux.empty()), + named("no registration", Flux.empty()), + named("no full name", Flux.empty()), + named("no acronym", Flux.empty()), + named("no dba fuzzy", Flux.empty()), + named("no dba full", Flux.empty()), + true, + true + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "C" + ), + named("no individual", Flux.empty()), + named("no fuzzy name", Flux.empty()), + named("registration matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000001"))), + named("no full name", Flux.empty()), + named("no acronym", Flux.empty()), + named("no dba fuzzy", Flux.empty()), + named("no dba full", Flux.empty()), + true, + false + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "C" + ), + named("no individual", Flux.empty()), + named("no fuzzy name", Flux.empty()), + named("no registration", Flux.empty()), + named("no full name", Flux.empty()), + named("acronym matched",Flux.just(ClientMatchDataGenerator.getForestClientDto("00000001"))), + named("no dba fuzzy", Flux.empty()), + named("no dba full", Flux.empty()), + true, + false + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "C" + ), + named("no individual", Flux.empty()), + named("fuzzy name matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000001"))), + named("no registration", Flux.empty()), + named("no full name", Flux.empty()), + named("no acronym", Flux.empty()), + named("no dba fuzzy", Flux.empty()), + named("no dba full", Flux.empty()), + true, + true + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "C" + ), + named("no individual", Flux.empty()), + named("fuzzy name matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000001"))), + named("no registration", Flux.empty()), + named("full name matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000002"))), + named("no acronym", Flux.empty()), + named("no dba fuzzy", Flux.empty()), + named("no dba full", Flux.empty()), + true, + false + ), + + + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "C" + ), + named("no individual", Flux.empty()), + named("no fuzzy name", Flux.empty()), + named("no registration", Flux.empty()), + named("no full name", Flux.empty()), + named("no acronym", Flux.empty()), + named("no dba fuzzy", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000001"))), + named("no dba full", Flux.empty()), + true, + true + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "C" + ), + named("no individual", Flux.empty()), + named("no fuzzy name", Flux.empty()), + named("no registration", Flux.empty()), + named("no full name", Flux.empty()), + named("no acronym", Flux.empty()), + named("dba fuzzy matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000001"))), + named("dba full matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000001"))), + true, + false + ), + Arguments.of( + ClientMatchDataGenerator + .getRegistered( + "C123456", + "Fake Corp", + "C", + StringUtils.EMPTY, + StringUtils.EMPTY, + StringUtils.EMPTY, + "C" + ), + named("no individual", Flux.empty()), + named("fuzzy name matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000001"))), + named("no registration", Flux.empty()), + named("full name matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000002"))), + named("no acronym", Flux.empty()), + named("dba fuzzy matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000003"))), + named("dba full matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000004"))), + true, + false + ), + Arguments.of( + ClientMatchDataGenerator + .getRegisteredSP( + "C123456", + "Fake Corp", + StringUtils.EMPTY, + "Fake Corp", + StringUtils.EMPTY, + "Johnathan", + "Wick", + LocalDate.of(1970, 1, 12) + ), + named("individual matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000001"))), + named("fuzzy name matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000002"))), + named("registration matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000003"))), + named("full name matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000004"))), + named("acronym matched",Flux.just(ClientMatchDataGenerator.getForestClientDto("00000005"))), + named("dba fuzzy matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000006"))), + named("dba full matched", Flux.just(ClientMatchDataGenerator.getForestClientDto("00000007"))), + true, + false + ) + ); + } + +} \ No newline at end of file diff --git a/frontend/cypress/e2e/FormStaffPage.Submission.cy.ts b/frontend/cypress/e2e/FormStaffPage.Submission.cy.ts index 1d427b29ab..5b3ca18e9f 100644 --- a/frontend/cypress/e2e/FormStaffPage.Submission.cy.ts +++ b/frontend/cypress/e2e/FormStaffPage.Submission.cy.ts @@ -143,6 +143,13 @@ describe("Staff Form Submission", () => { cy.fixture(`staffSubmit/${testSubmissionFixture}`).then((fixtureData: any) => { cy.intercept("POST", "/api/clients/submissions/staff", fixtureData).as("submitForm"); }); + + cy.intercept("POST", '**/api/clients/matches',{ + statusCode: 204, + headers:{ + "content-type": "application/json;charset=UTF-8" + } + }).as("doMatch"); cy.visit("/"); diff --git a/frontend/cypress/e2e/FormStaffPage.cy.ts b/frontend/cypress/e2e/FormStaffPage.cy.ts index bde25f7a86..1ca486adfa 100644 --- a/frontend/cypress/e2e/FormStaffPage.cy.ts +++ b/frontend/cypress/e2e/FormStaffPage.cy.ts @@ -1,6 +1,13 @@ /* eslint-disable no-undef */ describe("Staff Form", () => { beforeEach(() => { + cy.intercept("POST", '**/api/clients/matches',{ + statusCode: 204, + headers:{ + "content-type": "application/json;charset=UTF-8" + } + }).as("doMatch"); + cy.visit("/"); cy.wait(500); cy.get("#landing-title").should( @@ -133,45 +140,21 @@ describe("Staff Form", () => { }; const fillIndividual = (data = individualBaseData) => { - cy.get("#firstName").shadow().find("input").type(data.firstName); - - cy.get("#middleName").shadow().find("input").type(data.middleName); + cy.fillFormEntry("#firstName", data.firstName); + cy.fillFormEntry("#middleName", data.middleName); + cy.fillFormEntry("#lastName", data.lastName); - cy.get("#lastName").shadow().find("input").type(data.lastName); - - cy.get("#identificationType").find("[part='trigger-button']").click(); - cy.get("#identificationType") - .find( - `cds-combo-box-item[data-value="${data.identificationTypeValue}"]` - ) - .click(); + cy.selectFormEntry("#identificationType", data.identificationTypeValue, false); if (data.identificationProvinceValue) { - cy.get("#identificationProvince") - .find("[part='trigger-button']") - .click(); - cy.get("#identificationProvince") - .find( - `cds-combo-box-item[data-value="${data.identificationProvinceValue}"]` - ) - .click(); + cy.selectFormEntry("#identificationProvince", data.identificationProvinceValue, false); } - cy.get("#clientIdentification") - .shadow() - .find("input") - .type(data.clientIdentification); - - cy.get("#clientIdentification").shadow().find("input").blur(); - - cy.get("#birthdateYear").shadow().find("input").type(data.birthdateYear); - - cy.get("#birthdateMonth") - .shadow() - .find("input") - .type(data.birthdateMonth); + cy.fillFormEntry("#clientIdentification", data.clientIdentification); - cy.get("#birthdateDay").shadow().find("input").type(data.birthdateDay); + cy.fillFormEntry("#birthdateYear", data.birthdateYear); + cy.fillFormEntry("#birthdateMonth", data.birthdateMonth); + cy.fillFormEntry("#birthdateDay", data.birthdateDay); }; describe("when option Individual gets selected", () => { diff --git a/frontend/cypress/support/commands.ts b/frontend/cypress/support/commands.ts index fea9f2af03..3c991d61fc 100644 --- a/frontend/cypress/support/commands.ts +++ b/frontend/cypress/support/commands.ts @@ -147,8 +147,13 @@ Cypress.Commands.add("getMany", (names: string[]): Cypress.Chainable => { }); Cypress.Commands.add("fillFormEntry",(field: string, value: string, delayMS: number = 10, area: boolean = false) =>{ - cy.get(field).should("exist").shadow().find(area ? "textarea" : "input").type(value,{ delay: delayMS }); - cy.get(field).shadow().find(area ? "textarea" : "input").blur(); + cy.get(field) + .should("exist") + .shadow() + .find(area ? "textarea" : "input") + .focus() + .type(value,{ delay: delayMS }) + .blur(); }); Cypress.Commands.add("selectFormEntry", (field: string, value: string, box: boolean) => { diff --git a/frontend/src/components/forms/DateInputComponent/index.vue b/frontend/src/components/forms/DateInputComponent/index.vue index fa67d0c5fb..53a20fbb14 100644 --- a/frontend/src/components/forms/DateInputComponent/index.vue +++ b/frontend/src/components/forms/DateInputComponent/index.vue @@ -47,6 +47,7 @@ const props = withDefaults( autocomplete?: [string, string, string]; }>(), { + enabled: true, yearValidations: () => [], monthValidations: () => [], dayValidations: () => [], diff --git a/frontend/src/helpers/validators/StaffFormValidations.ts b/frontend/src/helpers/validators/StaffFormValidations.ts index c2bb7fa830..7c9068f9e2 100644 --- a/frontend/src/helpers/validators/StaffFormValidations.ts +++ b/frontend/src/helpers/validators/StaffFormValidations.ts @@ -90,6 +90,7 @@ fieldValidations["businessInformation.doingBusinessAs"] = [ fieldValidations["businessInformation.clientAcronym"] = [ optional(isMaxSizeMsg("acronym", 8)), + optional(isMinSizeMsg("acronym", 3)), optional(isAscii("acronym")), ]; diff --git a/frontend/src/pages/FormStaffPage.vue b/frontend/src/pages/FormStaffPage.vue index 4980bc8ac6..40c989bfb5 100644 --- a/frontend/src/pages/FormStaffPage.vue +++ b/frontend/src/pages/FormStaffPage.vue @@ -222,10 +222,6 @@ const onCancel = () => { }; const lookForMatches = (onEmpty: () => void) => { - - /* - Disabling for now, as this task wasn't supposed to have this yet - overlayBus.emit({ isVisible: true, message: "", showLoading: true }); fuzzyBus.emit(undefined); @@ -266,7 +262,6 @@ const lookForMatches = (onEmpty: () => void) => { setScrollPoint("top-notification"); }); - */ }; const moveToNextStep = () => { @@ -284,8 +279,7 @@ const onNext = () => { notificationBus.emit(undefined); if (currentTab.value + 1 < progressData.length) { if (checkStepValidity(currentTab.value)) { - //lookForMatches(moveToNextStep); - moveToNextStep(); + lookForMatches(moveToNextStep); } else { setScrollPoint("top-notification"); } diff --git a/frontend/src/pages/staffform/BcRegisteredClientInformationWizardStep.vue b/frontend/src/pages/staffform/BcRegisteredClientInformationWizardStep.vue index 6d659cd08c..13473fac97 100644 --- a/frontend/src/pages/staffform/BcRegisteredClientInformationWizardStep.vue +++ b/frontend/src/pages/staffform/BcRegisteredClientInformationWizardStep.vue @@ -80,7 +80,7 @@ const showFields = computed(() => { const standingMessage = computed(() =>{ if(formData.value.businessInformation.goodStandingInd === 'Y') return 'Good standing' if(formData.value.businessInformation.goodStandingInd === 'N') return 'Not in good standing' - return 'Unknow' + return 'Unknown' }); //TODO: Either load from BE or add to DataConversors.ts diff --git a/frontend/stub/__files/response-details-FM.json b/frontend/stub/__files/response-details-FM.json index 6083227791..76d6f5cb81 100644 --- a/frontend/stub/__files/response-details-FM.json +++ b/frontend/stub/__files/response-details-FM.json @@ -22,8 +22,8 @@ "contacts": [ { "contactType": { - "value": "P", - "text": "Person" + "value": "", + "text": "" }, "firstName": "Jhon", "lastName": "Wick", diff --git a/frontend/stub/__files/response-details-JJ.json b/frontend/stub/__files/response-details-JJ.json index cd4cf092df..03b83eeee9 100644 --- a/frontend/stub/__files/response-details-JJ.json +++ b/frontend/stub/__files/response-details-JJ.json @@ -6,8 +6,8 @@ "contacts": [ { "contactType": { - "value": "P", - "text": "Person" + "value": "", + "text": "" }, "firstName": "Jhon", "lastName": "Wick", diff --git a/frontend/stub/__files/response-details-UU.json b/frontend/stub/__files/response-details-UU.json index ae290867d5..47ee40b5f7 100644 --- a/frontend/stub/__files/response-details-UU.json +++ b/frontend/stub/__files/response-details-UU.json @@ -37,8 +37,8 @@ "contacts": [ { "contactType": { - "value": "P", - "text": "Person" + "value": "", + "text": "" }, "firstName": "Jhon", "lastName": "Wick", @@ -54,8 +54,8 @@ }, { "contactType": { - "value": "P", - "text": "Person" + "value": "", + "text": "" }, "firstName": "James", "lastName": "Kirk", diff --git a/frontend/stub/__files/response-details-XP.json b/frontend/stub/__files/response-details-XP.json index d39e5b8e22..13a71b0373 100644 --- a/frontend/stub/__files/response-details-XP.json +++ b/frontend/stub/__files/response-details-XP.json @@ -5,8 +5,8 @@ { "streetAddress": "123 Fake DR", "country": { - "value": "CA", - "text": "Canada" + "value": "", + "text": "" }, "province": { "value": "BC", @@ -21,8 +21,8 @@ "contacts": [ { "contactType": { - "value": "P", - "text": "Person" + "value": "", + "text": "" }, "firstName": "Jhon", "lastName": "Wick", diff --git a/frontend/stub/__files/response-details-XX.json b/frontend/stub/__files/response-details-XX.json index 3a0d9d6a56..3157bf99b6 100644 --- a/frontend/stub/__files/response-details-XX.json +++ b/frontend/stub/__files/response-details-XX.json @@ -22,8 +22,8 @@ "contacts": [ { "contactType": { - "value": "P", - "text": "Person" + "value": "", + "text": "" }, "firstName": "Jhon", "lastName": "Wick", diff --git a/frontend/stub/__files/response-details-YY.json b/frontend/stub/__files/response-details-YY.json index 3f14923927..3c925ccd8a 100644 --- a/frontend/stub/__files/response-details-YY.json +++ b/frontend/stub/__files/response-details-YY.json @@ -22,8 +22,8 @@ "contacts": [ { "contactType": { - "value": "P", - "text": "Person" + "value": "", + "text": "" }, "firstName": "Jhon", "lastName": "Wick", diff --git a/frontend/tests/components/pages/staffform/BcRegisteredClientInformationWizardStep.cy.ts b/frontend/tests/components/pages/staffform/BcRegisteredClientInformationWizardStep.cy.ts index 81b4c2da0f..f02c04c46b 100644 --- a/frontend/tests/components/pages/staffform/BcRegisteredClientInformationWizardStep.cy.ts +++ b/frontend/tests/components/pages/staffform/BcRegisteredClientInformationWizardStep.cy.ts @@ -103,7 +103,7 @@ describe('', () => { showBcRegDownNotification: false, showDuplicatedNotification: false, type: 'Corporation', - standing: 'Unknow', + standing: 'Unknown', dba: '', }, { @@ -130,7 +130,7 @@ describe('', () => { showNotGoodStandingNotification: false, showBcRegDownNotification: false, type: 'Limited liability partnership', - standing: 'Unknow', + standing: 'Unknown', dba: '', }, { @@ -158,7 +158,7 @@ describe('', () => { showBcRegDownNotification: false, showDuplicatedNotification: false, type: 'Corporation', - standing: 'Unknow', + standing: 'Unknown', dba: '', }, { @@ -186,7 +186,7 @@ describe('', () => { showBcRegDownNotification: false, showDuplicatedNotification: true, type: 'Corporation', - standing: 'Unknow', + standing: 'Unknown', dba: '', }, { @@ -535,9 +535,13 @@ describe('', () => { cy.checkInputErrorMessage('#acronym','The acronym has a 8 character limit'); cy.get('#acronym').shadow().find('input').clear(); - cy.fillFormEntry('#acronym', 'lá'); + cy.fillFormEntry('#acronym', 'láe'); cy.checkInputErrorMessage('#acronym','The acronym can only contain: A-Z, a-z, 0-9, space or common symbols'); + cy.get('#acronym').shadow().find('input').clear(); + cy.fillFormEntry('#acronym', 'I'); + cy.checkInputErrorMessage('#acronym','The acronym must contain at least 3 characters'); + });