diff --git a/src/test/java/com/iemr/tm/annotation/sqlInjectionSafe/SQLInjectionSafeConstraintValidatorTest.java b/src/test/java/com/iemr/tm/annotation/sqlInjectionSafe/SQLInjectionSafeConstraintValidatorTest.java new file mode 100644 index 00000000..38058efb --- /dev/null +++ b/src/test/java/com/iemr/tm/annotation/sqlInjectionSafe/SQLInjectionSafeConstraintValidatorTest.java @@ -0,0 +1,93 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/package com.iemr.tm.annotation.sqlInjectionSafe; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("SQLInjectionSafeConstraintValidator Test Suite") +class SQLInjectionSafeConstraintValidatorTest { + + private SQLInjectionSafeConstraintValidator validator; + + @BeforeEach + @DisplayName("Create the validator before each test") + void setUp() { + validator = new SQLInjectionSafeConstraintValidator(); + validator.initialize(null); + } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("isValid should accept a value that was left out") + void isValid_shouldAcceptOmittedValue(String value) { + assertTrue(validator.isValid(value, null)); + } + + @ParameterizedTest + @ValueSource(strings = { "Asha Devi", "9999999999", "Kamptee, Nagpur", "fever since 2 days", "B+", "Bearer token" }) + @DisplayName("isValid should accept the values the API is given in practice") + void isValid_shouldAcceptOrdinaryValues(String value) { + assertTrue(validator.isValid(value, null)); + } + + @ParameterizedTest + @ValueSource(strings = { + "SELECT * FROM m_beneficiary", + "INSERT INTO m_beneficiary VALUES (1)", + "UPDATE m_beneficiary SET deleted = true", + "DELETE FROM m_beneficiary", + "UPSERT m_beneficiary", + "SAVEPOINT before_drop", + "CALL sp_drop_all", + "ROLLBACK to_savepoint", + "KILL 1", + "DROP m_beneficiary", + "CREATE TABLE evil", + "ALTER TABLE m_beneficiary", + "TRUNCATE TABLE m_beneficiary", + "LOCK TABLE m_beneficiary", + "UNLOCK TABLE m_beneficiary", + "RELEASE SAVEPOINT before_drop", + "DESC m_beneficiary", + "DESCRIBE m_beneficiary", + "Asha; DROP", + "Asha /* comment", + "Asha -- comment" }) + @DisplayName("isValid should reject a value carrying SQL") + void isValid_shouldRejectValueCarryingSql(String value) { + assertFalse(validator.isValid(value, null)); + } + + @Test + @DisplayName("SQL_TYPES should name the object types the validator guards") + void sqlTypes_shouldNameGuardedObjectTypes() { + assertTrue(SQLInjectionSafeConstraintValidator.SQL_TYPES.contains("TABLE")); + assertTrue(SQLInjectionSafeConstraintValidator.SQL_TYPES.contains("PROCEDURE")); + } +} diff --git a/src/test/java/com/iemr/tm/common/PojoTestSupport.java b/src/test/java/com/iemr/tm/common/PojoTestSupport.java new file mode 100644 index 00000000..e4ae3d41 --- /dev/null +++ b/src/test/java/com/iemr/tm/common/PojoTestSupport.java @@ -0,0 +1,436 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.common; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URL; +import java.sql.Date; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.DynamicTest; + +/** + * Shared reflection harness that exercises the data-transfer objects of TM-API. + * + *

+ * The {@code com.iemr.tm.data} packages hold several hundred JPA entities and + * wrappers whose behaviour is limited to property access, the generated + * constructors and the {@code getXxx(ArrayList)} row-mapping helpers + * used by the native queries. Writing one hand-rolled test class per entity + * would run to tens of thousands of near-identical lines, so the accessor + * contract is asserted generically here and each data package gets a small test + * class that points this harness at it. + *

+ */ +public final class PojoTestSupport { + + private PojoTestSupport() { + } + + /** Discovers every concrete class directly inside the given package. */ + public static List> classesIn(String packageName) { + List> classes = new ArrayList<>(); + Set seen = new HashSet<>(); + String path = packageName.replace('.', '/'); + List names = new ArrayList<>(); + try { + java.util.Enumeration resources = Thread.currentThread().getContextClassLoader().getResources(path); + while (resources.hasMoreElements()) { + File directory = new File(resources.nextElement().getFile()); + File[] files = directory.listFiles(); + if (files == null) { + continue; + } + for (File file : files) { + String name = file.getName(); + if (!name.endsWith(".class") || name.contains("$") || name.endsWith("Test.class")) { + continue; + } + String simpleName = name.substring(0, name.length() - 6); + if (seen.add(simpleName)) { + names.add(simpleName); + } + } + } + } catch (java.io.IOException e) { + return classes; + } + Collections.sort(names); + for (String simpleName : names) { + try { + Class candidate = Class.forName(packageName + "." + simpleName); + if (!candidate.isInterface() && !candidate.isEnum() && !candidate.isAnnotation() + && !Modifier.isAbstract(candidate.getModifiers())) { + classes.add(candidate); + } + } catch (Throwable ignored) { + // A class that cannot be loaded in the test classpath is simply not exercised. + } + } + return classes; + } + + /** Builds one dynamic test per class in the package. */ + public static List accessorTestsFor(String packageName) { + List> classes = classesIn(packageName); + assertTrue(!classes.isEmpty(), "no classes discovered in " + packageName); + List tests = new ArrayList<>(); + for (Class type : classes) { + tests.add(DynamicTest.dynamicTest(type.getSimpleName() + " should round-trip every property", + () -> exercise(type))); + } + return tests; + } + + /** + * Instantiates the class, drives every constructor, asserts that each setter + * is observable through its getter and calls the remaining no-argument and + * row-mapping methods. + */ + public static void exercise(Class type) { + Object instance = instantiate(type); + assertNotNull(instance, "could not instantiate " + type.getName()); + + assertPropertyRoundTrip(type, instance); + invokeAllConstructors(type); + invokeRemainingMethods(type, instance); + invokeRowMappers(type); + + assertNotNull(instance.toString()); + assertEquals(instance.hashCode(), instance.hashCode()); + assertEquals(instance, instance); + } + + private static void assertPropertyRoundTrip(Class type, Object instance) { + for (Method setter : type.getMethods()) { + if (!isSetter(setter)) { + continue; + } + Class propertyType = setter.getParameterTypes()[0]; + Object value = sampleFor(propertyType, setter.getGenericParameterTypes()[0]); + try { + setter.invoke(instance, value); + } catch (Throwable t) { + continue; + } + Method getter = findGetter(type, setter.getName().substring(3), propertyType); + if (getter == null || !isCompatible(getter.getReturnType(), propertyType)) { + // Some entities expose a property through accessors of differing types; + // the round-trip contract only applies to a matching pair. + continue; + } + Object read; + try { + read = getter.invoke(instance); + } catch (Throwable t) { + continue; + } + if (propertyType.isArray()) { + continue; + } + assertEquals(value, read, + type.getSimpleName() + "." + getter.getName() + " should return the value that was set"); + } + } + + private static void invokeAllConstructors(Class type) { + for (Constructor constructor : type.getConstructors()) { + if (constructor.getParameterCount() == 0) { + continue; + } + Object[] arguments = new Object[constructor.getParameterCount()]; + Class[] parameterTypes = constructor.getParameterTypes(); + Type[] genericTypes = constructor.getGenericParameterTypes(); + for (int i = 0; i < arguments.length; i++) { + arguments[i] = sampleFor(parameterTypes[i], genericTypes[i]); + } + try { + assertNotNull(constructor.newInstance(arguments)); + } catch (Throwable ignored) { + // Constructors that validate their arguments are covered by the service tests. + } + } + } + + private static void invokeRemainingMethods(Class type, Object instance) { + for (Method method : type.getMethods()) { + if (method.getDeclaringClass() == Object.class || Modifier.isStatic(method.getModifiers()) + || method.getParameterCount() != 0 || isSetter(method)) { + continue; + } + try { + method.invoke(instance); + } catch (Throwable ignored) { + // Derived getters may depend on collaborators the harness cannot supply. + } + } + } + + /** + * Calls the {@code getXxx(ArrayList)} helpers that map native query + * rows onto the entity. Each helper is driven with an empty result set, with a + * single all-null row and with a row whose column types follow the declared + * field order of the entity, which is the order the native queries select in. + */ + private static void invokeRowMappers(Class type) { + for (Method method : type.getMethods()) { + if (!Modifier.isStatic(method.getModifiers()) || method.getParameterCount() != 1 + || !ArrayList.class.isAssignableFrom(method.getParameterTypes()[0])) { + continue; + } + for (ArrayList rows : rowSets(type)) { + try { + method.invoke(null, rows); + } catch (Throwable ignored) { + // Row mappers whose column order differs from the field order stop early; + // their remaining branches are covered by the service tests. + } + } + } + } + + private static List> rowSets(Class type) { + List> sets = new ArrayList<>(); + + sets.add(new ArrayList<>()); + + ArrayList nullRow = new ArrayList<>(); + nullRow.add(new Object[80]); + sets.add(nullRow); + + ArrayList typedRow = new ArrayList<>(); + typedRow.add(columnsFromFieldOrder(type)); + sets.add(typedRow); + + return sets; + } + + /** A row whose columns carry sample values for the declared fields, in order. */ + private static Object[] columnsFromFieldOrder(Class type) { + List columns = new ArrayList<>(); + for (java.lang.reflect.Field field : type.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) { + continue; + } + columns.add(sampleFor(field.getType(), field.getGenericType())); + } + while (columns.size() < 80) { + columns.add(null); + } + return columns.toArray(); + } + + private static boolean isSetter(Method method) { + return method.getName().startsWith("set") && method.getParameterCount() == 1 + && !Modifier.isStatic(method.getModifiers()); + } + + private static boolean isCompatible(Class returnType, Class propertyType) { + return returnType == propertyType || wrap(returnType) == wrap(propertyType); + } + + private static Class wrap(Class type) { + if (type == long.class) { + return Long.class; + } + if (type == int.class) { + return Integer.class; + } + if (type == short.class) { + return Short.class; + } + if (type == byte.class) { + return Byte.class; + } + if (type == double.class) { + return Double.class; + } + if (type == float.class) { + return Float.class; + } + if (type == char.class) { + return Character.class; + } + if (type == boolean.class) { + return Boolean.class; + } + return type; + } + + private static Method findGetter(Class type, String property, Class propertyType) { + for (String prefix : new String[] { "get", "is" }) { + try { + Method getter = type.getMethod(prefix + property); + if (getter.getParameterCount() == 0) { + return getter; + } + } catch (NoSuchMethodException ignored) { + // Try the next accessor naming convention. + } + } + return null; + } + + private static Object instantiate(Class type) { + try { + Constructor constructor = type.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (Throwable ignored) { + // Fall through to the widest constructor below. + } + Constructor[] constructors = type.getConstructors(); + Arrays.sort(constructors, (a, b) -> a.getParameterCount() - b.getParameterCount()); + for (Constructor constructor : constructors) { + Object[] arguments = new Object[constructor.getParameterCount()]; + Class[] parameterTypes = constructor.getParameterTypes(); + Type[] genericTypes = constructor.getGenericParameterTypes(); + for (int i = 0; i < arguments.length; i++) { + arguments[i] = sampleFor(parameterTypes[i], genericTypes[i]); + } + try { + constructor.setAccessible(true); + return constructor.newInstance(arguments); + } catch (Throwable ignored) { + // Try the next constructor. + } + } + return null; + } + + /** A distinguishable, type-correct sample value for the given property type. */ + public static Object sampleFor(Class type, Type genericType) { + if (type == String.class) { + return "sample"; + } + if (type == Long.class || type == long.class) { + return 11L; + } + if (type == Integer.class || type == int.class) { + return 12; + } + if (type == Short.class || type == short.class) { + return (short) 13; + } + if (type == Byte.class || type == byte.class) { + return (byte) 14; + } + if (type == Double.class || type == double.class) { + return 15.5d; + } + if (type == Float.class || type == float.class) { + return 16.5f; + } + if (type == Character.class || type == char.class) { + return 'A'; + } + if (type == Boolean.class || type == boolean.class) { + return Boolean.TRUE; + } + if (type == BigDecimal.class) { + return BigDecimal.valueOf(17.5d); + } + if (type == BigInteger.class) { + return BigInteger.valueOf(18L); + } + if (type == Timestamp.class) { + return new Timestamp(1_700_000_000_000L); + } + if (type == Date.class) { + return new Date(1_700_000_000_000L); + } + if (type == java.util.Date.class) { + return new java.util.Date(1_700_000_000_000L); + } + if (type == java.time.LocalDate.class) { + return java.time.LocalDate.of(2024, 1, 15); + } + if (type == java.time.LocalDateTime.class) { + return java.time.LocalDateTime.of(2024, 1, 15, 10, 30); + } + if (type == ArrayList.class) { + return new ArrayList<>(elementSample(genericType)); + } + if (type == List.class) { + return new ArrayList<>(elementSample(genericType)); + } + if (type == Set.class || type == HashSet.class) { + return new HashSet<>(elementSample(genericType)); + } + if (type == Map.class || type == HashMap.class) { + return new HashMap<>(); + } + if (type.isArray()) { + return Array.newInstance(type.getComponentType(), 1); + } + if (type.isEnum()) { + Object[] constants = type.getEnumConstants(); + return constants.length > 0 ? constants[0] : null; + } + return null; + } + + private static List elementSample(Type genericType) { + if (genericType instanceof ParameterizedType) { + Type[] arguments = ((ParameterizedType) genericType).getActualTypeArguments(); + if (arguments.length == 1 && arguments[0] instanceof Class) { + Class elementType = (Class) arguments[0]; + if (elementType == String.class) { + return Collections.singletonList("sample"); + } + if (elementType == Integer.class) { + return Collections.singletonList(12); + } + if (elementType == Long.class) { + return Collections.singletonList(11L); + } + } + } + return Collections.emptyList(); + } + + /** Fails with a readable message; kept for use by the package test classes. */ + public static void failMissingPackage(String packageName) { + fail("no classes discovered in " + packageName); + } +} diff --git a/src/test/java/com/iemr/tm/config/InterceptorConfigTest.java b/src/test/java/com/iemr/tm/config/InterceptorConfigTest.java new file mode 100644 index 00000000..f606614f --- /dev/null +++ b/src/test/java/com/iemr/tm/config/InterceptorConfigTest.java @@ -0,0 +1,67 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.config; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; + +import com.iemr.tm.utils.http.HTTPRequestInterceptor; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +@ExtendWith(MockitoExtension.class) +@DisplayName("InterceptorConfig Test Suite") +class InterceptorConfigTest { + + @Mock + private HTTPRequestInterceptor requestInterceptor; + + private InterceptorConfig interceptorConfig; + + @BeforeEach + @DisplayName("Wire the configuration with a mocked interceptor before each test") + void setUp() { + interceptorConfig = new InterceptorConfig(); + ReflectionTestUtils.setField(interceptorConfig, "requestInterceptor", requestInterceptor); + } + + @Test + @DisplayName("addInterceptors should register the HTTP request interceptor exactly once") + void addInterceptors_shouldRegisterRequestInterceptorOnce() { + InterceptorRegistry registry = new InterceptorRegistry(); + + interceptorConfig.addInterceptors(registry); + + List interceptors = (List) ReflectionTestUtils.invokeMethod(registry, "getInterceptors"); + assertEquals(1, interceptors.size()); + assertSame(requestInterceptor, interceptors.get(0)); + } +} diff --git a/src/test/java/com/iemr/tm/config/RedisConfigTest.java b/src/test/java/com/iemr/tm/config/RedisConfigTest.java new file mode 100644 index 00000000..eb2062cf --- /dev/null +++ b/src/test/java/com/iemr/tm/config/RedisConfigTest.java @@ -0,0 +1,80 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.config; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.session.data.redis.config.ConfigureRedisAction; + +import com.iemr.tm.data.login.Users; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(MockitoExtension.class) +@DisplayName("RedisConfig Test Suite") +class RedisConfigTest { + + @Mock + private RedisConnectionFactory connectionFactory; + + private RedisConfig redisConfig; + + @BeforeEach + @DisplayName("Create the configuration before each test") + void setUp() { + redisConfig = new RedisConfig(); + } + + @Test + @DisplayName("configureRedisAction should disable Spring Session's CONFIG command probing") + void configureRedisAction_shouldDisableConfigCommandProbing() { + assertSame(ConfigureRedisAction.NO_OP, redisConfig.configureRedisAction()); + } + + @Test + @DisplayName("redisTemplate should bind the supplied connection factory") + void redisTemplate_shouldBindSuppliedConnectionFactory() { + RedisTemplate template = redisConfig.redisTemplate(connectionFactory); + + assertNotNull(template); + assertSame(connectionFactory, template.getConnectionFactory()); + } + + @Test + @DisplayName("redisTemplate should serialise keys as plain strings and values as Users JSON") + void redisTemplate_shouldSerialiseKeysAsStringsAndValuesAsJson() { + RedisTemplate template = redisConfig.redisTemplate(connectionFactory); + + assertTrue(template.getKeySerializer() instanceof StringRedisSerializer); + assertTrue(template.getValueSerializer() instanceof Jackson2JsonRedisSerializer); + } +} diff --git a/src/test/java/com/iemr/tm/config/SwaggerConfigTest.java b/src/test/java/com/iemr/tm/config/SwaggerConfigTest.java new file mode 100644 index 00000000..b4d6ade3 --- /dev/null +++ b/src/test/java/com/iemr/tm/config/SwaggerConfigTest.java @@ -0,0 +1,101 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.config; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.mock.env.MockEnvironment; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.security.SecurityScheme; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@DisplayName("SwaggerConfig Test Suite") +class SwaggerConfigTest { + + private static final String SECURITY_SCHEME_NAME = "my security"; + private static final String DEFAULT_URL = "http://localhost:9090"; + + private SwaggerConfig swaggerConfig; + private MockEnvironment environment; + + @BeforeEach + @DisplayName("Create the configuration and an empty environment before each test") + void setUp() { + swaggerConfig = new SwaggerConfig(); + environment = new MockEnvironment(); + } + + @Test + @DisplayName("customOpenAPI should describe the TeleMedicine API") + void customOpenAPI_shouldDescribeTheApi() { + OpenAPI openAPI = swaggerConfig.customOpenAPI(environment); + + assertNotNull(openAPI.getInfo()); + assertEquals("TeleMedicine(TM) API", openAPI.getInfo().getTitle()); + assertEquals("1.0.0", openAPI.getInfo().getVersion()); + assertTrue(openAPI.getInfo().getDescription().contains("A microservice for TeleMedicine")); + } + + @Test + @DisplayName("customOpenAPI should declare a bearer security scheme and require it") + void customOpenAPI_shouldDeclareBearerSecurityScheme() { + OpenAPI openAPI = swaggerConfig.customOpenAPI(environment); + + SecurityScheme scheme = openAPI.getComponents().getSecuritySchemes().get(SECURITY_SCHEME_NAME); + assertNotNull(scheme); + assertEquals(SecurityScheme.Type.HTTP, scheme.getType()); + assertEquals("bearer", scheme.getScheme()); + assertEquals(1, openAPI.getSecurity().size()); + assertTrue(openAPI.getSecurity().get(0).containsKey(SECURITY_SCHEME_NAME)); + } + + @Test + @DisplayName("customOpenAPI should fall back to localhost for every unset server url") + void customOpenAPI_shouldFallBackToLocalhostForUnsetUrls() { + OpenAPI openAPI = swaggerConfig.customOpenAPI(environment); + + assertEquals(3, openAPI.getServers().size()); + openAPI.getServers().forEach(server -> assertEquals(DEFAULT_URL, server.getUrl())); + assertEquals("Dev", openAPI.getServers().get(0).getDescription()); + assertEquals("UAT", openAPI.getServers().get(1).getDescription()); + assertEquals("Demo", openAPI.getServers().get(2).getDescription()); + } + + @Test + @DisplayName("customOpenAPI should use the configured dev, UAT and demo urls when present") + void customOpenAPI_shouldUseConfiguredUrls() { + environment.setProperty("api.dev.url", "https://dev.amrit.example.org"); + environment.setProperty("api.uat.url", "https://uat.amrit.example.org"); + environment.setProperty("api.demo.url", "https://demo.amrit.example.org"); + + OpenAPI openAPI = swaggerConfig.customOpenAPI(environment); + + assertEquals("https://dev.amrit.example.org", openAPI.getServers().get(0).getUrl()); + assertEquals("https://uat.amrit.example.org", openAPI.getServers().get(1).getUrl()); + assertEquals("https://demo.amrit.example.org", openAPI.getServers().get(2).getUrl()); + } +} diff --git a/src/test/java/com/iemr/tm/controller/anc/AntenatalCareControllerTest.java b/src/test/java/com/iemr/tm/controller/anc/AntenatalCareControllerTest.java new file mode 100644 index 00000000..1ad3f0f3 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/anc/AntenatalCareControllerTest.java @@ -0,0 +1,465 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.anc; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.iemr.tm.service.anc.ANCServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("AntenatalCareController Test Suite") +class AntenatalCareControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_CODE = 22L; + private static final String REQUEST = "{\"beneficiaryRegID\":11,\"visitCode\":22}"; + private static final String VISIT_REQUEST = "{\"benRegID\":11,\"visitCode\":22}"; + + @Mock + private ANCServiceImpl ancServiceImpl; + + private AntenatalCareController controller; + + @BeforeEach + @DisplayName("Wire the controller with a mocked service") + void setUp() { + controller = new AntenatalCareController(); + controller.setAncServiceImpl(ancServiceImpl); + } + + @Nested + @DisplayName("saveBenANCNurseData") + class SavenurseTests { + + @Test + @DisplayName("saveBenANCNurseData should return the payload produced by the service") + void saveBenANCNurseData_shouldReturnServicePayload() throws Exception { + when(ancServiceImpl.saveANCNurseData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn("{\"visitCode\":\"22\"}"); + + String result = controller.saveBenANCNurseData(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("visitCode")); + } + + @Test + @DisplayName("saveBenANCNurseData should roll back the visit details when the service fails") + void saveBenANCNurseData_shouldRollBackVisitDetailsOnFailure() throws Exception { + when(ancServiceImpl.saveANCNurseData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("save failed")); + + assertTrue(controller.saveBenANCNurseData(REQUEST, AUTHORIZATION).contains("save failed")); + verify(ancServiceImpl).deleteVisitDetails(any(JsonObject.class)); + } + + @Test + @DisplayName("saveBenANCNurseData should return the untouched failure response for a null request") + void saveBenANCNurseData_shouldReturnGenericFailureForNullRequest() throws Exception { + assertTrue(controller.saveBenANCNurseData(null, AUTHORIZATION).contains("Failed with generic error")); + verify(ancServiceImpl, never()).saveANCNurseData(any(JsonObject.class), anyString()); + } + } + + @Nested + @DisplayName("saveBenANCDoctorData") + class SavedoctorTests { + + @Test + @DisplayName("saveBenANCDoctorData should confirm the save when the service returns an id") + void saveBenANCDoctorData_shouldConfirmSave() throws Exception { + when(ancServiceImpl.saveANCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(7L); + + assertTrue(controller.saveBenANCDoctorData(REQUEST, AUTHORIZATION).contains("Data saved successfully")); + } + + @Test + @DisplayName("saveBenANCDoctorData should report an unsuccessful save") + void saveBenANCDoctorData_shouldReportUnsuccessfulSave() throws Exception { + when(ancServiceImpl.saveANCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.saveBenANCDoctorData(REQUEST, AUTHORIZATION).contains("Unable to save data")); + } + + @Test + @DisplayName("saveBenANCDoctorData should surface a service failure") + void saveBenANCDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.saveANCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("doctor save failed")); + + assertTrue(controller.saveBenANCDoctorData(REQUEST, AUTHORIZATION).contains("doctor save failed")); + } + } + + @Nested + @DisplayName("getBenVisitDetailsFrmNurseANC") + class ReadvisitTests { + + @Test + @DisplayName("getBenVisitDetailsFrmNurseANC should return the details for a complete request") + void getBenVisitDetailsFrmNurseANC_shouldReturnDetails() throws Exception { + when(ancServiceImpl.getBenVisitDetailsFrmNurseANC(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenVisitDetailsFrmNurseANC(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseANC should reject an incomplete request") + void getBenVisitDetailsFrmNurseANC_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenVisitDetailsFrmNurseANC("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseANC should surface a service failure") + void getBenVisitDetailsFrmNurseANC_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.getBenVisitDetailsFrmNurseANC(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVisitDetailsFrmNurseANC(VISIT_REQUEST).contains("Error while getting beneficiary visit data")); + } + } + + @Nested + @DisplayName("getBenANCDetailsFrmNurseANC") + class ReadancTests { + + @Test + @DisplayName("getBenANCDetailsFrmNurseANC should return the details for a complete request") + void getBenANCDetailsFrmNurseANC_shouldReturnDetails() throws Exception { + when(ancServiceImpl.getBenANCDetailsFrmNurseANC(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenANCDetailsFrmNurseANC(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenANCDetailsFrmNurseANC should reject an incomplete request") + void getBenANCDetailsFrmNurseANC_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenANCDetailsFrmNurseANC("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenANCDetailsFrmNurseANC should surface a service failure") + void getBenANCDetailsFrmNurseANC_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.getBenANCDetailsFrmNurseANC(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenANCDetailsFrmNurseANC(VISIT_REQUEST).contains("Error while getting beneficiary ANC care data")); + } + } + + @Nested + @DisplayName("getBenANCHistoryDetails") + class ReadhistoryTests { + + @Test + @DisplayName("getBenANCHistoryDetails should return the details for a complete request") + void getBenANCHistoryDetails_shouldReturnDetails() throws Exception { + when(ancServiceImpl.getBenANCHistoryDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenANCHistoryDetails(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenANCHistoryDetails should reject an incomplete request") + void getBenANCHistoryDetails_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenANCHistoryDetails("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenANCHistoryDetails should surface a service failure") + void getBenANCHistoryDetails_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.getBenANCHistoryDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenANCHistoryDetails(VISIT_REQUEST).contains("Error while getting beneficiary history data")); + } + } + + @Nested + @DisplayName("getBenANCVitalDetailsFrmNurseANC") + class ReadvitalsTests { + + @Test + @DisplayName("getBenANCVitalDetailsFrmNurseANC should return the details for a complete request") + void getBenANCVitalDetailsFrmNurseANC_shouldReturnDetails() throws Exception { + when(ancServiceImpl.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenANCVitalDetailsFrmNurseANC(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenANCVitalDetailsFrmNurseANC should reject an incomplete request") + void getBenANCVitalDetailsFrmNurseANC_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenANCVitalDetailsFrmNurseANC("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenANCVitalDetailsFrmNurseANC should surface a service failure") + void getBenANCVitalDetailsFrmNurseANC_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenANCVitalDetailsFrmNurseANC(VISIT_REQUEST).contains("Error while getting beneficiary vital data")); + } + } + + @Nested + @DisplayName("getBenExaminationDetailsANC") + class ReadexaminationTests { + + @Test + @DisplayName("getBenExaminationDetailsANC should return the details for a complete request") + void getBenExaminationDetailsANC_shouldReturnDetails() throws Exception { + when(ancServiceImpl.getANCExaminationDetailsData(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenExaminationDetailsANC(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenExaminationDetailsANC should reject an incomplete request") + void getBenExaminationDetailsANC_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenExaminationDetailsANC("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenExaminationDetailsANC should surface a service failure") + void getBenExaminationDetailsANC_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.getANCExaminationDetailsData(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenExaminationDetailsANC(VISIT_REQUEST).contains("Error while getting beneficiary examination data")); + } + } + + @Nested + @DisplayName("getBenCaseRecordFromDoctorANC") + class ReadcaserecordTests { + + @Test + @DisplayName("getBenCaseRecordFromDoctorANC should return the details for a complete request") + void getBenCaseRecordFromDoctorANC_shouldReturnDetails() throws Exception { + when(ancServiceImpl.getBenCaseRecordFromDoctorANC(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenCaseRecordFromDoctorANC(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorANC should reject an incomplete request") + void getBenCaseRecordFromDoctorANC_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenCaseRecordFromDoctorANC("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorANC should surface a service failure") + void getBenCaseRecordFromDoctorANC_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.getBenCaseRecordFromDoctorANC(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCaseRecordFromDoctorANC(VISIT_REQUEST).contains("Error while getting beneficiary doctor data")); + } + } + + @Nested + @DisplayName("getHRPStatus") + class ReadhrpTests { + + @Test + @DisplayName("getHRPStatus should return the details for a complete request") + void getHRPStatus_shouldReturnDetails() throws Exception { + when(ancServiceImpl.getHRPStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getHRPStatus(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getHRPStatus should reject an incomplete request") + void getHRPStatus_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getHRPStatus("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getHRPStatus should surface a service failure") + void getHRPStatus_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.getHRPStatus(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getHRPStatus(VISIT_REQUEST).contains("error in getting HRP status")); + } + } + + @Nested + @DisplayName("updateANCCareNurse") + class UpdateancTests { + + @Test + @DisplayName("updateANCCareNurse should confirm the update when a row was changed") + void updateANCCareNurse_shouldConfirmUpdate() throws Exception { + when(ancServiceImpl.updateBenANCDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateANCCareNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateANCCareNurse should report that nothing was modified") + void updateANCCareNurse_shouldReportNothingModified() throws Exception { + when(ancServiceImpl.updateBenANCDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateANCCareNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateANCCareNurse should surface a service failure") + void updateANCCareNurse_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.updateBenANCDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateANCCareNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateANCHistoryNurse") + class UpdatehistoryTests { + + @Test + @DisplayName("updateANCHistoryNurse should confirm the update when a row was changed") + void updateANCHistoryNurse_shouldConfirmUpdate() throws Exception { + when(ancServiceImpl.updateBenANCHistoryDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateANCHistoryNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateANCHistoryNurse should report that nothing was modified") + void updateANCHistoryNurse_shouldReportNothingModified() throws Exception { + when(ancServiceImpl.updateBenANCHistoryDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateANCHistoryNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateANCHistoryNurse should surface a service failure") + void updateANCHistoryNurse_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.updateBenANCHistoryDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateANCHistoryNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateANCVitalNurse") + class UpdatevitalsTests { + + @Test + @DisplayName("updateANCVitalNurse should confirm the update when a row was changed") + void updateANCVitalNurse_shouldConfirmUpdate() throws Exception { + when(ancServiceImpl.updateBenANCVitalDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateANCVitalNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateANCVitalNurse should report that nothing was modified") + void updateANCVitalNurse_shouldReportNothingModified() throws Exception { + when(ancServiceImpl.updateBenANCVitalDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateANCVitalNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateANCVitalNurse should surface a service failure") + void updateANCVitalNurse_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.updateBenANCVitalDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateANCVitalNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateANCExaminationNurse") + class UpdateexaminationTests { + + @Test + @DisplayName("updateANCExaminationNurse should confirm the update when a row was changed") + void updateANCExaminationNurse_shouldConfirmUpdate() throws Exception { + when(ancServiceImpl.updateBenANCExaminationDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateANCExaminationNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateANCExaminationNurse should report that nothing was modified") + void updateANCExaminationNurse_shouldReportNothingModified() throws Exception { + when(ancServiceImpl.updateBenANCExaminationDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateANCExaminationNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateANCExaminationNurse should surface a service failure") + void updateANCExaminationNurse_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.updateBenANCExaminationDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateANCExaminationNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateANCDoctorData") + class UpdatedoctorTests { + + @Test + @DisplayName("updateANCDoctorData should confirm the update when a row was changed") + void updateANCDoctorData_shouldConfirmUpdate() throws Exception { + when(ancServiceImpl.updateANCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(1L); + + assertTrue(controller.updateANCDoctorData(REQUEST, AUTHORIZATION).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateANCDoctorData should report that nothing was modified") + void updateANCDoctorData_shouldReportNothingModified() throws Exception { + when(ancServiceImpl.updateANCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.updateANCDoctorData(REQUEST, AUTHORIZATION).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateANCDoctorData should surface a service failure") + void updateANCDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(ancServiceImpl.updateANCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("update failed")); + + assertTrue(controller.updateANCDoctorData(REQUEST, AUTHORIZATION).contains("update failed")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/cancerscreening/CancerScreeningControllerTest.java b/src/test/java/com/iemr/tm/controller/cancerscreening/CancerScreeningControllerTest.java new file mode 100644 index 00000000..8b37f895 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/cancerscreening/CancerScreeningControllerTest.java @@ -0,0 +1,540 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.cancerscreening; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.iemr.tm.service.cancerScreening.CSServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CancerScreeningController Test Suite") +class CancerScreeningControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_CODE = 22L; + private static final String REQUEST = "{\"beneficiaryRegID\":11,\"visitCode\":22}"; + private static final String VISIT_REQUEST = "{\"benRegID\":11,\"visitCode\":22}"; + + @Mock + private CSServiceImpl cSServiceImpl; + + private CancerScreeningController controller; + + @BeforeEach + @DisplayName("Wire the controller with mocked services") + void setUp() { + controller = new CancerScreeningController(); + controller.setCancerScreeningServiceImpl(cSServiceImpl); + } + + @Nested + @DisplayName("saveBenCancerScreeningNurseData") + class SaveNurseTests { + + @Test + @DisplayName("saveBenCancerScreeningNurseData should return the payload produced by the service") + void saveBenCancerScreeningNurseData_shouldReturnServicePayload() throws Exception { + when(cSServiceImpl.saveCancerScreeningNurseData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn("{\"visitCode\":\"22\"}"); + + String result = controller.saveBenCancerScreeningNurseData(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("visitCode")); + } + + @Test + @DisplayName("saveBenCancerScreeningNurseData should roll back the visit details when the service fails") + void saveBenCancerScreeningNurseData_shouldRollBackVisitDetailsOnFailure() throws Exception { + when(cSServiceImpl.saveCancerScreeningNurseData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("save failed")); + + assertTrue(controller.saveBenCancerScreeningNurseData(REQUEST, AUTHORIZATION).contains("save failed")); + verify(cSServiceImpl).deleteVisitDetails(any(JsonObject.class)); + } + + @Test + @DisplayName("saveBenCancerScreeningNurseData should return the untouched failure response for a null request") + void saveBenCancerScreeningNurseData_shouldReturnGenericFailureForNullRequest() throws Exception { + assertTrue(controller.saveBenCancerScreeningNurseData(null, AUTHORIZATION).contains("Failed with generic error")); + verify(cSServiceImpl, never()).saveCancerScreeningNurseData(any(JsonObject.class), anyString()); + } + } + + @Nested + @DisplayName("saveBenCancerScreeningDoctorData") + class SaveDoctorTests { + + @Test + @DisplayName("saveBenCancerScreeningDoctorData should confirm the save when the service returns an id") + void saveBenCancerScreeningDoctorData_shouldConfirmSave() throws Exception { + when(cSServiceImpl.saveCancerScreeningDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(7L); + + assertTrue(controller.saveBenCancerScreeningDoctorData(REQUEST, AUTHORIZATION).contains("Data saved successfully")); + } + + @Test + @DisplayName("saveBenCancerScreeningDoctorData should report an unsuccessful save") + void saveBenCancerScreeningDoctorData_shouldReportUnsuccessfulSave() throws Exception { + when(cSServiceImpl.saveCancerScreeningDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.saveBenCancerScreeningDoctorData(REQUEST, AUTHORIZATION).contains("Unable to save data")); + } + + @Test + @DisplayName("saveBenCancerScreeningDoctorData should surface a service failure") + void saveBenCancerScreeningDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.saveCancerScreeningDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("doctor save failed")); + + assertTrue(controller.saveBenCancerScreeningDoctorData(REQUEST, AUTHORIZATION).contains("doctor save failed")); + } + } + + @Nested + @DisplayName("getBenDataFrmNurseScrnToDocScrnVisitDetails") + class ReadVisitTests { + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnVisitDetails should return the details for a complete request") + void getBenDataFrmNurseScrnToDocScrnVisitDetails_shouldReturnDetails() throws Exception { + when(cSServiceImpl.getBenDataFrmNurseToDocVisitDetailsScreen(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnVisitDetails(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnVisitDetails should reject an incomplete request") + void getBenDataFrmNurseScrnToDocScrnVisitDetails_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnVisitDetails("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnVisitDetails should surface a service failure") + void getBenDataFrmNurseScrnToDocScrnVisitDetails_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.getBenDataFrmNurseToDocVisitDetailsScreen(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnVisitDetails(VISIT_REQUEST).contains("Error while getting beneficiary visit data")); + } + } + + @Nested + @DisplayName("getBenDataFrmNurseScrnToDocScrnHistory") + class ReadHistoryTests { + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnHistory should return the details for a complete request") + void getBenDataFrmNurseScrnToDocScrnHistory_shouldReturnDetails() throws Exception { + when(cSServiceImpl.getBenDataFrmNurseToDocHistoryScreen(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnHistory(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnHistory should reject an incomplete request") + void getBenDataFrmNurseScrnToDocScrnHistory_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnHistory("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnHistory should surface a service failure") + void getBenDataFrmNurseScrnToDocScrnHistory_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.getBenDataFrmNurseToDocHistoryScreen(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnHistory(VISIT_REQUEST).contains("Error while getting beneficiary history data")); + } + } + + @Nested + @DisplayName("getBenDataFrmNurseScrnToDocScrnVital") + class ReadVitalsTests { + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnVital should return the details for a complete request") + void getBenDataFrmNurseScrnToDocScrnVital_shouldReturnDetails() throws Exception { + when(cSServiceImpl.getBenDataFrmNurseToDocVitalScreen(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnVital(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnVital should reject an incomplete request") + void getBenDataFrmNurseScrnToDocScrnVital_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnVital("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnVital should surface a service failure") + void getBenDataFrmNurseScrnToDocScrnVital_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.getBenDataFrmNurseToDocVitalScreen(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnVital(VISIT_REQUEST).contains("Error while getting beneficiary vital data")); + } + } + + @Nested + @DisplayName("getBenDataFrmNurseScrnToDocScrnExamination") + class ReadExaminationTests { + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnExamination should return the details for a complete request") + void getBenDataFrmNurseScrnToDocScrnExamination_shouldReturnDetails() throws Exception { + when(cSServiceImpl.getBenDataFrmNurseToDocExaminationScreen(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnExamination(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnExamination should reject an incomplete request") + void getBenDataFrmNurseScrnToDocScrnExamination_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnExamination("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnExamination should surface a service failure") + void getBenDataFrmNurseScrnToDocScrnExamination_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.getBenDataFrmNurseToDocExaminationScreen(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnExamination(VISIT_REQUEST).contains("Error while getting beneficiary examination data")); + } + } + + @Nested + @DisplayName("getBenCaseRecordFromDoctorCS") + class ReadCaseRecordTests { + + @Test + @DisplayName("getBenCaseRecordFromDoctorCS should return the details for a complete request") + void getBenCaseRecordFromDoctorCS_shouldReturnDetails() throws Exception { + when(cSServiceImpl.getBenCaseRecordFromDoctorCS(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenCaseRecordFromDoctorCS(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorCS should reject an incomplete request") + void getBenCaseRecordFromDoctorCS_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenCaseRecordFromDoctorCS("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorCS should surface a service failure") + void getBenCaseRecordFromDoctorCS_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.getBenCaseRecordFromDoctorCS(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCaseRecordFromDoctorCS(VISIT_REQUEST).contains("Error while getting beneficiary doctor data")); + } + } + + @Nested + @DisplayName("upodateBenExaminationDetail") + class UpdateExaminationTests { + + @Test + @DisplayName("upodateBenExaminationDetail should confirm the update when a row was changed") + void upodateBenExaminationDetail_shouldConfirmUpdate() throws Exception { + when(cSServiceImpl.updateBenExaminationDetail(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.upodateBenExaminationDetail(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("upodateBenExaminationDetail should report that nothing was modified") + void upodateBenExaminationDetail_shouldReportNothingModified() throws Exception { + when(cSServiceImpl.updateBenExaminationDetail(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.upodateBenExaminationDetail(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("upodateBenExaminationDetail should surface a service failure") + void upodateBenExaminationDetail_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.updateBenExaminationDetail(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.upodateBenExaminationDetail(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateCancerScreeningDoctorData") + class UpdateDoctorTests { + + @Test + @DisplayName("updateCancerScreeningDoctorData should confirm the update when a row was changed") + void updateCancerScreeningDoctorData_shouldConfirmUpdate() throws Exception { + when(cSServiceImpl.updateCancerScreeningDoctorData(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateCancerScreeningDoctorData(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateCancerScreeningDoctorData should report that nothing was modified") + void updateCancerScreeningDoctorData_shouldReportNothingModified() throws Exception { + when(cSServiceImpl.updateCancerScreeningDoctorData(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateCancerScreeningDoctorData(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateCancerScreeningDoctorData should surface a service failure") + void updateCancerScreeningDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.updateCancerScreeningDoctorData(any(JsonObject.class))) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateCancerScreeningDoctorData(REQUEST).contains("db down")); + } + } + + @Nested + @DisplayName("getBenCancerFamilyHistory") + class ReadFamilyHistoryTests { + + @Test + @DisplayName("getBenCancerFamilyHistory should return the recorded family history") + void getBenCancerFamilyHistory_shouldReturnRecordedHistory() throws Exception { + when(cSServiceImpl.getBenFamilyHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + String result = controller.getBenCancerFamilyHistory("{\"benRegID\":11}"); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("columns")); + } + + @Test + @DisplayName("getBenCancerFamilyHistory should reject a request with no beneficiary") + void getBenCancerFamilyHistory_shouldRejectRequestWithoutBeneficiary() throws Exception { + assertTrue(controller.getBenCancerFamilyHistory("{}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCancerFamilyHistory should surface a service failure") + void getBenCancerFamilyHistory_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.getBenFamilyHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCancerFamilyHistory("{\"benRegID\":11}") + .contains("Error while getting beneficiary family history data")); + } + } + + @Nested + @DisplayName("getBenCancerPersonalHistory") + class ReadPersonalHistoryTests { + + @Test + @DisplayName("getBenCancerPersonalHistory should return the recorded personal history") + void getBenCancerPersonalHistory_shouldReturnRecordedHistory() throws Exception { + when(cSServiceImpl.getBenPersonalHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + String result = controller.getBenCancerPersonalHistory("{\"benRegID\":11}"); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("columns")); + } + + @Test + @DisplayName("getBenCancerPersonalHistory should reject a request with no beneficiary") + void getBenCancerPersonalHistory_shouldRejectRequestWithoutBeneficiary() throws Exception { + assertTrue(controller.getBenCancerPersonalHistory("{}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCancerPersonalHistory should surface a service failure") + void getBenCancerPersonalHistory_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.getBenPersonalHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCancerPersonalHistory("{\"benRegID\":11}") + .contains("Error while getting beneficiary personal history data")); + } + } + + @Nested + @DisplayName("getBenCancerPersonalDietHistory") + class ReadPersonalDietHistoryTests { + + @Test + @DisplayName("getBenCancerPersonalDietHistory should return the recorded personal diet history") + void getBenCancerPersonalDietHistory_shouldReturnRecordedHistory() throws Exception { + when(cSServiceImpl.getBenPersonalDietHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + String result = controller.getBenCancerPersonalDietHistory("{\"benRegID\":11}"); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("columns")); + } + + @Test + @DisplayName("getBenCancerPersonalDietHistory should reject a request with no beneficiary") + void getBenCancerPersonalDietHistory_shouldRejectRequestWithoutBeneficiary() throws Exception { + assertTrue(controller.getBenCancerPersonalDietHistory("{}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCancerPersonalDietHistory should surface a service failure") + void getBenCancerPersonalDietHistory_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.getBenPersonalDietHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCancerPersonalDietHistory("{\"benRegID\":11}") + .contains("Error while getting beneficiary personal diet history data")); + } + } + + @Nested + @DisplayName("getBenCancerObstetricHistory") + class ReadObstetricHistoryTests { + + @Test + @DisplayName("getBenCancerObstetricHistory should return the recorded obstetric history") + void getBenCancerObstetricHistory_shouldReturnRecordedHistory() throws Exception { + when(cSServiceImpl.getBenObstetricHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + String result = controller.getBenCancerObstetricHistory("{\"benRegID\":11}"); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("columns")); + } + + @Test + @DisplayName("getBenCancerObstetricHistory should reject a request with no beneficiary") + void getBenCancerObstetricHistory_shouldRejectRequestWithoutBeneficiary() throws Exception { + assertTrue(controller.getBenCancerObstetricHistory("{}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCancerObstetricHistory should surface a service failure") + void getBenCancerObstetricHistory_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.getBenObstetricHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCancerObstetricHistory("{\"benRegID\":11}") + .contains("Error while getting beneficiary obstetric history data")); + } + } + + @Nested + @DisplayName("updateCSHistoryNurse") + class UpdateHistoryTests { + + @Test + @DisplayName("updateCSHistoryNurse should confirm the updated history") + void updateCSHistoryNurse_shouldConfirmUpdate() throws Exception { + when(cSServiceImpl.UpdateCSHistoryNurseData(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateCSHistoryNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateCSHistoryNurse should report history it could not modify") + void updateCSHistoryNurse_shouldReportUnmodifiedHistory() throws Exception { + when(cSServiceImpl.UpdateCSHistoryNurseData(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateCSHistoryNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateCSHistoryNurse should surface a service failure") + void updateCSHistoryNurse_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.UpdateCSHistoryNurseData(any(JsonObject.class))) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateCSHistoryNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("upodateBenVitalDetail") + class UpdateVitalsTests { + + @Test + @DisplayName("upodateBenVitalDetail should confirm the updated vitals") + void upodateBenVitalDetail_shouldConfirmUpdate() throws Exception { + when(cSServiceImpl.updateBenVitalDetail(any())).thenReturn(1); + + assertTrue(controller.upodateBenVitalDetail(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("upodateBenVitalDetail should report vitals it could not modify") + void upodateBenVitalDetail_shouldReportUnmodifiedVitals() throws Exception { + when(cSServiceImpl.updateBenVitalDetail(any())).thenReturn(0); + + assertTrue(controller.upodateBenVitalDetail(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("upodateBenVitalDetail should surface a service failure") + void upodateBenVitalDetail_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.updateBenVitalDetail(any())).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.upodateBenVitalDetail(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateCancerDiagnosisDetailsByOncologist") + class UpdateOncologistDiagnosisTests { + + @Test + @DisplayName("updateCancerDiagnosisDetailsByOncologist should confirm the updated diagnosis") + void updateOncologistDiagnosis_shouldConfirmUpdate() throws Exception { + when(cSServiceImpl.updateCancerDiagnosisDetailsByOncologist(any())).thenReturn(1); + + assertTrue(controller.updateCancerDiagnosisDetailsByOncologist(REQUEST) + .contains("Data updated successfully")); + } + + @Test + @DisplayName("updateCancerDiagnosisDetailsByOncologist should report a diagnosis it could not modify") + void updateOncologistDiagnosis_shouldReportUnmodifiedDiagnosis() throws Exception { + when(cSServiceImpl.updateCancerDiagnosisDetailsByOncologist(any())).thenReturn(0); + + assertTrue(controller.updateCancerDiagnosisDetailsByOncologist(REQUEST) + .contains("Unable to modify data")); + } + + @Test + @DisplayName("updateCancerDiagnosisDetailsByOncologist should surface a service failure") + void updateOncologistDiagnosis_shouldSurfaceServiceFailure() throws Exception { + when(cSServiceImpl.updateCancerDiagnosisDetailsByOncologist(any())) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateCancerDiagnosisDetailsByOncologist(REQUEST) + .contains("Unable to modify data")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/common/main/WorklistControllerTest.java b/src/test/java/com/iemr/tm/controller/common/main/WorklistControllerTest.java new file mode 100644 index 00000000..5e0441a9 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/common/main/WorklistControllerTest.java @@ -0,0 +1,923 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.common.main; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.security.core.Authentication; + +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("WorklistController Test Suite") +class WorklistControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final String BEN_REQUEST = "{\"benRegID\":11,\"beneficiaryRegID\":11}"; + + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private Authentication authentication; + + private WorklistController controller; + + @BeforeEach + @DisplayName("Wire the controller with mocked services") + void setUp() { + controller = new WorklistController(); + controller.setCommonServiceImpl(commonServiceImpl); + controller.setCommonDoctorServiceImpl(commonDoctorServiceImpl); + controller.setCommonNurseServiceImpl(commonNurseServiceImpl); + } + + @Nested + @DisplayName("getNurseWorkListNew") + class GetNurseWorkListNewTests { + + @Test + @DisplayName("getNurseWorkListNew should return the worklist for the provider and van") + void getNurseWorkListNew_shouldReturnWorklist() { + when(commonNurseServiceImpl.getNurseWorkListNew(9, 7)).thenReturn("[]"); + + assertTrue(controller.getNurseWorkListNew(9, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getNurseWorkListNew should surface a service failure") + void getNurseWorkListNew_shouldSurfaceServiceFailure() { + when(commonNurseServiceImpl.getNurseWorkListNew(9, 7)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getNurseWorkListNew(9, 7).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getNurseWorkListTcCurrentDateNew") + class GetNurseWorkListTcCurrentDateNewTests { + + @Test + @DisplayName("getNurseWorkListTcCurrentDateNew should return the worklist for the provider and van") + void getNurseWorkListTcCurrentDateNew_shouldReturnWorklist() { + when(commonNurseServiceImpl.getNurseWorkListTcCurrentDate(9, 7)).thenReturn("[]"); + + assertTrue(controller.getNurseWorkListTcCurrentDateNew(9, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getNurseWorkListTcCurrentDateNew should surface a service failure") + void getNurseWorkListTcCurrentDateNew_shouldSurfaceServiceFailure() { + when(commonNurseServiceImpl.getNurseWorkListTcCurrentDate(9, 7)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getNurseWorkListTcCurrentDateNew(9, 7).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getNurseWorkListTcFutureDateNew") + class GetNurseWorkListTcFutureDateNewTests { + + @Test + @DisplayName("getNurseWorkListTcFutureDateNew should return the worklist for the provider and van") + void getNurseWorkListTcFutureDateNew_shouldReturnWorklist() { + when(commonNurseServiceImpl.getNurseWorkListTcFutureDate(9, 7)).thenReturn("[]"); + + assertTrue(controller.getNurseWorkListTcFutureDateNew(9, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getNurseWorkListTcFutureDateNew should surface a service failure") + void getNurseWorkListTcFutureDateNew_shouldSurfaceServiceFailure() { + when(commonNurseServiceImpl.getNurseWorkListTcFutureDate(9, 7)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getNurseWorkListTcFutureDateNew(9, 7).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getLabWorkListNew") + class GetLabWorkListNewTests { + + @Test + @DisplayName("getLabWorkListNew should return the worklist for the provider and van") + void getLabWorkListNew_shouldReturnWorklist() { + when(commonNurseServiceImpl.getLabWorkListNew(9, 7)).thenReturn("[]"); + + assertTrue(controller.getLabWorkListNew(9, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getLabWorkListNew should surface a service failure") + void getLabWorkListNew_shouldSurfaceServiceFailure() { + when(commonNurseServiceImpl.getLabWorkListNew(9, 7)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getLabWorkListNew(9, 7).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getRadiologistWorklistNew") + class GetRadiologistWorklistNewTests { + + @Test + @DisplayName("getRadiologistWorklistNew should return the worklist for the provider and van") + void getRadiologistWorklistNew_shouldReturnWorklist() { + when(commonNurseServiceImpl.getRadiologistWorkListNew(9, 7)).thenReturn("[]"); + + assertTrue(controller.getRadiologistWorklistNew(9, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getRadiologistWorklistNew should surface a service failure") + void getRadiologistWorklistNew_shouldSurfaceServiceFailure() { + when(commonNurseServiceImpl.getRadiologistWorkListNew(9, 7)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getRadiologistWorklistNew(9, 7).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getOncologistWorklistNew") + class GetOncologistWorklistNewTests { + + @Test + @DisplayName("getOncologistWorklistNew should return the worklist for the provider and van") + void getOncologistWorklistNew_shouldReturnWorklist() { + when(commonNurseServiceImpl.getOncologistWorkListNew(9, 7)).thenReturn("[]"); + + assertTrue(controller.getOncologistWorklistNew(9, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getOncologistWorklistNew should surface a service failure") + void getOncologistWorklistNew_shouldSurfaceServiceFailure() { + when(commonNurseServiceImpl.getOncologistWorkListNew(9, 7)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getOncologistWorklistNew(9, 7).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getPharmaWorklistNew") + class GetPharmaWorklistNewTests { + + @Test + @DisplayName("getPharmaWorklistNew should return the worklist for the provider and van") + void getPharmaWorklistNew_shouldReturnWorklist() { + when(commonNurseServiceImpl.getPharmaWorkListNew(9, 7)).thenReturn("[]"); + + assertTrue(controller.getPharmaWorklistNew(9, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getPharmaWorklistNew should surface a service failure") + void getPharmaWorklistNew_shouldSurfaceServiceFailure() { + when(commonNurseServiceImpl.getPharmaWorkListNew(9, 7)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getPharmaWorklistNew(9, 7).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getMmuNurseWorklistNew") + class GetMmuNurseWorklistNewTests { + + @Test + @DisplayName("getMmuNurseWorklistNew should return the worklist for the provider and van") + void getMmuNurseWorklistNew_shouldReturnWorklist() { + when(commonNurseServiceImpl.getMmuNurseWorkListNew(9, 7)).thenReturn("[]"); + + assertTrue(controller.getMmuNurseWorklistNew(9, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getMmuNurseWorklistNew should surface a service failure") + void getMmuNurseWorklistNew_shouldSurfaceServiceFailure() { + when(commonNurseServiceImpl.getMmuNurseWorkListNew(9, 7)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getMmuNurseWorklistNew(9, 7).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getDocWorkListNew") + class GetDocWorkListNewTests { + + @Test + @DisplayName("getDocWorkListNew should return the worklist for the provider and service") + void getDocWorkListNew_shouldReturnWorklist() { + when(commonDoctorServiceImpl.getDocWorkListNew(9, 2, 7)).thenReturn("[]"); + + assertTrue(controller.getDocWorkListNew(9, 2, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getDocWorkListNew should reject a request without a service id") + void getDocWorkListNew_shouldRejectRequestWithoutServiceId() { + assertTrue(controller.getDocWorkListNew(9, null, 7).contains("Invalid request")); + } + + @Test + @DisplayName("getDocWorkListNew should surface a service failure") + void getDocWorkListNew_shouldSurfaceServiceFailure() { + when(commonDoctorServiceImpl.getDocWorkListNew(9, 2, 7)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getDocWorkListNew(9, 2, 7).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getDocWorkListNewFutureScheduledForTM") + class GetDocWorkListNewFutureScheduledForTMTests { + + @Test + @DisplayName("getDocWorkListNewFutureScheduledForTM should return the worklist for the provider and service") + void getDocWorkListNewFutureScheduledForTM_shouldReturnWorklist() { + when(commonDoctorServiceImpl.getDocWorkListNewFutureScheduledForTM(9, 2, 7)).thenReturn("[]"); + + assertTrue(controller.getDocWorkListNewFutureScheduledForTM(9, 2, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getDocWorkListNewFutureScheduledForTM should reject a request without a service id") + void getDocWorkListNewFutureScheduledForTM_shouldRejectRequestWithoutServiceId() { + assertTrue(controller.getDocWorkListNewFutureScheduledForTM(9, null, 7).contains("Invalid request")); + } + + @Test + @DisplayName("getDocWorkListNewFutureScheduledForTM should surface a service failure") + void getDocWorkListNewFutureScheduledForTM_shouldSurfaceServiceFailure() { + when(commonDoctorServiceImpl.getDocWorkListNewFutureScheduledForTM(9, 2, 7)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getDocWorkListNewFutureScheduledForTM(9, 2, 7).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenPastHistory") + class GetBenPastHistoryTests { + + @Test + @DisplayName("getBenPastHistory should return the stored history for the beneficiary") + void getBenPastHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getBenPastHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenPastHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenPastHistory should reject a request without the beneficiary id") + void getBenPastHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenPastHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenPastHistory should surface a service failure") + void getBenPastHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getBenPastHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenPastHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenTobaccoHistory") + class GetBenTobaccoHistoryTests { + + @Test + @DisplayName("getBenTobaccoHistory should return the stored history for the beneficiary") + void getBenTobaccoHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getPersonalTobaccoHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenTobaccoHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenTobaccoHistory should reject a request without the beneficiary id") + void getBenTobaccoHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenTobaccoHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenTobaccoHistory should surface a service failure") + void getBenTobaccoHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getPersonalTobaccoHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenTobaccoHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenAlcoholHistory") + class GetBenAlcoholHistoryTests { + + @Test + @DisplayName("getBenAlcoholHistory should return the stored history for the beneficiary") + void getBenAlcoholHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getPersonalAlcoholHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenAlcoholHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenAlcoholHistory should reject a request without the beneficiary id") + void getBenAlcoholHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenAlcoholHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenAlcoholHistory should surface a service failure") + void getBenAlcoholHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getPersonalAlcoholHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenAlcoholHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenANCAllergyHistory") + class GetBenANCAllergyHistoryTests { + + @Test + @DisplayName("getBenANCAllergyHistory should return the stored history for the beneficiary") + void getBenANCAllergyHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getPersonalAllergyHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenANCAllergyHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenANCAllergyHistory should reject a request without the beneficiary id") + void getBenANCAllergyHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenANCAllergyHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenANCAllergyHistory should surface a service failure") + void getBenANCAllergyHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getPersonalAllergyHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenANCAllergyHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenMedicationHistory") + class GetBenMedicationHistoryTests { + + @Test + @DisplayName("getBenMedicationHistory should return the stored history for the beneficiary") + void getBenMedicationHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getMedicationHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenMedicationHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenMedicationHistory should reject a request without the beneficiary id") + void getBenMedicationHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenMedicationHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenMedicationHistory should surface a service failure") + void getBenMedicationHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getMedicationHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenMedicationHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenFamilyHistory") + class GetBenFamilyHistoryTests { + + @Test + @DisplayName("getBenFamilyHistory should return the stored history for the beneficiary") + void getBenFamilyHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getFamilyHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenFamilyHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenFamilyHistory should reject a request without the beneficiary id") + void getBenFamilyHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenFamilyHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenFamilyHistory should surface a service failure") + void getBenFamilyHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getFamilyHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenFamilyHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenMenstrualHistory") + class GetBenMenstrualHistoryTests { + + @Test + @DisplayName("getBenMenstrualHistory should return the stored history for the beneficiary") + void getBenMenstrualHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getMenstrualHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenMenstrualHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenMenstrualHistory should reject a request without the beneficiary id") + void getBenMenstrualHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenMenstrualHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenMenstrualHistory should surface a service failure") + void getBenMenstrualHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getMenstrualHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenMenstrualHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenPastObstetricHistory") + class GetBenPastObstetricHistoryTests { + + @Test + @DisplayName("getBenPastObstetricHistory should return the stored history for the beneficiary") + void getBenPastObstetricHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getObstetricHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenPastObstetricHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenPastObstetricHistory should reject a request without the beneficiary id") + void getBenPastObstetricHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenPastObstetricHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenPastObstetricHistory should surface a service failure") + void getBenPastObstetricHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getObstetricHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenPastObstetricHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenANCComorbidityConditionHistory") + class GetBenANCComorbidityConditionHistoryTests { + + @Test + @DisplayName("getBenANCComorbidityConditionHistory should return the stored history for the beneficiary") + void getBenANCComorbidityConditionHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getComorbidHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenANCComorbidityConditionHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenANCComorbidityConditionHistory should reject a request without the beneficiary id") + void getBenANCComorbidityConditionHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenANCComorbidityConditionHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenANCComorbidityConditionHistory should surface a service failure") + void getBenANCComorbidityConditionHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getComorbidHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenANCComorbidityConditionHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenOptionalVaccineHistory") + class GetBenOptionalVaccineHistoryTests { + + @Test + @DisplayName("getBenOptionalVaccineHistory should return the stored history for the beneficiary") + void getBenOptionalVaccineHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getChildVaccineHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenOptionalVaccineHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenOptionalVaccineHistory should reject a request without the beneficiary id") + void getBenOptionalVaccineHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenOptionalVaccineHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenOptionalVaccineHistory should surface a service failure") + void getBenOptionalVaccineHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getChildVaccineHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenOptionalVaccineHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenImmunizationHistory") + class GetBenImmunizationHistoryTests { + + @Test + @DisplayName("getBenImmunizationHistory should return the stored history for the beneficiary") + void getBenImmunizationHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getImmunizationHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenImmunizationHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenImmunizationHistory should reject a request without the beneficiary id") + void getBenImmunizationHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenImmunizationHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenImmunizationHistory should surface a service failure") + void getBenImmunizationHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getImmunizationHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenImmunizationHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenPerinatalHistory") + class GetBenPerinatalHistoryTests { + + @Test + @DisplayName("getBenPerinatalHistory should return the stored history for the beneficiary") + void getBenPerinatalHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getBenPerinatalHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenPerinatalHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenPerinatalHistory should reject a request without the beneficiary id") + void getBenPerinatalHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenPerinatalHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenPerinatalHistory should surface a service failure") + void getBenPerinatalHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getBenPerinatalHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenPerinatalHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenFeedingHistory") + class GetBenFeedingHistoryTests { + + @Test + @DisplayName("getBenFeedingHistory should return the stored history for the beneficiary") + void getBenFeedingHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getBenFeedingHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenFeedingHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenFeedingHistory should reject a request without the beneficiary id") + void getBenFeedingHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenFeedingHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenFeedingHistory should surface a service failure") + void getBenFeedingHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getBenFeedingHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenFeedingHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenDevelopmentHistory") + class GetBenDevelopmentHistoryTests { + + @Test + @DisplayName("getBenDevelopmentHistory should return the stored history for the beneficiary") + void getBenDevelopmentHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getBenDevelopmentHistoryData(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenDevelopmentHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenDevelopmentHistory should reject a request without the beneficiary id") + void getBenDevelopmentHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenDevelopmentHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenDevelopmentHistory should surface a service failure") + void getBenDevelopmentHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getBenDevelopmentHistoryData(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenDevelopmentHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBenPhysicalHistory") + class GetBenPhysicalHistoryTests { + + @Test + @DisplayName("getBenPhysicalHistory should return the stored history for the beneficiary") + void getBenPhysicalHistory_shouldReturnStoredHistory() throws Exception { + when(commonServiceImpl.getBenPhysicalHistory(BEN_REG_ID)).thenReturn("{\"columns\":[]}"); + + assertTrue(controller.getBenPhysicalHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenPhysicalHistory should reject a request without the beneficiary id") + void getBenPhysicalHistory_shouldRejectRequestWithoutBeneficiaryId() throws Exception { + assertTrue(controller.getBenPhysicalHistory("{}").contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenPhysicalHistory should surface a service failure") + void getBenPhysicalHistory_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getBenPhysicalHistory(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenPhysicalHistory(BEN_REQUEST).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getDoctorPreviousSignificantFindings") + class PreviousFindingsTests { + + @Test + @DisplayName("getDoctorPreviousSignificantFindings should return the earlier findings") + void getPreviousFindings_shouldReturnEarlierFindings() { + when(commonDoctorServiceImpl.fetchBenPreviousSignificantFindings(BEN_REG_ID)).thenReturn("{}"); + + assertTrue(controller.getDoctorPreviousSignificantFindings("{\"beneficiaryRegID\":11}") + .contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getDoctorPreviousSignificantFindings should reject a request without the beneficiary id") + void getPreviousFindings_shouldRejectRequestWithoutBeneficiaryId() { + assertTrue(controller.getDoctorPreviousSignificantFindings("{}").contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("getBeneficiaryCaseSheetHistory") + class CaseSheetHistoryTests { + + @Test + @DisplayName("getBeneficiaryCaseSheetHistory should return the earlier visit history") + void getCaseSheetHistory_shouldReturnEarlierVisitHistory() throws Exception { + when(commonServiceImpl.getBenPreviousVisitDataForCaseRecord(BEN_REQUEST)).thenReturn("{}"); + + assertTrue(controller.getBeneficiaryCaseSheetHistory(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBeneficiaryCaseSheetHistory should report a failure when nothing comes back") + void getCaseSheetHistory_shouldReportFailureWhenNothingReturned() throws Exception { + when(commonServiceImpl.getBenPreviousVisitDataForCaseRecord(BEN_REQUEST)).thenReturn(null); + + assertTrue(controller.getBeneficiaryCaseSheetHistory(BEN_REQUEST) + .contains("Error while fetching beneficiary previous visit history details")); + } + } + + @Nested + @DisplayName("teleconsultation specialist worklists") + class SpecialistWorklistTests { + + @Test + @DisplayName("getTCSpecialistWorkListNew should return the specialist worklist for an authenticated user") + void getSpecialistWorklist_shouldReturnWorklist() { + when(authentication.isAuthenticated()).thenReturn(true); + when(authentication.getPrincipal()).thenReturn("42"); + when(commonDoctorServiceImpl.getTCSpecialistWorkListNewForTM(9, 42, 4)).thenReturn("[]"); + + assertTrue(controller.getTCSpecialistWorkListNew(9, 4, authentication).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getTCSpecialistWorkListNew should reject a missing authentication") + void getSpecialistWorklist_shouldRejectMissingAuthentication() { + assertTrue(controller.getTCSpecialistWorkListNew(9, 4, null).contains("Unauthorized access")); + } + + @Test + @DisplayName("getTCSpecialistWorkListNewPatientApp should return the patient app worklist") + void getSpecialistWorklistPatientApp_shouldReturnWorklist() { + when(authentication.isAuthenticated()).thenReturn(true); + when(authentication.getPrincipal()).thenReturn("42"); + when(commonDoctorServiceImpl.getTCSpecialistWorkListNewForTMPatientApp(9, 42, 4, 7)).thenReturn("[]"); + + assertTrue(controller.getTCSpecialistWorkListNewPatientApp(9, 4, 7, authentication) + .contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getTCSpecialistWorkListNewPatientApp should reject a missing authentication") + void getSpecialistWorklistPatientApp_shouldRejectMissingAuthentication() { + assertTrue(controller.getTCSpecialistWorkListNewPatientApp(9, 4, 7, null) + .contains("Unauthorized access")); + } + + @Test + @DisplayName("getTCSpecialistWorklistFutureScheduled should return the future scheduled worklist") + void getSpecialistWorklistFutureScheduled_shouldReturnWorklist() { + when(authentication.isAuthenticated()).thenReturn(true); + when(authentication.getPrincipal()).thenReturn("42"); + when(commonDoctorServiceImpl.getTCSpecialistWorkListNewFutureScheduledForTM(9, 42, 4)).thenReturn("[]"); + + assertTrue(controller.getTCSpecialistWorklistFutureScheduled(9, 4, authentication) + .contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getTCSpecialistWorklistFutureScheduled should reject a missing authentication") + void getSpecialistWorklistFutureScheduled_shouldRejectMissingAuthentication() { + assertTrue(controller.getTCSpecialistWorklistFutureScheduled(9, 4, null).contains("Unauthorized access")); + } + } + + @Nested + @DisplayName("miscellaneous endpoints") + class MiscellaneousTests { + + @Test + @DisplayName("getKMFile should return the document url") + void getKMFile_shouldReturnDocumentUrl() throws Exception { + when(commonServiceImpl.getOpenKMDocURL(BEN_REQUEST, AUTHORIZATION)).thenReturn("https://km/doc"); + + assertTrue(controller.getKMFile(BEN_REQUEST, AUTHORIZATION).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getKMFile should report a failure when no url comes back") + void getKMFile_shouldReportFailureWithoutUrl() throws Exception { + when(commonServiceImpl.getOpenKMDocURL(BEN_REQUEST, AUTHORIZATION)).thenReturn(null); + + assertTrue(controller.getKMFile(BEN_REQUEST, AUTHORIZATION).contains("\"statusCode\":5000")); + } + + @Test + @DisplayName("getBenSymptomaticQuestionnaireDetails should return the screening questionnaire") + void getSymptomaticQuestionnaire_shouldReturnQuestionnaire() throws Exception { + when(commonServiceImpl.getBenSymptomaticQuestionnaireDetailsData(BEN_REG_ID)).thenReturn("{}"); + + assertTrue(controller.getBenSymptomaticQuestionnaireDetails(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenPreviousDiabetesHistoryDetails should return the earlier diabetes screening") + void getPreviousDiabetes_shouldReturnEarlierScreening() throws Exception { + when(commonServiceImpl.getBenPreviousDiabetesData(BEN_REG_ID)).thenReturn("{}"); + + assertTrue(controller.getBenPreviousDiabetesHistoryDetails(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenPreviousReferralHistoryDetails should return the earlier referrals") + void getPreviousReferral_shouldReturnEarlierReferrals() throws Exception { + when(commonServiceImpl.getBenPreviousReferralData(BEN_REG_ID)).thenReturn("{}"); + + assertTrue(controller.getBenPreviousReferralHistoryDetails(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getProviderSpecificData should return the MMU data for the request") + void getProviderSpecificData_shouldReturnMmuData() throws Exception { + when(commonServiceImpl.getProviderSpecificData(BEN_REQUEST)).thenReturn("{}"); + + assertTrue(controller.getProviderSpecificData(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getProviderSpecificData should surface a service failure") + void getProviderSpecificData_shouldSurfaceServiceFailure() throws Exception { + when(commonServiceImpl.getProviderSpecificData(BEN_REQUEST)) + .thenThrow(new IllegalStateException("mmu down")); + + assertTrue(controller.getProviderSpecificData(BEN_REQUEST).contains("mmu down")); + } + + @Test + @DisplayName("calculateBMIStatus should return the BMI classification") + void calculateBMIStatus_shouldReturnClassification() throws Exception { + when(commonNurseServiceImpl.calculateBMIStatus(BEN_REQUEST)).thenReturn("{\"bmiStatus\":\"Normal\"}"); + + assertTrue(controller.calculateBMIStatus(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("calculateBMIStatus should surface a service failure") + void calculateBMIStatus_shouldSurfaceServiceFailure() throws Exception { + when(commonNurseServiceImpl.calculateBMIStatus(BEN_REQUEST)) + .thenThrow(new IllegalStateException("bmi failed")); + + assertTrue(controller.calculateBMIStatus(BEN_REQUEST).contains("bmi failed")); + } + + @Test + @DisplayName("saveBeneficiaryVisitDetail should confirm the submission to the nurse worklist") + void saveVisitDetail_shouldConfirmSubmission() { + when(commonNurseServiceImpl.updateBeneficiaryStatus('R', BEN_REG_ID)).thenReturn(1); + + assertTrue(controller.saveBeneficiaryVisitDetail("{\"beneficiaryRegID\":11}") + .contains("Beneficiary Successfully Submitted to Nurse Work-List.")); + } + + @Test + @DisplayName("saveBeneficiaryVisitDetail should report a failure when the status was not changed") + void saveVisitDetail_shouldReportFailureWhenStatusUnchanged() { + when(commonNurseServiceImpl.updateBeneficiaryStatus('R', BEN_REG_ID)).thenReturn(0); + + assertTrue(controller.saveBeneficiaryVisitDetail("{\"beneficiaryRegID\":11}") + .contains("Something went Wrong")); + } + + @Test + @DisplayName("saveBeneficiaryVisitDetail should reject a request without the beneficiary id") + void saveVisitDetail_shouldRejectRequestWithoutBeneficiaryId() { + assertTrue(controller.saveBeneficiaryVisitDetail("{}") + .contains("Beneficiary Registration ID is Not valid !!!")); + } + + @Test + @DisplayName("extendRedisSession should confirm the extended session") + void extendRedisSession_shouldConfirmExtendedSession() { + assertTrue(controller.extendRedisSession().contains("Session extended for 30 mins")); + } + + @Test + @DisplayName("deletePrescribedMedicine should confirm the deletion") + void deletePrescribedMedicine_shouldConfirmDeletion() { + when(commonDoctorServiceImpl.deletePrescribedMedicine(any(org.json.JSONObject.class))) + .thenReturn("record deleted successfully"); + + assertTrue(controller.deletePrescribedMedicine("{\"id\":4}").contains("\"statusCode\":200")); + } + + @Test + @DisplayName("deletePrescribedMedicine should report a failure when nothing was deleted") + void deletePrescribedMedicine_shouldReportFailureWhenNothingDeleted() { + when(commonDoctorServiceImpl.deletePrescribedMedicine(any(org.json.JSONObject.class))).thenReturn(null); + + assertTrue(controller.deletePrescribedMedicine("{\"id\":4}").contains("error while deleting record")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/common/master/CommonMasterControllerTest.java b/src/test/java/com/iemr/tm/controller/common/master/CommonMasterControllerTest.java new file mode 100644 index 00000000..d48a7a84 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/common/master/CommonMasterControllerTest.java @@ -0,0 +1,95 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.common.master; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.iemr.tm.service.common.master.CommonMasterServiceImpl; + +@ExtendWith(MockitoExtension.class) +@DisplayName("CommonMasterController Test Suite") +class CommonMasterControllerTest { + + @Mock + private CommonMasterServiceImpl commonMasterServiceImpl; + + private CommonMasterController controller; + + @BeforeEach + @DisplayName("Wire the controller with a mocked master data service") + void setUp() { + controller = new CommonMasterController(); + controller.setCommonMasterServiceImpl(commonMasterServiceImpl); + } + + @Test + @DisplayName("getVisitReasonAndCategories should return the master data produced by the service") + void getVisitReasonAndCategories_shouldReturnMasterData() { + when(commonMasterServiceImpl.getVisitReasonAndCategories()).thenReturn("{\"visitCategories\":[]}"); + + String result = controller.getVisitReasonAndCategories(); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("visitCategories")); + } + + @Test + @DisplayName("NurseMasterData should return the nurse master data for the requested category") + void nurseMasterData_shouldReturnNurseMasterData() { + when(commonMasterServiceImpl.getMasterDataForNurse(1, 2, "Female")).thenReturn("{\"nurseMaster\":[]}"); + + String result = controller.NurseMasterData(1, 2, "Female"); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("nurseMaster")); + } + + @Test + @DisplayName("DoctorMasterData should return the doctor master data for the requested category") + void doctorMasterData_shouldReturnDoctorMasterData() { + when(commonMasterServiceImpl.getMasterDataForDoctor(1, 2, "Male", 3, 4)).thenReturn("{\"doctorMaster\":[]}"); + + String result = controller.DoctorMasterData(1, 2, "Male", 3, 4); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("doctorMaster")); + } + + @Test + @DisplayName("getECGAbnormalFindings should return the ECG findings master data") + void getECGAbnormalFindings_shouldReturnEcgFindings() { + when(commonMasterServiceImpl.getECGAbnormalFindings()).thenReturn("{\"ecgFindings\":[]}"); + + String result = controller.getECGAbnormalFindings(); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("ecgFindings")); + } +} diff --git a/src/test/java/com/iemr/tm/controller/covid19/CovidControllerTest.java b/src/test/java/com/iemr/tm/controller/covid19/CovidControllerTest.java new file mode 100644 index 00000000..a5bf9af6 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/covid19/CovidControllerTest.java @@ -0,0 +1,320 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.covid19; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.iemr.tm.service.covid19.Covid19ServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CovidController Test Suite") +class CovidControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_CODE = 22L; + private static final String REQUEST = "{\"beneficiaryRegID\":11,\"visitCode\":22}"; + private static final String VISIT_REQUEST = "{\"benRegID\":11,\"visitCode\":22}"; + + @Mock + private Covid19ServiceImpl covid19ServiceImpl; + + @InjectMocks + private CovidController controller; + + @Nested + @DisplayName("saveBenNCDCareNurseData") + class SaveNurseTests { + + @Test + @DisplayName("saveBenNCDCareNurseData should return the payload produced by the service") + void saveBenNCDCareNurseData_shouldReturnServicePayload() throws Exception { + when(covid19ServiceImpl.saveCovid19NurseData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn("{\"visitCode\":\"22\"}"); + + String result = controller.saveBenNCDCareNurseData(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("visitCode")); + } + + @Test + @DisplayName("saveBenNCDCareNurseData should roll back the visit details when the service fails") + void saveBenNCDCareNurseData_shouldRollBackVisitDetailsOnFailure() throws Exception { + when(covid19ServiceImpl.saveCovid19NurseData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("save failed")); + + assertTrue(controller.saveBenNCDCareNurseData(REQUEST, AUTHORIZATION).contains("save failed")); + verify(covid19ServiceImpl).deleteVisitDetails(any(JsonObject.class)); + } + + @Test + @DisplayName("saveBenNCDCareNurseData should return the untouched failure response for a null request") + void saveBenNCDCareNurseData_shouldReturnGenericFailureForNullRequest() throws Exception { + assertTrue(controller.saveBenNCDCareNurseData(null, AUTHORIZATION).contains("Failed with generic error")); + verify(covid19ServiceImpl, never()).saveCovid19NurseData(any(JsonObject.class), anyString()); + } + } + + @Nested + @DisplayName("saveBenCovidDoctorData") + class SaveDoctorTests { + + @Test + @DisplayName("saveBenCovidDoctorData should confirm the save when the service returns an id") + void saveBenCovidDoctorData_shouldConfirmSave() throws Exception { + when(covid19ServiceImpl.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(7L); + + assertTrue(controller.saveBenCovidDoctorData(REQUEST, AUTHORIZATION).contains("Data saved successfully")); + } + + @Test + @DisplayName("saveBenCovidDoctorData should report an unsuccessful save") + void saveBenCovidDoctorData_shouldReportUnsuccessfulSave() throws Exception { + when(covid19ServiceImpl.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.saveBenCovidDoctorData(REQUEST, AUTHORIZATION).contains("Unable to save data")); + } + + @Test + @DisplayName("saveBenCovidDoctorData should surface a service failure") + void saveBenCovidDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(covid19ServiceImpl.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("doctor save failed")); + + assertTrue(controller.saveBenCovidDoctorData(REQUEST, AUTHORIZATION).contains("doctor save failed")); + } + } + + @Nested + @DisplayName("getBenVisitDetailsFrmNurseCovid") + class ReadVisitTests { + + @Test + @DisplayName("getBenVisitDetailsFrmNurseCovid should return the details for a complete request") + void getBenVisitDetailsFrmNurseCovid19_shouldReturnDetails() throws Exception { + when(covid19ServiceImpl.getBenVisitDetailsFrmNurseCovid19(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenVisitDetailsFrmNurseCovid19(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseCovid should reject an incomplete request") + void getBenVisitDetailsFrmNurseCovid19_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenVisitDetailsFrmNurseCovid19("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseCovid should surface a service failure") + void getBenVisitDetailsFrmNurseCovid19_shouldSurfaceServiceFailure() throws Exception { + when(covid19ServiceImpl.getBenVisitDetailsFrmNurseCovid19(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVisitDetailsFrmNurseCovid19(VISIT_REQUEST).contains("Error while getting beneficiary visit data")); + } + } + + @Nested + @DisplayName("getBenCovidHistoryDetails") + class ReadHistoryTests { + + @Test + @DisplayName("getBenCovidHistoryDetails should return the details for a complete request") + void getBenCovid19HistoryDetails_shouldReturnDetails() throws Exception { + when(covid19ServiceImpl.getBenCovid19HistoryDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenCovid19HistoryDetails(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenCovidHistoryDetails should reject an incomplete request") + void getBenCovid19HistoryDetails_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenCovid19HistoryDetails("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCovidHistoryDetails should surface a service failure") + void getBenCovid19HistoryDetails_shouldSurfaceServiceFailure() throws Exception { + when(covid19ServiceImpl.getBenCovid19HistoryDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCovid19HistoryDetails(VISIT_REQUEST).contains("Error while getting beneficiary history data")); + } + } + + @Nested + @DisplayName("getBenVitalDetailsFrmNurseNCDCare") + class ReadVitalsTests { + + @Test + @DisplayName("getBenVitalDetailsFrmNurseNCDCare should return the details for a complete request") + void getBenVitalDetailsFrmNurseNCDCare_shouldReturnDetails() throws Exception { + when(covid19ServiceImpl.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenVitalDetailsFrmNurseNCDCare(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurseNCDCare should reject an incomplete request") + void getBenVitalDetailsFrmNurseNCDCare_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenVitalDetailsFrmNurseNCDCare("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurseNCDCare should surface a service failure") + void getBenVitalDetailsFrmNurseNCDCare_shouldSurfaceServiceFailure() throws Exception { + when(covid19ServiceImpl.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVitalDetailsFrmNurseNCDCare(VISIT_REQUEST).contains("Error while getting beneficiary vital data")); + } + } + + @Nested + @DisplayName("getBenCaseRecordFromDoctorCovid") + class ReadCaseRecordTests { + + @Test + @DisplayName("getBenCaseRecordFromDoctorCovid should return the details for a complete request") + void getBenCaseRecordFromDoctorCovid19_shouldReturnDetails() throws Exception { + when(covid19ServiceImpl.getBenCaseRecordFromDoctorCovid19(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenCaseRecordFromDoctorCovid19(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorCovid should reject an incomplete request") + void getBenCaseRecordFromDoctorCovid19_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenCaseRecordFromDoctorCovid19("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorCovid should surface a service failure") + void getBenCaseRecordFromDoctorCovid19_shouldSurfaceServiceFailure() throws Exception { + when(covid19ServiceImpl.getBenCaseRecordFromDoctorCovid19(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCaseRecordFromDoctorCovid19(VISIT_REQUEST).contains("Error while getting beneficiary doctor data")); + } + } + + @Nested + @DisplayName("updateHistoryNurse") + class UpdateHistoryTests { + + @Test + @DisplayName("updateHistoryNurse should confirm the update when a row was changed") + void updateHistoryNurse_shouldConfirmUpdate() throws Exception { + when(covid19ServiceImpl.updateBenHistoryDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateHistoryNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateHistoryNurse should report that nothing was modified") + void updateHistoryNurse_shouldReportNothingModified() throws Exception { + when(covid19ServiceImpl.updateBenHistoryDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateHistoryNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateHistoryNurse should surface a service failure") + void updateHistoryNurse_shouldSurfaceServiceFailure() throws Exception { + when(covid19ServiceImpl.updateBenHistoryDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateHistoryNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateVitalNurse") + class UpdateVitalsTests { + + @Test + @DisplayName("updateVitalNurse should confirm the update when a row was changed") + void updateVitalNurse_shouldConfirmUpdate() throws Exception { + when(covid19ServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateVitalNurse should report that nothing was modified") + void updateVitalNurse_shouldReportNothingModified() throws Exception { + when(covid19ServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateVitalNurse should surface a service failure") + void updateVitalNurse_shouldSurfaceServiceFailure() throws Exception { + when(covid19ServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateCovidDoctorData") + class UpdateDoctorTests { + + @Test + @DisplayName("updateCovidDoctorData should confirm the update when a row was changed") + void updateCovid19DoctorData_shouldConfirmUpdate() throws Exception { + when(covid19ServiceImpl.updateCovid19DoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(1L); + + assertTrue(controller.updateCovid19DoctorData(REQUEST, AUTHORIZATION).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateCovidDoctorData should report that nothing was modified") + void updateCovid19DoctorData_shouldReportNothingModified() throws Exception { + when(covid19ServiceImpl.updateCovid19DoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.updateCovid19DoctorData(REQUEST, AUTHORIZATION).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateCovidDoctorData should surface a service failure") + void updateCovid19DoctorData_shouldSurfaceServiceFailure() throws Exception { + when(covid19ServiceImpl.updateCovid19DoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("update failed")); + + assertTrue(controller.updateCovid19DoctorData(REQUEST, AUTHORIZATION).contains("update failed")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/dataSyncActivity/StartSyncActivityTest.java b/src/test/java/com/iemr/tm/controller/dataSyncActivity/StartSyncActivityTest.java new file mode 100644 index 00000000..afb65daa --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/dataSyncActivity/StartSyncActivityTest.java @@ -0,0 +1,216 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/package com.iemr.tm.controller.dataSyncActivity; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.service.dataSyncActivity.DownloadDataFromServerImpl; +import com.iemr.tm.service.dataSyncActivity.UploadDataToServerImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("StartSyncActivity Test Suite") +class StartSyncActivityTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final String SERVER_AUTHORIZATION = "Bearer server-token"; + + @Mock + private UploadDataToServerImpl uploadDataToServerImpl; + @Mock + private DownloadDataFromServerImpl downloadDataFromServerImpl; + + @InjectMocks + private StartSyncActivity controller; + + @Nested + @DisplayName("van to server sync") + class VanToServerTests { + + private static final String REQUEST = "{\"groupID\":3,\"user\":\"syncuser\"}"; + + @Test + @DisplayName("dataSyncToServer should return the uploaded payload") + void dataSyncToServer_shouldReturnUploadedPayload() throws Exception { + when(uploadDataToServerImpl.getDataToSyncToServer(3, "syncuser", SERVER_AUTHORIZATION)) + .thenReturn("{\"synced\":true}"); + + String result = controller.dataSyncToServer(REQUEST, AUTHORIZATION, SERVER_AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("synced")); + } + + @Test + @DisplayName("dataSyncToServer should report a sync the service could not complete") + void dataSyncToServer_shouldReportIncompleteSync() throws Exception { + when(uploadDataToServerImpl.getDataToSyncToServer(3, "syncuser", SERVER_AUTHORIZATION)).thenReturn(null); + + assertTrue(controller.dataSyncToServer(REQUEST, AUTHORIZATION, SERVER_AUTHORIZATION) + .contains("Error in data sync")); + } + + @Test + @DisplayName("dataSyncToServer should reject a request without a sync group or user") + void dataSyncToServer_shouldRejectRequestWithoutGroupOrUser() { + assertTrue(controller.dataSyncToServer("{}", AUTHORIZATION, SERVER_AUTHORIZATION) + .contains("Either of groupID or user is invalid or null")); + } + + @Test + @DisplayName("dataSyncToServer should report a request it cannot act on") + void dataSyncToServer_shouldReportRequestItCannotActOn() { + assertTrue(controller.dataSyncToServer("not json", AUTHORIZATION, SERVER_AUTHORIZATION) + .contains("\"statusCode\"")); + } + + @Test + @DisplayName("getSyncGroupDetails should return the configured sync groups") + void getSyncGroupDetails_shouldReturnConfiguredGroups() throws Exception { + when(uploadDataToServerImpl.getDataSyncGroupDetails()).thenReturn("{\"groups\":[]}"); + + assertTrue(controller.getSyncGroupDetails().contains("groups")); + } + + @Test + @DisplayName("getSyncGroupDetails should report groups the service could not read") + void getSyncGroupDetails_shouldReportUnreadableGroups() throws Exception { + when(uploadDataToServerImpl.getDataSyncGroupDetails()).thenReturn(null); + + assertTrue(controller.getSyncGroupDetails().contains("Error in getting data sync group details")); + } + + @Test + @DisplayName("getSyncGroupDetails should report a lookup it cannot complete") + void getSyncGroupDetails_shouldReportFailedLookup() throws Exception { + when(uploadDataToServerImpl.getDataSyncGroupDetails()) + .thenThrow(new IllegalStateException("sync group table unavailable")); + + assertTrue(controller.getSyncGroupDetails().contains("\"statusCode\"")); + } + } + + @Nested + @DisplayName("master download") + class MasterDownloadTests { + + private static final String REQUEST = "{\"vanID\":7,\"providerServiceMapID\":9}"; + + @Test + @DisplayName("startMasterDownload should return the downloaded master payload") + void startMasterDownload_shouldReturnDownloadedPayload() throws Exception { + when(downloadDataFromServerImpl.downloadMasterDataFromServer(SERVER_AUTHORIZATION, 7, 9)) + .thenReturn("done"); + + String result = controller.startMasterDownload(REQUEST, AUTHORIZATION, SERVER_AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("done")); + } + + @Test + @DisplayName("startMasterDownload should report a download already running on another device") + void startMasterDownload_shouldReportDownloadAlreadyRunning() throws Exception { + when(downloadDataFromServerImpl.downloadMasterDataFromServer(SERVER_AUTHORIZATION, 7, 9)) + .thenReturn("inProgress"); + + assertTrue(controller.startMasterDownload(REQUEST, AUTHORIZATION, SERVER_AUTHORIZATION) + .contains("Download is already in progress")); + } + + @Test + @DisplayName("startMasterDownload should report a download the service could not start") + void startMasterDownload_shouldReportUnstartedDownload() throws Exception { + when(downloadDataFromServerImpl.downloadMasterDataFromServer(SERVER_AUTHORIZATION, 7, 9)) + .thenReturn(null); + + assertTrue(controller.startMasterDownload(REQUEST, AUTHORIZATION, SERVER_AUTHORIZATION) + .contains("\"statusCode\"")); + } + + @Test + @DisplayName("startMasterDownload should reject a request without a van or provider") + void startMasterDownload_shouldRejectRequestWithoutVanOrProvider() { + assertTrue(controller.startMasterDownload("{}", AUTHORIZATION, SERVER_AUTHORIZATION) + .contains("Kindly contact the administrator")); + } + + @Test + @DisplayName("startMasterDownload should report a request it cannot act on") + void startMasterDownload_shouldReportRequestItCannotActOn() { + assertTrue(controller.startMasterDownload("not json", AUTHORIZATION, SERVER_AUTHORIZATION) + .contains("\"statusCode\"")); + } + + @Test + @DisplayName("checkMastersDownloadProgress should return the download status") + void checkMastersDownloadProgress_shouldReturnStatus() { + when(downloadDataFromServerImpl.getDownloadStatus()).thenReturn(new java.util.HashMap<>()); + + assertTrue(controller.checkMastersDownloadProgress().contains("\"statusCode\":200")); + } + + @Test + @DisplayName("checkMastersDownloadProgress should report a status it cannot read") + void checkMastersDownloadProgress_shouldReportUnreadableStatus() { + when(downloadDataFromServerImpl.getDownloadStatus()) + .thenThrow(new IllegalStateException("status unavailable")); + + assertTrue(controller.checkMastersDownloadProgress().contains("\"statusCode\"")); + } + + @Test + @DisplayName("getVanDetailsForMasterDownload should return the vans available to sync") + void getVanDetails_shouldReturnAvailableVans() throws Exception { + when(downloadDataFromServerImpl.getVanDetailsForMasterDownload()).thenReturn("{\"vans\":[]}"); + + assertTrue(controller.getVanDetailsForMasterDownload().contains("vans")); + } + + @Test + @DisplayName("getVanDetailsForMasterDownload should report vans the service could not read") + void getVanDetails_shouldReportUnreadableVans() throws Exception { + when(downloadDataFromServerImpl.getVanDetailsForMasterDownload()).thenReturn(null); + + assertTrue(controller.getVanDetailsForMasterDownload().contains("Error while getting van details")); + } + + @Test + @DisplayName("getVanDetailsForMasterDownload should report a lookup it cannot complete") + void getVanDetails_shouldReportFailedLookup() throws Exception { + when(downloadDataFromServerImpl.getVanDetailsForMasterDownload()) + .thenThrow(new IllegalStateException("van table unavailable")); + + assertTrue(controller.getVanDetailsForMasterDownload().contains("\"statusCode\"")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/dataSyncLayerCentral/MMUDataSyncVanToServerTest.java b/src/test/java/com/iemr/tm/controller/dataSyncLayerCentral/MMUDataSyncVanToServerTest.java new file mode 100644 index 00000000..15c7672f --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/dataSyncLayerCentral/MMUDataSyncVanToServerTest.java @@ -0,0 +1,126 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/package com.iemr.tm.controller.dataSyncLayerCentral; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.data.syncActivity_syncLayer.SyncDownloadMaster; +import com.iemr.tm.service.dataSyncLayerCentral.GetDataFromVanAndSyncToDBImpl; +import com.iemr.tm.service.dataSyncLayerCentral.GetMasterDataFromCentralForVanImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("MMUDataSyncVanToServer Test Suite") +class MMUDataSyncVanToServerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final String REQUEST = "{\"schemaName\":\"db_iemr\",\"tableName\":\"m_gender\"}"; + + @Mock + private GetDataFromVanAndSyncToDBImpl getDataFromVanAndSyncToDBImpl; + @Mock + private GetMasterDataFromCentralForVanImpl getMasterDataFromCentralForVanImpl; + + @InjectMocks + private MMUDataSyncVanToServer controller; + + private SyncDownloadMaster downloadRequest() { + SyncDownloadMaster request = new SyncDownloadMaster(); + request.setSchemaName("db_iemr"); + request.setTableName("m_gender"); + return request; + } + + @Test + @DisplayName("dataSyncToServer should return the synced payload") + void dataSyncToServer_shouldReturnSyncedPayload() throws Exception { + when(getDataFromVanAndSyncToDBImpl.syncDataToServer(REQUEST, AUTHORIZATION)).thenReturn("{\"synced\":true}"); + + String result = controller.dataSyncToServer(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("synced")); + } + + @Test + @DisplayName("dataSyncToServer should report a sync the service could not complete") + void dataSyncToServer_shouldReportIncompleteSync() throws Exception { + when(getDataFromVanAndSyncToDBImpl.syncDataToServer(REQUEST, AUTHORIZATION)).thenReturn(null); + + assertTrue(controller.dataSyncToServer(REQUEST, AUTHORIZATION).contains("data dync failed")); + } + + @Test + @DisplayName("dataSyncToServer should report a request it cannot act on") + void dataSyncToServer_shouldReportRequestItCannotActOn() throws Exception { + when(getDataFromVanAndSyncToDBImpl.syncDataToServer(REQUEST, AUTHORIZATION)) + .thenThrow(new IllegalStateException("sync failed")); + + assertTrue(controller.dataSyncToServer(REQUEST, AUTHORIZATION).contains("\"statusCode\"")); + } + + @Test + @DisplayName("dataDownloadFromServer should return the requested master table") + void dataDownloadFromServer_shouldReturnRequestedTable() throws Exception { + when(getMasterDataFromCentralForVanImpl.getMasterDataForVan(org.mockito.ArgumentMatchers.any())) + .thenReturn("{\"rows\":[]}"); + + String result = controller.dataDownloadFromServer(downloadRequest(), AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("rows")); + } + + @Test + @DisplayName("dataDownloadFromServer should name the table it could not download") + void dataDownloadFromServer_shouldNameUndownloadableTable() throws Exception { + when(getMasterDataFromCentralForVanImpl.getMasterDataForVan(org.mockito.ArgumentMatchers.any())) + .thenReturn(null); + + assertTrue(controller.dataDownloadFromServer(downloadRequest(), AUTHORIZATION) + .contains("db_iemr.m_gender")); + } + + @Test + @DisplayName("dataDownloadFromServer should reject a request with no table to download") + void dataDownloadFromServer_shouldRejectEmptyRequest() { + assertTrue(controller.dataDownloadFromServer(null, AUTHORIZATION).contains("Invalid request")); + } + + @Test + @DisplayName("dataDownloadFromServer should report a download it cannot complete") + void dataDownloadFromServer_shouldReportFailedDownload() throws Exception { + when(getMasterDataFromCentralForVanImpl.getMasterDataForVan(org.mockito.ArgumentMatchers.any())) + .thenThrow(new IllegalStateException("central database unavailable")); + + assertTrue(controller.dataDownloadFromServer(downloadRequest(), AUTHORIZATION).contains("\"statusCode\"")); + } +} diff --git a/src/test/java/com/iemr/tm/controller/foetalmonitor/FoetalMonitorControllerTest.java b/src/test/java/com/iemr/tm/controller/foetalmonitor/FoetalMonitorControllerTest.java new file mode 100644 index 00000000..00b974e2 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/foetalmonitor/FoetalMonitorControllerTest.java @@ -0,0 +1,239 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.foetalmonitor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import com.iemr.tm.data.foetalmonitor.FoetalMonitor; +import com.iemr.tm.service.foetalmonitor.FoetalMonitorService; +import com.iemr.tm.utils.exception.IEMRException; + +@ExtendWith(MockitoExtension.class) +@DisplayName("FoetalMonitorController Test Suite") +class FoetalMonitorControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final String TEST_REQUEST = "{\"beneficiaryRegID\":11,\"motherName\":\"Asha\"}"; + + @Mock + private FoetalMonitorService foetalMonitorService; + + @InjectMocks + private FoetalMonitorController controller; + + @Nested + @DisplayName("sendANCMotherTestDetailsToFoetalMonitor") + class SendTestDetailsTests { + + @Test + @DisplayName("sendANCMotherTestDetailsToFoetalMonitor should return 200 with the device response") + void sendTestDetails_shouldReturnDeviceResponse() throws Exception { + when(foetalMonitorService.sendFoetalMonitorTestDetails(any(FoetalMonitor.class), eq(AUTHORIZATION))) + .thenReturn("{\"fetosenseTestId\":9}"); + + ResponseEntity result = controller.sendANCMotherTestDetailsToFoetalMonitor(TEST_REQUEST, + AUTHORIZATION); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertTrue(result.getBody().contains("fetosenseTestId")); + } + + @Test + @DisplayName("sendANCMotherTestDetailsToFoetalMonitor should reject a null request") + void sendTestDetails_shouldRejectNullRequest() throws Exception { + ResponseEntity result = controller.sendANCMotherTestDetailsToFoetalMonitor(null, AUTHORIZATION); + + assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); + assertTrue(result.getBody().contains("Invalid request")); + verify(foetalMonitorService, never()).sendFoetalMonitorTestDetails(any(FoetalMonitor.class), anyString()); + } + + @Test + @DisplayName("sendANCMotherTestDetailsToFoetalMonitor should surface a service failure") + void sendTestDetails_shouldSurfaceServiceFailure() throws Exception { + when(foetalMonitorService.sendFoetalMonitorTestDetails(any(FoetalMonitor.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("device unreachable")); + + ResponseEntity result = controller.sendANCMotherTestDetailsToFoetalMonitor(TEST_REQUEST, + AUTHORIZATION); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, result.getStatusCode()); + assertTrue(result.getBody().contains("device unreachable")); + } + } + + @Test + @DisplayName("saveMother should acknowledge that the device test is in progress") + void saveMother_shouldAcknowledgeTestInProgress() { + assertTrue(controller.saveMother(TEST_REQUEST, AUTHORIZATION).contains("Test in progress")); + } + + @Nested + @DisplayName("getFoetalMonitorDetails") + class GetDetailsTests { + + @Test + @DisplayName("getFoetalMonitorDetails should return the monitor details for the flow") + void getFoetalMonitorDetails_shouldReturnDetails() throws Exception { + when(foetalMonitorService.getFoetalMonitorDetails(11L)).thenReturn("{\"benFlowID\":11}"); + + assertTrue(controller.getFoetalMonitorDetails(11L).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getFoetalMonitorDetails should report a failure when nothing comes back") + void getFoetalMonitorDetails_shouldReportFailureWhenNothingFound() throws Exception { + when(foetalMonitorService.getFoetalMonitorDetails(11L)).thenReturn(null); + + assertTrue(controller.getFoetalMonitorDetails(11L).contains("Error in fetching the details")); + } + + @Test + @DisplayName("getFoetalMonitorDetails should surface a service failure") + void getFoetalMonitorDetails_shouldSurfaceServiceFailure() throws Exception { + when(foetalMonitorService.getFoetalMonitorDetails(11L)).thenThrow(new IEMRException("db down")); + + assertTrue(controller.getFoetalMonitorDetails(11L).contains("db down")); + } + } + + @Nested + @DisplayName("getFoetalMonitorDetails report graph") + class ReportGraphTests { + + private FoetalMonitor request() { + FoetalMonitor request = new FoetalMonitor(); + request.setaMRITFilePath("/reports/test.pdf"); + return request; + } + + @Test + @DisplayName("the report endpoint should return the base64 encoded report") + void reportGraph_shouldReturnBase64Report() throws Exception { + when(foetalMonitorService.readPDFANDGetBase64("/reports/test.pdf")).thenReturn("base64-report"); + + ResponseEntity result = controller.getFoetalMonitorDetails(request()); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertTrue(result.getBody().contains("base64-report")); + } + + @Test + @DisplayName("the report endpoint should report a failure when no report comes back") + void reportGraph_shouldReportFailureWhenNoReport() throws Exception { + when(foetalMonitorService.readPDFANDGetBase64("/reports/test.pdf")).thenReturn(null); + + assertTrue(controller.getFoetalMonitorDetails(request()).getBody() + .contains("Error in fetching the details")); + } + + @Test + @DisplayName("the report endpoint should report a missing report file") + void reportGraph_shouldReportMissingFile() throws Exception { + when(foetalMonitorService.readPDFANDGetBase64("/reports/test.pdf")) + .thenThrow(new FileNotFoundException("test.pdf")); + + assertTrue(controller.getFoetalMonitorDetails(request()).getBody().contains("File not found")); + } + + @Test + @DisplayName("the report endpoint should report a read failure") + void reportGraph_shouldReportReadFailure() throws Exception { + when(foetalMonitorService.readPDFANDGetBase64("/reports/test.pdf")).thenThrow(new IOException("disk error")); + + assertTrue(controller.getFoetalMonitorDetails(request()).getBody().contains("File not found")); + } + + @Test + @DisplayName("the report endpoint should surface a service failure") + void reportGraph_shouldSurfaceServiceFailure() throws Exception { + when(foetalMonitorService.readPDFANDGetBase64("/reports/test.pdf")).thenThrow(new IEMRException("db down")); + + assertTrue(controller.getFoetalMonitorDetails(request()).getBody().contains("db down")); + } + } + + @Nested + @DisplayName("updateFoetalMonitorData") + class UpdateTests { + + @Test + @DisplayName("updateFoetalMonitorData should confirm the update when a row was changed") + void updateFoetalMonitorData_shouldConfirmUpdate() throws Exception { + when(foetalMonitorService.updateFoetalMonitorData(any(FoetalMonitor.class))).thenReturn(1); + + ResponseEntity result = controller.updateFoetalMonitorData(TEST_REQUEST, AUTHORIZATION); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertTrue(result.getBody().contains("Data updated successfully")); + } + + @Test + @DisplayName("updateFoetalMonitorData should leave the generic failure when no row was changed") + void updateFoetalMonitorData_shouldLeaveGenericFailureWhenNothingChanged() throws Exception { + when(foetalMonitorService.updateFoetalMonitorData(any(FoetalMonitor.class))).thenReturn(0); + + ResponseEntity result = controller.updateFoetalMonitorData(TEST_REQUEST, AUTHORIZATION); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, result.getStatusCode()); + } + + @Test + @DisplayName("updateFoetalMonitorData should reject a null request") + void updateFoetalMonitorData_shouldRejectNullRequest() throws Exception { + ResponseEntity result = controller.updateFoetalMonitorData(null, AUTHORIZATION); + + assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); + assertTrue(result.getBody().contains("Invalid request")); + } + + @Test + @DisplayName("updateFoetalMonitorData should surface a service failure") + void updateFoetalMonitorData_shouldSurfaceServiceFailure() throws Exception { + when(foetalMonitorService.updateFoetalMonitorData(any(FoetalMonitor.class))) + .thenThrow(new IEMRException("update failed")); + + assertTrue(controller.updateFoetalMonitorData(TEST_REQUEST, AUTHORIZATION).getBody() + .contains("update failed")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/generalOPD/GeneralOPDControllerTest.java b/src/test/java/com/iemr/tm/controller/generalOPD/GeneralOPDControllerTest.java new file mode 100644 index 00000000..8b54c5f2 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/generalOPD/GeneralOPDControllerTest.java @@ -0,0 +1,405 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.generalOPD; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.iemr.tm.service.generalOPD.GeneralOPDService; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("GeneralOPDController Test Suite") +class GeneralOPDControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_CODE = 22L; + private static final String REQUEST = "{\"beneficiaryRegID\":11,\"visitCode\":22}"; + private static final String VISIT_REQUEST = "{\"benRegID\":11,\"visitCode\":22}"; + + @Mock + private GeneralOPDService generalOPDService; + + @InjectMocks + private GeneralOPDController controller; + + @Nested + @DisplayName("saveBenGenOPDNurseData") + class SaveNurseTests { + + @Test + @DisplayName("saveBenGenOPDNurseData should return the payload produced by the service") + void saveBenGenOPDNurseData_shouldReturnServicePayload() throws Exception { + when(generalOPDService.saveNurseData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn("{\"visitCode\":\"22\"}"); + + String result = controller.saveBenGenOPDNurseData(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("visitCode")); + } + + @Test + @DisplayName("saveBenGenOPDNurseData should roll back the visit details when the service fails") + void saveBenGenOPDNurseData_shouldRollBackVisitDetailsOnFailure() throws Exception { + when(generalOPDService.saveNurseData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("save failed")); + + assertTrue(controller.saveBenGenOPDNurseData(REQUEST, AUTHORIZATION).contains("save failed")); + verify(generalOPDService).deleteVisitDetails(any(JsonObject.class)); + } + + @Test + @DisplayName("saveBenGenOPDNurseData should return the untouched failure response for a null request") + void saveBenGenOPDNurseData_shouldReturnGenericFailureForNullRequest() throws Exception { + assertTrue(controller.saveBenGenOPDNurseData(null, AUTHORIZATION).contains("Failed with generic error")); + verify(generalOPDService, never()).saveNurseData(any(JsonObject.class), anyString()); + } + } + + @Nested + @DisplayName("saveBenGenOPDDoctorData") + class SaveDoctorTests { + + @Test + @DisplayName("saveBenGenOPDDoctorData should confirm the save when the service returns an id") + void saveBenGenOPDDoctorData_shouldConfirmSave() throws Exception { + when(generalOPDService.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(7L); + + assertTrue(controller.saveBenGenOPDDoctorData(REQUEST, AUTHORIZATION).contains("Data saved successfully")); + } + + @Test + @DisplayName("saveBenGenOPDDoctorData should report an unsuccessful save") + void saveBenGenOPDDoctorData_shouldReportUnsuccessfulSave() throws Exception { + when(generalOPDService.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.saveBenGenOPDDoctorData(REQUEST, AUTHORIZATION).contains("Unable to save data")); + } + + @Test + @DisplayName("saveBenGenOPDDoctorData should surface a service failure") + void saveBenGenOPDDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("doctor save failed")); + + assertTrue(controller.saveBenGenOPDDoctorData(REQUEST, AUTHORIZATION).contains("doctor save failed")); + } + } + + @Nested + @DisplayName("getBenVisitDetailsFrmNurseGOPD") + class ReadVisitTests { + + @Test + @DisplayName("getBenVisitDetailsFrmNurseGOPD should return the details for a complete request") + void getBenVisitDetailsFrmNurseGOPD_shouldReturnDetails() throws Exception { + when(generalOPDService.getBenVisitDetailsFrmNurseGOPD(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenVisitDetailsFrmNurseGOPD(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseGOPD should reject an incomplete request") + void getBenVisitDetailsFrmNurseGOPD_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenVisitDetailsFrmNurseGOPD("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseGOPD should surface a service failure") + void getBenVisitDetailsFrmNurseGOPD_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.getBenVisitDetailsFrmNurseGOPD(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVisitDetailsFrmNurseGOPD(VISIT_REQUEST).contains("Error while getting beneficiary visit data")); + } + } + + @Nested + @DisplayName("getBenHistoryDetails") + class ReadHistoryTests { + + @Test + @DisplayName("getBenHistoryDetails should return the details for a complete request") + void getBenHistoryDetails_shouldReturnDetails() throws Exception { + when(generalOPDService.getBenHistoryDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenHistoryDetails(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenHistoryDetails should reject an incomplete request") + void getBenHistoryDetails_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenHistoryDetails("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenHistoryDetails should surface a service failure") + void getBenHistoryDetails_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.getBenHistoryDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenHistoryDetails(VISIT_REQUEST).contains("Error while getting beneficiary history data")); + } + } + + @Nested + @DisplayName("getBenVitalDetailsFrmNurse") + class ReadVitalsTests { + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should return the details for a complete request") + void getBenVitalDetailsFrmNurse_shouldReturnDetails() throws Exception { + when(generalOPDService.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenVitalDetailsFrmNurse(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should reject an incomplete request") + void getBenVitalDetailsFrmNurse_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenVitalDetailsFrmNurse("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should surface a service failure") + void getBenVitalDetailsFrmNurse_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVitalDetailsFrmNurse(VISIT_REQUEST).contains("Error while getting beneficiary vital data")); + } + } + + @Nested + @DisplayName("getBenExaminationDetails") + class ReadExaminationTests { + + @Test + @DisplayName("getBenExaminationDetails should return the details for a complete request") + void getBenExaminationDetails_shouldReturnDetails() throws Exception { + when(generalOPDService.getExaminationDetailsData(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenExaminationDetails(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenExaminationDetails should reject an incomplete request") + void getBenExaminationDetails_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenExaminationDetails("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenExaminationDetails should surface a service failure") + void getBenExaminationDetails_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.getExaminationDetailsData(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenExaminationDetails(VISIT_REQUEST).contains("Error while getting beneficiary examination data")); + } + } + + @Nested + @DisplayName("getBenCaseRecordFromDoctorGeneralOPD") + class ReadCaseRecordTests { + + @Test + @DisplayName("getBenCaseRecordFromDoctorGeneralOPD should return the details for a complete request") + void getBenCaseRecordFromDoctorGeneralOPD_shouldReturnDetails() throws Exception { + when(generalOPDService.getBenCaseRecordFromDoctorGeneralOPD(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenCaseRecordFromDoctorGeneralOPD(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorGeneralOPD should reject an incomplete request") + void getBenCaseRecordFromDoctorGeneralOPD_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenCaseRecordFromDoctorGeneralOPD("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorGeneralOPD should surface a service failure") + void getBenCaseRecordFromDoctorGeneralOPD_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.getBenCaseRecordFromDoctorGeneralOPD(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCaseRecordFromDoctorGeneralOPD(VISIT_REQUEST).contains("Error while getting beneficiary doctor data")); + } + } + + @Nested + @DisplayName("updateVisitNurse") + class UpdateVisitTests { + + @Test + @DisplayName("updateVisitNurse should confirm the update when a row was changed") + void updateVisitNurse_shouldConfirmUpdate() throws Exception { + when(generalOPDService.UpdateVisitDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateVisitNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateVisitNurse should report that nothing was modified") + void updateVisitNurse_shouldReportNothingModified() throws Exception { + when(generalOPDService.UpdateVisitDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateVisitNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateVisitNurse should surface a service failure") + void updateVisitNurse_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.UpdateVisitDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateVisitNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateHistoryNurse") + class UpdateHistoryTests { + + @Test + @DisplayName("updateHistoryNurse should confirm the update when a row was changed") + void updateHistoryNurse_shouldConfirmUpdate() throws Exception { + when(generalOPDService.updateBenHistoryDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateHistoryNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateHistoryNurse should report that nothing was modified") + void updateHistoryNurse_shouldReportNothingModified() throws Exception { + when(generalOPDService.updateBenHistoryDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateHistoryNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateHistoryNurse should surface a service failure") + void updateHistoryNurse_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.updateBenHistoryDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateHistoryNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateVitalNurse") + class UpdateVitalsTests { + + @Test + @DisplayName("updateVitalNurse should confirm the update when a row was changed") + void updateVitalNurse_shouldConfirmUpdate() throws Exception { + when(generalOPDService.updateBenVitalDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateVitalNurse should report that nothing was modified") + void updateVitalNurse_shouldReportNothingModified() throws Exception { + when(generalOPDService.updateBenVitalDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateVitalNurse should surface a service failure") + void updateVitalNurse_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.updateBenVitalDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateGeneralOPDExaminationNurse") + class UpdateExaminationTests { + + @Test + @DisplayName("updateGeneralOPDExaminationNurse should confirm the update when a row was changed") + void updateGeneralOPDExaminationNurse_shouldConfirmUpdate() throws Exception { + when(generalOPDService.updateBenExaminationDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateGeneralOPDExaminationNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateGeneralOPDExaminationNurse should report that nothing was modified") + void updateGeneralOPDExaminationNurse_shouldReportNothingModified() throws Exception { + when(generalOPDService.updateBenExaminationDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateGeneralOPDExaminationNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateGeneralOPDExaminationNurse should surface a service failure") + void updateGeneralOPDExaminationNurse_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.updateBenExaminationDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateGeneralOPDExaminationNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateGeneralOPDDoctorData") + class UpdateDoctorTests { + + @Test + @DisplayName("updateGeneralOPDDoctorData should confirm the update when a row was changed") + void updateGeneralOPDDoctorData_shouldConfirmUpdate() throws Exception { + when(generalOPDService.updateGeneralOPDDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(1L); + + assertTrue(controller.updateGeneralOPDDoctorData(REQUEST, AUTHORIZATION).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateGeneralOPDDoctorData should report that nothing was modified") + void updateGeneralOPDDoctorData_shouldReportNothingModified() throws Exception { + when(generalOPDService.updateGeneralOPDDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.updateGeneralOPDDoctorData(REQUEST, AUTHORIZATION).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateGeneralOPDDoctorData should surface a service failure") + void updateGeneralOPDDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(generalOPDService.updateGeneralOPDDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("update failed")); + + assertTrue(controller.updateGeneralOPDDoctorData(REQUEST, AUTHORIZATION).contains("update failed")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/health/HealthControllerTest.java b/src/test/java/com/iemr/tm/controller/health/HealthControllerTest.java new file mode 100644 index 00000000..df0c1d2f --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/health/HealthControllerTest.java @@ -0,0 +1,106 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.health; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import com.iemr.tm.service.health.HealthService; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +@DisplayName("HealthController Test Suite") +class HealthControllerTest { + + @Mock + private HealthService healthService; + + private MockMvc mockMvc; + + @BeforeEach + @DisplayName("Set up standalone MockMvc before each test") + void setUp() { + mockMvc = MockMvcBuilders.standaloneSetup(new HealthController(healthService)).build(); + } + + private Map healthResponse(String overallStatus) { + Map response = new LinkedHashMap<>(); + response.put("status", overallStatus); + response.put("checkedAt", "2025-06-25T10:00:00Z"); + return response; + } + + @Test + @DisplayName("checkHealth should return 200 with the payload when all services are UP") + void checkHealth_shouldReturnOkWhenStatusIsUp() throws Exception { + when(healthService.checkHealth()).thenReturn(healthResponse("UP")); + + mockMvc.perform(get("/health")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("UP")) + .andExpect(jsonPath("$.checkedAt").value("2025-06-25T10:00:00Z")); + } + + @Test + @DisplayName("checkHealth should return 200 when DEGRADED, since the instance is still operational") + void checkHealth_shouldReturnOkWhenStatusIsDegraded() throws Exception { + when(healthService.checkHealth()).thenReturn(healthResponse("DEGRADED")); + + mockMvc.perform(get("/health")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("DEGRADED")); + } + + @Test + @DisplayName("checkHealth should return 503 when a critical service is DOWN") + void checkHealth_shouldReturnServiceUnavailableWhenStatusIsDown() throws Exception { + when(healthService.checkHealth()).thenReturn(healthResponse("DOWN")); + + mockMvc.perform(get("/health")) + .andExpect(status().isServiceUnavailable()) + .andExpect(jsonPath("$.status").value("DOWN")); + } + + @Test + @DisplayName("checkHealth should return 503 with a DOWN payload when the service throws unexpectedly") + void checkHealth_shouldReturnServiceUnavailableWhenServiceThrows() throws Exception { + when(healthService.checkHealth()).thenThrow(new IllegalStateException("unexpected failure")); + + mockMvc.perform(get("/health")) + .andExpect(status().isServiceUnavailable()) + .andExpect(jsonPath("$.status").value("DOWN")) + .andExpect(jsonPath("$.timestamp").exists()); + } +} diff --git a/src/test/java/com/iemr/tm/controller/labtechnician/LabtechnicianControllerTest.java b/src/test/java/com/iemr/tm/controller/labtechnician/LabtechnicianControllerTest.java new file mode 100644 index 00000000..6b83c4aa --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/labtechnician/LabtechnicianControllerTest.java @@ -0,0 +1,196 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.labtechnician; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.google.gson.JsonObject; +import com.iemr.tm.service.labtechnician.LabTechnicianServiceImpl; + +@ExtendWith(MockitoExtension.class) +@DisplayName("LabtechnicianController Test Suite") +class LabtechnicianControllerTest { + + private static final String VISIT_REQUEST = "{\"beneficiaryRegID\":11,\"visitCode\":22}"; + + @Mock + private LabTechnicianServiceImpl labTechnicianServiceImpl; + + private LabtechnicianController controller; + + @BeforeEach + @DisplayName("Wire the controller with a mocked lab technician service") + void setUp() { + controller = new LabtechnicianController(); + controller.setLabTechnicianServiceImpl(labTechnicianServiceImpl); + } + + @Nested + @DisplayName("saveLabTestResult") + class SaveLabTestResultTests { + + @Test + @DisplayName("saveLabTestResult should confirm the save when rows were written") + void saveLabTestResult_shouldConfirmSave() throws Exception { + when(labTechnicianServiceImpl.saveLabTestResult(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.saveLabTestResult("{\"visitCode\":22}").contains("Data saved successfully")); + } + + @Test + @DisplayName("saveLabTestResult should report an unsuccessful save") + void saveLabTestResult_shouldReportUnsuccessfulSave() throws Exception { + when(labTechnicianServiceImpl.saveLabTestResult(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.saveLabTestResult("{\"visitCode\":22}").contains("Unable to save data")); + } + + @Test + @DisplayName("saveLabTestResult should report an unsuccessful save when no count comes back") + void saveLabTestResult_shouldReportUnsuccessfulSaveForNullCount() throws Exception { + when(labTechnicianServiceImpl.saveLabTestResult(any(JsonObject.class))).thenReturn(null); + + assertTrue(controller.saveLabTestResult("{\"visitCode\":22}").contains("Unable to save data")); + } + + @Test + @DisplayName("saveLabTestResult should surface a service failure") + void saveLabTestResult_shouldSurfaceServiceFailure() throws Exception { + when(labTechnicianServiceImpl.saveLabTestResult(any(JsonObject.class))) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.saveLabTestResult("{\"visitCode\":22}").contains("Unable to save data")); + } + + @Test + @DisplayName("saveLabTestResult should surface a malformed request") + void saveLabTestResult_shouldSurfaceMalformedRequest() { + assertTrue(controller.saveLabTestResult("not-json").contains("Unable to save data")); + } + } + + @Nested + @DisplayName("getBeneficiaryPrescribedProcedure") + class PrescribedProcedureTests { + + @Test + @DisplayName("getBeneficiaryPrescribedProcedure should return the prescribed procedures") + void getBeneficiaryPrescribedProcedure_shouldReturnProcedures() throws Exception { + when(labTechnicianServiceImpl.getBenePrescribedProcedureDetails(11L, 22L)).thenReturn("[{\"procedure\":1}]"); + + assertTrue(controller.getBeneficiaryPrescribedProcedure(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBeneficiaryPrescribedProcedure should report a failure when nothing comes back") + void getBeneficiaryPrescribedProcedure_shouldReportFailureWhenNothingFound() throws Exception { + when(labTechnicianServiceImpl.getBenePrescribedProcedureDetails(11L, 22L)).thenReturn(null); + + assertTrue(controller.getBeneficiaryPrescribedProcedure(VISIT_REQUEST) + .contains("Error in prescribed procedure details")); + } + + @Test + @DisplayName("getBeneficiaryPrescribedProcedure should reject a request without the visit code") + void getBeneficiaryPrescribedProcedure_shouldRejectIncompleteRequest() throws Exception { + String result = controller.getBeneficiaryPrescribedProcedure("{\"beneficiaryRegID\":11}"); + + assertTrue(result.contains("Invalid request")); + verify(labTechnicianServiceImpl, never()).getBenePrescribedProcedureDetails(anyLong(), anyLong()); + } + + @Test + @DisplayName("getBeneficiaryPrescribedProcedure should surface a service failure") + void getBeneficiaryPrescribedProcedure_shouldSurfaceServiceFailure() throws Exception { + when(labTechnicianServiceImpl.getBenePrescribedProcedureDetails(11L, 22L)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBeneficiaryPrescribedProcedure(VISIT_REQUEST) + .contains("Error while getting prescribed procedure data")); + } + + @Test + @DisplayName("getBeneficiaryPrescribedProcedure should surface a malformed request") + void getBeneficiaryPrescribedProcedure_shouldSurfaceMalformedRequest() { + assertTrue(controller.getBeneficiaryPrescribedProcedure("not-json") + .contains("Error while getting prescribed procedure data")); + } + } + + @Nested + @DisplayName("getLabResultForVisitCode") + class LabResultTests { + + @Test + @DisplayName("getLabResultForVisitCode should return the lab report for the visit") + void getLabResultForVisitCode_shouldReturnLabReport() throws Exception { + when(labTechnicianServiceImpl.getLabResultForVisitcode(11L, 22L)).thenReturn("[{\"result\":\"normal\"}]"); + + assertTrue(controller.getLabResultForVisitCode(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getLabResultForVisitCode should report a failure when no report comes back") + void getLabResultForVisitCode_shouldReportFailureWhenNoReport() throws Exception { + when(labTechnicianServiceImpl.getLabResultForVisitcode(11L, 22L)).thenReturn(null); + + assertTrue(controller.getLabResultForVisitCode(VISIT_REQUEST).contains("Error while getting lab report")); + } + + @Test + @DisplayName("getLabResultForVisitCode should reject a request without the visit code") + void getLabResultForVisitCode_shouldRejectIncompleteRequest() throws Exception { + String result = controller.getLabResultForVisitCode("{\"beneficiaryRegID\":11}"); + + assertTrue(result.contains("Invalid request")); + verify(labTechnicianServiceImpl, never()).getLabResultForVisitcode(anyLong(), anyLong()); + } + + @Test + @DisplayName("getLabResultForVisitCode should surface a service failure") + void getLabResultForVisitCode_shouldSurfaceServiceFailure() throws Exception { + when(labTechnicianServiceImpl.getLabResultForVisitcode(11L, 22L)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getLabResultForVisitCode(VISIT_REQUEST).contains("Error while getting lab report")); + } + + @Test + @DisplayName("getLabResultForVisitCode should surface a malformed request") + void getLabResultForVisitCode_shouldSurfaceMalformedRequest() { + assertTrue(controller.getLabResultForVisitCode("not-json").contains("Error while getting lab report")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/location/LocationControllerTest.java b/src/test/java/com/iemr/tm/controller/location/LocationControllerTest.java new file mode 100644 index 00000000..ef20a07a --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/location/LocationControllerTest.java @@ -0,0 +1,197 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.location; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.iemr.tm.service.location.LocationServiceImpl; + +@ExtendWith(MockitoExtension.class) +@DisplayName("LocationController Test Suite") +class LocationControllerTest { + + @Mock + private LocationServiceImpl locationServiceImpl; + + private LocationController controller; + + @BeforeEach + @DisplayName("Wire the controller with a mocked location service") + void setUp() { + controller = new LocationController(); + controller.setLocationServiceImpl(locationServiceImpl); + } + + @Nested + @DisplayName("location master lookups") + class MasterLookupTests { + + @Test + @DisplayName("getCountryMaster should return the country list") + void getCountryMaster_shouldReturnCountryList() { + when(locationServiceImpl.getCountryList()).thenReturn("[{\"countryID\":1}]"); + + assertTrue(controller.getCountryMaster().contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getCountryMaster should report a failure when no country list comes back") + void getCountryMaster_shouldReportFailureWhenNoList() { + when(locationServiceImpl.getCountryList()).thenReturn(null); + + assertTrue(controller.getCountryMaster().contains("Error while getting country")); + } + + @Test + @DisplayName("getCountryCityMaster should return the city list for the country") + void getCountryCityMaster_shouldReturnCityList() { + when(locationServiceImpl.getCountryCityList(1)).thenReturn("[{\"cityID\":9}]"); + + assertTrue(controller.getCountryCityMaster(1).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getCountryCityMaster should report a failure when no city list comes back") + void getCountryCityMaster_shouldReportFailureWhenNoList() { + when(locationServiceImpl.getCountryCityList(1)).thenReturn(null); + + assertTrue(controller.getCountryCityMaster(1).contains("Error while getting country city")); + } + + @Test + @DisplayName("getStateMaster should return the state list") + void getStateMaster_shouldReturnStateList() { + when(locationServiceImpl.getStateList()).thenReturn("[{\"stateID\":2}]"); + + assertTrue(controller.getStateMaster().contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getStateMaster should report a failure when no state list comes back") + void getStateMaster_shouldReportFailureWhenNoList() { + when(locationServiceImpl.getStateList()).thenReturn(null); + + assertTrue(controller.getStateMaster().contains("Error while getting states")); + } + + @Test + @DisplayName("getDistrictMaster should return the district list for the state") + void getDistrictMaster_shouldReturnDistrictList() { + when(locationServiceImpl.getDistrictList(2)).thenReturn("[{\"districtID\":3}]"); + + assertTrue(controller.getDistrictMaster(2).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getDistrictMaster should report a failure when no district list comes back") + void getDistrictMaster_shouldReportFailureWhenNoList() { + when(locationServiceImpl.getDistrictList(2)).thenReturn(null); + + assertTrue(controller.getDistrictMaster(2).contains("Error while getting districts")); + } + + @Test + @DisplayName("getDistrictBlockMaster should return the block list for the district") + void getDistrictBlockMaster_shouldReturnBlockList() { + when(locationServiceImpl.getDistrictBlockList(3)).thenReturn("[{\"blockID\":4}]"); + + assertTrue(controller.getDistrictBlockMaster(3).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getDistrictBlockMaster should report a failure when no block list comes back") + void getDistrictBlockMaster_shouldReportFailureWhenNoList() { + when(locationServiceImpl.getDistrictBlockList(3)).thenReturn(null); + + assertTrue(controller.getDistrictBlockMaster(3).contains("Error while getting district blocks")); + } + + @Test + @DisplayName("getVillageMaster should return the village list for the block") + void getVillageMaster_shouldReturnVillageList() { + when(locationServiceImpl.getVillageMasterFromBlockID(4)).thenReturn("[{\"villageID\":5}]"); + + assertTrue(controller.getVillageMaster(4).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getVillageMaster should report a failure when no village list comes back") + void getVillageMaster_shouldReportFailureWhenNoList() { + when(locationServiceImpl.getVillageMasterFromBlockID(4)).thenReturn(null); + + assertTrue(controller.getVillageMaster(4).contains("Error while getting villages")); + } + } + + @Nested + @DisplayName("getLocDetailsBasedOnSpIDAndPsmIDNew") + class LocationDetailsTests { + + @Test + @DisplayName("getLocDetailsBasedOnSpIDAndPsmIDNew should return the location details for a complete request") + void getLocDetails_shouldReturnLocationDetails() { + when(locationServiceImpl.getLocDetailsNew(7, 8)).thenReturn("{\"districtID\":3}"); + + String result = controller.getLocDetailsBasedOnSpIDAndPsmIDNew("{\"vanID\":7,\"spPSMID\":8}"); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("districtID")); + } + + @Test + @DisplayName("getLocDetailsBasedOnSpIDAndPsmIDNew should reject a request without spPSMID") + void getLocDetails_shouldRejectRequestWithoutSpPsmId() { + String result = controller.getLocDetailsBasedOnSpIDAndPsmIDNew("{\"vanID\":7}"); + + assertTrue(result.contains("Invalid request")); + verify(locationServiceImpl, never()).getLocDetailsNew(anyInt(), anyInt()); + } + + @Test + @DisplayName("getLocDetailsBasedOnSpIDAndPsmIDNew should surface a service failure") + void getLocDetails_shouldSurfaceServiceFailure() { + when(locationServiceImpl.getLocDetailsNew(7, 8)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getLocDetailsBasedOnSpIDAndPsmIDNew("{\"vanID\":7,\"spPSMID\":8}") + .contains("Error while getting location data")); + } + + @Test + @DisplayName("getLocDetailsBasedOnSpIDAndPsmIDNew should surface a malformed request") + void getLocDetails_shouldSurfaceMalformedRequest() { + assertTrue(controller.getLocDetailsBasedOnSpIDAndPsmIDNew("not-json") + .contains("Error while getting location data")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/login/IemrMmuLoginControllerTest.java b/src/test/java/com/iemr/tm/controller/login/IemrMmuLoginControllerTest.java new file mode 100644 index 00000000..6d5e78de --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/login/IemrMmuLoginControllerTest.java @@ -0,0 +1,211 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.login; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.Authentication; + +import com.iemr.tm.service.login.IemrMmuLoginServiceImpl; + +@ExtendWith(MockitoExtension.class) +@DisplayName("IemrMmuLoginController Test Suite") +class IemrMmuLoginControllerTest { + + @Mock + private IemrMmuLoginServiceImpl iemrMmuLoginServiceImpl; + + @Mock + private Authentication authentication; + + private IemrMmuLoginController controller; + + @BeforeEach + @DisplayName("Wire the controller with a mocked login service") + void setUp() { + controller = new IemrMmuLoginController(); + controller.setIemrMmuLoginServiceImpl(iemrMmuLoginServiceImpl); + } + + private void authenticateAs(String userId) { + when(authentication.isAuthenticated()).thenReturn(true); + when(authentication.getPrincipal()).thenReturn(userId); + } + + @Nested + @DisplayName("getUserServicePointVanDetails") + class ServicePointVanDetailsTests { + + @Test + @DisplayName("getUserServicePointVanDetails should return the van details for an authenticated user") + void getUserServicePointVanDetails_shouldReturnVanDetails() throws Exception { + authenticateAs("42"); + when(iemrMmuLoginServiceImpl.getUserServicePointVanDetails(42)).thenReturn("{\"vanID\":7}"); + + String result = controller.getUserServicePointVanDetails("{}", authentication); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("vanID")); + } + + @Test + @DisplayName("getUserServicePointVanDetails should reject a missing authentication") + void getUserServicePointVanDetails_shouldRejectMissingAuthentication() throws Exception { + String result = controller.getUserServicePointVanDetails("{}", null); + + assertTrue(result.contains("Unauthorized access")); + verify(iemrMmuLoginServiceImpl, never()).getUserServicePointVanDetails(anyInt()); + } + + @Test + @DisplayName("getUserServicePointVanDetails should reject an unauthenticated principal") + void getUserServicePointVanDetails_shouldRejectUnauthenticatedPrincipal() throws Exception { + when(authentication.isAuthenticated()).thenReturn(false); + + assertTrue(controller.getUserServicePointVanDetails("{}", authentication).contains("Unauthorized access")); + } + + @Test + @DisplayName("getUserServicePointVanDetails should surface a non-numeric principal as a failure") + void getUserServicePointVanDetails_shouldSurfaceNonNumericPrincipal() { + authenticateAs("not-a-number"); + + assertTrue(controller.getUserServicePointVanDetails("{}", authentication) + .contains("Error while getting service points and van data")); + } + + @Test + @DisplayName("getUserServicePointVanDetails should surface a service failure") + void getUserServicePointVanDetails_shouldSurfaceServiceFailure() throws Exception { + authenticateAs("42"); + when(iemrMmuLoginServiceImpl.getUserServicePointVanDetails(42)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getUserServicePointVanDetails("{}", authentication) + .contains("Error while getting service points and van data")); + } + } + + @Nested + @DisplayName("getServicepointVillages") + class ServicePointVillagesTests { + + @Test + @DisplayName("getServicepointVillages should return the villages for the service point") + void getServicepointVillages_shouldReturnVillages() throws Exception { + when(iemrMmuLoginServiceImpl.getServicepointVillages(3)).thenReturn("[{\"villageID\":5}]"); + + assertTrue(controller.getServicepointVillages("{\"servicePointID\":3}").contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getServicepointVillages should surface a request without the service point id") + void getServicepointVillages_shouldSurfaceRequestWithoutServicePointId() { + assertTrue(controller.getServicepointVillages("{}") + .contains("Error while getting service points and villages")); + } + + @Test + @DisplayName("getServicepointVillages should surface a service failure") + void getServicepointVillages_shouldSurfaceServiceFailure() throws Exception { + when(iemrMmuLoginServiceImpl.getServicepointVillages(3)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getServicepointVillages("{\"servicePointID\":3}") + .contains("Error while getting service points and villages")); + } + } + + @Nested + @DisplayName("getUserVanSpDetails") + class UserVanSpDetailsTests { + + @Test + @DisplayName("getUserVanSpDetails should return the van and service point details") + void getUserVanSpDetails_shouldReturnVanAndServicePointDetails() throws Exception { + authenticateAs("42"); + when(iemrMmuLoginServiceImpl.getUserVanSpDetails(42, 9)).thenReturn("{\"vanID\":7}"); + + assertTrue(controller.getUserVanSpDetails("{\"providerServiceMapID\":9}", authentication) + .contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getUserVanSpDetails should reject a missing authentication") + void getUserVanSpDetails_shouldRejectMissingAuthentication() { + assertTrue(controller.getUserVanSpDetails("{\"providerServiceMapID\":9}", null) + .contains("Unauthorized access")); + } + + @Test + @DisplayName("getUserVanSpDetails should reject a request without providerServiceMapID") + void getUserVanSpDetails_shouldRejectRequestWithoutProviderServiceMapId() throws Exception { + authenticateAs("42"); + + String result = controller.getUserVanSpDetails("{}", authentication); + + assertTrue(result.contains("Invalid request")); + verify(iemrMmuLoginServiceImpl, never()).getUserVanSpDetails(anyInt(), anyInt()); + } + + @Test + @DisplayName("getUserVanSpDetails should surface a service failure") + void getUserVanSpDetails_shouldSurfaceServiceFailure() throws Exception { + authenticateAs("42"); + when(iemrMmuLoginServiceImpl.getUserVanSpDetails(42, 9)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getUserVanSpDetails("{\"providerServiceMapID\":9}", authentication) + .contains("Error while getting van and service points data")); + } + } + + @Nested + @DisplayName("getUserSpokeDetails") + class UserSpokeDetailsTests { + + @Test + @DisplayName("getUserSpokeDetails should return the spoke details for the provider service map") + void getUserSpokeDetails_shouldReturnSpokeDetails() throws Exception { + when(iemrMmuLoginServiceImpl.getUserSpokeDetails(9)).thenReturn("[{\"spokeID\":1}]"); + + assertTrue(controller.getUserSpokeDetails(9).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getUserSpokeDetails should surface a service failure") + void getUserSpokeDetails_shouldSurfaceServiceFailure() throws Exception { + when(iemrMmuLoginServiceImpl.getUserSpokeDetails(9)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getUserSpokeDetails(9).contains("Error occurred while fetching van master")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/ncdCare/NCDCareControllerTest.java b/src/test/java/com/iemr/tm/controller/ncdCare/NCDCareControllerTest.java new file mode 100644 index 00000000..b35aa13d --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/ncdCare/NCDCareControllerTest.java @@ -0,0 +1,348 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.ncdCare; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.google.gson.JsonObject; +import com.iemr.tm.service.ncdCare.NCDCareServiceImpl; + +@ExtendWith(MockitoExtension.class) +@DisplayName("NCDCareController Test Suite") +class NCDCareControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final String BEN_REQUEST = "{\"benRegID\":11,\"visitCode\":22}"; + + @Mock + private NCDCareServiceImpl ncdCareServiceImpl; + + private NCDCareController controller; + + @BeforeEach + @DisplayName("Wire the controller with a mocked NCD care service") + void setUp() { + controller = new NCDCareController(); + controller.setNcdCareServiceImpl(ncdCareServiceImpl); + } + + @Nested + @DisplayName("saveBenNCDCareNurseData") + class SaveNurseDataTests { + + @Test + @DisplayName("saveBenNCDCareNurseData should return the payload produced by the service") + void saveBenNCDCareNurseData_shouldReturnServicePayload() throws Exception { + when(ncdCareServiceImpl.saveNCDCareNurseData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenReturn("{\"benVisitID\":5}"); + + String result = controller.saveBenNCDCareNurseData("{\"benVisitID\":5}", AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("benVisitID")); + } + + @Test + @DisplayName("saveBenNCDCareNurseData should roll back the visit details when the service fails") + void saveBenNCDCareNurseData_shouldRollBackVisitDetailsOnFailure() throws Exception { + when(ncdCareServiceImpl.saveNCDCareNurseData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("save failed")); + + String result = controller.saveBenNCDCareNurseData("{\"benVisitID\":5}", AUTHORIZATION); + + assertTrue(result.contains("save failed")); + verify(ncdCareServiceImpl).deleteVisitDetails(any(JsonObject.class)); + } + + @Test + @DisplayName("saveBenNCDCareNurseData should return the untouched failure response for a null request") + void saveBenNCDCareNurseData_shouldReturnGenericFailureForNullRequest() throws Exception { + String result = controller.saveBenNCDCareNurseData(null, AUTHORIZATION); + + assertTrue(result.contains("Failed with generic error")); + verify(ncdCareServiceImpl, never()).saveNCDCareNurseData(any(JsonObject.class), anyString()); + } + } + + @Nested + @DisplayName("saveBenNCDCareDoctorData") + class SaveDoctorDataTests { + + @Test + @DisplayName("saveBenNCDCareDoctorData should confirm the save when the service returns an id") + void saveBenNCDCareDoctorData_shouldConfirmSave() throws Exception { + when(ncdCareServiceImpl.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(7L); + + String result = controller.saveBenNCDCareDoctorData("{\"benRegID\":11}", AUTHORIZATION); + + assertTrue(result.contains("Data saved successfully")); + } + + @Test + @DisplayName("saveBenNCDCareDoctorData should report an unsuccessful save") + void saveBenNCDCareDoctorData_shouldReportUnsuccessfulSave() throws Exception { + when(ncdCareServiceImpl.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + String result = controller.saveBenNCDCareDoctorData("{\"benRegID\":11}", AUTHORIZATION); + + assertTrue(result.contains("Unable to save data")); + } + + @Test + @DisplayName("saveBenNCDCareDoctorData should report an unsuccessful save when no id comes back") + void saveBenNCDCareDoctorData_shouldReportUnsuccessfulSaveForNullId() throws Exception { + when(ncdCareServiceImpl.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(null); + + String result = controller.saveBenNCDCareDoctorData("{\"benRegID\":11}", AUTHORIZATION); + + assertTrue(result.contains("Unable to save data")); + } + + @Test + @DisplayName("saveBenNCDCareDoctorData should surface a service failure") + void saveBenNCDCareDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(ncdCareServiceImpl.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("doctor save failed")); + + assertTrue(controller.saveBenNCDCareDoctorData("{\"benRegID\":11}", AUTHORIZATION) + .contains("doctor save failed")); + } + + @Test + @DisplayName("saveBenNCDCareDoctorData should surface a malformed request") + void saveBenNCDCareDoctorData_shouldSurfaceMalformedRequest() { + assertTrue(controller.saveBenNCDCareDoctorData("not-json", AUTHORIZATION).contains("\"statusCode\":5000")); + } + } + + @Nested + @DisplayName("read endpoints") + class ReadTests + + { + @Test + @DisplayName("getBenVisitDetailsFrmNurseNCDCare should return the visit details for a complete request") + void getBenVisitDetailsFrmNurseNCDCare_shouldReturnVisitDetails() throws Exception { + when(ncdCareServiceImpl.getBenVisitDetailsFrmNurseNCDCare(11L, 22L)).thenReturn("{\"visitCode\":22}"); + + assertTrue(controller.getBenVisitDetailsFrmNurseNCDCare(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseNCDCare should reject a request with a single field") + void getBenVisitDetailsFrmNurseNCDCare_shouldRejectSingleFieldRequest() throws Exception { + String result = controller.getBenVisitDetailsFrmNurseNCDCare("{\"benRegID\":11}"); + + assertTrue(result.contains("Invalid request")); + verify(ncdCareServiceImpl, never()).getBenVisitDetailsFrmNurseNCDCare(anyLong(), anyLong()); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseNCDCare should surface a service failure") + void getBenVisitDetailsFrmNurseNCDCare_shouldSurfaceServiceFailure() throws Exception { + when(ncdCareServiceImpl.getBenVisitDetailsFrmNurseNCDCare(11L, 22L)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVisitDetailsFrmNurseNCDCare(BEN_REQUEST) + .contains("Error while getting beneficiary visit data")); + } + + @Test + @DisplayName("getBenNCDCareHistoryDetails should return the history for a complete request") + void getBenNCDCareHistoryDetails_shouldReturnHistory() throws Exception { + when(ncdCareServiceImpl.getBenNCDCareHistoryDetails(11L, 22L)).thenReturn("{\"history\":[]}"); + + assertTrue(controller.getBenNCDCareHistoryDetails(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenNCDCareHistoryDetails should reject a request without the visit code") + void getBenNCDCareHistoryDetails_shouldRejectRequestWithoutVisitCode() { + assertTrue(controller.getBenNCDCareHistoryDetails("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenNCDCareHistoryDetails should surface a service failure") + void getBenNCDCareHistoryDetails_shouldSurfaceServiceFailure() throws Exception { + when(ncdCareServiceImpl.getBenNCDCareHistoryDetails(11L, 22L)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenNCDCareHistoryDetails(BEN_REQUEST) + .contains("Error while getting beneficiary history data")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurseNCDCare should return the vitals for a complete request") + void getBenVitalDetailsFrmNurseNCDCare_shouldReturnVitals() throws Exception { + when(ncdCareServiceImpl.getBeneficiaryVitalDetails(11L, 22L)).thenReturn("{\"height\":170}"); + + assertTrue(controller.getBenVitalDetailsFrmNurseNCDCare(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurseNCDCare should reject a request without the visit code") + void getBenVitalDetailsFrmNurseNCDCare_shouldRejectRequestWithoutVisitCode() { + assertTrue(controller.getBenVitalDetailsFrmNurseNCDCare("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurseNCDCare should surface a service failure") + void getBenVitalDetailsFrmNurseNCDCare_shouldSurfaceServiceFailure() throws Exception { + when(ncdCareServiceImpl.getBeneficiaryVitalDetails(11L, 22L)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVitalDetailsFrmNurseNCDCare(BEN_REQUEST) + .contains("Error while getting beneficiary vital data")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorNCDCare should return the case record for a complete request") + void getBenCaseRecordFromDoctorNCDCare_shouldReturnCaseRecord() throws Exception { + when(ncdCareServiceImpl.getBenCaseRecordFromDoctorNCDCare(11L, 22L)).thenReturn("{\"diagnosis\":\"x\"}"); + + assertTrue(controller.getBenCaseRecordFromDoctorNCDCare(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorNCDCare should reject an incomplete request") + void getBenCaseRecordFromDoctorNCDCare_shouldRejectIncompleteRequest() { + assertTrue(controller.getBenCaseRecordFromDoctorNCDCare("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorNCDCare should surface a service failure") + void getBenCaseRecordFromDoctorNCDCare_shouldSurfaceServiceFailure() throws Exception { + when(ncdCareServiceImpl.getBenCaseRecordFromDoctorNCDCare(11L, 22L)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCaseRecordFromDoctorNCDCare(BEN_REQUEST) + .contains("Error while getting beneficiary doctor data")); + } + } + + @Nested + @DisplayName("update endpoints") + class UpdateTests { + + @Test + @DisplayName("updateHistoryNurse should confirm the update when a row was changed") + void updateHistoryNurse_shouldConfirmUpdate() throws Exception { + when(ncdCareServiceImpl.updateBenHistoryDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateHistoryNurse("{\"benRegID\":11}").contains("Data updated successfully")); + } + + @Test + @DisplayName("updateHistoryNurse should report that nothing was modified") + void updateHistoryNurse_shouldReportNothingModified() throws Exception { + when(ncdCareServiceImpl.updateBenHistoryDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateHistoryNurse("{\"benRegID\":11}").contains("Unable to modify data")); + } + + @Test + @DisplayName("updateHistoryNurse should surface a service failure") + void updateHistoryNurse_shouldSurfaceServiceFailure() throws Exception { + when(ncdCareServiceImpl.updateBenHistoryDetails(any(JsonObject.class))) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateHistoryNurse("{\"benRegID\":11}").contains("Unable to modify data")); + } + + @Test + @DisplayName("updateVitalNurse should confirm the update when a row was changed") + void updateVitalNurse_shouldConfirmUpdate() throws Exception { + when(ncdCareServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateVitalNurse("{\"benRegID\":11}").contains("Data updated successfully")); + } + + @Test + @DisplayName("updateVitalNurse should report that nothing was modified") + void updateVitalNurse_shouldReportNothingModified() throws Exception { + when(ncdCareServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateVitalNurse("{\"benRegID\":11}").contains("Unable to modify data")); + } + + @Test + @DisplayName("updateVitalNurse should surface a service failure") + void updateVitalNurse_shouldSurfaceServiceFailure() throws Exception { + when(ncdCareServiceImpl.updateBenVitalDetails(any(JsonObject.class))) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateVitalNurse("{\"benRegID\":11}").contains("Unable to modify data")); + } + + @Test + @DisplayName("updateNCDCareDoctorData should confirm the update when a row was changed") + void updateNCDCareDoctorData_shouldConfirmUpdate() throws Exception { + when(ncdCareServiceImpl.updateNCDCareDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(1L); + + assertTrue(controller.updateNCDCareDoctorData("{\"benRegID\":11}", AUTHORIZATION) + .contains("Data updated successfully")); + } + + @Test + @DisplayName("updateNCDCareDoctorData should report that nothing was modified") + void updateNCDCareDoctorData_shouldReportNothingModified() throws Exception { + when(ncdCareServiceImpl.updateNCDCareDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.updateNCDCareDoctorData("{\"benRegID\":11}", AUTHORIZATION) + .contains("Unable to modify data")); + } + + @Test + @DisplayName("updateNCDCareDoctorData should report that nothing was modified when no id comes back") + void updateNCDCareDoctorData_shouldReportNothingModifiedForNullId() throws Exception { + when(ncdCareServiceImpl.updateNCDCareDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(null); + + assertTrue(controller.updateNCDCareDoctorData("{\"benRegID\":11}", AUTHORIZATION) + .contains("Unable to modify data")); + } + + @Test + @DisplayName("updateNCDCareDoctorData should surface a service failure") + void updateNCDCareDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(ncdCareServiceImpl.updateNCDCareDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("doctor update failed")); + + assertTrue(controller.updateNCDCareDoctorData("{\"benRegID\":11}", AUTHORIZATION) + .contains("doctor update failed")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/ncdscreening/NCDScreeningControllerTest.java b/src/test/java/com/iemr/tm/controller/ncdscreening/NCDScreeningControllerTest.java new file mode 100644 index 00000000..a81d9518 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/ncdscreening/NCDScreeningControllerTest.java @@ -0,0 +1,371 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.ncdscreening; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.iemr.tm.service.ncdscreening.NCDScreeningServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NCDScreeningController Test Suite") +class NCDScreeningControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_CODE = 22L; + private static final String REQUEST = "{\"beneficiaryRegID\":11,\"visitCode\":22}"; + private static final String VISIT_REQUEST = "{\"benRegID\":11,\"visitCode\":22}"; + + @Mock + private NCDScreeningServiceImpl ncdScreeningServiceImpl; + + private NCDScreeningController controller; + + @BeforeEach + @DisplayName("Wire the controller with mocked services") + void setUp() { + controller = new NCDScreeningController(); + controller.setNcdScreeningServiceImpl(ncdScreeningServiceImpl); + } + + @Nested + @DisplayName("saveBeneficiaryNCDScreeningDetails") + class SaveNurseTests { + + @Test + @DisplayName("saveBeneficiaryNCDScreeningDetails should return the payload produced by the service") + void saveBeneficiaryNCDScreeningDetails_shouldReturnServicePayload() throws Exception { + when(ncdScreeningServiceImpl.saveNCDScreeningNurseData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn("{\"visitCode\":\"22\"}"); + + String result = controller.saveBeneficiaryNCDScreeningDetails(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("visitCode")); + } + + @Test + @DisplayName("saveBeneficiaryNCDScreeningDetails should roll back the visit details when the service fails") + void saveBeneficiaryNCDScreeningDetails_shouldRollBackVisitDetailsOnFailure() throws Exception { + when(ncdScreeningServiceImpl.saveNCDScreeningNurseData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("save failed")); + + assertTrue(controller.saveBeneficiaryNCDScreeningDetails(REQUEST, AUTHORIZATION).contains("save failed")); + verify(ncdScreeningServiceImpl).deleteVisitDetails(any(JsonObject.class)); + } + + @Test + @DisplayName("saveBeneficiaryNCDScreeningDetails should return the untouched failure response for a null request") + void saveBeneficiaryNCDScreeningDetails_shouldReturnGenericFailureForNullRequest() throws Exception { + assertTrue(controller.saveBeneficiaryNCDScreeningDetails(null, AUTHORIZATION).contains("Failed with generic error")); + verify(ncdScreeningServiceImpl, never()).saveNCDScreeningNurseData(any(JsonObject.class), anyString()); + } + } + + @Nested + @DisplayName("saveBenNCDScreeningDoctorData") + class SaveDoctorTests { + + @Test + @DisplayName("saveBenNCDScreeningDoctorData should confirm the save when the service returns an id") + void saveBenNCDScreeningDoctorData_shouldConfirmSave() throws Exception { + when(ncdScreeningServiceImpl.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(7L); + + assertTrue(controller.saveBenNCDScreeningDoctorData(REQUEST, AUTHORIZATION).contains("Data saved successfully")); + } + + @Test + @DisplayName("saveBenNCDScreeningDoctorData should report an unsuccessful save") + void saveBenNCDScreeningDoctorData_shouldReportUnsuccessfulSave() throws Exception { + when(ncdScreeningServiceImpl.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.saveBenNCDScreeningDoctorData(REQUEST, AUTHORIZATION).contains("Unable to save data")); + } + + @Test + @DisplayName("saveBenNCDScreeningDoctorData should surface a service failure") + void saveBenNCDScreeningDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(ncdScreeningServiceImpl.saveDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("doctor save failed")); + + assertTrue(controller.saveBenNCDScreeningDoctorData(REQUEST, AUTHORIZATION).contains("doctor save failed")); + } + } + + @Nested + @DisplayName("getNCDScreenigDetails") + class ReadScreeningTests { + + @Test + @DisplayName("getNCDScreenigDetails should return the details for a complete request") + void getNCDScreenigDetails_shouldReturnDetails() throws Exception { + when(ncdScreeningServiceImpl.getNCDScreeningDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getNCDScreenigDetails(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getNCDScreenigDetails should reject an incomplete request") + void getNCDScreenigDetails_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getNCDScreenigDetails("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getNCDScreenigDetails should surface a service failure") + void getNCDScreenigDetails_shouldSurfaceServiceFailure() throws Exception { + when(ncdScreeningServiceImpl.getNCDScreeningDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getNCDScreenigDetails(VISIT_REQUEST).contains("Error while getting NCD Screening data")); + } + } + + @Nested + @DisplayName("getNcdScreeningVisitCount") + class ReadVisitCountTests { + + @Test + @DisplayName("getNcdScreeningVisitCount should return the details for the beneficiary") + void getNcdScreeningVisitCount_shouldReturnDetails() throws Exception { + when(ncdScreeningServiceImpl.getNcdScreeningVisitCnt(BEN_REG_ID)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getNcdScreeningVisitCount(BEN_REG_ID).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getNcdScreeningVisitCount should surface a service failure") + void getNcdScreeningVisitCount_shouldSurfaceServiceFailure() throws Exception { + when(ncdScreeningServiceImpl.getNcdScreeningVisitCnt(BEN_REG_ID)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getNcdScreeningVisitCount(BEN_REG_ID).contains("Error while getting NCD screening Visit Count")); + } + } + + @Nested + @DisplayName("getBenCaseRecordFromDoctorNCDCare") + class ReadCaseRecordTests { + + @Test + @DisplayName("getBenCaseRecordFromDoctorNCDCare should return the details for a complete request") + void getBenCaseRecordFromDoctorNCDCare_shouldReturnDetails() throws Exception { + when(ncdScreeningServiceImpl.getBenCaseRecordFromDoctorNCDScreening(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenCaseRecordFromDoctorNCDCare(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorNCDCare should reject an incomplete request") + void getBenCaseRecordFromDoctorNCDCare_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenCaseRecordFromDoctorNCDCare("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorNCDCare should surface a service failure") + void getBenCaseRecordFromDoctorNCDCare_shouldSurfaceServiceFailure() throws Exception { + when(ncdScreeningServiceImpl.getBenCaseRecordFromDoctorNCDScreening(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCaseRecordFromDoctorNCDCare(VISIT_REQUEST).contains("Error while getting beneficiary doctor data")); + } + } + + @Nested + @DisplayName("getBenVisitDetailsFrmNurseGOPD") + class ReadVisitTests { + + @Test + @DisplayName("getBenVisitDetailsFrmNurseGOPD should return the details for a complete request") + void getBenVisitDetailsFrmNurseGOPD_shouldReturnDetails() throws Exception { + when(ncdScreeningServiceImpl.getBenVisitDetailsFrmNurseNCDScreening(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenVisitDetailsFrmNurseGOPD(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseGOPD should reject an incomplete request") + void getBenVisitDetailsFrmNurseGOPD_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenVisitDetailsFrmNurseGOPD("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseGOPD should surface a service failure") + void getBenVisitDetailsFrmNurseGOPD_shouldSurfaceServiceFailure() throws Exception { + when(ncdScreeningServiceImpl.getBenVisitDetailsFrmNurseNCDScreening(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVisitDetailsFrmNurseGOPD(VISIT_REQUEST).contains("Error while getting beneficiary visit data")); + } + } + + @Nested + @DisplayName("getBenHistoryDetails") + class ReadHistoryTests { + + @Test + @DisplayName("getBenHistoryDetails should return the details for a complete request") + void getBenHistoryDetails_shouldReturnDetails() throws Exception { + when(ncdScreeningServiceImpl.getBenHistoryDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenHistoryDetails(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenHistoryDetails should reject an incomplete request") + void getBenHistoryDetails_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenHistoryDetails("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenHistoryDetails should surface a service failure") + void getBenHistoryDetails_shouldSurfaceServiceFailure() throws Exception { + when(ncdScreeningServiceImpl.getBenHistoryDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenHistoryDetails(VISIT_REQUEST).contains("Error while getting beneficiary history data")); + } + } + + @Nested + @DisplayName("getBenVitalDetailsFrmNurse") + class ReadVitalsTests { + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should return the details for a complete request") + void getBenVitalDetailsFrmNurse_shouldReturnDetails() throws Exception { + when(ncdScreeningServiceImpl.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenVitalDetailsFrmNurse(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should reject an incomplete request") + void getBenVitalDetailsFrmNurse_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenVitalDetailsFrmNurse("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should surface a service failure") + void getBenVitalDetailsFrmNurse_shouldSurfaceServiceFailure() throws Exception { + when(ncdScreeningServiceImpl.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVitalDetailsFrmNurse(VISIT_REQUEST).contains("Error while getting beneficiary vital data")); + } + } + + @Nested + @DisplayName("getBenIdrsDetailsFrmNurse") + class ReadIdrsTests { + + @Test + @DisplayName("getBenIdrsDetailsFrmNurse should return the details for a complete request") + void getBenIdrsDetailsFrmNurse_shouldReturnDetails() throws Exception { + when(ncdScreeningServiceImpl.getBenIdrsDetailsFrmNurse(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenIdrsDetailsFrmNurse(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenIdrsDetailsFrmNurse should reject an incomplete request") + void getBenIdrsDetailsFrmNurse_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenIdrsDetailsFrmNurse("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenIdrsDetailsFrmNurse should surface a service failure") + void getBenIdrsDetailsFrmNurse_shouldSurfaceServiceFailure() throws Exception { + when(ncdScreeningServiceImpl.getBenIdrsDetailsFrmNurse(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenIdrsDetailsFrmNurse(VISIT_REQUEST).contains("Error while getting beneficiary Idrs data")); + } + } + + @Nested + @DisplayName("updateBeneficiaryNCDScreeningDetails") + class UpdateScreeningTests { + + @Test + @DisplayName("updateBeneficiaryNCDScreeningDetails should confirm the update when a row was changed") + void updateBeneficiaryNCDScreeningDetails_shouldConfirmUpdate() throws Exception { + when(ncdScreeningServiceImpl.updateNurseNCDScreeningDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateBeneficiaryNCDScreeningDetails(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateBeneficiaryNCDScreeningDetails should report that nothing was modified") + void updateBeneficiaryNCDScreeningDetails_shouldReportNothingModified() throws Exception { + when(ncdScreeningServiceImpl.updateNurseNCDScreeningDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateBeneficiaryNCDScreeningDetails(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateBeneficiaryNCDScreeningDetails should surface a service failure") + void updateBeneficiaryNCDScreeningDetails_shouldSurfaceServiceFailure() throws Exception { + when(ncdScreeningServiceImpl.updateNurseNCDScreeningDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateBeneficiaryNCDScreeningDetails(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateVitalNurse") + class UpdateVitalsTests { + + @Test + @DisplayName("updateVitalNurse should confirm the update when a row was changed") + void updateVitalNurse_shouldConfirmUpdate() throws Exception { + when(ncdScreeningServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateVitalNurse should report that nothing was modified") + void updateVitalNurse_shouldReportNothingModified() throws Exception { + when(ncdScreeningServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateVitalNurse should surface a service failure") + void updateVitalNurse_shouldSurfaceServiceFailure() throws Exception { + when(ncdScreeningServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Unable to modify data")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/nurse/vitals/AnthropometryVitalsControllerTest.java b/src/test/java/com/iemr/tm/controller/nurse/vitals/AnthropometryVitalsControllerTest.java new file mode 100644 index 00000000..11f74289 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/nurse/vitals/AnthropometryVitalsControllerTest.java @@ -0,0 +1,84 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.nurse.vitals; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.iemr.tm.service.nurse.vitals.AnthropometryVitalsService; + +@ExtendWith(MockitoExtension.class) +@DisplayName("AnthropometryVitalsController Test Suite") +class AnthropometryVitalsControllerTest { + + @Mock + private AnthropometryVitalsService anthropometryVitalsService; + + @InjectMocks + private AnthropometryVitalsController controller; + + @Test + @DisplayName("getBenHeightDetailsFrmNurse should return the height details for a valid request") + void getBenHeightDetailsFrmNurse_shouldReturnHeightDetails() throws Exception { + when(anthropometryVitalsService.getBeneficiaryHeightDetails(11L)).thenReturn("{\"height\":170}"); + + String result = controller.getBenHeightDetailsFrmNurse("{\"benRegID\":11}"); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("height")); + } + + @Test + @DisplayName("getBenHeightDetailsFrmNurse should reject a request without benRegID") + void getBenHeightDetailsFrmNurse_shouldRejectRequestWithoutBenRegId() throws Exception { + String result = controller.getBenHeightDetailsFrmNurse("{\"visitCode\":22}"); + + assertTrue(result.contains("Invalid request")); + verify(anthropometryVitalsService, never()).getBeneficiaryHeightDetails(anyLong()); + } + + @Test + @DisplayName("getBenHeightDetailsFrmNurse should surface a service failure") + void getBenHeightDetailsFrmNurse_shouldSurfaceServiceFailure() throws Exception { + when(anthropometryVitalsService.getBeneficiaryHeightDetails(11L)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenHeightDetailsFrmNurse("{\"benRegID\":11}") + .contains("Error while getting beneficiary height data")); + } + + @Test + @DisplayName("getBenHeightDetailsFrmNurse should surface a malformed request") + void getBenHeightDetailsFrmNurse_shouldSurfaceMalformedRequest() { + assertTrue(controller.getBenHeightDetailsFrmNurse("not-json") + .contains("Error while getting beneficiary height data")); + } +} diff --git a/src/test/java/com/iemr/tm/controller/patientApp/master/PatientAppCommonMasterControllerTest.java b/src/test/java/com/iemr/tm/controller/patientApp/master/PatientAppCommonMasterControllerTest.java new file mode 100644 index 00000000..b28a0a77 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/patientApp/master/PatientAppCommonMasterControllerTest.java @@ -0,0 +1,334 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.patientApp.master; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.service.patientApp.master.CommonPatientAppMasterService; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("PatientAppCommonMasterController Test Suite") +class PatientAppCommonMasterControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final String REQUEST = "{\"beneficiaryRegID\":11,\"visitCode\":22}"; + + @Mock + private CommonPatientAppMasterService commonPatientAppMasterService; + + private PatientAppCommonMasterController controller; + + @BeforeEach + @DisplayName("Wire the controller with a mocked service") + void setUp() { + controller = new PatientAppCommonMasterController(); + controller.setCommonPatientAppMasterService(commonPatientAppMasterService); + } + + @Nested + @DisplayName("patientAppChiefComplaintsMasterData") + class PatientAppChiefComplaintsMasterDataTests { + + @Test + @DisplayName("patientAppChiefComplaintsMasterData should return the master the service assembled") + void patientAppChiefComplaintsMasterData_shouldReturnServiceMaster() { + when(commonPatientAppMasterService.getChiefComplaintsMaster(1, 9, "Female")).thenReturn("{\"master\":[]}"); + + String result = controller.patientAppChiefComplaintsMasterData(1, 9, "Female"); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("master")); + } + } + + @Nested + @DisplayName("patientAppCovidMasterData") + class PatientAppCovidMasterDataTests { + + @Test + @DisplayName("patientAppCovidMasterData should return the master the service assembled") + void patientAppCovidMasterData_shouldReturnServiceMaster() { + when(commonPatientAppMasterService.getCovidMaster(1, 9, "Female")).thenReturn("{\"master\":[]}"); + + String result = controller.patientAppCovidMasterData(1, 9, "Female"); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("master")); + } + } + + @Nested + @DisplayName("saveBenCovidDoctorDataPatientApp") + class SaveBenCovidDoctorDataPatientAppTests { + + @Test + @DisplayName("saveBenCovidDoctorDataPatientApp should return the payload the service produced") + void saveBenCovidDoctorDataPatientApp_shouldReturnServicePayload() throws Exception { + when(commonPatientAppMasterService.saveCovidScreeningData(REQUEST)).thenReturn("saved"); + + String result = controller.saveBenCovidDoctorDataPatientApp(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("saved")); + } + + @Test + @DisplayName("saveBenCovidDoctorDataPatientApp should report a request the service could not act on") + void saveBenCovidDoctorDataPatientApp_shouldReportRequestItCannotActOn() throws Exception { + when(commonPatientAppMasterService.saveCovidScreeningData(REQUEST)) + .thenThrow(new IllegalStateException("save failed")); + + assertTrue(controller.saveBenCovidDoctorDataPatientApp(REQUEST, AUTHORIZATION).contains("Unable to save data")); + } + } + + @Nested + @DisplayName("saveBenChiefComplaintsDataPatientApp") + class SaveBenChiefComplaintsDataPatientAppTests { + + @Test + @DisplayName("saveBenChiefComplaintsDataPatientApp should return the payload the service produced") + void saveBenChiefComplaintsDataPatientApp_shouldReturnServicePayload() throws Exception { + when(commonPatientAppMasterService.savechiefComplaintsData(REQUEST)).thenReturn("saved"); + + String result = controller.saveBenChiefComplaintsDataPatientApp(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("saved")); + } + + @Test + @DisplayName("saveBenChiefComplaintsDataPatientApp should report a request the service could not act on") + void saveBenChiefComplaintsDataPatientApp_shouldReportRequestItCannotActOn() throws Exception { + when(commonPatientAppMasterService.savechiefComplaintsData(REQUEST)) + .thenThrow(new IllegalStateException("save failed")); + + assertTrue(controller.saveBenChiefComplaintsDataPatientApp(REQUEST, AUTHORIZATION).contains("Unable to save data")); + } + } + + @Nested + @DisplayName("saveTCSlotDataPatientApp") + class SaveTCSlotDataPatientAppTests { + + @Test + @DisplayName("saveTCSlotDataPatientApp should confirm the booked slot") + void saveTCSlotDataPatientApp_shouldConfirmBookedSlot() throws Exception { + when(commonPatientAppMasterService.bookTCSlotData(REQUEST, AUTHORIZATION)).thenReturn(1); + + String result = controller.saveTCSlotDataPatientApp(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + } + + @Test + @DisplayName("saveTCSlotDataPatientApp should report a slot the service could not book") + void saveTCSlotDataPatientApp_shouldReportUnbookedSlot() throws Exception { + when(commonPatientAppMasterService.bookTCSlotData(REQUEST, AUTHORIZATION)).thenReturn(null); + + assertTrue(controller.saveTCSlotDataPatientApp(REQUEST, AUTHORIZATION).contains("error in slot booking")); + } + + @Test + @DisplayName("saveTCSlotDataPatientApp should report a request the service could not act on") + void saveTCSlotDataPatientApp_shouldReportRequestItCannotActOn() throws Exception { + when(commonPatientAppMasterService.bookTCSlotData(REQUEST, AUTHORIZATION)) + .thenThrow(new IllegalStateException("booking failed")); + + assertTrue(controller.saveTCSlotDataPatientApp(REQUEST, AUTHORIZATION).contains("error in slot booking")); + } + } + + @Nested + @DisplayName("getPatientEpisodeDataMobileApp") + class GetPatientEpisodeDataMobileAppTests { + + @Test + @DisplayName("getPatientEpisodeDataMobileApp should return the payload the service produced") + void getPatientEpisodeDataMobileApp_shouldReturnServicePayload() throws Exception { + when(commonPatientAppMasterService.getPatientEpisodeData(REQUEST)).thenReturn("{\"data\":[]}"); + + String result = controller.getPatientEpisodeDataMobileApp(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getPatientEpisodeDataMobileApp should report an answer the service could not produce") + void getPatientEpisodeDataMobileApp_shouldReportMissingAnswer() throws Exception { + when(commonPatientAppMasterService.getPatientEpisodeData(REQUEST)).thenReturn(null); + + assertTrue(controller.getPatientEpisodeDataMobileApp(REQUEST, AUTHORIZATION).contains("error in getting beneficiary episode data")); + } + + @Test + @DisplayName("getPatientEpisodeDataMobileApp should report a request the service could not act on") + void getPatientEpisodeDataMobileApp_shouldReportRequestItCannotActOn() throws Exception { + when(commonPatientAppMasterService.getPatientEpisodeData(REQUEST)) + .thenThrow(new IllegalStateException("lookup failed")); + + assertTrue(controller.getPatientEpisodeDataMobileApp(REQUEST, AUTHORIZATION).contains("error in getting beneficiary episode data")); + } + } + + @Nested + @DisplayName("getPatientBookedSlotDetails") + class GetPatientBookedSlotDetailsTests { + + @Test + @DisplayName("getPatientBookedSlotDetails should return the payload the service produced") + void getPatientBookedSlotDetails_shouldReturnServicePayload() throws Exception { + when(commonPatientAppMasterService.getPatientBookedSlots(REQUEST)).thenReturn("{\"data\":[]}"); + + String result = controller.getPatientBookedSlotDetails(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getPatientBookedSlotDetails should report an answer the service could not produce") + void getPatientBookedSlotDetails_shouldReportMissingAnswer() throws Exception { + when(commonPatientAppMasterService.getPatientBookedSlots(REQUEST)).thenReturn(null); + + assertTrue(controller.getPatientBookedSlotDetails(REQUEST, AUTHORIZATION).contains("error in getting beneficiary booked slot data")); + } + + @Test + @DisplayName("getPatientBookedSlotDetails should report a request the service could not act on") + void getPatientBookedSlotDetails_shouldReportRequestItCannotActOn() throws Exception { + when(commonPatientAppMasterService.getPatientBookedSlots(REQUEST)) + .thenThrow(new IllegalStateException("lookup failed")); + + assertTrue(controller.getPatientBookedSlotDetails(REQUEST, AUTHORIZATION).contains("error in getting beneficiary booked slot data")); + } + } + + @Nested + @DisplayName("saveSpecialistDiagnosisData") + class SaveSpecialistDiagnosisDataTests { + + @Test + @DisplayName("saveSpecialistDiagnosisData should return the payload the service produced") + void saveSpecialistDiagnosisData_shouldReturnServicePayload() throws Exception { + when(commonPatientAppMasterService.saveSpecialistDiagnosisData(REQUEST)).thenReturn(4L); + + String result = controller.saveSpecialistDiagnosisData(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + } + + @Test + @DisplayName("saveSpecialistDiagnosisData should report an answer the service could not produce") + void saveSpecialistDiagnosisData_shouldReportMissingAnswer() throws Exception { + when(commonPatientAppMasterService.saveSpecialistDiagnosisData(REQUEST)).thenReturn(null); + + assertTrue(controller.saveSpecialistDiagnosisData(REQUEST, AUTHORIZATION).contains("error in saving diagnosis data")); + } + + @Test + @DisplayName("saveSpecialistDiagnosisData should report a request the service could not act on") + void saveSpecialistDiagnosisData_shouldReportRequestItCannotActOn() throws Exception { + when(commonPatientAppMasterService.saveSpecialistDiagnosisData(REQUEST)) + .thenThrow(new IllegalStateException("lookup failed")); + + assertTrue(controller.saveSpecialistDiagnosisData(REQUEST, AUTHORIZATION) + .contains("error in saving specialist diagnosis data")); + } + } + + @Nested + @DisplayName("getSpecialistDiagnosisData") + class GetSpecialistDiagnosisDataTests { + + @Test + @DisplayName("getSpecialistDiagnosisData should return the payload the service produced") + void getSpecialistDiagnosisData_shouldReturnServicePayload() throws Exception { + when(commonPatientAppMasterService.getSpecialistDiagnosisData(REQUEST)).thenReturn("{\"data\":[]}"); + + String result = controller.getSpecialistDiagnosisData(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getSpecialistDiagnosisData should report an answer the service could not produce") + void getSpecialistDiagnosisData_shouldReportMissingAnswer() throws Exception { + when(commonPatientAppMasterService.getSpecialistDiagnosisData(REQUEST)).thenReturn(null); + + assertTrue(controller.getSpecialistDiagnosisData(REQUEST, AUTHORIZATION).contains("error in getting diagnosis data")); + } + + @Test + @DisplayName("getSpecialistDiagnosisData should report a request the service could not act on") + void getSpecialistDiagnosisData_shouldReportRequestItCannotActOn() throws Exception { + when(commonPatientAppMasterService.getSpecialistDiagnosisData(REQUEST)) + .thenThrow(new IllegalStateException("lookup failed")); + + assertTrue(controller.getSpecialistDiagnosisData(REQUEST, AUTHORIZATION) + .contains("error in getting specialist diagnosis data")); + } + } + + @Nested + @DisplayName("getPatientsLast_3_Episode") + class GetPatientsLast_3_EpisodeTests { + + @Test + @DisplayName("getPatientsLast_3_Episode should return the payload the service produced") + void getPatientsLast_3_Episode_shouldReturnServicePayload() throws Exception { + when(commonPatientAppMasterService.getPatientsLast_3_Episode(REQUEST)).thenReturn("{\"data\":[]}"); + + String result = controller.getPatientsLast_3_Episode(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getPatientsLast_3_Episode should report an answer the service could not produce") + void getPatientsLast_3_Episode_shouldReportMissingAnswer() throws Exception { + when(commonPatientAppMasterService.getPatientsLast_3_Episode(REQUEST)).thenReturn(null); + + assertTrue(controller.getPatientsLast_3_Episode(REQUEST, AUTHORIZATION).contains("error in getPatientsLast_3_Episode data")); + } + + @Test + @DisplayName("getPatientsLast_3_Episode should report a request the service could not act on") + void getPatientsLast_3_Episode_shouldReportRequestItCannotActOn() throws Exception { + when(commonPatientAppMasterService.getPatientsLast_3_Episode(REQUEST)) + .thenThrow(new IllegalStateException("lookup failed")); + + assertTrue(controller.getPatientsLast_3_Episode(REQUEST, AUTHORIZATION).contains("error in getPatientsLast_3_Episode data")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/pnc/PostnatalCareControllerTest.java b/src/test/java/com/iemr/tm/controller/pnc/PostnatalCareControllerTest.java new file mode 100644 index 00000000..5d73be70 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/pnc/PostnatalCareControllerTest.java @@ -0,0 +1,438 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.pnc; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.iemr.tm.service.pnc.PNCServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("PostnatalCareController Test Suite") +class PostnatalCareControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_CODE = 22L; + private static final String REQUEST = "{\"beneficiaryRegID\":11,\"visitCode\":22}"; + private static final String VISIT_REQUEST = "{\"benRegID\":11,\"visitCode\":22}"; + + @Mock + private PNCServiceImpl pncServiceImpl; + + private PostnatalCareController controller; + + @BeforeEach + @DisplayName("Wire the controller with a mocked service") + void setUp() { + controller = new PostnatalCareController(); + controller.setPncServiceImpl(pncServiceImpl); + } + + @Nested + @DisplayName("saveBenPNCNurseData") + class SavenurseTests { + + @Test + @DisplayName("saveBenPNCNurseData should return the payload produced by the service") + void saveBenPNCNurseData_shouldReturnServicePayload() throws Exception { + when(pncServiceImpl.savePNCNurseData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn("{\"visitCode\":\"22\"}"); + + String result = controller.saveBenPNCNurseData(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("visitCode")); + } + + @Test + @DisplayName("saveBenPNCNurseData should roll back the visit details when the service fails") + void saveBenPNCNurseData_shouldRollBackVisitDetailsOnFailure() throws Exception { + when(pncServiceImpl.savePNCNurseData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("save failed")); + + assertTrue(controller.saveBenPNCNurseData(REQUEST, AUTHORIZATION).contains("save failed")); + verify(pncServiceImpl).deleteVisitDetails(any(JsonObject.class)); + } + + @Test + @DisplayName("saveBenPNCNurseData should return the untouched failure response for a null request") + void saveBenPNCNurseData_shouldReturnGenericFailureForNullRequest() throws Exception { + assertTrue(controller.saveBenPNCNurseData(null, AUTHORIZATION).contains("Failed with generic error")); + verify(pncServiceImpl, never()).savePNCNurseData(any(JsonObject.class), anyString()); + } + } + + @Nested + @DisplayName("saveBenPNCDoctorData") + class SavedoctorTests { + + @Test + @DisplayName("saveBenPNCDoctorData should confirm the save when the service returns an id") + void saveBenPNCDoctorData_shouldConfirmSave() throws Exception { + when(pncServiceImpl.savePNCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(7L); + + assertTrue(controller.saveBenPNCDoctorData(REQUEST, AUTHORIZATION).contains("Data saved successfully")); + } + + @Test + @DisplayName("saveBenPNCDoctorData should report an unsuccessful save") + void saveBenPNCDoctorData_shouldReportUnsuccessfulSave() throws Exception { + when(pncServiceImpl.savePNCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.saveBenPNCDoctorData(REQUEST, AUTHORIZATION).contains("Unable to save data")); + } + + @Test + @DisplayName("saveBenPNCDoctorData should surface a service failure") + void saveBenPNCDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.savePNCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("doctor save failed")); + + assertTrue(controller.saveBenPNCDoctorData(REQUEST, AUTHORIZATION).contains("doctor save failed")); + } + } + + @Nested + @DisplayName("getBenVisitDetailsFrmNursePNC") + class ReadvisitTests { + + @Test + @DisplayName("getBenVisitDetailsFrmNursePNC should return the details for a complete request") + void getBenVisitDetailsFrmNursePNC_shouldReturnDetails() throws Exception { + when(pncServiceImpl.getBenVisitDetailsFrmNursePNC(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenVisitDetailsFrmNursePNC(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNursePNC should reject an incomplete request") + void getBenVisitDetailsFrmNursePNC_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenVisitDetailsFrmNursePNC("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNursePNC should surface a service failure") + void getBenVisitDetailsFrmNursePNC_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.getBenVisitDetailsFrmNursePNC(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVisitDetailsFrmNursePNC(VISIT_REQUEST).contains("Error while getting beneficiary visit data")); + } + } + + @Nested + @DisplayName("getBenPNCDetailsFrmNursePNC") + class ReadpncTests { + + @Test + @DisplayName("getBenPNCDetailsFrmNursePNC should return the details for a complete request") + void getBenPNCDetailsFrmNursePNC_shouldReturnDetails() throws Exception { + when(pncServiceImpl.getBenPNCDetailsFrmNursePNC(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenPNCDetailsFrmNursePNC(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenPNCDetailsFrmNursePNC should reject an incomplete request") + void getBenPNCDetailsFrmNursePNC_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenPNCDetailsFrmNursePNC("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenPNCDetailsFrmNursePNC should surface a service failure") + void getBenPNCDetailsFrmNursePNC_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.getBenPNCDetailsFrmNursePNC(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenPNCDetailsFrmNursePNC(VISIT_REQUEST).contains("Error while getting beneficiary PNC Care data")); + } + } + + @Nested + @DisplayName("getBenHistoryDetails") + class ReadhistoryTests { + + @Test + @DisplayName("getBenHistoryDetails should return the details for a complete request") + void getBenHistoryDetails_shouldReturnDetails() throws Exception { + when(pncServiceImpl.getBenHistoryDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenHistoryDetails(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenHistoryDetails should reject an incomplete request") + void getBenHistoryDetails_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenHistoryDetails("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenHistoryDetails should surface a service failure") + void getBenHistoryDetails_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.getBenHistoryDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenHistoryDetails(VISIT_REQUEST).contains("Error while getting beneficiary history data")); + } + } + + @Nested + @DisplayName("getBenVitalDetailsFrmNurse") + class ReadvitalsTests { + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should return the details for a complete request") + void getBenVitalDetailsFrmNurse_shouldReturnDetails() throws Exception { + when(pncServiceImpl.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenVitalDetailsFrmNurse(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should reject an incomplete request") + void getBenVitalDetailsFrmNurse_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenVitalDetailsFrmNurse("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should surface a service failure") + void getBenVitalDetailsFrmNurse_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVitalDetailsFrmNurse(VISIT_REQUEST).contains("Error while getting beneficiary vital data")); + } + } + + @Nested + @DisplayName("getBenExaminationDetailsPNC") + class ReadexaminationTests { + + @Test + @DisplayName("getBenExaminationDetailsPNC should return the details for a complete request") + void getBenExaminationDetailsPNC_shouldReturnDetails() throws Exception { + when(pncServiceImpl.getPNCExaminationDetailsData(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenExaminationDetailsPNC(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenExaminationDetailsPNC should reject an incomplete request") + void getBenExaminationDetailsPNC_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenExaminationDetailsPNC("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenExaminationDetailsPNC should surface a service failure") + void getBenExaminationDetailsPNC_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.getPNCExaminationDetailsData(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenExaminationDetailsPNC(VISIT_REQUEST).contains("Error while getting beneficiary examination data")); + } + } + + @Nested + @DisplayName("getBenCaseRecordFromDoctorPNC") + class ReadcaserecordTests { + + @Test + @DisplayName("getBenCaseRecordFromDoctorPNC should return the details for a complete request") + void getBenCaseRecordFromDoctorPNC_shouldReturnDetails() throws Exception { + when(pncServiceImpl.getBenCaseRecordFromDoctorPNC(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenCaseRecordFromDoctorPNC(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorPNC should reject an incomplete request") + void getBenCaseRecordFromDoctorPNC_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenCaseRecordFromDoctorPNC("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorPNC should surface a service failure") + void getBenCaseRecordFromDoctorPNC_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.getBenCaseRecordFromDoctorPNC(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCaseRecordFromDoctorPNC(VISIT_REQUEST).contains("Error while getting beneficiary doctor data")); + } + } + + @Nested + @DisplayName("updatePNCCareNurse") + class UpdatepncTests { + + @Test + @DisplayName("updatePNCCareNurse should confirm the update when a row was changed") + void updatePNCCareNurse_shouldConfirmUpdate() throws Exception { + when(pncServiceImpl.updateBenPNCDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updatePNCCareNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updatePNCCareNurse should report that nothing was modified") + void updatePNCCareNurse_shouldReportNothingModified() throws Exception { + when(pncServiceImpl.updateBenPNCDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updatePNCCareNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updatePNCCareNurse should surface a service failure") + void updatePNCCareNurse_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.updateBenPNCDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updatePNCCareNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateHistoryNurse") + class UpdatehistoryTests { + + @Test + @DisplayName("updateHistoryNurse should confirm the update when a row was changed") + void updateHistoryNurse_shouldConfirmUpdate() throws Exception { + when(pncServiceImpl.updateBenHistoryDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateHistoryNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateHistoryNurse should report that nothing was modified") + void updateHistoryNurse_shouldReportNothingModified() throws Exception { + when(pncServiceImpl.updateBenHistoryDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateHistoryNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateHistoryNurse should surface a service failure") + void updateHistoryNurse_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.updateBenHistoryDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateHistoryNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateVitalNurse") + class UpdatevitalsTests { + + @Test + @DisplayName("updateVitalNurse should confirm the update when a row was changed") + void updateVitalNurse_shouldConfirmUpdate() throws Exception { + when(pncServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateVitalNurse should report that nothing was modified") + void updateVitalNurse_shouldReportNothingModified() throws Exception { + when(pncServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateVitalNurse should surface a service failure") + void updateVitalNurse_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.updateBenVitalDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateVitalNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updateGeneralOPDExaminationNurse") + class UpdateexaminationTests { + + @Test + @DisplayName("updateGeneralOPDExaminationNurse should confirm the update when a row was changed") + void updateGeneralOPDExaminationNurse_shouldConfirmUpdate() throws Exception { + when(pncServiceImpl.updateBenExaminationDetails(any(JsonObject.class))).thenReturn(1); + + assertTrue(controller.updateGeneralOPDExaminationNurse(REQUEST).contains("Data updated successfully")); + } + + @Test + @DisplayName("updateGeneralOPDExaminationNurse should report that nothing was modified") + void updateGeneralOPDExaminationNurse_shouldReportNothingModified() throws Exception { + when(pncServiceImpl.updateBenExaminationDetails(any(JsonObject.class))).thenReturn(0); + + assertTrue(controller.updateGeneralOPDExaminationNurse(REQUEST).contains("Unable to modify data")); + } + + @Test + @DisplayName("updateGeneralOPDExaminationNurse should surface a service failure") + void updateGeneralOPDExaminationNurse_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.updateBenExaminationDetails(any(JsonObject.class))).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateGeneralOPDExaminationNurse(REQUEST).contains("Unable to modify data")); + } + } + + @Nested + @DisplayName("updatePNCDoctorData") + class UpdatedoctorTests { + + @Test + @DisplayName("updatePNCDoctorData should confirm the update when a row was changed") + void updatePNCDoctorData_shouldConfirmUpdate() throws Exception { + when(pncServiceImpl.updatePNCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(1L); + + assertTrue(controller.updatePNCDoctorData(REQUEST, AUTHORIZATION).contains("Data updated successfully")); + } + + @Test + @DisplayName("updatePNCDoctorData should report that nothing was modified") + void updatePNCDoctorData_shouldReportNothingModified() throws Exception { + when(pncServiceImpl.updatePNCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn(0L); + + assertTrue(controller.updatePNCDoctorData(REQUEST, AUTHORIZATION).contains("Unable to modify data")); + } + + @Test + @DisplayName("updatePNCDoctorData should surface a service failure") + void updatePNCDoctorData_shouldSurfaceServiceFailure() throws Exception { + when(pncServiceImpl.updatePNCDoctorData(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("update failed")); + + assertTrue(controller.updatePNCDoctorData(REQUEST, AUTHORIZATION).contains("update failed")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/quickBlox/QuickbloxControllerTest.java b/src/test/java/com/iemr/tm/controller/quickBlox/QuickbloxControllerTest.java new file mode 100644 index 00000000..3e1a9a1d --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/quickBlox/QuickbloxControllerTest.java @@ -0,0 +1,67 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.quickBlox; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.iemr.tm.service.quickBlox.QuickbloxService; + +@ExtendWith(MockitoExtension.class) +@DisplayName("QuickbloxController Test Suite") +class QuickbloxControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final String REQUEST = "{\"userName\":\"nurse\"}"; + + @Mock + private QuickbloxService quickbloxService; + + @InjectMocks + private QuickbloxController controller; + + @Test + @DisplayName("getquickbloxIds should return the ids produced by the service") + void getquickbloxIds_shouldReturnServiceIds() throws Exception { + when(quickbloxService.getQuickbloxIds(REQUEST)).thenReturn("{\"quickbloxId\":\"99\"}"); + + String result = controller.getquickbloxIds(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("quickbloxId")); + } + + @Test + @DisplayName("getquickbloxIds should surface a service failure") + void getquickbloxIds_shouldSurfaceServiceFailure() throws Exception { + when(quickbloxService.getQuickbloxIds(REQUEST)).thenThrow(new IllegalStateException("quickblox down")); + + assertTrue(controller.getquickbloxIds(REQUEST, AUTHORIZATION).contains("Error while getting quickblox Ids")); + } +} diff --git a/src/test/java/com/iemr/tm/controller/quickconsult/QuickConsultControllerTest.java b/src/test/java/com/iemr/tm/controller/quickconsult/QuickConsultControllerTest.java new file mode 100644 index 00000000..8cf9bdc0 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/quickconsult/QuickConsultControllerTest.java @@ -0,0 +1,181 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.quickconsult; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.iemr.tm.service.quickConsultation.QuickConsultationServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("QuickConsultController Test Suite") +class QuickConsultControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_CODE = 22L; + private static final String REQUEST = "{\"beneficiaryRegID\":11,\"visitCode\":22}"; + private static final String VISIT_REQUEST = "{\"benRegID\":11,\"visitCode\":22}"; + + @Mock + private QuickConsultationServiceImpl quickConsultationServiceImpl; + + private QuickConsultController controller; + + @BeforeEach + @DisplayName("Wire the controller with mocked services") + void setUp() { + controller = new QuickConsultController(); + controller.setQuickConsultationServiceImpl(quickConsultationServiceImpl); + } + + @Nested + @DisplayName("saveBenQuickConsultDataNurse") + class SaveNurseTests { + + @Test + @DisplayName("saveBenQuickConsultDataNurse should return the payload produced by the service") + void saveBenQuickConsultDataNurse_shouldReturnServicePayload() throws Exception { + when(quickConsultationServiceImpl.quickConsultNurseDataInsert(any(JsonObject.class), eq(AUTHORIZATION))).thenReturn("{\"visitCode\":\"22\"}"); + + String result = controller.saveBenQuickConsultDataNurse(REQUEST, AUTHORIZATION); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("visitCode")); + } + + @Test + @DisplayName("saveBenQuickConsultDataNurse should roll back the visit details when the service fails") + void saveBenQuickConsultDataNurse_shouldRollBackVisitDetailsOnFailure() throws Exception { + when(quickConsultationServiceImpl.quickConsultNurseDataInsert(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("save failed")); + + assertTrue(controller.saveBenQuickConsultDataNurse(REQUEST, AUTHORIZATION).contains("save failed")); + verify(quickConsultationServiceImpl).deleteVisitDetails(any(JsonObject.class)); + } + + @Test + @DisplayName("saveBenQuickConsultDataNurse should return the untouched failure response for a null request") + void saveBenQuickConsultDataNurse_shouldReturnGenericFailureForNullRequest() throws Exception { + assertTrue(controller.saveBenQuickConsultDataNurse(null, AUTHORIZATION).contains("Failed with generic error")); + verify(quickConsultationServiceImpl, never()).quickConsultNurseDataInsert(any(JsonObject.class), anyString()); + } + } + + @Nested + @DisplayName("getBenDataFrmNurseScrnToDocScrnVisitDetails") + class ReadVisitTests { + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnVisitDetails should return the details for a complete request") + void getBenDataFrmNurseScrnToDocScrnVisitDetails_shouldReturnDetails() throws Exception { + when(quickConsultationServiceImpl.getBenDataFrmNurseToDocVisitDetailsScreen(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnVisitDetails(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnVisitDetails should reject an incomplete request") + void getBenDataFrmNurseScrnToDocScrnVisitDetails_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnVisitDetails("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenDataFrmNurseScrnToDocScrnVisitDetails should surface a service failure") + void getBenDataFrmNurseScrnToDocScrnVisitDetails_shouldSurfaceServiceFailure() throws Exception { + when(quickConsultationServiceImpl.getBenDataFrmNurseToDocVisitDetailsScreen(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenDataFrmNurseScrnToDocScrnVisitDetails(VISIT_REQUEST).contains("Error while getting visit data")); + } + } + + @Nested + @DisplayName("getBenVitalDetailsFrmNurse") + class ReadVitalsTests { + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should return the details for a complete request") + void getBenVitalDetailsFrmNurse_shouldReturnDetails() throws Exception { + when(quickConsultationServiceImpl.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenVitalDetailsFrmNurse(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should reject an incomplete request") + void getBenVitalDetailsFrmNurse_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenVitalDetailsFrmNurse("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenVitalDetailsFrmNurse should surface a service failure") + void getBenVitalDetailsFrmNurse_shouldSurfaceServiceFailure() throws Exception { + when(quickConsultationServiceImpl.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenVitalDetailsFrmNurse(VISIT_REQUEST).contains("Error while getting vital data")); + } + } + + @Nested + @DisplayName("getBenCaseRecordFromDoctorQuickConsult") + class ReadCaseRecordTests { + + @Test + @DisplayName("getBenCaseRecordFromDoctorQuickConsult should return the details for a complete request") + void getBenCaseRecordFromDoctorQuickConsult_shouldReturnDetails() throws Exception { + when(quickConsultationServiceImpl.getBenCaseRecordFromDoctorQuickConsult(BEN_REG_ID, VISIT_CODE)).thenReturn("{\"result\":1}"); + + assertTrue(controller.getBenCaseRecordFromDoctorQuickConsult(VISIT_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorQuickConsult should reject an incomplete request") + void getBenCaseRecordFromDoctorQuickConsult_shouldRejectIncompleteRequest() throws Exception { + assertTrue(controller.getBenCaseRecordFromDoctorQuickConsult("{\"benRegID\":11}").contains("Invalid request")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorQuickConsult should surface a service failure") + void getBenCaseRecordFromDoctorQuickConsult_shouldSurfaceServiceFailure() throws Exception { + when(quickConsultationServiceImpl.getBenCaseRecordFromDoctorQuickConsult(BEN_REG_ID, VISIT_CODE)).thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getBenCaseRecordFromDoctorQuickConsult(VISIT_REQUEST).contains("Error while getting doctor data")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/registrar/main/RegistrarControllerTest.java b/src/test/java/com/iemr/tm/controller/registrar/main/RegistrarControllerTest.java new file mode 100644 index 00000000..97e1f0c4 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/registrar/main/RegistrarControllerTest.java @@ -0,0 +1,399 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.registrar.main; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.data.registrar.BeneficiaryData; +import com.iemr.tm.service.common.master.RegistrarServiceMasterDataImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.nurse.NurseServiceImpl; +import com.iemr.tm.service.registrar.RegistrarServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("RegistrarController Test Suite") +class RegistrarControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final String BEN_REQUEST = "{\"beneficiaryRegID\":11}"; + + @Mock + private RegistrarServiceImpl registrarServiceImpl; + @Mock + private RegistrarServiceMasterDataImpl registrarServiceMasterDataImpl; + @Mock + private NurseServiceImpl nurseServiceImpl; + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + + private RegistrarController controller; + + @BeforeEach + @DisplayName("Wire the controller with mocked registrar services") + void setUp() { + controller = new RegistrarController(); + controller.setRegistrarServiceImpl(registrarServiceImpl); + controller.setRegistrarServiceMasterDataImpl(registrarServiceMasterDataImpl); + controller.setNurseServiceImpl(nurseServiceImpl); + org.springframework.test.util.ReflectionTestUtils.setField(controller, "commonNurseServiceImpl", + commonNurseServiceImpl); + } + + @Nested + @DisplayName("search endpoints") + class SearchTests { + + @Test + @DisplayName("getRegistrarWorkList should return the worklist for the service point") + void getRegistrarWorkList_shouldReturnWorklist() throws Exception { + when(registrarServiceImpl.getRegWorkList(9)).thenReturn("[]"); + + assertTrue(controller.getRegistrarWorkList("{\"spID\":9}").contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getRegistrarWorkList should surface a malformed request") + void getRegistrarWorkList_shouldSurfaceMalformedRequest() throws Exception { + assertTrue(controller.getRegistrarWorkList("{}").contains("\"statusCode\"")); + } + + @Test + @DisplayName("quickSearchBeneficiary should return the matched beneficiaries") + void quickSearchBeneficiary_shouldReturnMatches() throws Exception { + when(registrarServiceImpl.getQuickSearchBenData("BEN1")).thenReturn("[]"); + + assertTrue(controller.quickSearchBeneficiary("{\"benID\":\"BEN1\"}").contains("\"statusCode\":200")); + } + + @Test + @DisplayName("advanceSearch should return the matched beneficiaries") + void advanceSearch_shouldReturnMatches() throws Exception { + when(registrarServiceImpl.getAdvanceSearchBenData(any())).thenReturn("[]"); + + assertTrue(controller.advanceSearch("{\"firstName\":\"Asha\"}").contains("\"statusCode\":200")); + } + + @Test + @DisplayName("quickSearchNew should return the matched beneficiaries") + void quickSearchNew_shouldReturnMatches() throws Exception { + when(registrarServiceImpl.beneficiaryQuickSearch(BEN_REQUEST, AUTHORIZATION)).thenReturn("[{\"id\":1}]"); + + assertEquals("[{\"id\":1}]", controller.quickSearchNew(BEN_REQUEST, AUTHORIZATION)); + } + + @Test + @DisplayName("quickSearchNew should reject a null request") + void quickSearchNew_shouldRejectNullRequest() throws Exception { + assertTrue(controller.quickSearchNew(null, AUTHORIZATION).contains("Invalid request")); + } + + @Test + @DisplayName("quickSearchNew should surface a service failure") + void quickSearchNew_shouldSurfaceServiceFailure() throws Exception { + when(registrarServiceImpl.beneficiaryQuickSearch(BEN_REQUEST, AUTHORIZATION)) + .thenThrow(new IllegalStateException("identity down")); + + assertTrue(controller.quickSearchNew(BEN_REQUEST, AUTHORIZATION) + .contains("Error while searching beneficiary")); + } + + @Test + @DisplayName("advanceSearchNew should return the matched beneficiaries") + void advanceSearchNew_shouldReturnMatches() throws Exception { + when(registrarServiceImpl.beneficiaryAdvanceSearch(BEN_REQUEST, AUTHORIZATION)).thenReturn("[{\"id\":1}]"); + + assertEquals("[{\"id\":1}]", controller.advanceSearchNew(BEN_REQUEST, AUTHORIZATION)); + } + + @Test + @DisplayName("advanceSearchNew should reject a null request") + void advanceSearchNew_shouldRejectNullRequest() throws Exception { + assertTrue(controller.advanceSearchNew(null, AUTHORIZATION).contains("Invalid request")); + } + } + + @Nested + @DisplayName("beneficiary detail reads") + class DetailReadTests { + + @Test + @DisplayName("getBenDetailsByRegID should return the stored beneficiary") + void getBenDetailsByRegID_shouldReturnBeneficiary() throws Exception { + when(registrarServiceMasterDataImpl.getBenDetailsByRegID(BEN_REG_ID)).thenReturn("{}"); + + assertTrue(controller.getBenDetailsByRegID(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenDetailsByRegID should reject a non positive registration id") + void getBenDetailsByRegID_shouldRejectNonPositiveId() throws Exception { + assertTrue(controller.getBenDetailsByRegID("{\"beneficiaryRegID\":0}") + .contains("Please pass beneficiaryRegID")); + } + + @Test + @DisplayName("getBenDetailsByRegID should reject a request without the registration id") + void getBenDetailsByRegID_shouldRejectRequestWithoutId() throws Exception { + assertTrue(controller.getBenDetailsByRegID("{}").contains("Bad Request")); + } + + @Test + @DisplayName("getBeneficiaryDetails should return the stored beneficiary") + void getBeneficiaryDetails_shouldReturnBeneficiary() throws Exception { + when(registrarServiceImpl.getBeneficiaryDetails(BEN_REG_ID)).thenReturn("{}"); + + assertTrue(controller.getBeneficiaryDetails(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBeneficiaryDetails should report no data when the beneficiary is unknown") + void getBeneficiaryDetails_shouldReportNoDataForUnknownBeneficiary() throws Exception { + when(registrarServiceImpl.getBeneficiaryDetails(BEN_REG_ID)).thenReturn(null); + + assertTrue(controller.getBeneficiaryDetails(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBeneficiaryDetails should reject a request without the registration id") + void getBeneficiaryDetails_shouldRejectRequestWithoutId() throws Exception { + assertTrue(controller.getBeneficiaryDetails("{}").contains("\"statusCode\"")); + } + + @Test + @DisplayName("getBeneficiaryImage should return the stored image") + void getBeneficiaryImage_shouldReturnImage() throws Exception { + when(registrarServiceImpl.getBenImage(BEN_REG_ID)).thenReturn("base64-image"); + + assertTrue(controller.getBeneficiaryImage(BEN_REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBeneficiaryImage should reject a request without the registration id") + void getBeneficiaryImage_shouldRejectRequestWithoutId() throws Exception { + assertTrue(controller.getBeneficiaryImage("{}").contains("Bad Request")); + } + + @Test + @DisplayName("getBenDetailsForLeftSidePanelByRegID should return the panel details") + void getLeftSidePanelDetails_shouldReturnPanelDetails() throws Exception { + String request = "{\"beneficiaryRegID\":11,\"benFlowID\":5}"; + when(registrarServiceMasterDataImpl.getBenDetailsForLeftSideByRegIDNew(BEN_REG_ID, 5L, AUTHORIZATION, + request)).thenReturn("{}"); + + assertTrue(controller.getBenDetailsForLeftSidePanelByRegID(request, AUTHORIZATION) + .contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getBenDetailsForLeftSidePanelByRegID should reject a request without the registration id") + void getLeftSidePanelDetails_shouldRejectRequestWithoutId() throws Exception { + assertTrue(controller.getBenDetailsForLeftSidePanelByRegID("{}", AUTHORIZATION) + .contains("Invalid request")); + } + + @Test + @DisplayName("getBenImage should return the identity image") + void getBenImage_shouldReturnIdentityImage() throws Exception { + when(registrarServiceMasterDataImpl.getBenImageFromIdentityAPI(AUTHORIZATION, BEN_REQUEST)) + .thenReturn("base64-image"); + + assertEquals("base64-image", controller.getBenImage(BEN_REQUEST, AUTHORIZATION)); + } + + @Test + @DisplayName("getBenImage should surface a service failure") + void getBenImage_shouldSurfaceServiceFailure() throws Exception { + when(registrarServiceMasterDataImpl.getBenImageFromIdentityAPI(AUTHORIZATION, BEN_REQUEST)) + .thenThrow(new IllegalStateException("identity down")); + + assertTrue(controller.getBenImage(BEN_REQUEST, AUTHORIZATION) + .contains("Error while getting beneficiary image")); + } + + @Test + @DisplayName("masterDataForRegistration should return the registration master data") + void masterDataForRegistration_shouldReturnMasterData() throws Exception { + when(registrarServiceMasterDataImpl.getRegMasterData()).thenReturn("{}"); + + assertTrue(controller.masterDataForRegistration("{\"spID\":9}").contains("\"statusCode\":200")); + } + + @Test + @DisplayName("masterDataForRegistration should reject an invalid service point") + void masterDataForRegistration_shouldRejectInvalidServicePoint() throws Exception { + assertTrue(controller.masterDataForRegistration("{\"spID\":0}").contains("Invalid service point")); + } + + @Test + @DisplayName("masterDataForRegistration should reject a request without the service point") + void masterDataForRegistration_shouldRejectRequestWithoutServicePoint() throws Exception { + assertTrue(controller.masterDataForRegistration("{}").contains("Invalid request")); + } + } + + @Nested + @DisplayName("registration and update") + class RegistrationTests { + + private static final String BEN_PAYLOAD = "{\"benD\":{\"firstName\":\"Asha\",\"beneficiaryRegID\":11," + + "\"createdBy\":\"registrar1\"}}"; + + @Test + @DisplayName("createBeneficiary should register the beneficiary and submit them to the nurse worklist") + void createBeneficiary_shouldRegisterAndSubmitToNurse() throws Exception { + BeneficiaryData created = new BeneficiaryData(); + created.setBeneficiaryRegID(BEN_REG_ID); + created.setBeneficiaryID("BEN1"); + when(registrarServiceImpl.createBeneficiary(any())).thenReturn(created); + when(registrarServiceImpl.createBeneficiaryDemographic(any(), eq(BEN_REG_ID))).thenReturn(1L); + when(registrarServiceImpl.createBeneficiaryPhoneMapping(any(), eq(BEN_REG_ID))).thenReturn(1L); + when(registrarServiceImpl.createBenGovIdMapping(any(), eq(BEN_REG_ID))).thenReturn(1); + when(registrarServiceImpl.createBeneficiaryDemographicAdditional(any(), eq(BEN_REG_ID))).thenReturn(1L); + when(registrarServiceImpl.createBeneficiaryImage(any(), eq(BEN_REG_ID))).thenReturn(1L); + when(commonNurseServiceImpl.updateBeneficiaryStatus('R', BEN_REG_ID)).thenReturn(1); + + assertTrue(controller.createBeneficiary(BEN_PAYLOAD, AUTHORIZATION).contains("BEN1")); + } + + @Test + @DisplayName("createBeneficiary should report a failure when the registration did not complete") + void createBeneficiary_shouldReportFailureWhenRegistrationIncomplete() throws Exception { + BeneficiaryData created = new BeneficiaryData(); + created.setBeneficiaryRegID(BEN_REG_ID); + when(registrarServiceImpl.createBeneficiary(any())).thenReturn(created); + + assertTrue(controller.createBeneficiary(BEN_PAYLOAD, AUTHORIZATION).contains("Something Went-Wrong")); + } + + @Test + @DisplayName("createBeneficiary should reject a payload without beneficiary details") + void createBeneficiary_shouldRejectPayloadWithoutDetails() throws Exception { + assertTrue(controller.createBeneficiary("{}", AUTHORIZATION).contains("\"statusCode\"")); + } + + @Test + @DisplayName("registrarBeneficaryRegistrationNew should return the registration payload") + void registerBeneficiaryNew_shouldReturnRegistrationPayload() throws Exception { + when(registrarServiceImpl.registerBeneficiary(BEN_REQUEST, AUTHORIZATION)).thenReturn("{\"benID\":1}"); + + assertEquals("{\"benID\":1}", controller.registrarBeneficaryRegistrationNew(BEN_REQUEST, AUTHORIZATION)); + } + + @Test + @DisplayName("registrarBeneficaryRegistrationNew should surface a registration failure") + void registerBeneficiaryNew_shouldSurfaceRegistrationFailure() throws Exception { + when(registrarServiceImpl.registerBeneficiary(BEN_REQUEST, AUTHORIZATION)) + .thenThrow(new IllegalStateException("identity down")); + + assertTrue(controller.registrarBeneficaryRegistrationNew(BEN_REQUEST, AUTHORIZATION) + .contains("Error in registration")); + } + + @Test + @DisplayName("updateBeneficiary should update every beneficiary section") + void updateBeneficiary_shouldUpdateEverySection() throws Exception { + when(registrarServiceImpl.updateBeneficiary(any())).thenReturn(1); + when(registrarServiceImpl.updateBeneficiaryDemographic(any(), eq(BEN_REG_ID))).thenReturn(1); + when(registrarServiceImpl.updateBeneficiaryPhoneMapping(any(), eq(BEN_REG_ID))).thenReturn(1); + when(registrarServiceImpl.updateBenGovIdMapping(any(), eq(BEN_REG_ID))).thenReturn(1); + when(registrarServiceImpl.updateBeneficiaryDemographicAdditional(any(), eq(BEN_REG_ID))).thenReturn(1); + when(registrarServiceImpl.updateBeneficiaryImage(any(), eq(BEN_REG_ID))).thenReturn(1); + when(commonNurseServiceImpl.updateBeneficiaryStatus('R', BEN_REG_ID)).thenReturn(1); + + assertTrue(controller.updateBeneficiary(BEN_PAYLOAD).contains("Beneficiary Details updated successfully")); + } + + @Test + @DisplayName("updateBeneficiary should report a failure when a section did not change") + void updateBeneficiary_shouldReportFailureWhenSectionUnchanged() throws Exception { + when(registrarServiceImpl.updateBeneficiary(any())).thenReturn(0); + + assertTrue(controller.updateBeneficiary(BEN_PAYLOAD).contains("Something Went-Wrong")); + } + + @Test + @DisplayName("beneficiaryUpdate should confirm the update") + void beneficiaryUpdate_shouldConfirmUpdate() throws Exception { + when(registrarServiceImpl.updateBeneficiary(BEN_REQUEST, AUTHORIZATION)).thenReturn(1); + + assertTrue(controller.beneficiaryUpdate(BEN_REQUEST, AUTHORIZATION) + .contains("Beneficiary details updated successfully")); + } + + @Test + @DisplayName("createReVisitForBenToNurse should move the beneficiary to the nurse worklist") + void createReVisit_shouldMoveBeneficiaryToNurseWorklist() throws Exception { + when(registrarServiceImpl.searchAndSubmitBeneficiaryToNurse(BEN_REQUEST)).thenReturn(1); + + assertTrue(controller.createReVisitForBenToNurse(BEN_REQUEST) + .contains("Beneficiary moved to nurse worklist")); + } + + @Test + @DisplayName("createReVisitForBenToNurse should report a beneficiary already on the worklist") + void createReVisit_shouldReportBeneficiaryAlreadyOnWorklist() throws Exception { + when(registrarServiceImpl.searchAndSubmitBeneficiaryToNurse(BEN_REQUEST)).thenReturn(2); + + assertTrue(controller.createReVisitForBenToNurse(BEN_REQUEST) + .contains("Beneficiary already present in nurse worklist")); + } + + @Test + @DisplayName("createReVisitForBenToNurse should report a failed move") + void createReVisit_shouldReportFailedMove() throws Exception { + when(registrarServiceImpl.searchAndSubmitBeneficiaryToNurse(BEN_REQUEST)).thenReturn(0); + + assertTrue(controller.createReVisitForBenToNurse(BEN_REQUEST) + .contains("Error while moving beneficiary to nurse worklist")); + } + + @Test + @DisplayName("createReVisitForBenToNurse should surface a service failure") + void createReVisit_shouldSurfaceServiceFailure() throws Exception { + when(registrarServiceImpl.searchAndSubmitBeneficiaryToNurse(BEN_REQUEST)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.createReVisitForBenToNurse(BEN_REQUEST) + .contains("Error while moving beneficiary to nurse worklist")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/report/CRMReportControllerTest.java b/src/test/java/com/iemr/tm/controller/report/CRMReportControllerTest.java new file mode 100644 index 00000000..d21a3700 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/report/CRMReportControllerTest.java @@ -0,0 +1,144 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.report; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.iemr.tm.data.report.ConsultationReport; +import com.iemr.tm.data.report.ReportInput; +import com.iemr.tm.data.report.SpokeReport; +import com.iemr.tm.data.report.TMDailyReport; +import com.iemr.tm.service.report.CRMReportService; + +@ExtendWith(MockitoExtension.class) +@DisplayName("CRMReportController Test Suite") +class CRMReportControllerTest { + + @Mock + private CRMReportService cRMReportService; + + @InjectMocks + private CRMReportController controller; + + private final ReportInput input = new ReportInput(); + + @Test + @DisplayName("chiefcomplaintreport should return the chief complaint report") + void chiefcomplaintreport_shouldReturnReport() throws Exception { + Set report = Collections.singleton(new SpokeReport()); + when(cRMReportService.getChiefcomplaintreport(any(ReportInput.class))).thenReturn(report); + + assertTrue(controller.chiefcomplaintreport(input).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("chiefcomplaintreport should surface a service failure") + void chiefcomplaintreport_shouldSurfaceServiceFailure() throws Exception { + when(cRMReportService.getChiefcomplaintreport(any(ReportInput.class))) + .thenThrow(new IllegalStateException("report failed")); + + assertTrue(controller.chiefcomplaintreport(input).contains("report failed")); + } + + @Test + @DisplayName("getConsultationReport should return the consultation report with nulls serialised") + void getConsultationReport_shouldReturnReport() throws Exception { + List report = Collections.singletonList(new ConsultationReport()); + when(cRMReportService.getConsultationReport(any(ReportInput.class))).thenReturn(report); + + assertTrue(controller.getConsultationReport(input).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getConsultationReport should surface a service failure") + void getConsultationReport_shouldSurfaceServiceFailure() throws Exception { + when(cRMReportService.getConsultationReport(any(ReportInput.class))) + .thenThrow(new IllegalStateException("report failed")); + + assertTrue(controller.getConsultationReport(input).contains("report failed")); + } + + @Test + @DisplayName("getTotalConsultationReport should return the total consultation report") + void getTotalConsultationReport_shouldReturnReport() throws Exception { + when(cRMReportService.getTotalConsultationReport(any(ReportInput.class))).thenReturn("{\"total\":9}"); + + assertTrue(controller.getTotalConsultationReport(input).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getTotalConsultationReport should surface a service failure") + void getTotalConsultationReport_shouldSurfaceServiceFailure() throws Exception { + when(cRMReportService.getTotalConsultationReport(any(ReportInput.class))) + .thenThrow(new IllegalStateException("report failed")); + + assertTrue(controller.getTotalConsultationReport(input).contains("report failed")); + } + + @Test + @DisplayName("getMonthlyReport should return the monthly report") + void getMonthlyReport_shouldReturnReport() throws Exception { + when(cRMReportService.getMonthlyReport(any(ReportInput.class))).thenReturn("{\"month\":\"Jan\"}"); + + assertTrue(controller.getMonthlyReport(input).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getMonthlyReport should surface a service failure") + void getMonthlyReport_shouldSurfaceServiceFailure() throws Exception { + when(cRMReportService.getMonthlyReport(any(ReportInput.class))) + .thenThrow(new IllegalStateException("report failed")); + + assertTrue(controller.getMonthlyReport(input).contains("report failed")); + } + + @Test + @DisplayName("getDailyReport should return the daily report with nulls serialised") + void getDailyReport_shouldReturnReport() throws Exception { + List report = Collections.singletonList(new TMDailyReport()); + when(cRMReportService.getDailyReport(any(ReportInput.class))).thenReturn(report); + + assertTrue(controller.getDailyReport(input).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getDailyReport should surface a service failure") + void getDailyReport_shouldSurfaceServiceFailure() throws Exception { + when(cRMReportService.getDailyReport(any(ReportInput.class))) + .thenThrow(new IllegalStateException("report failed")); + + assertTrue(controller.getDailyReport(input).contains("report failed")); + } +} diff --git a/src/test/java/com/iemr/tm/controller/snomedct/SnomedControllerTest.java b/src/test/java/com/iemr/tm/controller/snomedct/SnomedControllerTest.java new file mode 100644 index 00000000..e93a888b --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/snomedct/SnomedControllerTest.java @@ -0,0 +1,149 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.snomedct; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.iemr.tm.data.snomedct.SCTDescription; +import com.iemr.tm.service.snomedct.SnomedService; + +@ExtendWith(MockitoExtension.class) +@DisplayName("SnomedController Test Suite") +class SnomedControllerTest { + + private static final String TERM_REQUEST = "{\"term\":\"fever\"}"; + + @Mock + private SnomedService snomedService; + + private SnomedController controller; + + @BeforeEach + @DisplayName("Wire the controller with a mocked SNOMED service") + void setUp() { + controller = new SnomedController(); + controller.setSnomedService(snomedService); + } + + private SCTDescription record(String conceptId, String term) { + SCTDescription description = new SCTDescription(); + description.setConceptID(conceptId); + description.setTerm(term); + return description; + } + + @Nested + @DisplayName("getSnomedCTRecord") + class GetSnomedCtRecordTests { + + @Test + @DisplayName("getSnomedCTRecord should return the matched clinical term") + void getSnomedCTRecord_shouldReturnMatchedTerm() { + when(snomedService.findSnomedCTRecordFromTerm("fever")).thenReturn(record("386661006", "Fever")); + + String result = controller.getSnomedCTRecord(TERM_REQUEST); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("386661006")); + } + + @Test + @DisplayName("getSnomedCTRecord should report no records when the service finds nothing") + void getSnomedCTRecord_shouldReportNoRecordsWhenNothingFound() { + when(snomedService.findSnomedCTRecordFromTerm("fever")).thenReturn(null); + + assertTrue(controller.getSnomedCTRecord(TERM_REQUEST).contains("No Records Found")); + } + + @Test + @DisplayName("getSnomedCTRecord should report no records when the match carries no concept id") + void getSnomedCTRecord_shouldReportNoRecordsWithoutConceptId() { + when(snomedService.findSnomedCTRecordFromTerm("fever")).thenReturn(record(null, "Fever")); + + assertTrue(controller.getSnomedCTRecord(TERM_REQUEST).contains("No Records Found")); + } + + @Test + @DisplayName("getSnomedCTRecord should surface a service failure") + void getSnomedCTRecord_shouldSurfaceServiceFailure() { + when(snomedService.findSnomedCTRecordFromTerm("fever")).thenThrow(new IllegalStateException("snomed down")); + + assertTrue(controller.getSnomedCTRecord(TERM_REQUEST).contains("snomed down")); + } + + @Test + @DisplayName("getSnomedCTRecord should surface a malformed request") + void getSnomedCTRecord_shouldSurfaceMalformedRequest() { + assertTrue(controller.getSnomedCTRecord("not-json").contains("\"statusCode\"")); + } + } + + @Nested + @DisplayName("getSnomedCTRecordList") + class GetSnomedCtRecordListTests { + + @Test + @DisplayName("getSnomedCTRecordList should return the matched clinical term list") + void getSnomedCTRecordList_shouldReturnMatchedList() throws Exception { + when(snomedService.findSnomedCTRecordList(any(SCTDescription.class))) + .thenReturn("[{\"conceptID\":\"386661006\"}]"); + + String result = controller.getSnomedCTRecordList(TERM_REQUEST); + + assertTrue(result.contains("\"statusCode\":200")); + assertTrue(result.contains("386661006")); + } + + @Test + @DisplayName("getSnomedCTRecordList should report no records when the service returns nothing") + void getSnomedCTRecordList_shouldReportNoRecords() throws Exception { + when(snomedService.findSnomedCTRecordList(any(SCTDescription.class))).thenReturn(null); + + assertTrue(controller.getSnomedCTRecordList(TERM_REQUEST).contains("No Records Found")); + } + + @Test + @DisplayName("getSnomedCTRecordList should surface a service failure") + void getSnomedCTRecordList_shouldSurfaceServiceFailure() throws Exception { + when(snomedService.findSnomedCTRecordList(any(SCTDescription.class))) + .thenThrow(new IllegalStateException("snomed down")); + + assertTrue(controller.getSnomedCTRecordList(TERM_REQUEST).contains("snomed down")); + } + + @Test + @DisplayName("getSnomedCTRecordList should surface a malformed request") + void getSnomedCTRecordList_shouldSurfaceMalformedRequest() { + assertTrue(controller.getSnomedCTRecordList("not-json").contains("\"statusCode\"")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/teleconsultation/TeleConsultationControllerTest.java b/src/test/java/com/iemr/tm/controller/teleconsultation/TeleConsultationControllerTest.java new file mode 100644 index 00000000..1724d56d --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/teleconsultation/TeleConsultationControllerTest.java @@ -0,0 +1,321 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.teleconsultation; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.Authentication; + +import com.google.gson.JsonObject; +import com.iemr.tm.service.tele_consultation.TeleConsultationServiceImpl; + +@ExtendWith(MockitoExtension.class) +@DisplayName("TeleConsultationController Test Suite") +class TeleConsultationControllerTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final String REQUEST = "{\"benRegID\":11,\"visitCode\":22}"; + + @Mock + private TeleConsultationServiceImpl teleConsultationServiceImpl; + + @Mock + private Authentication authentication; + + @InjectMocks + private TeleConsultationController controller; + + @Nested + @DisplayName("benArrivalStatusUpdater") + class ArrivalStatusTests { + + @Test + @DisplayName("benArrivalStatusUpdater should confirm the update when a row was changed") + void benArrivalStatusUpdater_shouldConfirmUpdate() throws Exception { + when(teleConsultationServiceImpl.updateBeneficiaryArrivalStatus(REQUEST)).thenReturn(1); + + assertTrue(controller.benArrivalStatusUpdater(REQUEST) + .contains("Beneficiary arrival status updated successfully.")); + } + + @Test + @DisplayName("benArrivalStatusUpdater should report a failure when no row was changed") + void benArrivalStatusUpdater_shouldReportFailureWhenNothingChanged() throws Exception { + when(teleConsultationServiceImpl.updateBeneficiaryArrivalStatus(REQUEST)).thenReturn(0); + + assertTrue(controller.benArrivalStatusUpdater(REQUEST) + .contains("Error in updating beneficiary arrival status.")); + } + + @Test + @DisplayName("benArrivalStatusUpdater should reject a null request") + void benArrivalStatusUpdater_shouldRejectNullRequest() throws Exception { + String result = controller.benArrivalStatusUpdater(null); + + assertTrue(result.contains("Invalid request")); + verify(teleConsultationServiceImpl, never()).updateBeneficiaryArrivalStatus(anyString()); + } + + @Test + @DisplayName("benArrivalStatusUpdater should surface a service failure") + void benArrivalStatusUpdater_shouldSurfaceServiceFailure() throws Exception { + when(teleConsultationServiceImpl.updateBeneficiaryArrivalStatus(REQUEST)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.benArrivalStatusUpdater(REQUEST) + .contains("Error while updating beneficiary arrival status.")); + } + } + + @Nested + @DisplayName("updateBeneficiaryStatusToCancelTCRequest") + class CancelRequestTests { + + @Test + @DisplayName("updateBeneficiaryStatusToCancelTCRequest should confirm the cancellation") + void cancelTCRequest_shouldConfirmCancellation() throws Exception { + when(teleConsultationServiceImpl.updateBeneficiaryStatusToCancelTCRequest(REQUEST, AUTHORIZATION)) + .thenReturn(1); + + assertTrue(controller.updateBeneficiaryStatusToCancelTCRequest(REQUEST, AUTHORIZATION) + .contains("Beneficiary TC request cancelled successfully.")); + } + + @Test + @DisplayName("updateBeneficiaryStatusToCancelTCRequest should report a failed cancellation") + void cancelTCRequest_shouldReportFailedCancellation() throws Exception { + when(teleConsultationServiceImpl.updateBeneficiaryStatusToCancelTCRequest(REQUEST, AUTHORIZATION)) + .thenReturn(0); + + assertTrue(controller.updateBeneficiaryStatusToCancelTCRequest(REQUEST, AUTHORIZATION) + .contains("Teleconsultation cancel request failed.")); + } + + @Test + @DisplayName("updateBeneficiaryStatusToCancelTCRequest should reject a null request") + void cancelTCRequest_shouldRejectNullRequest() { + assertTrue(controller.updateBeneficiaryStatusToCancelTCRequest(null, AUTHORIZATION) + .contains("Invalid request")); + } + + @Test + @DisplayName("updateBeneficiaryStatusToCancelTCRequest should surface a service failure") + void cancelTCRequest_shouldSurfaceServiceFailure() throws Exception { + when(teleConsultationServiceImpl.updateBeneficiaryStatusToCancelTCRequest(REQUEST, AUTHORIZATION)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.updateBeneficiaryStatusToCancelTCRequest(REQUEST, AUTHORIZATION) + .contains("Error while updating beneficiary status")); + } + } + + @Nested + @DisplayName("checkBeneficiaryStatusToProceedWithSpecialist") + class CheckStatusTests { + + @Test + @DisplayName("checkBeneficiaryStatusToProceedWithSpecialist should allow the specialist to proceed") + void checkStatus_shouldAllowSpecialistToProceed() throws Exception { + when(teleConsultationServiceImpl.checkBeneficiaryStatusForSpecialistTransaction(REQUEST)).thenReturn(1); + + assertTrue(controller.checkBeneficiaryStatusToProceedWithSpecialist(REQUEST) + .contains("Specialist can proceed with beneficiary TM session.")); + } + + @Test + @DisplayName("checkBeneficiaryStatusToProceedWithSpecialist should report a lookup failure") + void checkStatus_shouldReportLookupFailure() throws Exception { + when(teleConsultationServiceImpl.checkBeneficiaryStatusForSpecialistTransaction(REQUEST)).thenReturn(0); + + assertTrue(controller.checkBeneficiaryStatusToProceedWithSpecialist(REQUEST) + .contains("Issue while fetching beneficiary status.")); + } + + @Test + @DisplayName("checkBeneficiaryStatusToProceedWithSpecialist should reject a null request") + void checkStatus_shouldRejectNullRequest() { + assertTrue(controller.checkBeneficiaryStatusToProceedWithSpecialist(null).contains("Invalid request")); + } + + @Test + @DisplayName("checkBeneficiaryStatusToProceedWithSpecialist should surface a service failure") + void checkStatus_shouldSurfaceServiceFailure() throws Exception { + when(teleConsultationServiceImpl.checkBeneficiaryStatusForSpecialistTransaction(REQUEST)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.checkBeneficiaryStatusToProceedWithSpecialist(REQUEST) + .contains("Issue while fetching beneficiary status")); + } + } + + @Nested + @DisplayName("createTCRequestForBeneficiary") + class CreateRequestTests { + + @Test + @DisplayName("createTCRequestForBeneficiary should confirm the created request") + void createTCRequest_shouldConfirmCreatedRequest() throws Exception { + when(teleConsultationServiceImpl.createTCRequestFromWorkList(any(JsonObject.class), eq(AUTHORIZATION))) + .thenReturn(1); + + assertTrue(controller.createTCRequestForBeneficiary(REQUEST, AUTHORIZATION) + .contains("Teleconsultation request created successfully.")); + } + + @Test + @DisplayName("createTCRequestForBeneficiary should report a failed creation") + void createTCRequest_shouldReportFailedCreation() throws Exception { + when(teleConsultationServiceImpl.createTCRequestFromWorkList(any(JsonObject.class), eq(AUTHORIZATION))) + .thenReturn(0); + + assertTrue(controller.createTCRequestForBeneficiary(REQUEST, AUTHORIZATION) + .contains("Issue while creating Teleconsultation request.")); + } + + @Test + @DisplayName("createTCRequestForBeneficiary should reject a null request") + void createTCRequest_shouldRejectNullRequest() { + assertTrue(controller.createTCRequestForBeneficiary(null, AUTHORIZATION).contains("Invalid request")); + } + + @Test + @DisplayName("createTCRequestForBeneficiary should surface a service failure") + void createTCRequest_shouldSurfaceServiceFailure() throws Exception { + when(teleConsultationServiceImpl.createTCRequestFromWorkList(any(JsonObject.class), eq(AUTHORIZATION))) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.createTCRequestForBeneficiary(REQUEST, AUTHORIZATION) + .contains("Issue while creating Teleconsultation request")); + } + } + + @Nested + @DisplayName("getTCSpecialistWorkListNew") + class WorkListTests { + + private static final String LIST_REQUEST = "{\"psmID\":9,\"date\":\"2024-01-15\"}"; + + @Test + @DisplayName("getTCSpecialistWorkListNew should return the request list for the specialist") + void getTCSpecialistWorkListNew_shouldReturnRequestList() throws Exception { + when(authentication.isAuthenticated()).thenReturn(true); + when(authentication.getPrincipal()).thenReturn("42"); + when(teleConsultationServiceImpl.getTCRequestListBySpecialistIdAndDate(9, 42, "2024-01-15")) + .thenReturn("[{\"benRegID\":11}]"); + + assertTrue(controller.getTCSpecialistWorkListNew(LIST_REQUEST, authentication) + .contains("\"statusCode\":200")); + } + + @Test + @DisplayName("getTCSpecialistWorkListNew should reject a missing authentication") + void getTCSpecialistWorkListNew_shouldRejectMissingAuthentication() throws Exception { + String result = controller.getTCSpecialistWorkListNew(LIST_REQUEST, null); + + assertTrue(result.contains("Unauthorized access")); + verify(teleConsultationServiceImpl, never()).getTCRequestListBySpecialistIdAndDate(anyInt(), anyInt(), + anyString()); + } + + @Test + @DisplayName("getTCSpecialistWorkListNew should reject an unauthenticated principal") + void getTCSpecialistWorkListNew_shouldRejectUnauthenticatedPrincipal() { + when(authentication.isAuthenticated()).thenReturn(false); + + assertTrue(controller.getTCSpecialistWorkListNew(LIST_REQUEST, authentication) + .contains("Unauthorized access")); + } + + @Test + @DisplayName("getTCSpecialistWorkListNew should reject a null request body") + void getTCSpecialistWorkListNew_shouldRejectNullRequestBody() { + when(authentication.isAuthenticated()).thenReturn(true); + when(authentication.getPrincipal()).thenReturn("42"); + + assertTrue(controller.getTCSpecialistWorkListNew(null, authentication) + .contains("Invalid request, either ProviderServiceMapID or RequestDate is invalid")); + } + + @Test + @DisplayName("getTCSpecialistWorkListNew should surface a service failure") + void getTCSpecialistWorkListNew_shouldSurfaceServiceFailure() throws Exception { + when(authentication.isAuthenticated()).thenReturn(true); + when(authentication.getPrincipal()).thenReturn("42"); + when(teleConsultationServiceImpl.getTCRequestListBySpecialistIdAndDate(9, 42, "2024-01-15")) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.getTCSpecialistWorkListNew(LIST_REQUEST, authentication) + .contains("Error while getting TC requestList")); + } + } + + @Nested + @DisplayName("startconsultation") + class StartConsultationTests { + + @Test + @DisplayName("startconsultation should return the number of updated rows") + void startconsultation_shouldReturnUpdatedRowCount() { + when(teleConsultationServiceImpl.startconsultation(11L, 22L)).thenReturn(1); + + assertTrue(controller.startconsultation(REQUEST).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("startconsultation should leave the generic failure when nothing comes back") + void startconsultation_shouldLeaveGenericFailureWhenNothingReturned() { + when(teleConsultationServiceImpl.startconsultation(11L, 22L)).thenReturn(null); + + assertTrue(controller.startconsultation(REQUEST).contains("Failed with generic error")); + } + + @Test + @DisplayName("startconsultation should reject a null request") + void startconsultation_shouldRejectNullRequest() { + assertTrue(controller.startconsultation(null) + .contains("Invalid request, either ProviderServiceMapID or UserID or RequestDate is invalid")); + } + + @Test + @DisplayName("startconsultation should surface a service failure") + void startconsultation_shouldSurfaceServiceFailure() { + when(teleConsultationServiceImpl.startconsultation(11L, 22L)) + .thenThrow(new IllegalStateException("db down")); + + assertTrue(controller.startconsultation(REQUEST).contains("Error while getting TC requestList")); + } + } +} diff --git a/src/test/java/com/iemr/tm/controller/version/VersionControllerTest.java b/src/test/java/com/iemr/tm/controller/version/VersionControllerTest.java new file mode 100644 index 00000000..96c15db2 --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/version/VersionControllerTest.java @@ -0,0 +1,78 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.version; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +@DisplayName("VersionController Test Suite") +class VersionControllerTest { + + private MockMvc mockMvc; + private ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + VersionController versionController = new VersionController(); + + mockMvc = MockMvcBuilders.standaloneSetup(versionController).build(); + + objectMapper = new ObjectMapper(); + } + + @Test + @DisplayName("Should return version details sourced from git.properties on the classpath") + void versionInformation_shouldReturnGitPropertiesContent() throws Exception { + Properties gitProperties = new Properties(); + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("git.properties")) { + if (inputStream == null) { + throw new IOException("git.properties file not found in test resources."); + } + gitProperties.load(inputStream); + } + + mockMvc.perform(get("/version")) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.buildTimestamp").value(gitProperties.getProperty("git.build.time", "unknown"))) + .andExpect(jsonPath("$.version").value(gitProperties.getProperty("git.build.version", "unknown"))) + .andExpect(jsonPath("$.branch").value(gitProperties.getProperty("git.branch", "unknown"))) + .andExpect(jsonPath("$.commitHash").value(gitProperties.getProperty("git.commit.id.abbrev", "unknown"))); + } +} diff --git a/src/test/java/com/iemr/tm/controller/videoconsultationcontroller/VideoConsultationControllerTest.java b/src/test/java/com/iemr/tm/controller/videoconsultationcontroller/VideoConsultationControllerTest.java new file mode 100644 index 00000000..b50c93fd --- /dev/null +++ b/src/test/java/com/iemr/tm/controller/videoconsultationcontroller/VideoConsultationControllerTest.java @@ -0,0 +1,218 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.controller.videoconsultationcontroller; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.Authentication; + +import com.iemr.tm.service.videoconsultation.VideoConsultationService; + +@ExtendWith(MockitoExtension.class) +@DisplayName("VideoConsultationController Test Suite") +class VideoConsultationControllerTest { + + @Mock + private VideoConsultationService videoConsultationService; + + @Mock + private Authentication authentication; + + @InjectMocks + private VideoConsultationController controller; + + @Nested + @DisplayName("login") + class LoginTests { + + @Test + @DisplayName("login should return the session data when the principal matches the requested user") + void login_shouldReturnSessionDataForMatchingPrincipal() throws Exception { + when(authentication.isAuthenticated()).thenReturn(true); + when(authentication.getPrincipal()).thenReturn("42"); + when(videoConsultationService.login(42L)).thenReturn("{\"sessionId\":\"abc\"}"); + + assertTrue(controller.login(42L, authentication).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("login should reject a missing authentication") + void login_shouldRejectMissingAuthentication() throws Exception { + String result = controller.login(42L, null); + + assertTrue(result.contains("Unauthorized access")); + verify(videoConsultationService, never()).login(anyLong()); + } + + @Test + @DisplayName("login should reject an unauthenticated principal") + void login_shouldRejectUnauthenticatedPrincipal() { + when(authentication.isAuthenticated()).thenReturn(false); + + assertTrue(controller.login(42L, authentication).contains("Unauthorized access")); + } + + @Test + @DisplayName("login should reject a principal that does not match the requested user") + void login_shouldRejectMismatchedPrincipal() throws Exception { + when(authentication.isAuthenticated()).thenReturn(true); + when(authentication.getPrincipal()).thenReturn("99"); + + String result = controller.login(42L, authentication); + + assertTrue(result.contains("Unauthorized access!")); + verify(videoConsultationService, never()).login(anyLong()); + } + + @Test + @DisplayName("login should surface a service failure") + void login_shouldSurfaceServiceFailure() throws Exception { + when(authentication.isAuthenticated()).thenReturn(true); + when(authentication.getPrincipal()).thenReturn("42"); + when(videoConsultationService.login(42L)).thenThrow(new IllegalStateException("swymed down")); + + assertTrue(controller.login(42L, authentication).contains("swymed down")); + } + } + + @Nested + @DisplayName("call") + class CallTests { + + @Test + @DisplayName("call should return the call details produced by the service") + void call_shouldReturnCallDetails() throws Exception { + when(videoConsultationService.callUser(42L, 43L)).thenReturn("{\"callId\":\"c1\"}"); + + assertTrue(controller.call(42L, 43L).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("call should surface a service failure") + void call_shouldSurfaceServiceFailure() throws Exception { + when(videoConsultationService.callUser(42L, 43L)).thenThrow(new IllegalStateException("swymed down")); + + assertTrue(controller.call(42L, 43L).contains("swymed down")); + } + + @Test + @DisplayName("callSwymedAndJitsi should place a video consultation call for the VideoConsultation type") + void callSwymedAndJitsi_shouldPlaceVideoConsultationCall() throws Exception { + when(videoConsultationService.callUser(42L, 43L)).thenReturn("{\"callId\":\"c1\"}"); + + assertTrue(controller.callSwymedAndJitsi(42L, 43L, "VideoConsultation").contains("\"statusCode\":200")); + } + + @Test + @DisplayName("callSwymedAndJitsi should fall back to a Jitsi call for any other type") + void callSwymedAndJitsi_shouldFallBackToJitsi() throws Exception { + when(videoConsultationService.callUserjitsi(42L, 43L)).thenReturn("{\"callId\":\"j1\"}"); + + assertTrue(controller.callSwymedAndJitsi(42L, 43L, "Jitsi").contains("\"statusCode\":200")); + } + + @Test + @DisplayName("callSwymedAndJitsi should surface a service failure") + void callSwymedAndJitsi_shouldSurfaceServiceFailure() throws Exception { + when(videoConsultationService.callUserjitsi(42L, 43L)).thenThrow(new IllegalStateException("jitsi down")); + + assertTrue(controller.callSwymedAndJitsi(42L, 43L, "Jitsi").contains("jitsi down")); + } + } + + @Nested + @DisplayName("callvan") + class CallVanTests { + + @Test + @DisplayName("callvan should return the van call details") + void callvan_shouldReturnVanCallDetails() throws Exception { + when(videoConsultationService.callVan(42L, 7)).thenReturn("{\"callId\":\"v1\"}"); + + assertTrue(controller.callvan(42L, 7).contains("\"statusCode\":200")); + } + + @Test + @DisplayName("callvan should surface a service failure") + void callvan_shouldSurfaceServiceFailure() throws Exception { + when(videoConsultationService.callVan(42L, 7)).thenThrow(new IllegalStateException("swymed down")); + + assertTrue(controller.callvan(42L, 7).contains("swymed down")); + } + + @Test + @DisplayName("callVanSwymedAndJitsi should place a Swymed van call for the Swymed type") + void callVanSwymedAndJitsi_shouldPlaceSwymedVanCall() throws Exception { + when(videoConsultationService.callVan(42L, 7)).thenReturn("{\"callId\":\"v1\"}"); + + assertTrue(controller.callVanSwymedAndJitsi(42L, 7, "Swymed").contains("\"statusCode\":200")); + } + + @Test + @DisplayName("callVanSwymedAndJitsi should fall back to a Jitsi van call for any other type") + void callVanSwymedAndJitsi_shouldFallBackToJitsi() throws Exception { + when(videoConsultationService.callVanJitsi(42L, 7)).thenReturn("{\"callId\":\"j1\"}"); + + assertTrue(controller.callVanSwymedAndJitsi(42L, 7, "Jitsi").contains("\"statusCode\":200")); + } + + @Test + @DisplayName("callVanSwymedAndJitsi should surface a service failure") + void callVanSwymedAndJitsi_shouldSurfaceServiceFailure() throws Exception { + when(videoConsultationService.callVanJitsi(42L, 7)).thenThrow(new IllegalStateException("jitsi down")); + + assertTrue(controller.callVanSwymedAndJitsi(42L, 7, "Jitsi").contains("jitsi down")); + } + } + + @Nested + @DisplayName("logout") + class LogoutTests { + + @Test + @DisplayName("logout should return the logout confirmation from the service") + void logout_shouldReturnLogoutConfirmation() throws Exception { + when(videoConsultationService.logout()).thenReturn("logged out"); + + assertTrue(controller.logout().contains("\"statusCode\":200")); + } + + @Test + @DisplayName("logout should surface a service failure") + void logout_shouldSurfaceServiceFailure() throws Exception { + when(videoConsultationService.logout()).thenThrow(new IllegalStateException("swymed down")); + + assertTrue(controller.logout().contains("swymed down")); + } + } +} diff --git a/src/test/java/com/iemr/tm/data/anc/DataAncDataTest.java b/src/test/java/com/iemr/tm/data/anc/DataAncDataTest.java new file mode 100644 index 00000000..f2b11810 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/anc/DataAncDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.anc; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.anc}. + */ +@DisplayName("com.iemr.tm.data.anc data classes") +class DataAncDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.anc"); + } +} diff --git a/src/test/java/com/iemr/tm/data/benFlowStatus/DataBenFlowStatusDataTest.java b/src/test/java/com/iemr/tm/data/benFlowStatus/DataBenFlowStatusDataTest.java new file mode 100644 index 00000000..6afe355a --- /dev/null +++ b/src/test/java/com/iemr/tm/data/benFlowStatus/DataBenFlowStatusDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.benFlowStatus; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.benFlowStatus}. + */ +@DisplayName("com.iemr.tm.data.benFlowStatus data classes") +class DataBenFlowStatusDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.benFlowStatus"); + } +} diff --git a/src/test/java/com/iemr/tm/data/bmi/DataBmiDataTest.java b/src/test/java/com/iemr/tm/data/bmi/DataBmiDataTest.java new file mode 100644 index 00000000..0678af86 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/bmi/DataBmiDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.bmi; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.bmi}. + */ +@DisplayName("com.iemr.tm.data.bmi data classes") +class DataBmiDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.bmi"); + } +} diff --git a/src/test/java/com/iemr/tm/data/covid19/DataCovid19DataTest.java b/src/test/java/com/iemr/tm/data/covid19/DataCovid19DataTest.java new file mode 100644 index 00000000..1be56758 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/covid19/DataCovid19DataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.covid19; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.covid19}. + */ +@DisplayName("com.iemr.tm.data.covid19 data classes") +class DataCovid19DataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.covid19"); + } +} diff --git a/src/test/java/com/iemr/tm/data/doctor/DataDoctorDataTest.java b/src/test/java/com/iemr/tm/data/doctor/DataDoctorDataTest.java new file mode 100644 index 00000000..cbd22198 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/doctor/DataDoctorDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.doctor; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.doctor}. + */ +@DisplayName("com.iemr.tm.data.doctor data classes") +class DataDoctorDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.doctor"); + } +} diff --git a/src/test/java/com/iemr/tm/data/factory/RowFactoryTest.java b/src/test/java/com/iemr/tm/data/factory/RowFactoryTest.java new file mode 100644 index 00000000..d9b74d01 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/factory/RowFactoryTest.java @@ -0,0 +1,721 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.factory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +/** + * The data classes expose static factories that turn a native query's + * {@code Object[]} rows into the payloads the API answers with. These tests + * feed them rows shaped the way the queries return them. + */ +@DisplayName("Row factory Test Suite") +class RowFactoryTest { + + /** + * Builds one query row from a compact type spec: {@code L} long, {@code I} + * integer, {@code S} short, {@code T} comma separated text, {@code D} + * timestamp, {@code B} boolean, {@code C} character, {@code A} sql date and + * {@code _} null. + */ + private static Object[] row(String spec) { + Object[] values = new Object[spec.length()]; + for (int i = 0; i < spec.length(); i++) { + switch (spec.charAt(i)) { + case 'L': + values[i] = Long.valueOf(i + 1); + break; + case 'I': + values[i] = Integer.valueOf(i + 1); + break; + case 'S': + values[i] = Short.valueOf((short) (i + 1)); + break; + case 'T': + values[i] = "1,2"; + break; + case 'D': + values[i] = new Timestamp(System.currentTimeMillis()); + break; + case 'B': + values[i] = Boolean.TRUE; + break; + case 'C': + values[i] = Character.valueOf('Y'); + break; + case 'A': + values[i] = java.sql.Date.valueOf(java.time.LocalDate.now().minusYears(31)); + break; + default: + values[i] = null; + } + } + return values; + } + + private static ArrayList rows(Object[]... values) { + return new ArrayList<>(Arrays.asList(values)); + } + + @Nested + @DisplayName("registrar worklist rows") + class RegistrarWorklistTests { + + /** benRegID, benID, name, dob, genderID, gender, visitID, visitNo, flag, category, … */ + private Object[] worklistRow(String flowStatus, java.sql.Date dob) { + Object[] values = row("LTTASTLSTTTTTTT"); + values[3] = dob; + values[8] = flowStatus; + return values; + } + + @Test + @DisplayName("getDocWorkListData should read a beneficiary pending consultation") + void docWorkList_shouldReadPendingBeneficiary() { + String result = com.iemr.tm.data.registrar.WrapperRegWorklist.getDocWorkListData( + Collections.singletonList(worklistRow("N", + java.sql.Date.valueOf(java.time.LocalDate.now().minusYears(31))))); + + assertTrue(result.contains("Pending For Consultation")); + assertTrue(result.contains("years")); + } + + @Test + @DisplayName("getDocWorkListData should read a beneficiary whose consultation is done") + void docWorkList_shouldReadConsultationDone() { + assertTrue(com.iemr.tm.data.registrar.WrapperRegWorklist.getDocWorkListData( + Collections.singletonList(worklistRow("D", + java.sql.Date.valueOf(java.time.LocalDate.now().minusMonths(5))))) + .contains("Consultation Done")); + } + + @Test + @DisplayName("getDocWorkListData should read a visit the nurse closed") + void docWorkList_shouldReadVisitClosedByNurse() { + assertTrue(com.iemr.tm.data.registrar.WrapperRegWorklist.getDocWorkListData( + Collections.singletonList(worklistRow("C", + java.sql.Date.valueOf(java.time.LocalDate.now().minusDays(11))))) + .contains("Visit closed by Nurse")); + } + + @Test + @DisplayName("getDocWorkListData should read a beneficiary with no date of birth") + void docWorkList_shouldReadBeneficiaryWithoutDob() { + assertNotNull(com.iemr.tm.data.registrar.WrapperRegWorklist + .getDocWorkListData(Collections.singletonList(worklistRow("N", null)))); + } + + @Test + @DisplayName("getDocWorkListData should render an empty list for no rows") + void docWorkList_shouldRenderEmptyListForNoRows() { + assertEquals("[]", com.iemr.tm.data.registrar.WrapperRegWorklist + .getDocWorkListData(new ArrayList())); + } + + @Test + @DisplayName("getRegistrarWorkList should read the registered beneficiary") + void registrarWorkList_shouldReadRegisteredBeneficiary() { + String result = com.iemr.tm.data.registrar.WrapperRegWorklist + .getRegistrarWorkList(Collections.singletonList(row("LTTASTTTTTTT"))); + + assertTrue(result.contains("years")); + assertTrue(result.contains("genderName")); + } + + @Test + @DisplayName("getRegistrarWorkList should read a beneficiary aged in months") + void registrarWorkList_shouldReadBeneficiaryAgedInMonths() { + Object[] values = row("LTTASTTTTTTT"); + values[3] = java.sql.Date.valueOf(java.time.LocalDate.now().minusMonths(5)); + + assertTrue(com.iemr.tm.data.registrar.WrapperRegWorklist + .getRegistrarWorkList(Collections.singletonList(values)).contains("months")); + } + + @Test + @DisplayName("getRegistrarWorkList should read a beneficiary aged in days") + void registrarWorkList_shouldReadBeneficiaryAgedInDays() { + Object[] values = row("LTTASTTTTTTT"); + values[3] = java.sql.Date.valueOf(java.time.LocalDate.now().minusDays(11)); + + assertTrue(com.iemr.tm.data.registrar.WrapperRegWorklist + .getRegistrarWorkList(Collections.singletonList(values)).contains("days")); + } + + @Test + @DisplayName("getRegistrarWorkList should render an empty list for no rows") + void registrarWorkList_shouldRenderEmptyListForNoRows() { + assertEquals("[]", com.iemr.tm.data.registrar.WrapperRegWorklist + .getRegistrarWorkList(new ArrayList())); + } + } + + @Nested + @DisplayName("prescription and investigation rows") + class PrescriptionRowTests { + + @Test + @DisplayName("getprescribedDrugs should read the prescribed drug rows") + void getPrescribedDrugs_shouldReadRows() { + ArrayList drugs = + com.iemr.tm.data.quickConsultation.PrescribedDrugDetail + .getprescribedDrugs(rows(row("LLTTITTTTTTTTTIBTTD"))); + + assertEquals(1, drugs.size()); + assertNotNull(drugs.get(0)); + } + + @Test + @DisplayName("getprescribedDrugs should read an empty result set") + void getPrescribedDrugs_shouldReadEmptyResultSet() { + assertTrue(com.iemr.tm.data.quickConsultation.PrescribedDrugDetail + .getprescribedDrugs(new ArrayList()).isEmpty()); + } + + @Test + @DisplayName("getBenChiefComplaints should read the recorded complaint rows") + void getChiefComplaints_shouldReadRows() { + ArrayList complaints = + com.iemr.tm.data.quickConsultation.BenChiefComplaint + .getBenChiefComplaints(rows(row("LLLIITITTLT"))); + + assertEquals(1, complaints.size()); + assertNotNull(complaints.get(0).getChiefComplaint()); + } + + @Test + @DisplayName("getBenChiefComplaintList should read the complaints out of a case sheet") + void getChiefComplaintList_shouldReadFromCaseSheet() { + com.google.gson.JsonObject caseSheet = com.google.gson.JsonParser.parseString( + "{\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22,\"providerServiceMapID\":9," + + "\"vanID\":7,\"parkingPlaceID\":2,\"createdBy\":\"doctor1\"," + + "\"chiefComplaintList\":[{\"chiefComplaintID\":4,\"chiefComplaint\":\"fever\"," + + "\"duration\":2,\"unitOfDuration\":\"days\",\"description\":\"since monday\"}," + + "{\"chiefComplaint\":\"cough\"}]}").getAsJsonObject(); + + ArrayList complaints = + com.iemr.tm.data.quickConsultation.BenChiefComplaint.getBenChiefComplaintList(caseSheet); + + assertEquals(2, complaints.size()); + assertEquals("fever", complaints.get(0).getChiefComplaint()); + assertEquals(22L, complaints.get(0).getVisitCode()); + } + + @Test + @DisplayName("getBenChiefComplaintList should read a case sheet with no complaint") + void getChiefComplaintList_shouldReadCaseSheetWithoutComplaint() { + assertTrue(com.iemr.tm.data.quickConsultation.BenChiefComplaint.getBenChiefComplaintList( + com.google.gson.JsonParser.parseString("{\"beneficiaryRegID\":11}").getAsJsonObject()) + .isEmpty()); + } + + @Test + @DisplayName("getLabTestOrderDetails should group the ordered laboratory tests") + void getLabTestOrderDetails_shouldGroupOrderedTests() { + com.iemr.tm.data.anc.WrapperBenInvestigationANC orders = + com.iemr.tm.data.quickConsultation.LabTestOrderDetail + .getLabTestOrderDetails(rows(row("LLIIT_L"), row("LLIIT_L"))); + + assertEquals(2, orders.getLaboratoryList().size()); + assertNotNull(orders.getBeneficiaryRegID()); + } + + @Test + @DisplayName("getLabTestOrderDetails should read an empty result set") + void getLabTestOrderDetails_shouldReadEmptyResultSet() { + assertNotNull(com.iemr.tm.data.quickConsultation.LabTestOrderDetail + .getLabTestOrderDetails(new ArrayList())); + } + + @Test + @DisplayName("getRBSTestOrderDetailsFromVitals should add the RBS test taken with the vitals") + void getRbsTestOrderFromVitals_shouldAddRbsTest() { + com.iemr.tm.data.nurse.BenPhysicalVitalDetail vitals = + new com.iemr.tm.data.nurse.BenPhysicalVitalDetail(); + vitals.setBeneficiaryRegID(11L); + vitals.setVisitCode(22L); + vitals.setRbsTestResult("110"); + + com.iemr.tm.data.anc.WrapperBenInvestigationANC orders = + com.iemr.tm.data.quickConsultation.LabTestOrderDetail + .getRBSTestOrderDetailsFromVitals(vitals); + + assertEquals(1, orders.getLaboratoryList().size()); + assertEquals("RBS Test", orders.getLaboratoryList().get(0).getProcedureName()); + } + + @Test + @DisplayName("getRBSTestOrderDetailsFromVitals should skip the RBS test when it was not taken") + void getRbsTestOrderFromVitals_shouldSkipUntakenRbsTest() { + assertTrue(com.iemr.tm.data.quickConsultation.LabTestOrderDetail + .getRBSTestOrderDetailsFromVitals(new com.iemr.tm.data.nurse.BenPhysicalVitalDetail()) + .getLaboratoryList().isEmpty()); + } + + @Test + @DisplayName("getLabTestOrderDetailList should read the ordered tests out of a case sheet") + void getLabTestOrderDetailList_shouldReadFromCaseSheet() { + com.google.gson.JsonObject caseSheet = com.google.gson.JsonParser.parseString( + "{\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22,\"providerServiceMapID\":9," + + "\"vanID\":7,\"parkingPlaceID\":2,\"createdBy\":\"doctor1\"," + + "\"labTestOrders\":[{\"procedureID\":4,\"procedureName\":\"CBC\"," + + "\"testingRequirements\":\"fasting\"},{\"procedureName\":\"ECG\"}]}") + .getAsJsonObject(); + + ArrayList orders = + com.iemr.tm.data.quickConsultation.LabTestOrderDetail + .getLabTestOrderDetailList(caseSheet, 31L); + + assertEquals(2, orders.size()); + assertEquals("CBC", orders.get(0).getProcedureName()); + assertEquals(31L, orders.get(0).getPrescriptionID()); + } + + @Test + @DisplayName("getLabTestOrderDetailList should read a case sheet with no ordered test") + void getLabTestOrderDetailList_shouldReadCaseSheetWithoutOrder() { + assertTrue(com.iemr.tm.data.quickConsultation.LabTestOrderDetail.getLabTestOrderDetailList( + com.google.gson.JsonParser.parseString("{\"beneficiaryRegID\":11}").getAsJsonObject(), 31L) + .isEmpty()); + } + } + + @Nested + @DisplayName("lab result rows") + class LabResultRowTests { + + @Test + @DisplayName("getVisitCodeAndDate should read the tested visits") + void getVisitCodeAndDate_shouldReadTestedVisits() { + ArrayList resultSet = rows(new Object[] { 22L, "2026-08-26" }); + + ArrayList visits = + com.iemr.tm.data.labModule.LabResultEntry.getVisitCodeAndDate(resultSet); + + assertEquals(1, visits.size()); + assertEquals(22L, visits.get(0).getVisitCode()); + } + + @Test + @DisplayName("getVisitCodeAndDate should read an empty result set") + void getVisitCodeAndDate_shouldReadEmptyResultSet() { + assertTrue(com.iemr.tm.data.labModule.LabResultEntry + .getVisitCodeAndDate(new ArrayList()).isEmpty()); + } + + private com.iemr.tm.data.labModule.LabResultEntry stored(Integer procedureID, String reportPath) { + com.iemr.tm.data.labModule.LabResultEntry entry = new com.iemr.tm.data.labModule.LabResultEntry(); + entry.setPrescriptionID(31L); + entry.setProcedureID(procedureID); + entry.setTestComponentID(2); + entry.setTestResultValue("12"); + entry.setTestResultUnit("g/dL"); + entry.setTestReportFilePath(reportPath); + entry.setCreatedDate(new Timestamp(System.currentTimeMillis())); + com.iemr.tm.data.labModule.ProcedureData procedure = new com.iemr.tm.data.labModule.ProcedureData(); + procedure.setProcedureName("CBC"); + procedure.setProcedureType("Laboratory"); + entry.setProcedureData(procedure); + com.iemr.tm.data.labModule.TestComponentMaster component = + new com.iemr.tm.data.labModule.TestComponentMaster(); + component.setTestComponentName("Haemoglobin"); + entry.setTestComponentMaster(component); + return entry; + } + + @Test + @DisplayName("getLabResultEntry should group the components of one procedure and read its report files") + void getLabResultEntry_shouldGroupComponentsAndReadReportFiles() { + ArrayList comingList = new ArrayList<>(); + comingList.add(stored(1, "81,82,")); + comingList.add(stored(1, null)); + comingList.add(stored(5, "")); + + ArrayList grouped = + com.iemr.tm.data.labModule.LabResultEntry.getLabResultEntry(comingList); + + assertEquals(2, grouped.size()); + assertEquals(2, grouped.get(0).getComponentList().size()); + assertEquals("CBC", grouped.get(0).getProcedureName()); + } + + @Test + @DisplayName("getLabResultEntry should read an empty result list") + void getLabResultEntry_shouldReadEmptyList() { + assertTrue(com.iemr.tm.data.labModule.LabResultEntry + .getLabResultEntry(new ArrayList<>()).isEmpty()); + } + } + + @Nested + @DisplayName("obstetric and personal history rows") + class HistoryRowTests { + + @Test + @DisplayName("getFemaleObstetricHistory should read every recorded pregnancy") + void getFemaleObstetricHistory_shouldReadRecordedPregnancies() { + com.iemr.tm.data.anc.WrapperFemaleObstetricHistory history = + com.iemr.tm.data.anc.WrapperFemaleObstetricHistory.getFemaleObstetricHistory( + rows(row("LLISSTTTSTSTSTTTTTSTTTTSTTTSTTLITIITTT"))); + + assertEquals(1, history.getFemaleObstetricHistoryList().size()); + assertNotNull(history.getBeneficiaryRegID()); + } + + @Test + @DisplayName("getFemaleObstetricHistory should read a beneficiary with no recorded pregnancy") + void getFemaleObstetricHistory_shouldReadNoRecordedPregnancy() { + Object[] values = row("LLISSTTTSTSTSTTTTTSTTTTSTTTSTTLITIITTT"); + values[4] = Short.valueOf((short) 0); + + assertTrue(com.iemr.tm.data.anc.WrapperFemaleObstetricHistory + .getFemaleObstetricHistory(rows(values)).getFemaleObstetricHistoryList().isEmpty()); + } + + @Test + @DisplayName("getFemaleObstetricHistory should read an empty result set") + void getFemaleObstetricHistory_shouldReadEmptyResultSet() { + assertNotNull(com.iemr.tm.data.anc.WrapperFemaleObstetricHistory + .getFemaleObstetricHistory(new ArrayList())); + } + + @Test + @DisplayName("getPersonalDetails should read the recorded tobacco and alcohol habits") + void getPersonalDetails_shouldReadTobaccoAndAlcoholHabits() { + com.iemr.tm.data.anc.BenPersonalHabit habits = com.iemr.tm.data.anc.BenPersonalHabit + .getPersonalDetails(rows(row("LLITTTTTTSD_TTTTTDCDL"), row("LLITTTTTTSD_TTTTTDCDL"))); + + assertNotNull(habits); + assertFalse(habits.getTobaccoList().isEmpty()); + assertFalse(habits.getAlcoholList().isEmpty()); + } + + @Test + @DisplayName("getPersonalDetails should read a beneficiary with no recorded habit") + void getPersonalDetails_shouldReadNoRecordedHabit() { + org.junit.jupiter.api.Assertions.assertNull( + com.iemr.tm.data.anc.BenPersonalHabit.getPersonalDetails(new ArrayList())); + } + } + + @Nested + @DisplayName("row builder") + class RowBuilderTests { + + @Test + @DisplayName("row should build one value of each supported column type") + void row_shouldBuildOneValueOfEachType() { + Object[] values = row("LISTDBA_"); + + assertEquals(Long.valueOf(1L), values[0]); + assertEquals(Integer.valueOf(2), values[1]); + assertEquals(Short.valueOf((short) 3), values[2]); + assertEquals("1,2", values[3]); + assertNotNull(values[4]); + assertEquals(Boolean.TRUE, values[5]); + assertNotNull(values[6]); + org.junit.jupiter.api.Assertions.assertNull(values[7]); + } + + @Test + @DisplayName("rows should collect the built rows in order") + void rows_shouldCollectRowsInOrder() { + List collected = rows(row("L"), row("I")); + + assertEquals(2, collected.size()); + assertEquals(Long.valueOf(1L), collected.get(0)[0]); + } + } + + @Nested + @DisplayName("beneficiary search and detail rows") + class BeneficiarySearchRowTests { + + @Test + @DisplayName("getSearchData should read the matched beneficiaries with their age") + void getSearchData_shouldReadMatchedBeneficiaries() { + String result = com.iemr.tm.data.registrar.V_BenAdvanceSearch + .getSearchData(Collections.singletonList(row("LTTSTATTITITTT"))); + + assertTrue(result.contains("years")); + assertTrue(result.contains("villageName")); + } + + @Test + @DisplayName("getSearchData should read a beneficiary aged in months") + void getSearchData_shouldReadBeneficiaryAgedInMonths() { + Object[] values = row("LTTSTATTITITTT"); + values[5] = java.sql.Date.valueOf(java.time.LocalDate.now().minusMonths(5)); + + assertTrue(com.iemr.tm.data.registrar.V_BenAdvanceSearch + .getSearchData(Collections.singletonList(values)).contains("months")); + } + + @Test + @DisplayName("getSearchData should read a beneficiary aged in days") + void getSearchData_shouldReadBeneficiaryAgedInDays() { + Object[] values = row("LTTSTATTITITTT"); + values[5] = java.sql.Date.valueOf(java.time.LocalDate.now().minusDays(11)); + + assertTrue(com.iemr.tm.data.registrar.V_BenAdvanceSearch + .getSearchData(Collections.singletonList(values)).contains("days")); + } + + @Test + @DisplayName("getSearchData should read a beneficiary with no date of birth") + void getSearchData_shouldReadBeneficiaryWithoutDob() { + Object[] values = row("LTTSTATTITITTT"); + values[5] = null; + + assertNotNull(com.iemr.tm.data.registrar.V_BenAdvanceSearch + .getSearchData(Collections.singletonList(values))); + } + + @Test + @DisplayName("getSearchData should render an empty list for no match") + void getSearchData_shouldRenderEmptyListForNoMatch() { + assertEquals("[]", com.iemr.tm.data.registrar.V_BenAdvanceSearch + .getSearchData(new ArrayList())); + } + + /** The registration row the beneficiary details screen is built from. */ + private Object[] detailRow() { + Object[] values = row("LTTTSASTSSSITITISTTITITTSTBATTTTTTT"); + values[5] = java.sql.Date.valueOf(java.time.LocalDate.now().minusYears(31)); + values[27] = java.sql.Date.valueOf(java.time.LocalDate.now().minusYears(9)); + return values; + } + + @Test + @DisplayName("getBeneficiaryDetails should read the registration row and work out the age at marriage") + void getBeneficiaryDetails_shouldReadRowAndAgeAtMarriage() { + com.iemr.tm.data.registrar.FetchBeneficiaryDetails details = + com.iemr.tm.data.registrar.FetchBeneficiaryDetails.getBeneficiaryDetails(detailRow(), + new ArrayList<>(), "Yes", new ArrayList<>()); + + assertNotNull(details); + assertNotNull(details.getBeneficiaryRegID()); + } + + @Test + @DisplayName("getBeneficiaryDetails should leave the age at marriage out when no marriage date is stored") + void getBeneficiaryDetails_shouldLeaveAgeAtMarriageOut() { + Object[] values = detailRow(); + values[27] = null; + + assertNotNull(com.iemr.tm.data.registrar.FetchBeneficiaryDetails.getBeneficiaryDetails(values, + new ArrayList<>(), "No", new ArrayList<>())); + } + + @Test + @DisplayName("getFetchBeneficiaryDetailsObj should answer with nothing") + void getFetchBeneficiaryDetailsObj_shouldAnswerWithNothing() { + org.junit.jupiter.api.Assertions.assertNull(com.iemr.tm.data.registrar.FetchBeneficiaryDetails + .getFetchBeneficiaryDetailsObj(row("L"), new ArrayList<>())); + } + + @Test + @DisplayName("getBeneficiaryData should read the beneficiary rows") + void getBeneficiaryData_shouldReadRows() { + ArrayList beneficiaries = + com.iemr.tm.data.registrar.BeneficiaryData + .getBeneficiaryData(Collections.singletonList(row("LTTASDTT"))); + + assertEquals(1, beneficiaries.size()); + assertNotNull(beneficiaries.get(0)); + } + + @Test + @DisplayName("getBeneficiaryPersonalData should read the beneficiary rows without the extra columns") + void getBeneficiaryPersonalData_shouldReadRows() { + assertEquals(1, com.iemr.tm.data.registrar.BeneficiaryData + .getBeneficiaryPersonalData(Collections.singletonList(row("LTTASD"))).size()); + } + } + + @Nested + @DisplayName("history and master rows") + class HistoryAndMasterRowTests { + + @Test + @DisplayName("getBenFamilyHistory should group the recorded family diseases") + void getBenFamilyHistory_shouldGroupRecordedDiseases() { + com.iemr.tm.data.anc.BenFamilyHistory history = com.iemr.tm.data.anc.BenFamilyHistory + .getBenFamilyHistory(rows(row("LLITSTTBTBLTT"), row("LLITSTTBTBLTT"))); + + assertNotNull(history); + assertEquals(2, history.getFamilyDiseaseList().size()); + assertTrue(history.getFamilyDiseaseList().get(0).containsKey("familyMembers")); + } + + @Test + @DisplayName("getBenFamilyHistory should read no recorded family disease") + void getBenFamilyHistory_shouldReadNoRecordedDisease() { + org.junit.jupiter.api.Assertions.assertNull(com.iemr.tm.data.anc.BenFamilyHistory + .getBenFamilyHistory(new ArrayList())); + } + + @Test + @DisplayName("getBenFamilyHist should group the recorded family diseases with their row ids") + void getBenFamilyHist_shouldGroupRecordedDiseasesWithIds() { + com.iemr.tm.data.anc.BenFamilyHistory history = com.iemr.tm.data.anc.BenFamilyHistory + .getBenFamilyHist(rows(row("LLLITSTTBTBLTT"), row("LLLITSTTBTBLTT"))); + + assertNotNull(history); + assertEquals(2, history.getFamilyDiseaseList().size()); + assertEquals(Boolean.FALSE, history.getFamilyDiseaseList().get(0).get("deleted")); + } + + @Test + @DisplayName("getBenFamilyHist should read no recorded family disease") + void getBenFamilyHist_shouldReadNoRecordedDisease() { + org.junit.jupiter.api.Assertions.assertNull(com.iemr.tm.data.anc.BenFamilyHistory + .getBenFamilyHist(new ArrayList())); + } + + @Test + @DisplayName("getBenAllergicHistory should expand the recorded reaction types") + void getBenAllergicHistory_shouldExpandReactionTypes() { + ArrayList allergies = + com.iemr.tm.data.anc.BenAllergyHistory + .getBenAllergicHistory(rows(row("LLITTTTTTTLTT"))); + + assertEquals(1, allergies.size()); + assertFalse(allergies.get(0).getTypeOfAllergicReactions().isEmpty()); + } + + @Test + @DisplayName("getBenAllergicHistory should read no recorded allergy") + void getBenAllergicHistory_shouldReadNoRecordedAllergy() { + assertTrue(com.iemr.tm.data.anc.BenAllergyHistory + .getBenAllergicHistory(new ArrayList()).isEmpty()); + } + + @Test + @DisplayName("getBenChildDevelopmentDetails should split the recorded milestones") + void getChildDevelopmentDetails_shouldSplitRecordedMilestones() { + com.iemr.tm.data.anc.BenChildDevelopmentHistory history = + com.iemr.tm.data.anc.BenChildDevelopmentHistory + .getBenChildDevelopmentDetails(rows(row("LLITBTBTBTBTL"))); + + assertNotNull(history); + assertEquals(2, history.getGrossMotorMilestones().size()); + assertEquals(2, history.getFineMotorMilestones().size()); + assertEquals(2, history.getSocialMilestones().size()); + assertEquals(2, history.getLanguageMilestones().size()); + assertEquals(2, history.getDevelopmentProblems().size()); + } + + @Test + @DisplayName("getBenChildDevelopmentDetails should read no recorded development history") + void getChildDevelopmentDetails_shouldReadNoRecordedHistory() { + org.junit.jupiter.api.Assertions.assertNull(com.iemr.tm.data.anc.BenChildDevelopmentHistory + .getBenChildDevelopmentDetails(new ArrayList())); + } + + @Test + @DisplayName("getDevelopmentHistory should flatten the picked milestones for storage") + void getDevelopmentHistory_shouldFlattenPickedMilestones() { + com.iemr.tm.data.anc.BenChildDevelopmentHistory history = + new com.iemr.tm.data.anc.BenChildDevelopmentHistory(); + history.setGrossMotorMilestones(Arrays.asList("Sits", "Stands")); + history.setFineMotorMilestones(Arrays.asList("Grasps")); + history.setSocialMilestones(Arrays.asList("Smiles")); + history.setLanguageMilestones(Arrays.asList("Babbles")); + history.setDevelopmentProblems(Arrays.asList("None")); + + com.iemr.tm.data.anc.BenChildDevelopmentHistory flattened = + com.iemr.tm.data.anc.BenChildDevelopmentHistory.getDevelopmentHistory(history); + + assertEquals("Sits,Stands,", flattened.getGrossMotorMilestone()); + assertEquals("Grasps,", flattened.getFineMotorMilestone()); + assertEquals("Smiles,", flattened.getSocialMilestone()); + assertEquals("Babbles,", flattened.getLanguageMilestone()); + } + + @Test + @DisplayName("getDevelopmentHistory should flatten an empty pick into empty text") + void getDevelopmentHistory_shouldFlattenEmptyPick() { + assertEquals("", com.iemr.tm.data.anc.BenChildDevelopmentHistory + .getDevelopmentHistory(new com.iemr.tm.data.anc.BenChildDevelopmentHistory()) + .getGrossMotorMilestone()); + } + + @org.junit.jupiter.params.ParameterizedTest + @org.junit.jupiter.params.provider.CsvSource({ "1", "2", "3", "4" }) + @DisplayName("getComplicationTypes should read the master rows for each complication master") + void getComplicationTypes_shouldReadEachMaster(int masterType) { + assertEquals(1, com.iemr.tm.data.masterdata.anc.ComplicationTypes + .getComplicationTypes(rows(row("ST")), masterType).size()); + } + + @Test + @DisplayName("getANCWomenVaccineDetails should read all three tetanus doses") + void getAncWomenVaccineDetails_shouldReadAllThreeDoses() { + com.iemr.tm.data.anc.WrapperAncImmunization immunization = + com.iemr.tm.data.anc.ANCWomenVaccineDetail.getANCWomenVaccineDetails( + rows(vaccineRow(1L), vaccineRow(2L), vaccineRow(3L))); + + assertNotNull(immunization.getDateReceivedForTT_1()); + assertNotNull(immunization.getDateReceivedForTT_2()); + assertNotNull(immunization.getDateReceivedForTT_3()); + assertNotNull(immunization.getFacilityNameOfTT_1()); + } + + private Object[] vaccineRow(Long id) { + Object[] values = row("LLLI_TATL"); + values[0] = id; + values[6] = java.sql.Date.valueOf(java.time.LocalDate.now()); + return values; + } + + @Test + @DisplayName("getANCWomenVaccineDetails should read a dose with no received date") + void getAncWomenVaccineDetails_shouldReadDoseWithoutDate() { + Object[] values = vaccineRow(1L); + values[6] = null; + + assertNotNull(com.iemr.tm.data.anc.ANCWomenVaccineDetail + .getANCWomenVaccineDetails(rows(values))); + } + + @Test + @DisplayName("getANCWomenVaccineDetails should read no recorded dose") + void getAncWomenVaccineDetails_shouldReadNoRecordedDose() { + assertNotNull(com.iemr.tm.data.anc.ANCWomenVaccineDetail + .getANCWomenVaccineDetails(new ArrayList())); + } + } +} diff --git a/src/test/java/com/iemr/tm/data/foetalmonitor/DataFoetalmonitorDataTest.java b/src/test/java/com/iemr/tm/data/foetalmonitor/DataFoetalmonitorDataTest.java new file mode 100644 index 00000000..922b867b --- /dev/null +++ b/src/test/java/com/iemr/tm/data/foetalmonitor/DataFoetalmonitorDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.foetalmonitor; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.foetalmonitor}. + */ +@DisplayName("com.iemr.tm.data.foetalmonitor data classes") +class DataFoetalmonitorDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.foetalmonitor"); + } +} diff --git a/src/test/java/com/iemr/tm/data/institution/DataInstitutionDataTest.java b/src/test/java/com/iemr/tm/data/institution/DataInstitutionDataTest.java new file mode 100644 index 00000000..edd8c7a9 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/institution/DataInstitutionDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.institution; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.institution}. + */ +@DisplayName("com.iemr.tm.data.institution data classes") +class DataInstitutionDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.institution"); + } +} diff --git a/src/test/java/com/iemr/tm/data/labModule/DataLabModuleDataTest.java b/src/test/java/com/iemr/tm/data/labModule/DataLabModuleDataTest.java new file mode 100644 index 00000000..66550e02 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/labModule/DataLabModuleDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.labModule; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.labModule}. + */ +@DisplayName("com.iemr.tm.data.labModule data classes") +class DataLabModuleDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.labModule"); + } +} diff --git a/src/test/java/com/iemr/tm/data/labtechnician/DataLabtechnicianDataTest.java b/src/test/java/com/iemr/tm/data/labtechnician/DataLabtechnicianDataTest.java new file mode 100644 index 00000000..bbe87814 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/labtechnician/DataLabtechnicianDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.labtechnician; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.labtechnician}. + */ +@DisplayName("com.iemr.tm.data.labtechnician data classes") +class DataLabtechnicianDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.labtechnician"); + } +} diff --git a/src/test/java/com/iemr/tm/data/location/DataLocationDataTest.java b/src/test/java/com/iemr/tm/data/location/DataLocationDataTest.java new file mode 100644 index 00000000..c9d6464f --- /dev/null +++ b/src/test/java/com/iemr/tm/data/location/DataLocationDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.location; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.location}. + */ +@DisplayName("com.iemr.tm.data.location data classes") +class DataLocationDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.location"); + } +} diff --git a/src/test/java/com/iemr/tm/data/login/DataLoginDataTest.java b/src/test/java/com/iemr/tm/data/login/DataLoginDataTest.java new file mode 100644 index 00000000..96d6974c --- /dev/null +++ b/src/test/java/com/iemr/tm/data/login/DataLoginDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.login; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.login}. + */ +@DisplayName("com.iemr.tm.data.login data classes") +class DataLoginDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.login"); + } +} diff --git a/src/test/java/com/iemr/tm/data/masterdata/anc/DataMasterdataAncDataTest.java b/src/test/java/com/iemr/tm/data/masterdata/anc/DataMasterdataAncDataTest.java new file mode 100644 index 00000000..fe7447d6 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/masterdata/anc/DataMasterdataAncDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.masterdata.anc; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.masterdata.anc}. + */ +@DisplayName("com.iemr.tm.data.masterdata.anc data classes") +class DataMasterdataAncDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.masterdata.anc"); + } +} diff --git a/src/test/java/com/iemr/tm/data/masterdata/doctor/DataMasterdataDoctorDataTest.java b/src/test/java/com/iemr/tm/data/masterdata/doctor/DataMasterdataDoctorDataTest.java new file mode 100644 index 00000000..09a62c50 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/masterdata/doctor/DataMasterdataDoctorDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.masterdata.doctor; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.masterdata.doctor}. + */ +@DisplayName("com.iemr.tm.data.masterdata.doctor data classes") +class DataMasterdataDoctorDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.masterdata.doctor"); + } +} diff --git a/src/test/java/com/iemr/tm/data/masterdata/ncdcare/DataMasterdataNcdcareDataTest.java b/src/test/java/com/iemr/tm/data/masterdata/ncdcare/DataMasterdataNcdcareDataTest.java new file mode 100644 index 00000000..bdaad864 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/masterdata/ncdcare/DataMasterdataNcdcareDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.masterdata.ncdcare; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.masterdata.ncdcare}. + */ +@DisplayName("com.iemr.tm.data.masterdata.ncdcare data classes") +class DataMasterdataNcdcareDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.masterdata.ncdcare"); + } +} diff --git a/src/test/java/com/iemr/tm/data/masterdata/ncdscreening/DataMasterdataNcdscreeningDataTest.java b/src/test/java/com/iemr/tm/data/masterdata/ncdscreening/DataMasterdataNcdscreeningDataTest.java new file mode 100644 index 00000000..49c478c1 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/masterdata/ncdscreening/DataMasterdataNcdscreeningDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.masterdata.ncdscreening; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.masterdata.ncdscreening}. + */ +@DisplayName("com.iemr.tm.data.masterdata.ncdscreening data classes") +class DataMasterdataNcdscreeningDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.masterdata.ncdscreening"); + } +} diff --git a/src/test/java/com/iemr/tm/data/masterdata/nurse/DataMasterdataNurseDataTest.java b/src/test/java/com/iemr/tm/data/masterdata/nurse/DataMasterdataNurseDataTest.java new file mode 100644 index 00000000..78d713e7 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/masterdata/nurse/DataMasterdataNurseDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.masterdata.nurse; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.masterdata.nurse}. + */ +@DisplayName("com.iemr.tm.data.masterdata.nurse data classes") +class DataMasterdataNurseDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.masterdata.nurse"); + } +} diff --git a/src/test/java/com/iemr/tm/data/masterdata/pnc/DataMasterdataPncDataTest.java b/src/test/java/com/iemr/tm/data/masterdata/pnc/DataMasterdataPncDataTest.java new file mode 100644 index 00000000..10c30f17 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/masterdata/pnc/DataMasterdataPncDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.masterdata.pnc; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.masterdata.pnc}. + */ +@DisplayName("com.iemr.tm.data.masterdata.pnc data classes") +class DataMasterdataPncDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.masterdata.pnc"); + } +} diff --git a/src/test/java/com/iemr/tm/data/masterdata/registrar/DataMasterdataRegistrarDataTest.java b/src/test/java/com/iemr/tm/data/masterdata/registrar/DataMasterdataRegistrarDataTest.java new file mode 100644 index 00000000..fd5a2177 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/masterdata/registrar/DataMasterdataRegistrarDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.masterdata.registrar; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.masterdata.registrar}. + */ +@DisplayName("com.iemr.tm.data.masterdata.registrar data classes") +class DataMasterdataRegistrarDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.masterdata.registrar"); + } +} diff --git a/src/test/java/com/iemr/tm/data/ncdScreening/DataNcdScreeningDataTest.java b/src/test/java/com/iemr/tm/data/ncdScreening/DataNcdScreeningDataTest.java new file mode 100644 index 00000000..2a238bbc --- /dev/null +++ b/src/test/java/com/iemr/tm/data/ncdScreening/DataNcdScreeningDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.ncdScreening; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.ncdScreening}. + */ +@DisplayName("com.iemr.tm.data.ncdScreening data classes") +class DataNcdScreeningDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.ncdScreening"); + } +} diff --git a/src/test/java/com/iemr/tm/data/ncdcare/DataNcdcareDataTest.java b/src/test/java/com/iemr/tm/data/ncdcare/DataNcdcareDataTest.java new file mode 100644 index 00000000..209c8783 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/ncdcare/DataNcdcareDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.ncdcare; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.ncdcare}. + */ +@DisplayName("com.iemr.tm.data.ncdcare data classes") +class DataNcdcareDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.ncdcare"); + } +} diff --git a/src/test/java/com/iemr/tm/data/nurse/DataNurseDataTest.java b/src/test/java/com/iemr/tm/data/nurse/DataNurseDataTest.java new file mode 100644 index 00000000..420d8cbf --- /dev/null +++ b/src/test/java/com/iemr/tm/data/nurse/DataNurseDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.nurse; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.nurse}. + */ +@DisplayName("com.iemr.tm.data.nurse data classes") +class DataNurseDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.nurse"); + } +} diff --git a/src/test/java/com/iemr/tm/data/patientApp/DataPatientAppDataTest.java b/src/test/java/com/iemr/tm/data/patientApp/DataPatientAppDataTest.java new file mode 100644 index 00000000..3eeb16c0 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/patientApp/DataPatientAppDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.patientApp; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.patientApp}. + */ +@DisplayName("com.iemr.tm.data.patientApp data classes") +class DataPatientAppDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.patientApp"); + } +} diff --git a/src/test/java/com/iemr/tm/data/pnc/DataPncDataTest.java b/src/test/java/com/iemr/tm/data/pnc/DataPncDataTest.java new file mode 100644 index 00000000..777f1625 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/pnc/DataPncDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.pnc; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.pnc}. + */ +@DisplayName("com.iemr.tm.data.pnc data classes") +class DataPncDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.pnc"); + } +} diff --git a/src/test/java/com/iemr/tm/data/provider/DataProviderDataTest.java b/src/test/java/com/iemr/tm/data/provider/DataProviderDataTest.java new file mode 100644 index 00000000..71c7ed85 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/provider/DataProviderDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.provider; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.provider}. + */ +@DisplayName("com.iemr.tm.data.provider data classes") +class DataProviderDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.provider"); + } +} diff --git a/src/test/java/com/iemr/tm/data/quickBlox/DataQuickBloxDataTest.java b/src/test/java/com/iemr/tm/data/quickBlox/DataQuickBloxDataTest.java new file mode 100644 index 00000000..8e6c6871 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/quickBlox/DataQuickBloxDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.quickBlox; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.quickBlox}. + */ +@DisplayName("com.iemr.tm.data.quickBlox data classes") +class DataQuickBloxDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.quickBlox"); + } +} diff --git a/src/test/java/com/iemr/tm/data/quickConsultation/DataQuickConsultationDataTest.java b/src/test/java/com/iemr/tm/data/quickConsultation/DataQuickConsultationDataTest.java new file mode 100644 index 00000000..cfad778e --- /dev/null +++ b/src/test/java/com/iemr/tm/data/quickConsultation/DataQuickConsultationDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.quickConsultation; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.quickConsultation}. + */ +@DisplayName("com.iemr.tm.data.quickConsultation data classes") +class DataQuickConsultationDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.quickConsultation"); + } +} diff --git a/src/test/java/com/iemr/tm/data/registrar/DataRegistrarDataTest.java b/src/test/java/com/iemr/tm/data/registrar/DataRegistrarDataTest.java new file mode 100644 index 00000000..b6318858 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/registrar/DataRegistrarDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.registrar; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.registrar}. + */ +@DisplayName("com.iemr.tm.data.registrar data classes") +class DataRegistrarDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.registrar"); + } +} diff --git a/src/test/java/com/iemr/tm/data/report/DataReportDataTest.java b/src/test/java/com/iemr/tm/data/report/DataReportDataTest.java new file mode 100644 index 00000000..7535aaf2 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/report/DataReportDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.report; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.report}. + */ +@DisplayName("com.iemr.tm.data.report data classes") +class DataReportDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.report"); + } +} diff --git a/src/test/java/com/iemr/tm/data/snomedct/DataSnomedctDataTest.java b/src/test/java/com/iemr/tm/data/snomedct/DataSnomedctDataTest.java new file mode 100644 index 00000000..c610c2f5 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/snomedct/DataSnomedctDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.snomedct; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.snomedct}. + */ +@DisplayName("com.iemr.tm.data.snomedct data classes") +class DataSnomedctDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.snomedct"); + } +} diff --git a/src/test/java/com/iemr/tm/data/syncActivity_syncLayer/DataSyncActivitySyncLayerDataTest.java b/src/test/java/com/iemr/tm/data/syncActivity_syncLayer/DataSyncActivitySyncLayerDataTest.java new file mode 100644 index 00000000..1f874daa --- /dev/null +++ b/src/test/java/com/iemr/tm/data/syncActivity_syncLayer/DataSyncActivitySyncLayerDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.syncActivity_syncLayer; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.syncActivity_syncLayer}. + */ +@DisplayName("com.iemr.tm.data.syncActivity_syncLayer data classes") +class DataSyncActivitySyncLayerDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.syncActivity_syncLayer"); + } +} diff --git a/src/test/java/com/iemr/tm/data/tele_consultation/DataTeleConsultationDataTest.java b/src/test/java/com/iemr/tm/data/tele_consultation/DataTeleConsultationDataTest.java new file mode 100644 index 00000000..451199ed --- /dev/null +++ b/src/test/java/com/iemr/tm/data/tele_consultation/DataTeleConsultationDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.tele_consultation; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.tele_consultation}. + */ +@DisplayName("com.iemr.tm.data.tele_consultation data classes") +class DataTeleConsultationDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.tele_consultation"); + } +} diff --git a/src/test/java/com/iemr/tm/data/videoconsultation/DataVideoconsultationDataTest.java b/src/test/java/com/iemr/tm/data/videoconsultation/DataVideoconsultationDataTest.java new file mode 100644 index 00000000..86f988c2 --- /dev/null +++ b/src/test/java/com/iemr/tm/data/videoconsultation/DataVideoconsultationDataTest.java @@ -0,0 +1,44 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.data.videoconsultation; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +/** + * Accessor, constructor and row-mapper coverage for every data class in + * {@code com.iemr.tm.data.videoconsultation}. + */ +@DisplayName("com.iemr.tm.data.videoconsultation data classes") +class DataVideoconsultationDataTest { + + @TestFactory + @DisplayName("every data class should round-trip its properties and map query rows") + List dataClassesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.data.videoconsultation"); + } +} diff --git a/src/test/java/com/iemr/tm/repo/location/ZoneDistrictMappingTest.java b/src/test/java/com/iemr/tm/repo/location/ZoneDistrictMappingTest.java new file mode 100644 index 00000000..ec4fe046 --- /dev/null +++ b/src/test/java/com/iemr/tm/repo/location/ZoneDistrictMappingTest.java @@ -0,0 +1,98 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/package com.iemr.tm.repo.location; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.Timestamp; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; + +import com.iemr.tm.common.PojoTestSupport; + +@DisplayName("Location repository entity Test Suite") +class ZoneDistrictMappingTest { + + @TestFactory + @DisplayName("The location entities should round trip every mapped property") + List locationEntitiesShouldRoundTripProperties() { + return PojoTestSupport.accessorTestsFor("com.iemr.tm.repo.location"); + } + + @Test + @DisplayName("The mapping should hold the zone to district link it was built with") + void mapping_shouldHoldZoneToDistrictLink() { + Timestamp createdDate = new Timestamp(System.currentTimeMillis()); + + ZoneDistrictMapping mapping = new ZoneDistrictMapping(1, 2, 3, 9, false, "N", "admin", createdDate, + "admin", createdDate); + + assertEquals(1, mapping.getZoneDistrictMapID()); + assertEquals(2, mapping.getZoneID()); + assertEquals(3, mapping.getDistrictID()); + assertEquals(9, mapping.getProviderServiceMapID()); + assertEquals(Boolean.FALSE, mapping.getDeleted()); + assertEquals("N", mapping.getProcessed()); + assertEquals("admin", mapping.getCreatedBy()); + assertEquals(createdDate, mapping.getCreatedDate()); + assertEquals("admin", mapping.getModifiedBy()); + assertEquals(createdDate, mapping.getLastModDate()); + } + + @Test + @DisplayName("The mapping should hold the districts it was built with") + void mapping_shouldHoldDistricts() { + Timestamp createdDate = new Timestamp(System.currentTimeMillis()); + com.iemr.tm.data.location.Districts district = new com.iemr.tm.data.location.Districts(); + + ZoneDistrictMapping mapping = new ZoneDistrictMapping(1, 2, 3, 9, false, "N", "admin", createdDate, + "admin", createdDate, Collections.singleton(district)); + + assertEquals(1, mapping.getDistrictsSet().size()); + assertTrue(mapping.getDistrictsSet().contains(district)); + } + + @Test + @DisplayName("An empty mapping should accept every mapped property") + void emptyMapping_shouldAcceptEveryProperty() { + ZoneDistrictMapping mapping = new ZoneDistrictMapping(); + mapping.setZoneDistrictMapID(1); + mapping.setZoneID(2); + mapping.setDistrictID(3); + mapping.setProviderServiceMapID(9); + mapping.setDeleted(true); + mapping.setProcessed("U"); + mapping.setCreatedBy("admin"); + mapping.setModifiedBy("admin"); + mapping.setDistrictsSet(Collections.emptySet()); + + assertEquals(1, mapping.getZoneDistrictMapID()); + assertEquals(Boolean.TRUE, mapping.getDeleted()); + assertEquals("U", mapping.getProcessed()); + assertTrue(mapping.getDistrictsSet().isEmpty()); + } +} diff --git a/src/test/java/com/iemr/tm/service/anc/ANCDoctorServiceImplTest.java b/src/test/java/com/iemr/tm/service/anc/ANCDoctorServiceImplTest.java new file mode 100644 index 00000000..83030221 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/anc/ANCDoctorServiceImplTest.java @@ -0,0 +1,68 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.anc; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.nurse.anc.ANCDiagnosisRepo; +import com.iemr.tm.repo.quickConsultation.PrescriptionDetailRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ANCDoctorServiceImpl Test Suite") +class ANCDoctorServiceImplTest { + + @Mock + private ANCDiagnosisRepo ancDiagnosisRepo; + @Mock + private PrescriptionDetailRepo prescriptionDetailRepo; + + @InjectMocks + private ANCDoctorServiceImpl service; + + @Test + @DisplayName("saveBenANCDiagnosis should answer for a well formed request") + void saveBenANCDiagnosis_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenANCDiagnosis(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("getANCDiagnosisDetails should answer for a well formed request") + void getANCDiagnosisDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getANCDiagnosisDetails(11L, 11L)); + } + + @Test + @DisplayName("updateBenANCDiagnosis should answer for a well formed request") + void updateBenANCDiagnosis_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenANCDiagnosis(new com.iemr.tm.data.anc.ANCDiagnosis())); + } +} diff --git a/src/test/java/com/iemr/tm/service/anc/ANCNurseServiceImplTest.java b/src/test/java/com/iemr/tm/service/anc/ANCNurseServiceImplTest.java new file mode 100644 index 00000000..dd5c640a --- /dev/null +++ b/src/test/java/com/iemr/tm/service/anc/ANCNurseServiceImplTest.java @@ -0,0 +1,249 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.anc; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.nurse.anc.ANCCareRepo; +import com.iemr.tm.repo.nurse.anc.ANCWomenVaccineRepo; +import com.iemr.tm.repo.nurse.anc.BenAdherenceRepo; +import com.iemr.tm.repo.nurse.anc.SysObstetricExaminationRepo; +import com.iemr.tm.repo.quickConsultation.LabTestOrderDetailRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ANCNurseServiceImpl Test Suite") +class ANCNurseServiceImplTest { + + @Mock + private ANCCareRepo ancCareRepo; + @Mock + private ANCWomenVaccineRepo ancWomenVaccineRepo; + @Mock + private BenAdherenceRepo benAdherenceRepo; + @Mock + private SysObstetricExaminationRepo sysObstetricExaminationRepo; + @Mock + private LabTestOrderDetailRepo labTestOrderDetailRepo; + + @InjectMocks + private ANCNurseServiceImpl service; + + @Test + @DisplayName("saveBeneficiaryANCDetails should answer for a well formed request") + void saveBeneficiaryANCDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBeneficiaryANCDetails(new com.iemr.tm.data.anc.ANCCareDetails())); + } + + @Test + @DisplayName("saveANCWomenVaccineDetails should answer for a well formed request") + void saveANCWomenVaccineDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveANCWomenVaccineDetails(new java.util.ArrayList<>())); + } + + @Test + @DisplayName("saveBenInvestigationFromDoc should answer for a well formed request") + void saveBenInvestigationFromDoc_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenInvestigationFromDoc(new com.iemr.tm.data.anc.WrapperBenInvestigationANC())); + } + + @Test + @DisplayName("saveBenAncCareDetails should answer for a well formed request") + void saveBenAncCareDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenAncCareDetails(new com.iemr.tm.data.anc.ANCCareDetails())); + } + + @Test + @DisplayName("saveAncImmunizationDetails should answer for a well formed request") + void saveAncImmunizationDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveAncImmunizationDetails(new com.iemr.tm.data.anc.WrapperAncImmunization())); + } + + @Test + @DisplayName("saveSysObstetricExamination should answer for a well formed request") + void saveSysObstetricExamination_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveSysObstetricExamination(org.mockito.Mockito.mock(com.iemr.tm.data.anc.SysObstetricExamination.class))); + } + + @Test + @DisplayName("getSysObstetricExamination should answer for a well formed request") + void getSysObstetricExamination_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getSysObstetricExamination(11L, 11L)); + } + + @Test + @DisplayName("getANCCareDetails should answer for a well formed request") + void getANCCareDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getANCCareDetails(11L, 11L)); + } + + @Test + @DisplayName("getANCWomenVaccineDetails should answer for a well formed request") + void getANCWomenVaccineDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getANCWomenVaccineDetails(11L, 11L)); + } + + @Test + @DisplayName("updateBenAdherenceDetails should answer for a well formed request") + void updateBenAdherenceDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenAdherenceDetails(new com.iemr.tm.data.anc.BenAdherence())); + } + + @Test + @DisplayName("updateBenAncCareDetails should answer for a well formed request") + void updateBenAncCareDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenAncCareDetails(new com.iemr.tm.data.anc.ANCCareDetails())); + } + + @Test + @DisplayName("updateBenAncImmunizationDetails should answer for a well formed request") + void updateBenAncImmunizationDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenAncImmunizationDetails(new com.iemr.tm.data.anc.WrapperAncImmunization())); + } + + @Test + @DisplayName("updateSysObstetricExamination should answer for a well formed request") + void updateSysObstetricExamination_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateSysObstetricExamination(org.mockito.Mockito.mock(com.iemr.tm.data.anc.SysObstetricExamination.class))); + } + + @org.junit.jupiter.api.Nested + @DisplayName("ANC care capture") + class AncCareTests { + + private com.iemr.tm.data.anc.ANCCareDetails careDetails() { + com.iemr.tm.data.anc.ANCCareDetails details = new com.iemr.tm.data.anc.ANCCareDetails(); + details.setBeneficiaryRegID(11L); + details.setVisitCode(22L); + return details; + } + + @Test + @DisplayName("saveBeneficiaryANCDetails should return the stored row id") + void saveAncDetails_shouldReturnStoredId() { + com.iemr.tm.data.anc.ANCCareDetails stored = careDetails(); + stored.setID(4L); + org.mockito.Mockito.when(ancCareRepo.save(org.mockito.ArgumentMatchers.any())).thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, service.saveBeneficiaryANCDetails(careDetails())); + } + + @Test + @DisplayName("saveBeneficiaryANCDetails should return null when nothing was stored") + void saveAncDetails_shouldReturnNullWhenNothingStored() { + org.mockito.Mockito.when(ancCareRepo.save(org.mockito.ArgumentMatchers.any())).thenReturn(null); + + org.junit.jupiter.api.Assertions.assertNull(service.saveBeneficiaryANCDetails(careDetails())); + } + + @Test + @DisplayName("saveBenAncCareDetails should store the ANC care details for the visit") + void saveAncCareDetails_shouldStoreCareDetails() throws Exception { + com.iemr.tm.data.anc.ANCCareDetails stored = careDetails(); + stored.setID(4L); + org.mockito.Mockito.when(ancCareRepo.save(org.mockito.ArgumentMatchers.any())).thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, service.saveBenAncCareDetails(careDetails())); + } + + @Test + @DisplayName("getANCCareDetails should render the stored ANC care for the visit") + void getAncCareDetails_shouldRenderStoredCare() { + org.mockito.Mockito.when(ancCareRepo.getANCCareDetails(11L, 22L)).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getANCCareDetails(11L, 22L)); + } + + @Test + @DisplayName("getANCWomenVaccineDetails should render the stored vaccine details for the visit") + void getWomenVaccineDetails_shouldRenderStoredVaccines() { + org.mockito.Mockito.when(ancWomenVaccineRepo.getANCWomenVaccineDetails(11L, 22L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getANCWomenVaccineDetails(11L, 22L)); + } + + @Test + @DisplayName("getSysObstetricExamination should delegate to the obstetric examination repository") + void getObstetricExamination_shouldDelegateToRepo() { + com.iemr.tm.data.anc.SysObstetricExamination stored = new com.iemr.tm.data.anc.SysObstetricExamination(); + org.mockito.Mockito.when(sysObstetricExaminationRepo.getSysObstetricExaminationData(11L, 22L)) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(stored, service.getSysObstetricExamination(11L, 22L)); + } + + @Test + @DisplayName("saveSysObstetricExamination should return the stored row id") + void saveObstetricExamination_shouldReturnStoredId() { + com.iemr.tm.data.anc.SysObstetricExamination stored = new com.iemr.tm.data.anc.SysObstetricExamination(); + stored.setID(4L); + org.mockito.Mockito.when(sysObstetricExaminationRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, service.saveSysObstetricExamination( + new com.iemr.tm.data.anc.SysObstetricExamination())); + } + + @Test + @DisplayName("updateBenAncCareDetails should mark an already processed row as updated") + void updateAncCareDetails_shouldMarkProcessedAsUpdated() throws Exception { + org.mockito.Mockito.when(ancCareRepo.getBenANCCareDetailsStatus(11L, 22L)).thenReturn("P"); + + org.junit.jupiter.api.Assertions.assertEquals(0, service.updateBenAncCareDetails(careDetails())); + } + + @Test + @DisplayName("updateSysObstetricExamination should mark an already processed row as updated") + void updateObstetricExamination_shouldMarkProcessedAsUpdated() { + com.iemr.tm.data.anc.SysObstetricExamination examination = + new com.iemr.tm.data.anc.SysObstetricExamination(); + examination.setBeneficiaryRegID(11L); + examination.setVisitCode(22L); + org.mockito.Mockito.when(sysObstetricExaminationRepo.getBenObstetricExaminationStatus(11L, 22L)) + .thenReturn("P"); + + org.junit.jupiter.api.Assertions.assertEquals(0, service.updateSysObstetricExamination(examination)); + } + + @Test + @DisplayName("updateBenAdherenceDetails should mark an already processed row as updated") + void updateAdherenceDetails_shouldMarkProcessedAsUpdated() { + com.iemr.tm.data.anc.BenAdherence adherence = new com.iemr.tm.data.anc.BenAdherence(); + adherence.setBeneficiaryRegID(11L); + adherence.setVisitCode(22L); + org.mockito.Mockito.when(benAdherenceRepo.getBenAdherenceDetailsStatus(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.any())).thenReturn("P"); + + org.junit.jupiter.api.Assertions.assertEquals(0, service.updateBenAdherenceDetails(adherence)); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/anc/ANCServiceImplTest.java b/src/test/java/com/iemr/tm/service/anc/ANCServiceImplTest.java new file mode 100644 index 00000000..af39f95f --- /dev/null +++ b/src/test/java/com/iemr/tm/service/anc/ANCServiceImplTest.java @@ -0,0 +1,722 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.anc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyShort; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.iemr.tm.data.anc.ANCCareDetails; +import com.iemr.tm.data.anc.FemaleObstetricHistory; +import com.iemr.tm.data.nurse.CommonUtilityClass; +import com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ; +import com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo; +import com.iemr.tm.repo.nurse.anc.ANCCareRepo; +import com.iemr.tm.repo.nurse.BenAnthropometryRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.nurse.anc.BenAdherenceRepo; +import com.iemr.tm.repo.nurse.anc.BenMedHistoryRepo; +import com.iemr.tm.repo.nurse.anc.BenMenstrualDetailsRepo; +import com.iemr.tm.repo.nurse.anc.BencomrbidityCondRepo; +import com.iemr.tm.repo.nurse.anc.FemaleObstetricHistoryRepo; +import com.iemr.tm.repo.quickConsultation.BenChiefComplaintRepo; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.service.labtechnician.LabTechnicianServiceImpl; +import com.iemr.tm.service.tele_consultation.SMSGatewayServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ANCServiceImpl Test Suite") +class ANCServiceImplTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_ID = 3L; + private static final Long VISIT_CODE = 22L; + + @Mock + private ANCNurseServiceImpl ancNurseServiceImpl; + @Mock + private ANCDoctorServiceImpl ancDoctorServiceImpl; + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private LabTechnicianServiceImpl labTechnicianServiceImpl; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private BenAnthropometryRepo benAnthropometryRepo; + @Mock + private BenMedHistoryRepo benMedHistoryRepo; + @Mock + private BencomrbidityCondRepo bencomrbidityCondRepo; + @Mock + private ANCCareRepo ancCareRepo; + @Mock + private FemaleObstetricHistoryRepo femaleObstetricHistoryRepo; + @Mock + private com.iemr.tm.repo.nurse.anc.ANCDiagnosisRepo aNCDiagnosisRepo; + @Mock + private com.iemr.tm.repo.foetalmonitor.FoetalMonitorRepo foetalMonitorRepo; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private BenChiefComplaintRepo benChiefComplaintRepo; + @Mock + private BenAdherenceRepo benAdherenceRepo; + @Mock + private BenMenstrualDetailsRepo benMenstrualDetailsRepo; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + + @InjectMocks + private ANCServiceImpl service; + + private static final String FULL_HISTORY = "{\"pastHistory\":{},\"comorbidConditions\":{}," + + "\"medicationHistory\":{\"medicationHistoryList\":[{\"currentMedication\":\"Iron\"}]}," + + "\"personalHistory\":{},\"familyHistory\":{},\"menstrualHistory\":{}," + + "\"femaleObstetricHistory\":{},\"immunizationHistory\":{},\"childVaccineDetails\":{}}"; + + private static final String FULL_EXAMINATION = "{\"generalExamination\":{},\"headToToeExamination\":{}," + + "\"cardioVascularExamination\":{},\"respiratorySystemExamination\":{}," + + "\"centralNervousSystemExamination\":{},\"musculoskeletalSystemExamination\":{}," + + "\"genitoUrinarySystemExamination\":{},\"obstetricExamination\":{}}"; + + private JsonObject json(String raw) { + return JsonParser.parseString(raw).getAsJsonObject(); + } + + private JsonObject nurseRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"providerServiceMapID\":9,\"vanID\":7,\"sessionID\":1," + + "\"benFlowID\":5,\"createdBy\":\"nurse1\"," + + "\"visitDetails\":{" + + " \"visitDetails\":{\"beneficiaryRegID\":11,\"visitReason\":\"New Chief Complaint\"," + + " \"visitCategory\":\"ANC\"}," + + " \"chiefComplaints\":[{\"chiefComplaintID\":3}],\"adherence\":{\"toDrugs\":true}," + + " \"investigation\":{\"laboratoryList\":[{\"testID\":1}]}" + + "}," + + "\"ancDetails\":{\"ancObstetricDetails\":{},\"ancImmunization\":{}}," + + "\"historyDetails\":" + FULL_HISTORY + ",\"vitalDetails\":{\"height_cm\":170}," + + "\"examinationDetails\":" + FULL_EXAMINATION + "}"); + } + + private CommonUtilityClass utility() { + CommonUtilityClass utility = new CommonUtilityClass(); + utility.setBeneficiaryRegID(BEN_REG_ID); + utility.setVanID(7); + utility.setSessionID(1); + utility.setProviderServiceMapID(9); + utility.setCreatedBy("nurse1"); + return utility; + } + + private TeleconsultationRequestOBJ teleconsultationRequest(boolean walkIn) { + TeleconsultationRequestOBJ request = new TeleconsultationRequestOBJ(); + request.setWalkIn(walkIn); + request.setUserID(42); + request.setSpecializationID(2); + request.setTmRequestID(8L); + request.setAllocationDate(new Timestamp(1_700_000_000_000L)); + return request; + } + + @BeforeEach + @DisplayName("Wire the collaborators that every save path needs") + void setUp() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(0); + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(VISIT_ID); + when(commonNurseServiceImpl.generateVisitCode(anyLong(), any(), any())).thenReturn(VISIT_CODE); + when(commonNurseServiceImpl.saveBenChiefComplaints(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenAdherenceDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenInvestigationDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalAnthropometryDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePhyGeneralExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePhyHeadToToeExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysCardiovascularExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysRespiratoryExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysCentralNervousExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysMusculoskeletalSystemExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysGenitourinarySystemExamination(any())).thenReturn(1L); + when(ancNurseServiceImpl.saveSysObstetricExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPastHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenComorbidConditions(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMedicationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePersonalHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveAllergyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenFamilyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMenstrualHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveFemaleObstetricHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveImmunizationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildOptionalVaccineDetail(any())).thenReturn(1L); + when(ancNurseServiceImpl.saveBenAncCareDetails(any())).thenReturn(1L); + when(ancNurseServiceImpl.saveAncImmunizationDetails(any())).thenReturn(1L); + when(foetalMonitorRepo.getFoetalMonitorDetailsByFlowId(any())).thenReturn(new ArrayList<>()); + when(commonBenStatusFlowServiceImpl.updateBenFlowNurseAfterNurseActivityANC(any(), anyLong(), anyLong(), + anyString(), anyString(), anyShort(), anyShort(), anyShort(), anyShort(), anyShort(), anyLong(), any(), + anyShort(), any(), any(), anyShort())).thenReturn(1); + } + + @Nested + @DisplayName("saveANCNurseData") + class SaveNurseDataTests { + + @Test + @DisplayName("saveANCNurseData should save the visit, ANC, history, vitals and examination") + void saveNurseData_shouldSaveEverySection() throws Exception { + String result = service.saveANCNurseData(nurseRequest(), AUTHORIZATION); + + assertTrue(result.contains("Data saved successfully")); + assertTrue(result.contains("\"visitCode\":\"22\"")); + } + + @Test + @DisplayName("saveANCNurseData should report an already saved visit") + void saveNurseData_shouldReportAlreadySavedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.saveANCNurseData(nurseRequest(), AUTHORIZATION).contains("Data already saved")); + } + + @Test + @DisplayName("saveANCNurseData should reject a request without visit details") + void saveNurseData_shouldRejectRequestWithoutVisitDetails() { + assertThrows(Exception.class, () -> service.saveANCNurseData(json("{}"), AUTHORIZATION)); + } + + @Test + @DisplayName("saveANCNurseData should fail when the visit could not be created") + void saveNurseData_shouldFailWhenVisitNotCreated() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(0L); + + assertThrows(RuntimeException.class, () -> service.saveANCNurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveANCNurseData should fail when the ANC details could not be stored") + void saveNurseData_shouldFailWhenAncDetailsNotStored() throws Exception { + when(ancNurseServiceImpl.saveBenAncCareDetails(any())).thenReturn(null); + + assertThrows(RuntimeException.class, () -> service.saveANCNurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveANCNurseData should notify a scheduled teleconsultation by SMS") + void saveNurseData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.saveANCNurseData(nurseRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), + anyString(), anyString(), any(), eq(AUTHORIZATION)); + } + + @Test + @DisplayName("saveANCNurseData should not notify a walk-in teleconsultation") + void saveNurseData_shouldNotNotifyWalkInTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())).thenReturn(teleconsultationRequest(true)); + + service.saveANCNurseData(nurseRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl, never()).smsSenderGateway(anyString(), anyLong(), anyInt(), anyLong(), any(), + anyString(), anyString(), any(), anyString()); + } + } + + @Nested + @DisplayName("deleteVisitDetails") + class DeleteVisitDetailsTests { + + @Test + @DisplayName("deleteVisitDetails should remove the visit rows for a created visit") + void deleteVisitDetails_shouldRemoveVisitRows() throws Exception { + when(benVisitDetailRepo.getVisitCode(BEN_REG_ID, 9)).thenReturn(VISIT_CODE); + + service.deleteVisitDetails(nurseRequest()); + + verify(benVisitDetailRepo).deleteVisitDetails(VISIT_CODE); + } + + @Test + @DisplayName("deleteVisitDetails should do nothing for a request without visit details") + void deleteVisitDetails_shouldDoNothingWithoutVisitDetails() throws Exception { + service.deleteVisitDetails(json("{}")); + + verify(benVisitDetailRepo, never()).deleteVisitDetails(anyLong()); + } + } + + @Nested + @DisplayName("nurse section saves") + class SectionSaveTests { + + @Test + @DisplayName("saveBenVisitDetails should return the new visit id and code") + void saveBenVisitDetails_shouldReturnVisitIdAndCode() throws Exception { + Map result = service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), + utility()); + + assertEquals(VISIT_ID, result.get("visitID")); + assertEquals(VISIT_CODE, result.get("visitCode")); + } + + @Test + @DisplayName("saveBenVisitDetails should return nothing when the visit was already recorded") + void saveBenVisitDetails_shouldReturnNothingForAlreadyRecordedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), utility()) + .isEmpty()); + } + + @Test + @DisplayName("saveBenANCDetails should store the obstetric details and the immunisation") + void saveAncDetails_shouldStoreObstetricAndImmunisation() throws Exception { + assertEquals(1L, service.saveBenANCDetails( + json("{\"ancObstetricDetails\":{},\"ancImmunization\":{}}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenANCDetails should report a failure when the obstetric details were not captured") + void saveAncDetails_shouldReportFailureWithoutObstetricDetails() throws Exception { + assertNull(service.saveBenANCDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenANCHistoryDetails should report a failure when no history section was captured") + void saveHistory_shouldReportFailureWithoutCapturedSections() throws Exception { + assertNull(service.saveBenANCHistoryDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenANCHistoryDetails should store every captured history section") + void saveHistory_shouldStoreCapturedSections() throws Exception { + when(commonNurseServiceImpl.saveBenPastHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenComorbidConditions(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMedicationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePersonalHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveAllergyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenFamilyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMenstrualHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveFemaleObstetricHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveImmunizationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildOptionalVaccineDetail(any())).thenReturn(1L); + + JsonObject history = json("{\"pastHistory\":{},\"comorbidConditions\":{}," + + "\"medicationHistory\":{\"medicationHistoryList\":[{\"currentMedication\":\"Iron\"}]}," + + "\"personalHistory\":{},\"familyHistory\":{},\"menstrualHistory\":{}," + + "\"femaleObstetricHistory\":{},\"immunizationHistory\":{},\"childVaccineDetails\":{}}"); + + assertEquals(1L, service.saveBenANCHistoryDetails(history, VISIT_ID, VISIT_CODE)); + verify(commonNurseServiceImpl).saveBenPastHistory(any()); + } + + @Test + @DisplayName("saveBenANCVitalDetails should store the anthropometry and the physical vitals") + void saveVitals_shouldStoreAnthropometryAndPhysicalVitals() throws Exception { + assertEquals(1L, service.saveBenANCVitalDetails(json("{\"height_cm\":170}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenANCVitalDetails should report a failure when the physical vitals were not stored") + void saveVitals_shouldReportFailureWhenPhysicalVitalsNotStored() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(null); + + assertNull(service.saveBenANCVitalDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenANCExaminationDetails should report a failure when no examination section was captured") + void saveExamination_shouldReportFailureWithoutCapturedSections() throws Exception { + assertNull(service.saveBenANCExaminationDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenANCExaminationDetails should store every captured examination section") + void saveExamination_shouldStoreCapturedSections() throws Exception { + when(commonNurseServiceImpl.savePhyHeadToToeExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysCardiovascularExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysRespiratoryExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysCentralNervousExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysMusculoskeletalSystemExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysGenitourinarySystemExamination(any())).thenReturn(1L); + when(ancNurseServiceImpl.saveSysObstetricExamination(any())).thenReturn(1L); + + JsonObject examination = json("{\"generalExamination\":{},\"headToToeExamination\":{}," + + "\"cardioVascularExamination\":{},\"respiratorySystemExamination\":{}," + + "\"centralNervousSystemExamination\":{},\"musculoskeletalSystemExamination\":{}," + + "\"genitoUrinarySystemExamination\":{},\"obstetricExamination\":{}}"); + + assertEquals(1L, service.saveBenANCExaminationDetails(examination, VISIT_ID, VISIT_CODE)); + verify(commonNurseServiceImpl).savePhyHeadToToeExamination(any()); + } + } + + @Nested + @DisplayName("read endpoints") + class ReadTests { + + @Test + @DisplayName("getBenVisitDetailsFrmNurseANC should assemble the visit, adherence, complaints and tests") + void getVisitDetails_shouldAssembleVisitSections() { + when(commonNurseServiceImpl.getBenAdherence(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + when(commonNurseServiceImpl.getBenChiefComplaints(BEN_REG_ID, VISIT_CODE)).thenReturn("[]"); + when(commonNurseServiceImpl.getLabTestOrders(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + + String result = service.getBenVisitDetailsFrmNurseANC(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("ANCNurseVisitDetail")); + assertTrue(result.contains("BenAdherence")); + } + + @Test + @DisplayName("getBenANCDetailsFrmNurseANC should assemble the ANC care and vaccine details") + void getAncDetails_shouldAssembleAncSections() { + when(ancNurseServiceImpl.getANCCareDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + when(ancNurseServiceImpl.getANCWomenVaccineDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + + String result = service.getBenANCDetailsFrmNurseANC(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("ANCCareDetail")); + assertTrue(result.contains("ANCWomenVaccineDetails")); + } + + @Test + @DisplayName("getBenANCHistoryDetails should assemble every stored history section") + void getHistoryDetails_shouldAssembleStoredSections() { + when(commonNurseServiceImpl.getPastHistoryData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.BenMedHistory()); + + assertTrue(service.getBenANCHistoryDetails(BEN_REG_ID, VISIT_CODE).contains("PastHistory")); + } + + @Test + @DisplayName("getBeneficiaryVitalDetails should assemble the anthropometry and the physical vitals") + void getVitalDetails_shouldAssembleVitalSections() { + when(commonNurseServiceImpl.getBeneficiaryPhysicalAnthropometryDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn("{}"); + when(commonNurseServiceImpl.getBeneficiaryPhysicalVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + + assertTrue(service.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE).contains("benAnthropometryDetail")); + } + + @Test + @DisplayName("getANCExaminationDetailsData should assemble every stored examination section") + void getExaminationDetails_shouldAssembleStoredSections() { + when(commonNurseServiceImpl.getGeneralExaminationData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.PhyGeneralExamination()); + + assertTrue(service.getANCExaminationDetailsData(BEN_REG_ID, VISIT_CODE).contains("generalExamination")); + } + + @Test + @DisplayName("getBenANCNurseData should assemble the nurse captured sections") + void getNurseData_shouldAssembleNurseSections() { + assertTrue(service.getBenANCNurseData(BEN_REG_ID, VISIT_CODE).contains("history")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorANC should assemble the whole doctor case record") + void getCaseRecord_shouldAssembleDoctorCaseRecord() throws Exception { + when(commonNurseServiceImpl.getGraphicalTrendData(BEN_REG_ID, "anc")).thenReturn(new HashMap<>()); + + String result = service.getBenCaseRecordFromDoctorANC(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("findings")); + assertTrue(result.contains("fetosenseData")); + assertTrue(result.contains("GraphData")); + } + } + + @Nested + @DisplayName("saveANCDoctorData") + class SaveDoctorDataTests { + + private JsonObject doctorRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22,\"providerServiceMapID\":9," + + "\"createdBy\":\"doctor1\",\"doctorSignatureFlag\":true,\"findings\":{}," + + "\"diagnosis\":{\"specialistDiagnosis\":\"ANC follow up\"}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + } + + @BeforeEach + void stubDoctorCollaborators() throws Exception { + when(commonDoctorServiceImpl.saveDocFindings(any())).thenReturn(1); + when(commonNurseServiceImpl.savePrescriptionDetailsAndGetPrescriptionID(any(), any(), any(), any(), any(), + any(), any(), any(), any(), any())).thenReturn(4L); + when(ancDoctorServiceImpl.saveBenANCDiagnosis(any(), any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.saveBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + } + + @Test + @DisplayName("saveANCDoctorData should save the findings, diagnosis, tests, drugs and referral") + void saveDoctorData_shouldSaveWholeCaseRecord() throws Exception { + assertEquals(1L, service.saveANCDoctorData(doctorRequest(), AUTHORIZATION)); + + verify(commonDoctorServiceImpl).saveDocFindings(any()); + verify(commonNurseServiceImpl).saveBenInvestigation(any()); + } + + @Test + @DisplayName("saveANCDoctorData should succeed for a case record with only an investigation section") + void saveDoctorData_shouldSucceedForMinimalCaseRecord() throws Exception { + assertEquals(1L, + service.saveANCDoctorData(json("{\"beneficiaryRegID\":11,\"investigation\":{}}"), AUTHORIZATION)); + } + + @Test + @DisplayName("saveANCDoctorData should fail when the beneficiary flow could not be advanced") + void saveDoctorData_shouldFailWhenFlowNotAdvanced() throws Exception { + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(0); + + assertThrows(RuntimeException.class, () -> service.saveANCDoctorData(doctorRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveANCDoctorData should notify a scheduled teleconsultation by SMS") + void saveDoctorData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.saveANCDoctorData(doctorRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), any(), + anyString(), any(), eq(AUTHORIZATION)); + } + } + + @Nested + @DisplayName("update endpoints") + class UpdateTests { + + @Test + @DisplayName("updateBenANCDetails should report no change when no ANC section was captured") + void updateAncDetails_shouldReportNoChangeWithoutCapturedSections() throws Exception { + assertEquals(0, service.updateBenANCDetails(json("{}"))); + } + + @Test + @DisplayName("updateBenANCHistoryDetails should succeed when no history section was captured") + void updateHistory_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1, service.updateBenANCHistoryDetails(json("{}"))); + } + + @Test + @DisplayName("updateBenANCVitalDetails should confirm the update when both sections changed") + void updateVitals_shouldConfirmUpdate() throws Exception { + when(commonNurseServiceImpl.updateANCAnthropometryDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.updateANCPhysicalVitalDetails(any())).thenReturn(1); + + assertEquals(1, service.updateBenANCVitalDetails(json("{\"height_cm\":170}"))); + } + + @Test + @DisplayName("updateBenANCExaminationDetails should report no change when no section was captured") + void updateExamination_shouldReportNoChangeWithoutCapturedSections() throws Exception { + assertEquals(0, service.updateBenANCExaminationDetails(json("{}"))); + } + + @Test + @DisplayName("updateANCDoctorData should return nothing for a null request") + void updateDoctorData_shouldReturnNothingForNullRequest() throws Exception { + assertNull(service.updateANCDoctorData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("updateANCDoctorData should update the whole case record") + void updateDoctorData_shouldUpdateWholeCaseRecord() throws Exception { + when(commonDoctorServiceImpl.updateDocFindings(any())).thenReturn(1); + when(commonNurseServiceImpl.updatePrescription(any())).thenReturn(1); + when(ancDoctorServiceImpl.updateBenANCDiagnosis(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.updateBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataUpdate(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + + JsonObject request = json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22," + + "\"findings\":{},\"diagnosis\":{\"prescriptionID\":4}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + + assertEquals(1L, service.updateANCDoctorData(request, AUTHORIZATION)); + } + } + + @Nested + @DisplayName("getHRPStatus") + class HighRiskPregnancyTests { + + @Test + @DisplayName("getHRPStatus should flag a beneficiary who is younger than twenty") + void getHRPStatus_shouldFlagYoungBeneficiary() throws Exception { + java.time.LocalDate dob = java.time.LocalDate.now().minusYears(18); + when(beneficiaryFlowStatusRepo.getBenAgeVal(BEN_REG_ID)) + .thenReturn(Timestamp.valueOf(dob.atStartOfDay())); + + assertTrue(service.getHRPStatus(BEN_REG_ID, VISIT_CODE).contains("true")); + } + + @Test + @DisplayName("getHRPStatus should flag a beneficiary shorter than 145 cm") + void getHRPStatus_shouldFlagShortBeneficiary() throws Exception { + when(benAnthropometryRepo.getBenLatestHeight(BEN_REG_ID)).thenReturn(140d); + + assertTrue(service.getHRPStatus(BEN_REG_ID, VISIT_CODE).contains("true")); + } + + @Test + @DisplayName("getHRPStatus should flag a beneficiary with a risk marker in the ANC care screen") + void getHRPStatus_shouldFlagAncCareRiskMarker() throws Exception { + when(ancCareRepo.getANCCareDataForHRP(BEN_REG_ID)) + .thenReturn(new ArrayList<>(Collections.singletonList(new ANCCareDetails()))); + + assertTrue(service.getHRPStatus(BEN_REG_ID, VISIT_CODE).contains("true")); + } + + @Test + @DisplayName("getHRPStatus should flag a beneficiary with a relevant past illness") + void getHRPStatus_shouldFlagPastIllness() throws Exception { + when(benMedHistoryRepo.getHRPStatus(BEN_REG_ID)) + .thenReturn(new ArrayList<>(Collections.singletonList(1L))); + + assertTrue(service.getHRPStatus(BEN_REG_ID, VISIT_CODE).contains("true")); + } + + @Test + @DisplayName("getHRPStatus should flag a beneficiary with a relevant comorbidity") + void getHRPStatus_shouldFlagComorbidity() throws Exception { + when(bencomrbidityCondRepo.getHRPStatus(BEN_REG_ID)) + .thenReturn(new ArrayList<>(Collections.singletonList(1L))); + + assertTrue(service.getHRPStatus(BEN_REG_ID, VISIT_CODE).contains("true")); + } + + @Test + @DisplayName("getHRPStatus should flag a beneficiary with a relevant obstetric history") + void getHRPStatus_shouldFlagObstetricHistory() throws Exception { + when(femaleObstetricHistoryRepo.getPastObestetricDataForHRP(anyLong(), anyString(), anyString(), + anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), + anyString(), anyString(), anyString(), anyString(), anyString(), any())) + .thenReturn(new ArrayList<>(Collections.singletonList(new FemaleObstetricHistory()))); + + assertTrue(service.getHRPStatus(BEN_REG_ID, VISIT_CODE).contains("true")); + } + + @Test + @DisplayName("getHRPStatus should flag a beneficiary with a relevant recorded diagnosis") + void getHRPStatus_shouldFlagRecordedDiagnosis() throws Exception { + when(aNCDiagnosisRepo.getANCDiagnosisDataForHRP(anyLong(), anyString(), anyString(), anyString(), + anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), + anyString(), anyString())).thenReturn(new ArrayList<>(Collections.singletonList(1L))); + + assertTrue(service.getHRPStatus(BEN_REG_ID, VISIT_CODE).contains("true")); + } + + @Test + @DisplayName("getHRPStatus should not flag a beneficiary without any risk marker") + void getHRPStatus_shouldNotFlagWithoutRiskMarker() throws Exception { + java.time.LocalDate dob = java.time.LocalDate.now().minusYears(28); + when(beneficiaryFlowStatusRepo.getBenAgeVal(BEN_REG_ID)) + .thenReturn(Timestamp.valueOf(dob.atStartOfDay())); + when(benAnthropometryRepo.getBenLatestHeight(BEN_REG_ID)).thenReturn(160d); + + assertTrue(service.getHRPStatus(BEN_REG_ID, VISIT_CODE).contains("false")); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("ANC captured-section updates") + class CapturedSectionUpdateTests { + + @Test + @DisplayName("updateBenANCHistoryDetails should update every captured history section") + void updateBenANCHistoryDetails_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertDoesNotThrow(() -> service.updateBenANCHistoryDetails( + com.google.gson.JsonParser.parseString("{\"pastHistory\":{},\"comorbidConditions\":{},\"medicationHistory\":{},\"personalHistory\":{},\"familyHistory\":{},\"menstrualHistory\":{},\"femaleObstetricHistory\":{},\"immunizationHistory\":{},\"childVaccineDetails\":{},\"developmentHistory\":{},\"feedingHistory\":{},\"perinatalHistroy\":{},\"allergyHistory\":{}}").getAsJsonObject())); + } + + @Test + @DisplayName("updateBenANCExaminationDetails should update every captured examination section") + void updateBenANCExaminationDetails_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertDoesNotThrow(() -> service.updateBenANCExaminationDetails( + com.google.gson.JsonParser.parseString("{\"generalExamination\":{},\"headToToeExamination\":{},\"gastroIntestinalExamination\":{},\"cardioVascularExamination\":{},\"respiratorySystemExamination\":{},\"centralNervousSystemExamination\":{},\"musculoskeletalSystemExamination\":{},\"genitoUrinarySystemExamination\":{},\"obstetricExamination\":{}}").getAsJsonObject())); + } + + @Test + @DisplayName("updateBenANCDetails should update every captured ANC section") + void updateBenANCDetails_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertDoesNotThrow(() -> service.updateBenANCDetails( + com.google.gson.JsonParser.parseString( + "{\"ancObstetricDetails\":{},\"ancImmunization\":{}}").getAsJsonObject())); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/benFlowStatus/CommonBenStatusFlowServiceImplTest.java b/src/test/java/com/iemr/tm/service/benFlowStatus/CommonBenStatusFlowServiceImplTest.java new file mode 100644 index 00000000..0f05529e --- /dev/null +++ b/src/test/java/com/iemr/tm/service/benFlowStatus/CommonBenStatusFlowServiceImplTest.java @@ -0,0 +1,345 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.benFlowStatus; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CommonBenStatusFlowServiceImpl Test Suite") +class CommonBenStatusFlowServiceImplTest { + + @Mock + private BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + + @InjectMocks + private CommonBenStatusFlowServiceImpl service; + + @Test + @DisplayName("createBenFlowRecord should answer for a well formed request") + void createBenFlowRecord_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.createBenFlowRecord("{}", 11L, 11L)); + } + + @Test + @DisplayName("updateBenFlowNurseAfterNurseActivity should answer for a well formed request") + void updateBenFlowNurseAfterNurseActivity_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenFlowNurseAfterNurseActivity(11L, 11L, 11L, "{}", "{}", (short) 1, (short) 1, (short) 1, (short) 1, (short) 1, 11L, 9, (short) 1, new java.sql.Timestamp(1_700_000_000_000L), 9)); + } + + @Test + @DisplayName("updateBenFlowNurseAfterNurseActivityANC should answer for a well formed request") + void updateBenFlowNurseAfterNurseActivityANC_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenFlowNurseAfterNurseActivityANC(11L, 11L, 11L, "{}", "{}", (short) 1, (short) 1, (short) 1, (short) 1, (short) 1, 11L, 9, (short) 1, new java.sql.Timestamp(1_700_000_000_000L), 9, (short) 1)); + } + + @Test + @DisplayName("updateBenFlowNurseAfterNurseUpdateNCD_Screening should answer for a well formed request") + void updateBenFlowNurseAfterNurseUpdateNCD_Screening_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenFlowNurseAfterNurseUpdateNCD_Screening(11L, 11L, (short) 1)); + } + + @Test + @DisplayName("updateBenFlowAfterDocData should answer for a well formed request") + void updateBenFlowAfterDocData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenFlowAfterDocData(11L, 11L, 11L, 11L, (short) 1, (short) 1, (short) 1, (short) 1, 9, new java.sql.Timestamp(1_700_000_000_000L), (short) 1, Boolean.FALSE)); + } + + @Test + @DisplayName("updateBenFlowAfterDocDataFromSpecialist should answer for a well formed request") + void updateBenFlowAfterDocDataFromSpecialist_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenFlowAfterDocDataFromSpecialist(11L, 11L, 11L, 11L, (short) 1, (short) 1, (short) 1, (short) 1, (short) 1, Boolean.FALSE)); + } + + @Test + @DisplayName("updateBenFlowAfterDocDataFromSpecialistANC should answer for a well formed request") + void updateBenFlowAfterDocDataFromSpecialistANC_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenFlowAfterDocDataFromSpecialistANC(11L, 11L, 11L, 11L, (short) 1, (short) 1, (short) 1, (short) 1, (short) 1, Boolean.FALSE)); + } + + @Test + @DisplayName("updateBenFlowAfterDocDataUpdate should answer for a well formed request") + void updateBenFlowAfterDocDataUpdate_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenFlowAfterDocDataUpdate(11L, 11L, 11L, 11L, (short) 1, (short) 1, (short) 1, (short) 1, 9, new java.sql.Timestamp(1_700_000_000_000L), (short) 1, Boolean.FALSE)); + } + + @Test + @DisplayName("updateBenFlowAfterDocDataUpdateTCSpecialist should answer for a well formed request") + void updateBenFlowAfterDocDataUpdateTCSpecialist_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenFlowAfterDocDataUpdateTCSpecialist(11L, 11L, 11L, 11L, (short) 1, (short) 1, (short) 1, (short) 1, 9, new java.sql.Timestamp(1_700_000_000_000L), (short) 1, Boolean.FALSE)); + } + + @Test + @DisplayName("updateFlowAfterLabResultEntry should answer for a well formed request") + void updateFlowAfterLabResultEntry_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateFlowAfterLabResultEntry(11L, 11L, 11L, (short) 1, (short) 1, (short) 1)); + } + + @Test + @DisplayName("updateFlowAfterLabResultEntryForTCSpecialist should answer for a well formed request") + void updateFlowAfterLabResultEntryForTCSpecialist_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateFlowAfterLabResultEntryForTCSpecialist(11L, 11L, (short) 1)); + } + + @org.junit.jupiter.api.Nested + @DisplayName("beneficiary flow record creation") + class FlowRecordCreationTests { + + private static final String REGISTRATION = "{\"beneficiaryRegID\":11,\"beneficiaryID\":9," + + "\"providerServiceMapID\":3,\"vanID\":7,\"firstName\":\"Asha\",\"lastName\":\"Devi\"," + + "\"createdBy\":\"registrar1\",\"genderID\":2,\"genderName\":\"Female\"," + + "\"dOB\":\"1990-05-14T00:00:00.000Z\"," + + "\"i_bendemographics\":{\"districtID\":21,\"districtName\":\"Nagpur\"," + + "\"districtBranchID\":31,\"districtBranchName\":\"Kamptee\"," + + "\"servicePointID\":41,\"servicePointName\":\"PHC Kamptee\"}," + + "\"benPhoneMaps\":[{\"phoneNo\":\"9999999999\"}]," + + "\"m_gender\":{\"genderID\":2,\"genderName\":\"Female\"}}"; + + @org.junit.jupiter.api.BeforeEach + void stubVisitCount() { + org.mockito.Mockito.when(benVisitDetailRepo + .getVisitCountForBeneficiary(org.mockito.ArgumentMatchers.anyLong())).thenReturn((short) 2); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.save(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + } + + @Test + @DisplayName("createBenFlowRecord should store the flow record for a registered beneficiary") + void createFlowRecord_shouldStoreRecordForRegisteredBeneficiary() { + org.junit.jupiter.api.Assertions.assertEquals(1, service.createBenFlowRecord(REGISTRATION, 11L, 9L)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus.class); + org.mockito.Mockito.verify(beneficiaryFlowStatusRepo).save(captor.capture()); + com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus stored = captor.getValue(); + org.junit.jupiter.api.Assertions.assertEquals("Asha Devi", stored.getBenName()); + org.junit.jupiter.api.Assertions.assertEquals(21, stored.getDistrictID()); + org.junit.jupiter.api.Assertions.assertEquals(31, stored.getVillageID()); + org.junit.jupiter.api.Assertions.assertEquals("PHC Kamptee", stored.getServicePointName()); + org.junit.jupiter.api.Assertions.assertEquals("9999999999", stored.getPreferredPhoneNum()); + org.junit.jupiter.api.Assertions.assertEquals((short) 3, stored.getBenVisitNo()); + org.junit.jupiter.api.Assertions.assertEquals((short) 1, stored.getNurseFlag()); + org.junit.jupiter.api.Assertions.assertTrue(stored.getAge().contains("years")); + org.junit.jupiter.api.Assertions.assertNotNull(stored.getRegistrationDate()); + } + + @Test + @DisplayName("createBenFlowRecord should report a beneficiary already in the nurse worklist") + void createFlowRecord_shouldReportBeneficiaryAlreadyInWorklist() { + org.springframework.test.util.ReflectionTestUtils.setField(service, "nurseWL", 10); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.checkBenAlreadyInNurseWorkList( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(5L))); + + org.junit.jupiter.api.Assertions.assertEquals(3, service.createBenFlowRecord(REGISTRATION, null, null)); + } + + @Test + @DisplayName("createBenFlowRecord should store a fresh record when the beneficiary is not in the worklist") + void createFlowRecord_shouldStoreFreshRecordWhenNotInWorklist() { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.checkBenAlreadyInNurseWorkList( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.createBenFlowRecord(REGISTRATION, null, null)); + } + + @Test + @DisplayName("createBenFlowRecord should report a failure when the record could not be stored") + void createFlowRecord_shouldReportStoreFailure() { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(null); + + org.junit.jupiter.api.Assertions.assertEquals(0, service.createBenFlowRecord(REGISTRATION, 11L, 9L)); + } + + @Test + @DisplayName("createBenFlowRecord should describe a beneficiary aged in months") + void createFlowRecord_shouldDescribeAgeInMonths() { + String dob = java.time.LocalDate.now().minusMonths(4).toString() + "T00:00:00.000Z"; + + org.junit.jupiter.api.Assertions.assertEquals(1, service.createBenFlowRecord( + REGISTRATION.replace("1990-05-14T00:00:00.000Z", dob), 11L, 9L)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus.class); + org.mockito.Mockito.verify(beneficiaryFlowStatusRepo).save(captor.capture()); + org.junit.jupiter.api.Assertions.assertTrue(captor.getValue().getAge().contains("months")); + } + + @Test + @DisplayName("createBenFlowRecord should describe a beneficiary aged in days") + void createFlowRecord_shouldDescribeAgeInDays() { + String dob = java.time.LocalDate.now().minusDays(9).toString() + "T00:00:00.000Z"; + + org.junit.jupiter.api.Assertions.assertEquals(1, service.createBenFlowRecord( + REGISTRATION.replace("1990-05-14T00:00:00.000Z", dob), 11L, 9L)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus.class); + org.mockito.Mockito.verify(beneficiaryFlowStatusRepo).save(captor.capture()); + org.junit.jupiter.api.Assertions.assertTrue(captor.getValue().getAge().contains("days")); + } + + @Test + @DisplayName("createBenFlowRecord should use the beneficiary first name alone when there is no surname") + void createFlowRecord_shouldUseFirstNameAloneWithoutSurname() { + org.junit.jupiter.api.Assertions.assertEquals(1, service.createBenFlowRecord( + REGISTRATION.replace(",\"lastName\":\"Devi\"", ""), 11L, 9L)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus.class); + org.mockito.Mockito.verify(beneficiaryFlowStatusRepo).save(captor.capture()); + org.junit.jupiter.api.Assertions.assertEquals("Asha", captor.getValue().getBenName()); + } + + @Test + @DisplayName("createBenFlowRecord should take the gender from the master when the request omits it") + void createFlowRecord_shouldTakeGenderFromMaster() { + org.junit.jupiter.api.Assertions.assertEquals(1, service.createBenFlowRecord( + REGISTRATION.replace("\"genderID\":2,\"genderName\":\"Female\",", ""), 11L, 9L)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus.class); + org.mockito.Mockito.verify(beneficiaryFlowStatusRepo).save(captor.capture()); + org.junit.jupiter.api.Assertions.assertEquals("Female", captor.getValue().getGenderName()); + } + + @Test + @DisplayName("createBenFlowRecord should count the first visit for a new beneficiary") + void createFlowRecord_shouldCountFirstVisitForNewBeneficiary() { + org.mockito.Mockito.when(benVisitDetailRepo + .getVisitCountForBeneficiary(org.mockito.ArgumentMatchers.anyLong())).thenReturn(null); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.createBenFlowRecord(REGISTRATION, 11L, 9L)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus.class); + org.mockito.Mockito.verify(beneficiaryFlowStatusRepo).save(captor.capture()); + org.junit.jupiter.api.Assertions.assertEquals((short) 1, captor.getValue().getBenVisitNo()); + } + + @Test + @DisplayName("createBenFlowRecord should absorb a malformed registration request") + void createFlowRecord_shouldAbsorbMalformedRequest() { + org.junit.jupiter.api.Assertions.assertEquals(0, service.createBenFlowRecord("{}", 11L, 9L)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("pharmacist flag carry over") + class PharmacistFlagTests { + + @Test + @DisplayName("updateBenFlowAfterDocDataUpdate should keep a pharmacist flag already raised on the flow") + void updateAfterDocUpdate_shouldKeepRaisedPharmacistFlag() throws Exception { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.getPharmaFlag(5L)).thenReturn((short) 1); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateBenFlowStatusAfterDoctorActivity( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyInt(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyBoolean())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBenFlowAfterDocDataUpdate(5L, 11L, 9L, 3L, + (short) 9, (short) 0, (short) 0, (short) 0, 0, null, (short) 0, Boolean.TRUE)); + + org.mockito.Mockito.verify(beneficiaryFlowStatusRepo).updateBenFlowStatusAfterDoctorActivity( + org.mockito.ArgumentMatchers.eq(5L), org.mockito.ArgumentMatchers.eq(11L), + org.mockito.ArgumentMatchers.eq(9L), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.eq((short) 1), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyInt(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyBoolean()); + } + + @Test + @DisplayName("updateBenFlowAfterDocDataUpdate should raise the pharmacist flag from the request") + void updateAfterDocUpdate_shouldRaisePharmacistFlagFromRequest() throws Exception { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.getPharmaFlag(5L)).thenReturn((short) 0); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateBenFlowStatusAfterDoctorActivity( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyInt(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyBoolean())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBenFlowAfterDocDataUpdate(5L, 11L, 9L, 3L, + (short) 9, (short) 1, (short) 0, (short) 0, 0, null, (short) 0, Boolean.TRUE)); + } + + @Test + @DisplayName("updateBenFlowAfterDocDataUpdate should surface a repository failure") + void updateAfterDocUpdate_shouldSurfaceRepositoryFailure() { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.getPharmaFlag(5L)) + .thenThrow(new RuntimeException("flow table locked")); + + org.junit.jupiter.api.Assertions.assertThrows(Exception.class, + () -> service.updateBenFlowAfterDocDataUpdate(5L, 11L, 9L, 3L, (short) 9, (short) 0, (short) 0, + (short) 0, 0, null, (short) 0, Boolean.TRUE)); + } + + @Test + @DisplayName("updateBenFlowAfterDocDataUpdateTCSpecialist should keep a pharmacist flag already raised") + void updateAfterSpecialistUpdate_shouldKeepRaisedPharmacistFlag() throws Exception { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.getPharmaFlag(5L)).thenReturn((short) 1); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateBenFlowStatusAfterDoctorActivityTCSpecialist( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyBoolean())) + .thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBenFlowAfterDocDataUpdateTCSpecialist(5L, + 11L, 9L, 3L, (short) 0, (short) 0, (short) 0, (short) 9, 0, null, (short) 0, Boolean.TRUE)); + } + + @Test + @DisplayName("updateBenFlowAfterDocDataUpdateTCSpecialist should surface a repository failure") + void updateAfterSpecialistUpdate_shouldSurfaceRepositoryFailure() { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.getPharmaFlag(5L)) + .thenThrow(new RuntimeException("flow table locked")); + + org.junit.jupiter.api.Assertions.assertThrows(Exception.class, + () -> service.updateBenFlowAfterDocDataUpdateTCSpecialist(5L, 11L, 9L, 3L, (short) 0, (short) 0, + (short) 0, (short) 9, 0, null, (short) 0, Boolean.TRUE)); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/cancerScreening/CSCarestreamServiceImplTest.java b/src/test/java/com/iemr/tm/service/cancerScreening/CSCarestreamServiceImplTest.java new file mode 100644 index 00000000..72aabd44 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/cancerScreening/CSCarestreamServiceImplTest.java @@ -0,0 +1,53 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.cancerScreening; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.utils.CookieUtil; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CSCarestreamServiceImpl Test Suite") +class CSCarestreamServiceImplTest { + + @Mock + private CookieUtil cookieUtil; + + @InjectMocks + private CSCarestreamServiceImpl service; + + @Test + @DisplayName("createMamographyRequest should answer for a well formed request") + void createMamographyRequest_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.createMamographyRequest(new java.util.ArrayList<>(), 11L, 11L, "{}")); + } +} diff --git a/src/test/java/com/iemr/tm/service/cancerScreening/CSDoctorServiceImplTest.java b/src/test/java/com/iemr/tm/service/cancerScreening/CSDoctorServiceImplTest.java new file mode 100644 index 00000000..02682cdf --- /dev/null +++ b/src/test/java/com/iemr/tm/service/cancerScreening/CSDoctorServiceImplTest.java @@ -0,0 +1,77 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.cancerScreening; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.doctor.CancerDiagnosisRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CSDoctorServiceImpl Test Suite") +class CSDoctorServiceImplTest { + + @Mock + private CancerDiagnosisRepo cancerDiagnosisRepo; + + @InjectMocks + private CSDoctorServiceImpl service; + + @Test + @DisplayName("saveCancerDiagnosisData should answer for a well formed request") + void saveCancerDiagnosisData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveCancerDiagnosisData(new com.iemr.tm.data.doctor.CancerDiagnosis())); + } + + @Test + @DisplayName("getCancerDiagnosisObj should answer for a well formed request") + void getCancerDiagnosisObj_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getCancerDiagnosisObj(new com.iemr.tm.data.doctor.CancerDiagnosis())); + } + + @Test + @DisplayName("getBenDoctorEnteredDataForCaseSheet should answer for a well formed request") + void getBenDoctorEnteredDataForCaseSheet_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDoctorEnteredDataForCaseSheet(11L, 11L)); + } + + @Test + @DisplayName("getBenCancerDiagnosisData should answer for a well formed request") + void getBenCancerDiagnosisData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenCancerDiagnosisData(11L, 11L)); + } + + @Test + @DisplayName("updateCancerDiagnosisDetailsByDoctor should answer for a well formed request") + void updateCancerDiagnosisDetailsByDoctor_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateCancerDiagnosisDetailsByDoctor(new com.iemr.tm.data.doctor.CancerDiagnosis())); + } +} diff --git a/src/test/java/com/iemr/tm/service/cancerScreening/CSNurseServiceImplTest.java b/src/test/java/com/iemr/tm/service/cancerScreening/CSNurseServiceImplTest.java new file mode 100644 index 00000000..5de31bd4 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/cancerScreening/CSNurseServiceImplTest.java @@ -0,0 +1,929 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.cancerScreening; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.data.nurse.BenCancerVitalDetail; +import com.iemr.tm.data.nurse.BeneficiaryVisitDetail; +import com.iemr.tm.data.doctor.CancerAbdominalExamination; +import com.iemr.tm.data.doctor.CancerBreastExamination; +import com.iemr.tm.data.doctor.CancerExaminationImageAnnotation; +import com.iemr.tm.data.doctor.CancerGynecologicalExamination; +import com.iemr.tm.data.doctor.CancerLymphNodeDetails; +import com.iemr.tm.data.doctor.CancerOralExamination; +import com.iemr.tm.data.doctor.CancerSignAndSymptoms; +import com.iemr.tm.data.doctor.WrapperCancerExamImgAnotasn; +import com.iemr.tm.data.nurse.BenFamilyCancerHistory; +import com.iemr.tm.data.nurse.BenObstetricCancerHistory; +import com.iemr.tm.data.nurse.BenPersonalCancerDietHistory; +import com.iemr.tm.data.nurse.BenPersonalCancerHistory; +import com.iemr.tm.repo.nurse.BenCancerVitalDetailRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.doctor.CancerAbdominalExaminationRepo; +import com.iemr.tm.repo.doctor.CancerBreastExaminationRepo; +import com.iemr.tm.repo.doctor.CancerExaminationImageAnnotationRepo; +import com.iemr.tm.repo.doctor.CancerGynecologicalExaminationRepo; +import com.iemr.tm.repo.doctor.CancerLymphNodeExaminationRepo; +import com.iemr.tm.repo.doctor.CancerOralExaminationRepo; +import com.iemr.tm.repo.doctor.CancerSignAndSymptomsRepo; +import com.iemr.tm.repo.nurse.BenFamilyCancerHistoryRepo; +import com.iemr.tm.repo.nurse.BenObstetricCancerHistoryRepo; +import com.iemr.tm.repo.nurse.BenPersonalCancerDietHistoryRepo; +import com.iemr.tm.repo.nurse.BenPersonalCancerHistoryRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CSNurseServiceImpl Test Suite") +class CSNurseServiceImplTest { + + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_ID = 3L; + private static final Long VISIT_CODE = 22L; + + @Mock + private BenFamilyCancerHistoryRepo benFamilyCancerHistoryRepo; + @Mock + private BenPersonalCancerHistoryRepo benPersonalCancerHistoryRepo; + @Mock + private BenPersonalCancerDietHistoryRepo benPersonalCancerDietHistoryRepo; + @Mock + private BenObstetricCancerHistoryRepo benObstetricCancerHistoryRepo; + @Mock + private BenCancerVitalDetailRepo benCancerVitalDetailRepo; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private CancerAbdominalExaminationRepo cancerAbdominalExaminationRepo; + @Mock + private CancerBreastExaminationRepo cancerBreastExaminationRepo; + @Mock + private CancerGynecologicalExaminationRepo cancerGynecologicalExaminationRepo; + @Mock + private CancerSignAndSymptomsRepo cancerSignAndSymptomsRepo; + @Mock + private CancerLymphNodeExaminationRepo cancerLymphNodeExaminationRepo; + @Mock + private CancerOralExaminationRepo cancerOralExaminationRepo; + @Mock + private CancerExaminationImageAnnotationRepo cancerExaminationImageAnnotationRepo; + + @InjectMocks + private CSNurseServiceImpl service; + + /** One already-processed row and one fresh row, as a re-edited visit returns. */ + private ArrayList statuses() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { 1L, "P" }); + rows.add(new Object[] { 2L, "N" }); + return rows; + } + + private static org.mockito.stubbing.Answer echoList() { + return invocation -> invocation.getArgument(0); + } + + private BenFamilyCancerHistory familyHistory(List familyMembers) { + BenFamilyCancerHistory history = new BenFamilyCancerHistory(); + history.setBeneficiaryRegID(BEN_REG_ID); + history.setVisitCode(VISIT_CODE); + history.setFamilyMemberList(familyMembers); + return history; + } + + @Nested + @DisplayName("history saves") + class HistorySaveTests { + + @Test + @DisplayName("saveBenFamilyCancerHistory should flatten the family member list before storing") + void saveFamilyHistory_shouldFlattenFamilyMembers() { + BenFamilyCancerHistory history = familyHistory(Arrays.asList("Mother", "Father")); + when(benFamilyCancerHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.saveBenFamilyCancerHistory(Collections.singletonList(history))); + assertEquals("Mother,Father", history.getFamilyMember()); + } + + @Test + @DisplayName("saveBenFamilyCancerHistory should skip an entry without any family member") + void saveFamilyHistory_shouldSkipEntryWithoutFamilyMember() { + when(benFamilyCancerHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.saveBenFamilyCancerHistory( + Collections.singletonList(familyHistory(Collections.emptyList())))); + } + + @Test + @DisplayName("saveBenPersonalCancerHistory should flatten the tobacco product list before storing") + void savePersonalHistory_shouldFlattenTobaccoProducts() { + BenPersonalCancerHistory history = new BenPersonalCancerHistory(); + history.setTypeOfTobaccoProductList(Arrays.asList("Cigarette", "Beedi")); + BenPersonalCancerHistory saved = new BenPersonalCancerHistory(); + saved.setID(4L); + when(benPersonalCancerHistoryRepo.save(history)).thenReturn(saved); + + assertEquals(4L, service.saveBenPersonalCancerHistory(history)); + assertEquals("Cigarette,Beedi,", history.getTypeOfTobaccoProduct()); + } + + @Test + @DisplayName("saveBenPersonalCancerHistory should return null when nothing was stored") + void savePersonalHistory_shouldReturnNullWhenNothingStored() { + BenPersonalCancerHistory history = new BenPersonalCancerHistory(); + when(benPersonalCancerHistoryRepo.save(history)).thenReturn(null); + + assertNull(service.saveBenPersonalCancerHistory(history)); + } + + @Test + @DisplayName("saveBenPersonalCancerDietHistory should flatten the oil list before storing") + void savePersonalDietHistory_shouldFlattenOilList() { + BenPersonalCancerDietHistory history = new BenPersonalCancerDietHistory(); + history.setTypeOfOilConsumedList(Arrays.asList("Mustard", "Sunflower")); + BenPersonalCancerDietHistory saved = new BenPersonalCancerDietHistory(); + saved.setID(4L); + when(benPersonalCancerDietHistoryRepo.save(history)).thenReturn(saved); + + assertEquals(4L, service.saveBenPersonalCancerDietHistory(history)); + assertEquals("Mustard,Sunflower,", history.getTypeOfOilConsumed()); + } + + @Test + @DisplayName("saveBenPersonalCancerDietHistory should return null when nothing was stored") + void savePersonalDietHistory_shouldReturnNullWhenNothingStored() { + BenPersonalCancerDietHistory history = new BenPersonalCancerDietHistory(); + when(benPersonalCancerDietHistoryRepo.save(history)).thenReturn(null); + + assertNull(service.saveBenPersonalCancerDietHistory(history)); + } + + @Test + @DisplayName("saveBenObstetricCancerHistory should return the stored row id") + void saveObstetricHistory_shouldReturnStoredId() { + BenObstetricCancerHistory history = new BenObstetricCancerHistory(); + BenObstetricCancerHistory saved = new BenObstetricCancerHistory(); + saved.setID(4L); + when(benObstetricCancerHistoryRepo.save(history)).thenReturn(saved); + + assertEquals(4L, service.saveBenObstetricCancerHistory(history)); + } + + @Test + @DisplayName("saveBenObstetricCancerHistory should return null when nothing was stored") + void saveObstetricHistory_shouldReturnNullWhenNothingStored() { + BenObstetricCancerHistory history = new BenObstetricCancerHistory(); + when(benObstetricCancerHistoryRepo.save(history)).thenReturn(null); + + assertNull(service.saveBenObstetricCancerHistory(history)); + } + + @Test + @DisplayName("saveBenVitalDetail should return the stored row id") + void saveVitalDetail_shouldReturnStoredId() { + BenCancerVitalDetail vital = new BenCancerVitalDetail(); + BenCancerVitalDetail saved = new BenCancerVitalDetail(); + saved.setID(4L); + when(benCancerVitalDetailRepo.save(vital)).thenReturn(saved); + + assertEquals(4L, service.saveBenVitalDetail(vital)); + } + + @Test + @DisplayName("saveBenVitalDetail should return null when nothing was stored") + void saveVitalDetail_shouldReturnNullWhenNothingStored() { + BenCancerVitalDetail vital = new BenCancerVitalDetail(); + when(benCancerVitalDetailRepo.save(vital)).thenReturn(null); + + assertNull(service.saveBenVitalDetail(vital)); + } + } + + @Nested + @DisplayName("examination saves") + class ExaminationSaveTests { + + @Test + @DisplayName("saveLymphNodeDetails should stamp the visit onto every lymph node row") + void saveLymphNodeDetails_shouldStampVisitOntoRows() { + CancerLymphNodeDetails node = new CancerLymphNodeDetails(); + node.setID(4L); + when(cancerLymphNodeExaminationRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(4L, service.saveLymphNodeDetails(Collections.singletonList(node), VISIT_ID, VISIT_CODE)); + assertEquals(VISIT_ID, node.getBenVisitID()); + assertEquals(VISIT_CODE, node.getVisitCode()); + } + + @Test + @DisplayName("saveLymphNodeDetails should return null when nothing was stored") + void saveLymphNodeDetails_shouldReturnNullWhenNothingStored() { + when(cancerLymphNodeExaminationRepo.saveAll(any())).thenReturn(new ArrayList<>()); + + assertNull(service.saveLymphNodeDetails(Collections.singletonList(new CancerLymphNodeDetails()), VISIT_ID, + VISIT_CODE)); + } + + @Test + @DisplayName("saveCancerSignAndSymptomsData should stamp the visit before storing") + void saveSignAndSymptoms_shouldStampVisitBeforeStoring() { + CancerSignAndSymptoms symptoms = new CancerSignAndSymptoms(); + CancerSignAndSymptoms saved = new CancerSignAndSymptoms(); + saved.setID(4L); + when(cancerSignAndSymptomsRepo.save(symptoms)).thenReturn(saved); + + assertEquals(4L, service.saveCancerSignAndSymptomsData(symptoms, VISIT_ID, VISIT_CODE)); + assertEquals(VISIT_ID, symptoms.getBenVisitID()); + } + + @Test + @DisplayName("saveCancerSignAndSymptomsData should return the stored row id") + void saveSignAndSymptoms_shouldReturnStoredId() { + CancerSignAndSymptoms symptoms = new CancerSignAndSymptoms(); + CancerSignAndSymptoms saved = new CancerSignAndSymptoms(); + saved.setID(4L); + when(cancerSignAndSymptomsRepo.save(symptoms)).thenReturn(saved); + + assertEquals(4L, service.saveCancerSignAndSymptomsData(symptoms)); + } + + @Test + @DisplayName("saveCancerSignAndSymptomsData should return null when nothing was stored") + void saveSignAndSymptoms_shouldReturnNullWhenNothingStored() { + CancerSignAndSymptoms symptoms = new CancerSignAndSymptoms(); + when(cancerSignAndSymptomsRepo.save(symptoms)).thenReturn(null); + + assertNull(service.saveCancerSignAndSymptomsData(symptoms)); + } + + @Test + @DisplayName("saveCancerOralExaminationData should return the stored row id") + void saveOralExamination_shouldReturnStoredId() { + CancerOralExamination examination = new CancerOralExamination(); + CancerOralExamination saved = new CancerOralExamination(); + saved.setID(4L); + when(cancerOralExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.saveCancerOralExaminationData(examination)); + } + + @Test + @DisplayName("saveCancerOralExaminationData should return null when nothing was stored") + void saveOralExamination_shouldReturnNullWhenNothingStored() { + CancerOralExamination examination = new CancerOralExamination(); + when(cancerOralExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.saveCancerOralExaminationData(examination)); + } + + @Test + @DisplayName("saveCancerBreastExaminationData should return the stored row id") + void saveBreastExamination_shouldReturnStoredId() { + CancerBreastExamination examination = new CancerBreastExamination(); + CancerBreastExamination saved = new CancerBreastExamination(); + saved.setID(4L); + when(cancerBreastExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.saveCancerBreastExaminationData(examination)); + } + + @Test + @DisplayName("saveCancerBreastExaminationData should return null when nothing was stored") + void saveBreastExamination_shouldReturnNullWhenNothingStored() { + CancerBreastExamination examination = new CancerBreastExamination(); + when(cancerBreastExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.saveCancerBreastExaminationData(examination)); + } + + @Test + @DisplayName("saveCancerAbdominalExaminationData should return the stored row id") + void saveAbdominalExamination_shouldReturnStoredId() { + CancerAbdominalExamination examination = new CancerAbdominalExamination(); + CancerAbdominalExamination saved = new CancerAbdominalExamination(); + saved.setID(4L); + when(cancerAbdominalExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.saveCancerAbdominalExaminationData(examination)); + } + + @Test + @DisplayName("saveCancerAbdominalExaminationData should return null when nothing was stored") + void saveAbdominalExamination_shouldReturnNullWhenNothingStored() { + CancerAbdominalExamination examination = new CancerAbdominalExamination(); + when(cancerAbdominalExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.saveCancerAbdominalExaminationData(examination)); + } + + @Test + @DisplayName("saveCancerGynecologicalExaminationData should return the stored row id") + void saveGynecologicalExamination_shouldReturnStoredId() { + CancerGynecologicalExamination examination = new CancerGynecologicalExamination(); + CancerGynecologicalExamination saved = new CancerGynecologicalExamination(); + saved.setID(4L); + when(cancerGynecologicalExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.saveCancerGynecologicalExaminationData(examination)); + } + + @Test + @DisplayName("saveDocExaminationImageAnnotation should expand every marker of the annotation") + void saveImageAnnotation_shouldExpandMarkers() { + WrapperCancerExamImgAnotasn wrapper = new WrapperCancerExamImgAnotasn(); + wrapper.setBeneficiaryRegID(BEN_REG_ID); + wrapper.setVisitID(VISIT_ID); + ArrayList> markers = new ArrayList<>(); + Map marker = new HashMap<>(); + marker.put("xCord", 10d); + marker.put("yCord", 20d); + marker.put("point", 1d); + marker.put("description", "Lesion"); + markers.add(marker); + wrapper.setMarkers(markers); + CancerExaminationImageAnnotation saved = new CancerExaminationImageAnnotation(); + saved.setID(4L); + when(cancerExaminationImageAnnotationRepo.saveAll(any())) + .thenReturn(Collections.singletonList(saved)); + + assertNotNull(service.saveDocExaminationImageAnnotation(Collections.singletonList(wrapper), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getCancerExaminationImageAnnotationList should return nothing for an annotation without markers") + void getImageAnnotationList_shouldReturnNothingWithoutMarkers() { + WrapperCancerExamImgAnotasn wrapper = new WrapperCancerExamImgAnotasn(); + + assertTrue(service.getCancerExaminationImageAnnotationList(Collections.singletonList(wrapper), VISIT_CODE) + .isEmpty()); + } + } + + @Nested + @DisplayName("per visit lookups") + class PerVisitLookupTests { + + @Test + @DisplayName("getBenFamilyHisData should expand the comma separated family members") + void getFamilyHistory_shouldExpandFamilyMembers() { + BenFamilyCancerHistory stored = new BenFamilyCancerHistory(); + stored.setFamilyMember("Mother,Father"); + when(benFamilyCancerHistoryRepo.getBenFamilyHistory(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new ArrayList<>(Collections.singletonList(stored))); + + List result = service.getBenFamilyHisData(BEN_REG_ID, VISIT_CODE); + + assertEquals(2, result.get(0).getFamilyMemberList().size()); + } + + @Test + @DisplayName("getBenFamilyHisData should leave the family member list empty when none is stored") + void getFamilyHistory_shouldLeaveListEmptyWhenNoneStored() { + when(benFamilyCancerHistoryRepo.getBenFamilyHistory(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new ArrayList<>(Collections.singletonList(new BenFamilyCancerHistory()))); + + assertTrue(service.getBenFamilyHisData(BEN_REG_ID, VISIT_CODE).get(0).getFamilyMemberList().isEmpty()); + } + + @Test + @DisplayName("getBenObstetricDetailsData should delegate to the obstetric history repository") + void getObstetricDetails_shouldDelegateToRepo() { + BenObstetricCancerHistory stored = new BenObstetricCancerHistory(); + when(benObstetricCancerHistoryRepo.getBenObstetricCancerHistory(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getBenObstetricDetailsData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBenPersonalCancerHistoryData should expand the comma separated tobacco products") + void getPersonalHistory_shouldExpandTobaccoProducts() { + BenPersonalCancerHistory stored = new BenPersonalCancerHistory(); + stored.setTypeOfTobaccoProduct("Cigarette,Beedi"); + when(benPersonalCancerHistoryRepo.getBenPersonalHistory(BEN_REG_ID, VISIT_CODE)).thenReturn(stored); + + BenPersonalCancerHistory result = service.getBenPersonalCancerHistoryData(BEN_REG_ID, VISIT_CODE); + + assertEquals(2, result.getTypeOfTobaccoProductList().size()); + } + + @Test + @DisplayName("getBenPersonalCancerHistoryData should return null when the visit has no personal history") + void getPersonalHistory_shouldReturnNullWithoutStoredHistory() { + when(benPersonalCancerHistoryRepo.getBenPersonalHistory(BEN_REG_ID, VISIT_CODE)).thenReturn(null); + + assertNull(service.getBenPersonalCancerHistoryData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBenPersonalCancerDietHistoryData should expand the comma separated oil list") + void getPersonalDietHistory_shouldExpandOilList() { + BenPersonalCancerDietHistory stored = new BenPersonalCancerDietHistory(); + stored.setTypeOfOilConsumed("Mustard,Sunflower"); + when(benPersonalCancerDietHistoryRepo.getBenPersonaDietHistory(BEN_REG_ID, VISIT_CODE)).thenReturn(stored); + + BenPersonalCancerDietHistory result = service.getBenPersonalCancerDietHistoryData(BEN_REG_ID, VISIT_CODE); + + assertEquals(2, result.getTypeOfOilConsumedList().size()); + } + + @Test + @DisplayName("getBenPersonalCancerDietHistoryData should return null when the visit has no diet history") + void getPersonalDietHistory_shouldReturnNullWithoutStoredHistory() { + when(benPersonalCancerDietHistoryRepo.getBenPersonaDietHistory(BEN_REG_ID, VISIT_CODE)).thenReturn(null); + + assertNull(service.getBenPersonalCancerDietHistoryData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBenCancerVitalDetailData should delegate to the cancer vitals repository") + void getVitalDetail_shouldDelegateToRepo() { + BenCancerVitalDetail stored = new BenCancerVitalDetail(); + when(benCancerVitalDetailRepo.getBenCancerVitalDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(stored); + + assertEquals(stored, service.getBenCancerVitalDetailData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBenCancerAbdominalExaminationData should delegate to the abdominal repository") + void getAbdominalExamination_shouldDelegateToRepo() { + CancerAbdominalExamination stored = new CancerAbdominalExamination(); + when(cancerAbdominalExaminationRepo.getBenCancerAbdominalExaminationDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getBenCancerAbdominalExaminationData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBenCancerBreastExaminationData should delegate to the breast repository") + void getBreastExamination_shouldDelegateToRepo() { + CancerBreastExamination stored = new CancerBreastExamination(); + when(cancerBreastExaminationRepo.getBenCancerBreastExaminationDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getBenCancerBreastExaminationData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBenCancerGynecologicalExaminationData should delegate to the gynecological repository") + void getGynecologicalExamination_shouldDelegateToRepo() { + CancerGynecologicalExamination stored = new CancerGynecologicalExamination(); + when(cancerGynecologicalExaminationRepo.getBenCancerGynecologicalExaminationDetails(BEN_REG_ID, + VISIT_CODE)).thenReturn(stored); + + assertEquals(stored, service.getBenCancerGynecologicalExaminationData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBenCancerSignAndSymptomsData should delegate to the sign and symptoms repository") + void getSignAndSymptoms_shouldDelegateToRepo() { + CancerSignAndSymptoms stored = new CancerSignAndSymptoms(); + when(cancerSignAndSymptomsRepo.getBenCancerSignAndSymptomsDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getBenCancerSignAndSymptomsData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBenCancerLymphNodeDetailsData should delegate to the lymph node repository") + void getLymphNodeDetails_shouldDelegateToRepo() { + List stored = Collections.singletonList(new CancerLymphNodeDetails()); + when(cancerLymphNodeExaminationRepo.getBenCancerLymphNodeDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getBenCancerLymphNodeDetailsData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBenCancerOralExaminationData should delegate to the oral repository") + void getOralExamination_shouldDelegateToRepo() { + CancerOralExamination stored = new CancerOralExamination(); + when(cancerOralExaminationRepo.getBenCancerOralExaminationDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getBenCancerOralExaminationData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getCancerExaminationImageAnnotationCasesheet should group the stored markers by image") + void getImageAnnotationCasesheet_shouldGroupMarkersByImage() { + CancerExaminationImageAnnotation stored = new CancerExaminationImageAnnotation(); + stored.setBeneficiaryRegID(BEN_REG_ID); + stored.setVisitCode(VISIT_CODE); + stored.setCancerImageID(1); + stored.setxCoordinate(10); + stored.setyCoordinate(20); + stored.setPoint(1); + when(cancerExaminationImageAnnotationRepo.getCancerExaminationImageAnnotationList(BEN_REG_ID, VISIT_CODE)) + .thenReturn(Collections.singletonList(stored)); + + assertNotNull(service.getCancerExaminationImageAnnotationCasesheet(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getCancerExaminationImageAnnotationCasesheet should return nothing when no annotation is stored") + void getImageAnnotationCasesheet_shouldReturnNothingWhenNoneStored() { + when(cancerExaminationImageAnnotationRepo.getCancerExaminationImageAnnotationList(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new ArrayList<>()); + + assertTrue(service.getCancerExaminationImageAnnotationCasesheet(BEN_REG_ID, VISIT_CODE).isEmpty()); + } + + @Test + @DisplayName("getBeneficiaryVisitDetails should build the visit from the stored row") + void getBeneficiaryVisitDetails_shouldBuildVisitFromRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[40]); + when(benVisitDetailRepo.getBeneficiaryVisitDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new ArrayList<>(rows)); + + assertNotNull(service.getBeneficiaryVisitDetails(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBenNurseDataForCaseSheet should assemble every nurse captured section") + void getNurseDataForCaseSheet_shouldAssembleEverySection() { + when(benFamilyCancerHistoryRepo.getBenFamilyHistory(BEN_REG_ID, VISIT_CODE)).thenReturn(new ArrayList<>()); + when(benVisitDetailRepo.getBeneficiaryVisitDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(new ArrayList<>()); + + Map result = service.getBenNurseDataForCaseSheet(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.containsKey("benVisitDetail")); + assertTrue(result.containsKey("oralExamination")); + } + } + + @Nested + @DisplayName("cross visit history reports") + class HistoryReportTests { + + private ArrayList oneEmptyRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[40]); + return rows; + } + + @Test + @DisplayName("getBenCancerFamilyHistory should render the stored rows with the report columns") + void getFamilyHistoryReport_shouldRenderRowsWithColumns() { + when(benFamilyCancerHistoryRepo.getBenCancerFamilyHistory(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.getBenCancerFamilyHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("getBenCancerFamilyHistory should render only the columns when nothing is stored") + void getFamilyHistoryReport_shouldRenderColumnsOnlyWhenEmpty() { + when(benFamilyCancerHistoryRepo.getBenCancerFamilyHistory(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.getBenCancerFamilyHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("getBenCancerPersonalHistory should render the stored rows with the report columns") + void getPersonalHistoryReport_shouldRenderRowsWithColumns() { + when(benPersonalCancerHistoryRepo.getBenPersonalHistory(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.getBenCancerPersonalHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("getBenCancerPersonalDietHistory should render the stored rows with the report columns") + void getPersonalDietHistoryReport_shouldRenderRowsWithColumns() { + when(benPersonalCancerDietHistoryRepo.getBenPersonaDietHistory(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.getBenCancerPersonalDietHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("getBenCancerObstetricHistory should render the stored rows with the report columns") + void getObstetricHistoryReport_shouldRenderRowsWithColumns() { + when(benObstetricCancerHistoryRepo.getBenObstetricCancerHistoryData(BEN_REG_ID)) + .thenReturn(oneEmptyRow()); + + assertTrue(service.getBenCancerObstetricHistory(BEN_REG_ID).contains("\"columns\"")); + } + } + + @Nested + @DisplayName("history and examination updates") + class UpdateTests { + + @Test + @DisplayName("updateBeneficiaryFamilyCancerHistory should soft delete the stored rows before writing") + void updateFamilyHistory_shouldSoftDeleteBeforeWriting() { + BenFamilyCancerHistory history = familyHistory(Arrays.asList("Mother")); + when(benFamilyCancerHistoryRepo.getFamilyCancerHistoryStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn(statuses()); + when(benFamilyCancerHistoryRepo.deleteExistingFamilyRecord(anyLong(), anyString())).thenReturn(1); + when(benFamilyCancerHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.updateBeneficiaryFamilyCancerHistory(new ArrayList<>( + Collections.singletonList(history)))); + verify(benFamilyCancerHistoryRepo).deleteExistingFamilyRecord(1L, "U"); + verify(benFamilyCancerHistoryRepo).deleteExistingFamilyRecord(2L, "N"); + } + + @Test + @DisplayName("updateBenObstetricCancerHistory should read the processed flag before updating") + void updateObstetricHistory_shouldReadProcessedFlag() { + BenObstetricCancerHistory history = new BenObstetricCancerHistory(); + history.setBeneficiaryRegID(BEN_REG_ID); + history.setVisitCode(VISIT_CODE); + when(benObstetricCancerHistoryRepo.getObstetricCancerHistoryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateBenObstetricCancerHistory(history)); + } + + @Test + @DisplayName("updateBenPersonalCancerHistory should read the processed flag before updating") + void updatePersonalHistory_shouldReadProcessedFlag() { + BenPersonalCancerHistory history = new BenPersonalCancerHistory(); + history.setBeneficiaryRegID(BEN_REG_ID); + history.setVisitCode(VISIT_CODE); + when(benPersonalCancerHistoryRepo.getPersonalCancerHistoryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateBenPersonalCancerHistory(history)); + } + + @Test + @DisplayName("updateBenPersonalCancerDietHistory should read the processed flag before updating") + void updatePersonalDietHistory_shouldReadProcessedFlag() { + BenPersonalCancerDietHistory history = new BenPersonalCancerDietHistory(); + history.setBeneficiaryRegID(BEN_REG_ID); + history.setVisitCode(VISIT_CODE); + when(benPersonalCancerDietHistoryRepo.getPersonalCancerDietHistoryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateBenPersonalCancerDietHistory(history)); + } + + @Test + @DisplayName("updateBenVitalDetail should read the processed flag before updating") + void updateVitalDetail_shouldReadProcessedFlag() { + BenCancerVitalDetail vital = new BenCancerVitalDetail(); + vital.setBeneficiaryRegID(BEN_REG_ID); + vital.setVisitCode(VISIT_CODE); + when(benCancerVitalDetailRepo.getCancerVitalStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateBenVitalDetail(vital)); + } + + @Test + @DisplayName("updateSignAndSymptomsExaminationDetails should read the processed flag before updating") + void updateSignAndSymptoms_shouldReadProcessedFlag() { + CancerSignAndSymptoms symptoms = new CancerSignAndSymptoms(); + symptoms.setBeneficiaryRegID(BEN_REG_ID); + symptoms.setVisitCode(VISIT_CODE); + when(cancerSignAndSymptomsRepo.getCancerSignAndSymptomsStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateSignAndSymptomsExaminationDetails(symptoms)); + } + + @Test + @DisplayName("updateCancerOralDetails should read the processed flag before updating") + void updateOralExamination_shouldReadProcessedFlag() { + CancerOralExamination examination = new CancerOralExamination(); + examination.setBeneficiaryRegID(BEN_REG_ID); + examination.setVisitCode(VISIT_CODE); + when(cancerOralExaminationRepo.getCancerOralExaminationStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateCancerOralDetails(examination)); + } + + @Test + @DisplayName("updateCancerBreastDetails should read the processed flag before updating") + void updateBreastExamination_shouldReadProcessedFlag() { + CancerBreastExamination examination = new CancerBreastExamination(); + examination.setBeneficiaryRegID(BEN_REG_ID); + examination.setVisitCode(VISIT_CODE); + when(cancerBreastExaminationRepo.getCancerBreastExaminationStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateCancerBreastDetails(examination)); + } + + @Test + @DisplayName("updateCancerAbdominalExaminationDetails should read the processed flag before updating") + void updateAbdominalExamination_shouldReadProcessedFlag() { + CancerAbdominalExamination examination = new CancerAbdominalExamination(); + examination.setBeneficiaryRegID(BEN_REG_ID); + examination.setVisitCode(VISIT_CODE); + when(cancerAbdominalExaminationRepo.getCancerAbdominalExaminationStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateCancerAbdominalExaminationDetails(examination)); + } + + @Test + @DisplayName("updateCancerGynecologicalExaminationDetails should read the processed flag before updating") + void updateGynecologicalExamination_shouldReadProcessedFlag() { + CancerGynecologicalExamination examination = new CancerGynecologicalExamination(); + examination.setBeneficiaryRegID(BEN_REG_ID); + examination.setVisitCode(VISIT_CODE); + when(cancerGynecologicalExaminationRepo.getCancerGynecologicalExaminationStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateCancerGynecologicalExaminationDetails(examination)); + } + } + + @Nested + @DisplayName("lymph node and image annotation updates") + class LymphNodeAndAnnotationUpdateTests { + + private com.iemr.tm.data.doctor.WrapperCancerSymptoms symptoms(Boolean enlarged, boolean withMeasurements) { + com.iemr.tm.data.doctor.WrapperCancerSymptoms wrapper = + new com.iemr.tm.data.doctor.WrapperCancerSymptoms(); + com.iemr.tm.data.doctor.CancerSignAndSymptoms signs = new com.iemr.tm.data.doctor.CancerSignAndSymptoms(); + signs.setBeneficiaryRegID(BEN_REG_ID); + signs.setVisitCode(VISIT_CODE); + signs.setLymphNode_Enlarged(enlarged); + wrapper.setCancerSignAndSymptoms(signs); + + com.iemr.tm.data.doctor.CancerLymphNodeDetails cervical = + new com.iemr.tm.data.doctor.CancerLymphNodeDetails(); + cervical.setBeneficiaryRegID(BEN_REG_ID); + cervical.setVisitCode(VISIT_CODE); + cervical.setLymphNodeName("Cervical"); + com.iemr.tm.data.doctor.CancerLymphNodeDetails axillary = + new com.iemr.tm.data.doctor.CancerLymphNodeDetails(); + axillary.setBeneficiaryRegID(BEN_REG_ID); + axillary.setVisitCode(VISIT_CODE); + axillary.setLymphNodeName("Cervical"); + if (withMeasurements) { + cervical.setMobility_Left(Boolean.TRUE); + cervical.setSize_Right("2 cm"); + } + wrapper.setCancerLymphNodeDetails(Arrays.asList(cervical, axillary)); + return wrapper; + } + + @Test + @DisplayName("updateLymphNodeExaminationDetails should replace the measured nodes for the visit") + void updateLymphNode_shouldReplaceMeasuredNodes() { + when(cancerLymphNodeExaminationRepo.getCancerLymphNodeDetailsStatusForLymphnodeNameList( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyList())).thenReturn(statuses()); + when(cancerLymphNodeExaminationRepo.deleteExistingLymphNodeDetails( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())).thenReturn(1); + when(cancerLymphNodeExaminationRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.updateLymphNodeExaminationDetails(symptoms(Boolean.TRUE, true))); + } + + @Test + @DisplayName("updateLymphNodeExaminationDetails should succeed when no node was measured") + void updateLymphNode_shouldSucceedWithoutMeasurements() { + when(cancerLymphNodeExaminationRepo.getCancerLymphNodeDetailsStatusForLymphnodeNameList( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyList())).thenReturn(new ArrayList<>()); + + assertEquals(1, service.updateLymphNodeExaminationDetails(symptoms(Boolean.TRUE, false))); + } + + @Test + @DisplayName("updateLymphNodeExaminationDetails should clear the stored nodes when none are enlarged") + void updateLymphNode_shouldClearStoredNodesWhenNoneEnlarged() { + when(cancerLymphNodeExaminationRepo.getCancerLymphNodeDetailsStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn(statuses()); + when(cancerLymphNodeExaminationRepo.deleteExistingLymphNodeDetails( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())).thenReturn(1); + + assertEquals(1, service.updateLymphNodeExaminationDetails(symptoms(Boolean.FALSE, false))); + } + + @Test + @DisplayName("updateLymphNodeExaminationDetails should succeed when nothing was stored to clear") + void updateLymphNode_shouldSucceedWhenNothingStoredToClear() { + when(cancerLymphNodeExaminationRepo.getCancerLymphNodeDetailsStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new ArrayList<>()); + + assertEquals(1, service.updateLymphNodeExaminationDetails(symptoms(Boolean.FALSE, false))); + } + + @Test + @DisplayName("updateLymphNodeExaminationDetails should report a failure when the stored nodes cannot be cleared") + void updateLymphNode_shouldReportFailureWhenClearFails() { + when(cancerLymphNodeExaminationRepo.getCancerLymphNodeDetailsStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn(statuses()); + when(cancerLymphNodeExaminationRepo.deleteExistingLymphNodeDetails( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())).thenReturn(0); + + assertEquals(0, service.updateLymphNodeExaminationDetails(symptoms(Boolean.FALSE, false))); + } + + private com.iemr.tm.data.doctor.CancerExaminationImageAnnotation annotation(boolean complete) { + com.iemr.tm.data.doctor.CancerExaminationImageAnnotation annotation = + new com.iemr.tm.data.doctor.CancerExaminationImageAnnotation(); + annotation.setBeneficiaryRegID(BEN_REG_ID); + annotation.setVisitCode(VISIT_CODE); + annotation.setCancerImageID(2); + if (complete) { + annotation.setxCoordinate(120); + annotation.setyCoordinate(240); + annotation.setCreatedBy("tester"); + } + return annotation; + } + + @Test + @DisplayName("updateCancerExamImgAnotasnDetails should replace the stored markers for the visit") + void updateImageAnnotation_shouldReplaceStoredMarkers() { + when(cancerExaminationImageAnnotationRepo.getCancerExaminationImageAnnotationDetailsStatus( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyList())).thenReturn(statuses()); + when(cancerExaminationImageAnnotationRepo.deleteExistingImageAnnotationDetails( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())).thenReturn(1); + when(cancerExaminationImageAnnotationRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.updateCancerExamImgAnotasnDetails( + new ArrayList<>(Collections.singletonList(annotation(true))))); + } + + @Test + @DisplayName("updateCancerExamImgAnotasnDetails should succeed when no marker was stored before") + void updateImageAnnotation_shouldSucceedWhenNothingStoredBefore() { + when(cancerExaminationImageAnnotationRepo.getCancerExaminationImageAnnotationDetailsStatus( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyList())).thenReturn(new ArrayList<>()); + when(cancerExaminationImageAnnotationRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.updateCancerExamImgAnotasnDetails( + new ArrayList<>(Collections.singletonList(annotation(true))))); + } + + @Test + @DisplayName("updateCancerExamImgAnotasnDetails should skip markers without coordinates") + void updateImageAnnotation_shouldSkipMarkersWithoutCoordinates() { + when(cancerExaminationImageAnnotationRepo.getCancerExaminationImageAnnotationDetailsStatus( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyList())).thenReturn(new ArrayList<>()); + when(cancerExaminationImageAnnotationRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.updateCancerExamImgAnotasnDetails( + new ArrayList<>(Collections.singletonList(annotation(false))))); + } + + @Test + @DisplayName("updateCancerExamImgAnotasnDetails should succeed for an empty marker list") + void updateImageAnnotation_shouldSucceedForEmptyList() { + assertEquals(1, service.updateCancerExamImgAnotasnDetails(new ArrayList<>())); + } + + @Test + @DisplayName("updateCancerExamImgAnotasnDetails should report a failure when the stored markers cannot be cleared") + void updateImageAnnotation_shouldReportFailureWhenClearFails() { + when(cancerExaminationImageAnnotationRepo.getCancerExaminationImageAnnotationDetailsStatus( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyList())).thenReturn(statuses()); + when(cancerExaminationImageAnnotationRepo.deleteExistingImageAnnotationDetails( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())).thenReturn(0); + + assertEquals(0, service.updateCancerExamImgAnotasnDetails( + new ArrayList<>(Collections.singletonList(annotation(true))))); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/cancerScreening/CSOncologistServiceImplTest.java b/src/test/java/com/iemr/tm/service/cancerScreening/CSOncologistServiceImplTest.java new file mode 100644 index 00000000..886a1920 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/cancerScreening/CSOncologistServiceImplTest.java @@ -0,0 +1,53 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.cancerScreening; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.doctor.CancerDiagnosisRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CSOncologistServiceImpl Test Suite") +class CSOncologistServiceImplTest { + + @Mock + private CancerDiagnosisRepo cancerDiagnosisRepo; + + @InjectMocks + private CSOncologistServiceImpl service; + + @Test + @DisplayName("updateCancerDiagnosisDetailsByOncologist should answer for a well formed request") + void updateCancerDiagnosisDetailsByOncologist_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateCancerDiagnosisDetailsByOncologist(new com.iemr.tm.data.doctor.CancerDiagnosis())); + } +} diff --git a/src/test/java/com/iemr/tm/service/cancerScreening/CSServiceImplTest.java b/src/test/java/com/iemr/tm/service/cancerScreening/CSServiceImplTest.java new file mode 100644 index 00000000..b211fe4c --- /dev/null +++ b/src/test/java/com/iemr/tm/service/cancerScreening/CSServiceImplTest.java @@ -0,0 +1,697 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.cancerScreening; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.nurse.anc.BenAdherenceRepo; +import com.iemr.tm.repo.quickConsultation.BenChiefComplaintRepo; +import com.iemr.tm.repo.registrar.RegistrarRepoBenData; +import com.iemr.tm.repo.tc_consultation.TCRequestModelRepo; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.service.tele_consultation.SMSGatewayServiceImpl; +import com.iemr.tm.service.tele_consultation.TeleConsultationServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CSServiceImpl Test Suite") +class CSServiceImplTest { + + @Mock + private CSNurseServiceImpl cSNurseServiceImpl; + @Mock + private CSDoctorServiceImpl cSDoctorServiceImpl; + @Mock + private CSOncologistServiceImpl csOncologistServiceImpl; + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private CSCarestreamServiceImpl cSCarestreamServiceImpl; + @Mock + private RegistrarRepoBenData registrarRepoBenData; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private TeleConsultationServiceImpl teleConsultationServiceImpl; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private TCRequestModelRepo tCRequestModelRepo; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private BenChiefComplaintRepo benChiefComplaintRepo; + @Mock + private BenAdherenceRepo benAdherenceRepo; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + + @InjectMocks + private CSServiceImpl service; + + @Test + @DisplayName("saveCancerScreeningNurseData should reject a request it cannot act on") + void saveCancerScreeningNurseData_shouldRejectRequestItCannotActOn() { + assertThrows(Exception.class, () -> service.saveCancerScreeningNurseData(new com.google.gson.JsonObject(), "{}")); + } + + @Test + @DisplayName("deleteVisitDetails should answer for a well formed request") + void deleteVisitDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.deleteVisitDetails(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("saveBenVisitDetails should answer for a well formed request") + void saveBenVisitDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenVisitDetails(new com.iemr.tm.data.nurse.BeneficiaryVisitDetail(), org.mockito.Mockito.mock(com.iemr.tm.data.nurse.CommonUtilityClass.class))); + } + + @Test + @DisplayName("saveBenHistoryDetails should answer for a well formed request") + void saveBenHistoryDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenHistoryDetails(new com.google.gson.JsonObject(), 11L, 11L)); + } + + @Test + @DisplayName("saveBenFamilyHistoryDetails should answer for a well formed request") + void saveBenFamilyHistoryDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenFamilyHistoryDetails()); + } + + @Test + @DisplayName("saveBenVitalsDetails should answer for a well formed request") + void saveBenVitalsDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenVitalsDetails(new com.google.gson.JsonObject(), 11L, 11L)); + } + + @Test + @DisplayName("UpdateCSHistoryNurseData should answer for a well formed request") + void UpdateCSHistoryNurseData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.UpdateCSHistoryNurseData(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("updateBenExaminationDetail should answer for a well formed request") + void updateBenExaminationDetail_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenExaminationDetail(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("updateBenVitalDetail should answer for a well formed request") + void updateBenVitalDetail_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenVitalDetail(new com.iemr.tm.data.nurse.BenCancerVitalDetail())); + } + + @Test + @DisplayName("getBenDataFrmNurseToDocVisitDetailsScreen should answer for a well formed request") + void getBenDataFrmNurseToDocVisitDetailsScreen_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDataFrmNurseToDocVisitDetailsScreen(11L, 11L)); + } + + @Test + @DisplayName("getBenDataFrmNurseToDocHistoryScreen should answer for a well formed request") + void getBenDataFrmNurseToDocHistoryScreen_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDataFrmNurseToDocHistoryScreen(11L, 11L)); + } + + @Test + @DisplayName("getBenDataFrmNurseToDocVitalScreen should answer for a well formed request") + void getBenDataFrmNurseToDocVitalScreen_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDataFrmNurseToDocVitalScreen(11L, 11L)); + } + + @Test + @DisplayName("getBenDataFrmNurseToDocExaminationScreen should answer for a well formed request") + void getBenDataFrmNurseToDocExaminationScreen_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDataFrmNurseToDocExaminationScreen(11L, 11L)); + } + + @Test + @DisplayName("saveCancerScreeningDoctorData should answer for a well formed request") + void saveCancerScreeningDoctorData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveCancerScreeningDoctorData(new com.google.gson.JsonObject(), "{}")); + } + + @Test + @DisplayName("saveBenExaminationDetails should answer for a well formed request") + void saveBenExaminationDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenExaminationDetails(new com.google.gson.JsonObject(), 11L, "{}", 11L, 11L)); + } + + @Test + @DisplayName("saveBenDiagnosisDetails should answer for a well formed request") + void saveBenDiagnosisDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenDiagnosisDetails(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("getCancerCasesheetData should answer for a well formed request") + void getCancerCasesheetData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getCancerCasesheetData(new org.json.JSONObject(), "{}")); + } + + @Test + @DisplayName("getBenDataForCaseSheet should answer for a well formed request") + void getBenDataForCaseSheet_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDataForCaseSheet(11L, 11L, 11L, "{}")); + } + + @Test + @DisplayName("getBenNurseDataForCaseSheet should answer for a well formed request") + void getBenNurseDataForCaseSheet_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenNurseDataForCaseSheet(11L, 11L)); + } + + @Test + @DisplayName("getBenFamilyHistoryData should answer for a well formed request") + void getBenFamilyHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenFamilyHistoryData(11L)); + } + + @Test + @DisplayName("getBenPersonalHistoryData should answer for a well formed request") + void getBenPersonalHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenPersonalHistoryData(11L)); + } + + @Test + @DisplayName("getBenPersonalDietHistoryData should answer for a well formed request") + void getBenPersonalDietHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenPersonalDietHistoryData(11L)); + } + + @Test + @DisplayName("getBenObstetricHistoryData should answer for a well formed request") + void getBenObstetricHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenObstetricHistoryData(11L)); + } + + @Test + @DisplayName("updateCancerDiagnosisDetailsByOncologist should answer for a well formed request") + void updateCancerDiagnosisDetailsByOncologist_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateCancerDiagnosisDetailsByOncologist(new com.iemr.tm.data.doctor.CancerDiagnosis())); + } + + @Test + @DisplayName("getBenDoctorDiagnosisData should answer for a well formed request") + void getBenDoctorDiagnosisData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDoctorDiagnosisData(11L, 11L)); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorCS should answer for a well formed request") + void getBenCaseRecordFromDoctorCS_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenCaseRecordFromDoctorCS(11L, 11L)); + } + + @Test + @DisplayName("updateCancerScreeningDoctorData should reject a request it cannot act on") + void updateCancerScreeningDoctorData_shouldRejectRequestItCannotActOn() { + assertThrows(RuntimeException.class, () -> service.updateCancerScreeningDoctorData(new com.google.gson.JsonObject())); + } + + /** A nurse request carrying a visit, history, examination and vitals. */ + private com.google.gson.JsonObject nurseRequest() { + return com.google.gson.JsonParser.parseString("{" + + "\"beneficiaryRegID\":11,\"providerServiceMapID\":9,\"vanID\":7,\"sessionID\":1," + + "\"benFlowID\":5,\"createdBy\":\"nurse1\",\"sendToDoctorWorklist\":true," + + "\"visitDetails\":{\"beneficiaryRegID\":11,\"visitReason\":\"New Chief Complaint\"," + + " \"visitCategory\":\"Cancer Screening\"}," + + "\"historyDetails\":{\"familyHistory\":{\"diseases\":[{\"cancerDiseaseType\":\"Breast\"}]},\"personalHistory\":{}," + + " \"pastObstetricHistory\":{}}," + + "\"examinationDetails\":{\"signsDetails\":{},\"oralDetails\":{}," + + " \"breastDetails\":{\"referredToMammogram\":false}," + + " \"abdominalDetails\":{},\"gynecologicalDetails\":{}}," + + "\"vitalsDetails\":{\"height_cm\":170}}").getAsJsonObject(); + } + + @org.junit.jupiter.api.Nested + @DisplayName("nurse data capture") + class NurseDataCaptureTests { + + @org.junit.jupiter.api.BeforeEach + void stubNurseCollaborators() throws Exception { + org.mockito.Mockito.when(commonNurseServiceImpl.getMaxCurrentdate(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString())).thenReturn(0); + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBeneficiaryVisitDetails(org.mockito.ArgumentMatchers.any())).thenReturn(3L); + org.mockito.Mockito.when(commonNurseServiceImpl.generateVisitCode(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(22L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveBenFamilyCancerHistory(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveBenPersonalCancerHistory(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveBenPersonalCancerDietHistory(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveBenObstetricCancerHistory(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveBenVitalDetail(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl.saveCancerSignAndSymptomsData( + org.mockito.ArgumentMatchers.any(com.iemr.tm.data.doctor.CancerSignAndSymptoms.class))) + .thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveCancerOralExaminationData(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveCancerBreastExaminationData(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveCancerAbdominalExaminationData(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveCancerGynecologicalExaminationData(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateBenFlowNurseAfterNurseActivity( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + } + + @Test + @DisplayName("saveCancerScreeningNurseData should save the visit, history, examination and vitals") + void saveNurseData_shouldSaveEverySection() throws Exception { + String result = service.saveCancerScreeningNurseData(nurseRequest(), "Bearer session-token"); + + assertTrue(result.contains("Data saved successfully")); + assertTrue(result.contains("\"visitCode\":\"22\"")); + } + + @Test + @DisplayName("saveCancerScreeningNurseData should report an already saved visit") + void saveNurseData_shouldReportAlreadySavedVisit() throws Exception { + org.mockito.Mockito.when(commonNurseServiceImpl.getMaxCurrentdate(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString())).thenReturn(1); + + assertTrue(service.saveCancerScreeningNurseData(nurseRequest(), "Bearer session-token") + .contains("Data already saved")); + } + + @Test + @DisplayName("saveCancerScreeningNurseData should fail when the visit could not be created") + void saveNurseData_shouldFailWhenVisitNotCreated() throws Exception { + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBeneficiaryVisitDetails(org.mockito.ArgumentMatchers.any())).thenReturn(0L); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.saveCancerScreeningNurseData(nurseRequest(), "Bearer session-token")); + } + + @Test + @DisplayName("saveBenVisitDetails should return the new visit id and code") + void saveBenVisitDetails_shouldReturnVisitIdAndCode() throws Exception { + com.iemr.tm.data.nurse.BeneficiaryVisitDetail visit = new com.iemr.tm.data.nurse.BeneficiaryVisitDetail(); + visit.setBeneficiaryRegID(11L); + visit.setVisitReason("New Chief Complaint"); + visit.setVisitCategory("Cancer Screening"); + com.iemr.tm.data.nurse.CommonUtilityClass utility = new com.iemr.tm.data.nurse.CommonUtilityClass(); + utility.setVanID(7); + utility.setSessionID(1); + + java.util.Map result = service.saveBenVisitDetails(visit, utility); + + assertTrue(result.containsKey("visitID")); + assertTrue(result.containsKey("visitCode")); + } + + @Test + @DisplayName("saveBenHistoryDetails should store every captured history section") + void saveHistory_shouldStoreCapturedSections() throws Exception { + assertTrue(service.saveBenHistoryDetails(nurseRequest(), 3L, 22L) > 0); + } + + @Test + @DisplayName("saveBenHistoryDetails should succeed when no history section was captured") + void saveHistory_shouldSucceedWithoutCapturedSections() throws Exception { + assertTrue(service.saveBenHistoryDetails( + com.google.gson.JsonParser.parseString("{}").getAsJsonObject(), 3L, 22L) > 0); + } + + @Test + @DisplayName("saveBenVitalsDetails should store the captured vitals") + void saveVitals_shouldStoreCapturedVitals() throws Exception { + assertTrue(service.saveBenVitalsDetails(nurseRequest(), 3L, 22L) > 0); + } + + @Test + @DisplayName("saveBenVitalsDetails should succeed when no vitals were captured") + void saveVitals_shouldSucceedWithoutCapturedVitals() throws Exception { + assertTrue(service.saveBenVitalsDetails( + com.google.gson.JsonParser.parseString("{}").getAsJsonObject(), 3L, 22L) > 0); + } + + @Test + @DisplayName("saveBenExaminationDetails should store every captured examination section") + void saveExamination_shouldStoreCapturedSections() throws Exception { + assertTrue(service.saveBenExaminationDetails(nurseRequest(), 3L, "Bearer session-token", 22L, 5L) > 0); + } + + @Test + @DisplayName("saveBenExaminationDetails should succeed when no examination section was captured") + void saveExamination_shouldSucceedWithoutCapturedSections() throws Exception { + assertTrue(service.saveBenExaminationDetails( + com.google.gson.JsonParser.parseString("{}").getAsJsonObject(), 3L, "Bearer session-token", 22L, + 5L) > 0); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("doctor data capture") + class DoctorDataCaptureTests { + + private com.google.gson.JsonObject doctorRequest(boolean specialist, boolean withTcRequest) { + StringBuilder request = new StringBuilder("{\"doctorSignatureFlag\":true,\"diagnosis\":{" + + "\"beneficiaryRegID\":11,\"beneficiaryID\":9,\"benVisitID\":3,\"visitCode\":22," + + "\"benFlowID\":5,\"createdBy\":\"tester\",\"vanID\":7,\"serviceID\":4," + + "\"provisionalDiagnosisPrimaryDoctor\":\"suspected\",\"isSpecialist\":" + specialist + "}"); + if (withTcRequest) { + request.append(",\"tcRequest\":{\"userID\":41,\"specializationID\":3,\"walkIn\":false," + + "\"allocationDate\":\"2026-08-26\",\"fromTime\":\"10:00:00\",\"toTime\":\"10:30:00\"}"); + } + return com.google.gson.JsonParser.parseString(request.append("}").toString()).getAsJsonObject(); + } + + @org.junit.jupiter.api.BeforeEach + void stubDoctorCollaborators() throws Exception { + org.mockito.Mockito.when(cSDoctorServiceImpl + .saveCancerDiagnosisData(org.mockito.ArgumentMatchers.any())).thenReturn(4L); + org.mockito.Mockito.when(commonDoctorServiceImpl.callTmForSpecialistSlotBook( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())).thenReturn(1); + org.mockito.Mockito.when(teleConsultationServiceImpl + .createTCRequest(org.mockito.ArgumentMatchers.any())).thenReturn(77L); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocData( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyInt(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyBoolean())) + .thenReturn(1); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocDataFromSpecialist( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyBoolean())) + .thenReturn(1); + org.mockito.Mockito.when(tCRequestModelRepo.updateStatusIfConsultationCompleted( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyString())).thenReturn(1); + } + + @Test + @DisplayName("saveCancerScreeningDoctorData should save the diagnosis for a general doctor") + void saveDoctorData_shouldSaveDiagnosisForGeneralDoctor() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(Long.valueOf(4L), service.saveCancerScreeningDoctorData(doctorRequest(false, false), + "Bearer session-token")); + } + + @Test + @DisplayName("saveCancerScreeningDoctorData should route a specialist consultation through the flow update") + void saveDoctorData_shouldRouteSpecialistConsultation() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(Long.valueOf(4L), service.saveCancerScreeningDoctorData(doctorRequest(true, false), + "Bearer session-token")); + } + + @Test + @DisplayName("saveCancerScreeningDoctorData should book a slot and raise the teleconsultation request") + void saveDoctorData_shouldBookSlotAndRaiseTcRequest() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(Long.valueOf(4L), service.saveCancerScreeningDoctorData(doctorRequest(false, true), + "Bearer session-token")); + + org.mockito.Mockito.verify(teleConsultationServiceImpl).createTCRequest(org.mockito.ArgumentMatchers.any()); + org.mockito.Mockito.verify(sMSGatewayServiceImpl).smsSenderGateway( + org.mockito.ArgumentMatchers.eq("schedule"), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyInt(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.anyString()); + } + + @Test + @DisplayName("saveCancerScreeningDoctorData should fail when the specialist slot could not be booked") + void saveDoctorData_shouldFailWhenSlotBookingFails() { + org.mockito.Mockito.when(commonDoctorServiceImpl.callTmForSpecialistSlotBook( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())).thenReturn(0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.saveCancerScreeningDoctorData(doctorRequest(false, true), "Bearer session-token")); + } + + @Test + @DisplayName("saveCancerScreeningDoctorData should fail when the beneficiary flow update fails") + void saveDoctorData_shouldFailWhenFlowUpdateFails() { + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocData( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyInt(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyBoolean())) + .thenReturn(0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.saveCancerScreeningDoctorData(doctorRequest(false, false), "Bearer session-token")); + } + + @Test + @DisplayName("saveBenDiagnosisDetails should succeed when no diagnosis was captured") + void saveDiagnosis_shouldSucceedWithoutCapturedDiagnosis() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(Long.valueOf(1L), service.saveBenDiagnosisDetails( + com.google.gson.JsonParser.parseString("{}").getAsJsonObject())); + } + + @Test + @DisplayName("updateCancerScreeningDoctorData should close the specialist consultation") + void updateDoctorData_shouldCloseSpecialistConsultation() throws Exception { + org.mockito.Mockito.when(cSDoctorServiceImpl + .updateCancerDiagnosisDetailsByDoctor(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo + .updateBenFlowAfterTCSpcialistDoneForCanceScreening(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong())) + .thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateCancerScreeningDoctorData(doctorRequest(false, false))); + } + + @Test + @DisplayName("updateCancerScreeningDoctorData should fail when the flow status update fails") + void updateDoctorData_shouldFailWhenFlowStatusUpdateFails() { + org.mockito.Mockito.when(cSDoctorServiceImpl + .updateCancerDiagnosisDetailsByDoctor(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo + .updateBenFlowAfterTCSpcialistDoneForCanceScreening(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong())) + .thenReturn(0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.updateCancerScreeningDoctorData(doctorRequest(false, false))); + } + + @Test + @DisplayName("updateCancerScreeningDoctorData should fail when the diagnosis could not be updated") + void updateDoctorData_shouldFailWhenDiagnosisUpdateFails() { + org.mockito.Mockito.when(cSDoctorServiceImpl + .updateCancerDiagnosisDetailsByDoctor(org.mockito.ArgumentMatchers.any())).thenReturn(0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.updateCancerScreeningDoctorData(doctorRequest(false, false))); + } + + @Test + @DisplayName("updateCancerScreeningDoctorData should reject a null request") + void updateDoctorData_shouldRejectNullRequest() { + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.updateCancerScreeningDoctorData(null)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("captured section updates") + class CapturedSectionUpdateTests { + + private static final String FULL_HISTORY = "{\"familyHistory\":[{\"beneficiaryRegID\":11,\"visitCode\":22," + + " \"deleted\":false,\"familyMembers\":[\"Mother\"]}]," + + "\"pastObstetricHistory\":{\"beneficiaryRegID\":11,\"visitCode\":22}," + + "\"personalHistory\":{\"beneficiaryRegID\":11,\"visitCode\":22}}"; + + private static final String FULL_EXAMINATION = "{\"beneficiaryRegID\":11,\"visitCode\":22," + + "\"signsDetails\":{\"cancerSignAndSymptoms\":{\"beneficiaryRegID\":11,\"visitCode\":22," + + " \"lymphNode_Enlarged\":true}," + + " \"cancerLymphNodeDetails\":[{\"beneficiaryRegID\":11,\"visitCode\":22," + + " \"lymphNodeName\":\"Cervical\",\"mobility_Left\":true,\"size_Right\":\"2 cm\"}]}," + + "\"oralDetails\":{\"beneficiaryRegID\":11,\"visitCode\":22}," + + "\"breastDetails\":{\"beneficiaryRegID\":11,\"visitCode\":22}," + + "\"abdominalDetails\":{\"beneficiaryRegID\":11,\"visitCode\":22}," + + "\"gynecologicalDetails\":{\"beneficiaryRegID\":11,\"visitCode\":22}," + + "\"imageCoordinates\":[{\"cancerImageID\":2,\"createdBy\":\"nurse1\"," + + " \"markers\":[{\"xCord\":120,\"yCord\":240,\"point\":\"1\"}]}]}"; + + private com.google.gson.JsonObject json(String raw) { + return com.google.gson.JsonParser.parseString(raw).getAsJsonObject(); + } + + @org.junit.jupiter.api.BeforeEach + void stubUpdateCollaborators() { + org.mockito.Mockito.when(cSNurseServiceImpl + .updateBeneficiaryFamilyCancerHistory(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl + .updateBenObstetricCancerHistory(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl + .updateBenPersonalCancerHistory(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl + .updateBenPersonalCancerDietHistory(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl + .updateSignAndSymptomsExaminationDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl + .updateLymphNodeExaminationDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl + .updateCancerOralDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl + .updateCancerBreastDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl + .updateCancerAbdominalExaminationDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl + .updateCancerGynecologicalExaminationDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(cSNurseServiceImpl.getCancerExaminationImageAnnotationList( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>()); + org.mockito.Mockito.when(cSNurseServiceImpl + .updateCancerExamImgAnotasnDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1); + } + + @Test + @DisplayName("UpdateCSHistoryNurseData should update every captured history section") + void updateHistory_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(1, service.UpdateCSHistoryNurseData(json(FULL_HISTORY))); + + org.mockito.Mockito.verify(cSNurseServiceImpl) + .updateBeneficiaryFamilyCancerHistory(org.mockito.ArgumentMatchers.any()); + org.mockito.Mockito.verify(cSNurseServiceImpl) + .updateBenPersonalCancerDietHistory(org.mockito.ArgumentMatchers.any()); + } + + @Test + @DisplayName("UpdateCSHistoryNurseData should succeed when no history section was captured") + void updateHistory_shouldSucceedWithoutCapturedSections() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(1, service.UpdateCSHistoryNurseData(json("{}"))); + } + + @Test + @DisplayName("UpdateCSHistoryNurseData should succeed when the family history list is empty") + void updateHistory_shouldSucceedWithEmptyFamilyHistory() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(1, service.UpdateCSHistoryNurseData(json("{\"familyHistory\":[]}"))); + } + + @Test + @DisplayName("UpdateCSHistoryNurseData should report history it could not update") + void updateHistory_shouldReportUnupdatedHistory() throws Exception { + org.mockito.Mockito.when(cSNurseServiceImpl + .updateBenObstetricCancerHistory(org.mockito.ArgumentMatchers.any())).thenReturn(0); + + org.junit.jupiter.api.Assertions.assertEquals(0, service.UpdateCSHistoryNurseData(json(FULL_HISTORY))); + } + + @Test + @DisplayName("updateBenExaminationDetail should update every captured examination section") + void updateExamination_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBenExaminationDetail(json(FULL_EXAMINATION))); + + org.mockito.Mockito.verify(cSNurseServiceImpl) + .updateSignAndSymptomsExaminationDetails(org.mockito.ArgumentMatchers.any()); + org.mockito.Mockito.verify(cSNurseServiceImpl) + .updateLymphNodeExaminationDetails(org.mockito.ArgumentMatchers.any()); + org.mockito.Mockito.verify(cSNurseServiceImpl) + .updateCancerOralDetails(org.mockito.ArgumentMatchers.any()); + org.mockito.Mockito.verify(cSNurseServiceImpl) + .updateCancerBreastDetails(org.mockito.ArgumentMatchers.any()); + org.mockito.Mockito.verify(cSNurseServiceImpl) + .updateCancerAbdominalExaminationDetails(org.mockito.ArgumentMatchers.any()); + org.mockito.Mockito.verify(cSNurseServiceImpl) + .updateCancerGynecologicalExaminationDetails(org.mockito.ArgumentMatchers.any()); + org.mockito.Mockito.verify(cSNurseServiceImpl) + .updateCancerExamImgAnotasnDetails(org.mockito.ArgumentMatchers.any()); + } + + @Test + @DisplayName("updateBenExaminationDetail should succeed when no examination section was captured") + void updateExamination_shouldSucceedWithoutCapturedSections() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBenExaminationDetail(json("{}"))); + } + + @Test + @DisplayName("updateBenExaminationDetail should report an examination it could not update") + void updateExamination_shouldReportUnupdatedExamination() throws Exception { + org.mockito.Mockito.when(cSNurseServiceImpl + .updateCancerOralDetails(org.mockito.ArgumentMatchers.any())).thenReturn(0); + + org.junit.jupiter.api.Assertions.assertEquals(0, service.updateBenExaminationDetail(json(FULL_EXAMINATION))); + } + + @Test + @DisplayName("saveBenExaminationDetails should store the lymph nodes captured with the signs") + void saveExamination_shouldStoreLymphNodesWithSigns() throws Exception { + org.mockito.Mockito.when(cSNurseServiceImpl.saveCancerSignAndSymptomsData( + org.mockito.ArgumentMatchers.any(com.iemr.tm.data.doctor.CancerSignAndSymptoms.class), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl.saveLymphNodeDetails(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveCancerOralExaminationData(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveCancerBreastExaminationData(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveCancerAbdominalExaminationData(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl + .saveCancerGynecologicalExaminationData(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(cSNurseServiceImpl.saveDocExaminationImageAnnotation( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1L); + + assertTrue(service.saveBenExaminationDetails( + json("{\"examinationDetails\":" + FULL_EXAMINATION + "}"), 3L, "Bearer session-token", 22L, + 5L) > 0); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/common/master/ANCMasterDataServiceImplTest.java b/src/test/java/com/iemr/tm/service/common/master/ANCMasterDataServiceImplTest.java new file mode 100644 index 00000000..f47e28dd --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/master/ANCMasterDataServiceImplTest.java @@ -0,0 +1,210 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.common.master; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.doctor.ChiefComplaintMasterRepo; +import com.iemr.tm.repo.doctor.DrugDoseMasterRepo; +import com.iemr.tm.repo.doctor.DrugDurationUnitMasterRepo; +import com.iemr.tm.repo.doctor.DrugFrequencyMasterRepo; +import com.iemr.tm.repo.foetalmonitor.FoetalMonitorTestsRepo; +import com.iemr.tm.repo.labModule.ProcedureRepo; +import com.iemr.tm.repo.login.MasterVanRepo; +import com.iemr.tm.repo.masterrepo.anc.AllergicReactionTypesRepo; +import com.iemr.tm.repo.masterrepo.anc.BloodGroupsRepo; +import com.iemr.tm.repo.masterrepo.anc.ChildVaccinationsRepo; +import com.iemr.tm.repo.masterrepo.anc.ComorbidConditionRepo; +import com.iemr.tm.repo.masterrepo.anc.CompFeedsRepo; +import com.iemr.tm.repo.masterrepo.anc.ComplicationTypesRepo; +import com.iemr.tm.repo.masterrepo.anc.CounsellingTypeRepo; +import com.iemr.tm.repo.masterrepo.anc.DeliveryPlaceRepo; +import com.iemr.tm.repo.masterrepo.anc.DeliveryTypeRepo; +import com.iemr.tm.repo.masterrepo.anc.DevelopmentProblemsRepo; +import com.iemr.tm.repo.masterrepo.anc.DiseaseTypeRepo; +import com.iemr.tm.repo.masterrepo.anc.FundalHeightRepo; +import com.iemr.tm.repo.masterrepo.anc.GestationRepo; +import com.iemr.tm.repo.masterrepo.anc.GrossMotorMilestoneRepo; +import com.iemr.tm.repo.masterrepo.anc.IllnessTypesRepo; +import com.iemr.tm.repo.masterrepo.anc.JointTypesRepo; +import com.iemr.tm.repo.masterrepo.anc.MenstrualCycleRangeRepo; +import com.iemr.tm.repo.masterrepo.anc.MenstrualCycleStatusRepo; +import com.iemr.tm.repo.masterrepo.anc.MenstrualProblemRepo; +import com.iemr.tm.repo.masterrepo.anc.MusculoskeletalRepo; +import com.iemr.tm.repo.masterrepo.anc.OptionalVaccinationsRepo; +import com.iemr.tm.repo.masterrepo.anc.PersonalHabitTypeRepo; +import com.iemr.tm.repo.masterrepo.anc.PregDurationRepo; +import com.iemr.tm.repo.masterrepo.anc.PregOutcomeRepo; +import com.iemr.tm.repo.masterrepo.anc.ServiceFacilityMasterRepo; +import com.iemr.tm.repo.masterrepo.anc.ServiceMasterRepo; +import com.iemr.tm.repo.masterrepo.anc.SurgeryTypesRepo; +import com.iemr.tm.repo.masterrepo.covid19.CovidContactHistoryMasterRepo; +import com.iemr.tm.repo.masterrepo.covid19.CovidRecommnedationMasterRepo; +import com.iemr.tm.repo.masterrepo.covid19.CovidSymptomsMasterRepo; +import com.iemr.tm.repo.masterrepo.doctor.InstituteRepo; +import com.iemr.tm.repo.masterrepo.doctor.ItemFormMasterRepo; +import com.iemr.tm.repo.masterrepo.doctor.ItemMasterRepo; +import com.iemr.tm.repo.masterrepo.doctor.RouteOfAdminRepo; +import com.iemr.tm.repo.masterrepo.doctor.V_DrugPrescriptionRepo; +import com.iemr.tm.repo.masterrepo.ncdCare.NCDCareTypeRepo; +import com.iemr.tm.repo.masterrepo.nurse.FamilyMemberMasterRepo; +import com.iemr.tm.repo.masterrepo.pnc.NewbornHealthStatusRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("ANCMasterDataServiceImpl Test Suite") +class ANCMasterDataServiceImplTest { + + @Mock + private AllergicReactionTypesRepo allergicReactionTypesRepo; + @Mock + private BloodGroupsRepo bloodGroupsRepo; + @Mock + private ChildVaccinationsRepo childVaccinationsRepo; + @Mock + private DeliveryPlaceRepo deliveryPlaceRepo; + @Mock + private DeliveryTypeRepo deliveryTypeRepo; + @Mock + private DevelopmentProblemsRepo developmentProblemsRepo; + @Mock + private GestationRepo gestationRepo; + @Mock + private IllnessTypesRepo illnessTypesRepo; + @Mock + private JointTypesRepo jointTypesRepo; + @Mock + private MenstrualCycleRangeRepo menstrualCycleRangeRepo; + @Mock + private MenstrualCycleStatusRepo menstrualCycleStatusRepo; + @Mock + private MenstrualProblemRepo menstrualProblemRepo; + @Mock + private MusculoskeletalRepo musculoskeletalRepo; + @Mock + private PregDurationRepo pregDurationRepo; + @Mock + private SurgeryTypesRepo surgeryTypesRepo; + @Mock + private ComorbidConditionRepo comorbidConditionRepo; + @Mock + private CompFeedsRepo compFeedsRepo; + @Mock + private FundalHeightRepo fundalHeightRepo; + @Mock + private GrossMotorMilestoneRepo grossMotorMilestoneRepo; + @Mock + private ServiceMasterRepo serviceMasterRepo; + @Mock + private CounsellingTypeRepo counsellingTypeRepo; + @Mock + private InstituteRepo instituteRepo; + @Mock + private PersonalHabitTypeRepo personalHabitTypeRepo; + @Mock + private PregOutcomeRepo pregOutcomeRepo; + @Mock + private DiseaseTypeRepo diseaseTypeRepo; + @Mock + private ComplicationTypesRepo complicationTypesRepo; + @Mock + private ChiefComplaintMasterRepo chiefComplaintMasterRepo; + @Mock + private FamilyMemberMasterRepo familyMemberMasterRepo; + @Mock + private DrugDoseMasterRepo drugDoseMasterRepo; + @Mock + private DrugDurationUnitMasterRepo drugDurationUnitMasterRepo; + @Mock + private DrugFrequencyMasterRepo drugFrequencyMasterRepo; + @Mock + private NewbornHealthStatusRepo newbornHealthStatusRepo; + @Mock + private NCDCareTypeRepo ncdCareTypeRepo; + @Mock + private ProcedureRepo procedureRepo; + @Mock + private OptionalVaccinationsRepo optionalVaccinationsRepo; + @Mock + private ItemMasterRepo itemMasterRepo; + @Mock + private ItemFormMasterRepo itemFormMasterRepo; + @Mock + private RouteOfAdminRepo routeOfAdminRepo; + @Mock + private V_DrugPrescriptionRepo v_DrugPrescriptionRepo; + @Mock + private CovidSymptomsMasterRepo covidSymptomsMasterRepo; + @Mock + private CovidContactHistoryMasterRepo covidContactHistoryMasterRepo; + @Mock + private CovidRecommnedationMasterRepo covidRecommnedationMasterRepo; + @Mock + private MasterVanRepo masterVanRepo; + @Mock + private FoetalMonitorTestsRepo foetakMonitorTestRepo; + @Mock + private ServiceFacilityMasterRepo serviceFacilityMasterRepo; + + @InjectMocks + private ANCMasterDataServiceImpl service; + + @Test + @DisplayName("getCommonNurseMasterDataForGenopdAncNcdcarePnc should assemble the nurse master data") + void getNurseMasterData_shouldAssembleMasterData() { + String result = service.getCommonNurseMasterDataForGenopdAncNcdcarePnc(1, 9, "Female"); + + assertNotNull(result); + assertTrue(result.contains("bloodGroups")); + } + + @Test + @DisplayName("getCommonNurseMasterDataForGenopdAncNcdcarePnc should assemble the master data for a male") + void getNurseMasterData_shouldAssembleMasterDataForMale() { + assertNotNull(service.getCommonNurseMasterDataForGenopdAncNcdcarePnc(1, 9, "Male")); + } + + @Test + @DisplayName("getCommonDoctorMasterDataForGenopdAncNcdcarePnc should assemble the doctor master data") + void getDoctorMasterData_shouldAssembleMasterData() { + String result = service.getCommonDoctorMasterDataForGenopdAncNcdcarePnc(1, 9, "Female", 3, 7); + + assertNotNull(result); + assertTrue(result.contains("additionalServices")); + } + + @Test + @DisplayName("getCommonDoctorMasterDataForGenopdAncNcdcarePnc should assemble the cancer screening master data") + void getDoctorMasterData_shouldAssembleCancerScreeningMasterData() { + assertNotNull(service.getCommonDoctorMasterDataForGenopdAncNcdcarePnc(7, 9, "Female", 3, 7)); + } +} diff --git a/src/test/java/com/iemr/tm/service/common/master/CommonMasterServiceImplTest.java b/src/test/java/com/iemr/tm/service/common/master/CommonMasterServiceImplTest.java new file mode 100644 index 00000000..93c8062e --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/master/CommonMasterServiceImplTest.java @@ -0,0 +1,190 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/package com.iemr.tm.service.common.master; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +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.ValueSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.service.labtechnician.LabTechnicianServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CommonMasterServiceImpl Test Suite") +class CommonMasterServiceImplTest { + + private static final Integer PROVIDER_SERVICE_MAP_ID = 9; + private static final Integer FACILITY_ID = 2; + private static final Integer VAN_ID = 7; + private static final String GENDER = "Female"; + + @Mock + private ANCMasterDataServiceImpl ancMasterDataServiceImpl; + @Mock + private NurseMasterDataServiceImpl nurseMasterDataServiceImpl; + @Mock + private DoctorMasterDataServiceImpl doctorMasterDataServiceImpl; + @Mock + private RegistrarServiceMasterDataImpl registrarServiceMasterDataImpl; + @Mock + private NCDScreeningMasterServiceImpl ncdScreeningServiceImpl; + @Mock + private QCMasterDataServiceImpl qCMasterDataServiceImpl; + @Mock + private NCDCareMasterDataServiceImpl ncdCareMasterDataServiceImpl; + @Mock + private LabTechnicianServiceImpl labTechnicianServiceImpl; + + private CommonMasterServiceImpl service; + + @BeforeEach + @DisplayName("Wire the service with the mocked master services") + void setUp() { + service = new CommonMasterServiceImpl(); + service.setAncMasterDataServiceImpl(ancMasterDataServiceImpl); + service.setNurseMasterDataServiceImpl(nurseMasterDataServiceImpl); + service.setDoctorMasterDataServiceImpl(doctorMasterDataServiceImpl); + service.setRegistrarServiceMasterDataImpl(registrarServiceMasterDataImpl); + service.setNcdScreeningServiceImpl(ncdScreeningServiceImpl); + service.setqCMasterDataServiceImpl(qCMasterDataServiceImpl); + service.setNcdCareMasterDataServiceImpl(ncdCareMasterDataServiceImpl); + service.setLabTechnicianServiceImpl(labTechnicianServiceImpl); + } + + @Test + @DisplayName("getVisitReasonAndCategories should return the visit master the nurse service assembled") + void getVisitReasonAndCategories_shouldReturnNurseMaster() { + when(nurseMasterDataServiceImpl.GetVisitReasonAndCategories()).thenReturn("{\"visitReasons\":[]}"); + + assertEquals("{\"visitReasons\":[]}", service.getVisitReasonAndCategories()); + } + + @Test + @DisplayName("getECGAbnormalFindings should return the findings master the lab service assembled") + void getECGAbnormalFindings_shouldReturnLabMaster() { + when(labTechnicianServiceImpl.getECGAbnormalFindings()).thenReturn("{\"findings\":[]}"); + + assertEquals("{\"findings\":[]}", service.getECGAbnormalFindings()); + } + + @Nested + @DisplayName("nurse master data") + class NurseMasterTests { + + @Test + @DisplayName("getMasterDataForNurse should route cancer screening to the nurse master service") + void getMasterDataForNurse_shouldRouteCancerScreening() { + when(nurseMasterDataServiceImpl.getCancerScreeningMasterDataForNurse()).thenReturn("cancer-master"); + + assertEquals("cancer-master", + service.getMasterDataForNurse(1, PROVIDER_SERVICE_MAP_ID, GENDER)); + } + + @Test + @DisplayName("getMasterDataForNurse should route NCD screening to the screening master service") + void getMasterDataForNurse_shouldRouteNcdScreening() { + when(ncdScreeningServiceImpl.getNCDScreeningMasterData(anyInt(), anyInt(), anyString())) + .thenReturn("ncd-screening-master"); + + assertEquals("ncd-screening-master", + service.getMasterDataForNurse(2, PROVIDER_SERVICE_MAP_ID, GENDER)); + } + + @ParameterizedTest + @ValueSource(ints = { 3, 4, 5, 6, 7, 8, 10 }) + @DisplayName("getMasterDataForNurse should route the remaining visit categories to the shared master service") + void getMasterDataForNurse_shouldRouteRemainingCategories(int visitCategoryID) { + when(ancMasterDataServiceImpl.getCommonNurseMasterDataForGenopdAncNcdcarePnc(anyInt(), anyInt(), + anyString())).thenReturn("shared-nurse-master"); + + assertEquals("shared-nurse-master", + service.getMasterDataForNurse(visitCategoryID, PROVIDER_SERVICE_MAP_ID, GENDER)); + } + + @Test + @DisplayName("getMasterDataForNurse should reject a visit category it does not know") + void getMasterDataForNurse_shouldRejectUnknownCategory() { + assertEquals("Invalid VisitCategoryID", + service.getMasterDataForNurse(99, PROVIDER_SERVICE_MAP_ID, GENDER)); + } + + @Test + @DisplayName("getMasterDataForNurse should reject a request with no visit category") + void getMasterDataForNurse_shouldRejectMissingCategory() { + assertEquals("Invalid VisitCategoryID", + service.getMasterDataForNurse(null, PROVIDER_SERVICE_MAP_ID, GENDER)); + } + } + + @Nested + @DisplayName("doctor master data") + class DoctorMasterTests { + + @Test + @DisplayName("getMasterDataForDoctor should route cancer screening to the doctor master service") + void getMasterDataForDoctor_shouldRouteCancerScreening() { + when(doctorMasterDataServiceImpl.getCancerScreeningMasterDataForDoctor(PROVIDER_SERVICE_MAP_ID)) + .thenReturn("cancer-doctor-master"); + + assertEquals("cancer-doctor-master", + service.getMasterDataForDoctor(1, PROVIDER_SERVICE_MAP_ID, GENDER, FACILITY_ID, VAN_ID)); + } + + @ParameterizedTest + @ValueSource(ints = { 2, 3, 4, 5, 6, 7, 8, 10 }) + @DisplayName("getMasterDataForDoctor should route the remaining visit categories to the shared master service") + void getMasterDataForDoctor_shouldRouteRemainingCategories(int visitCategoryID) { + when(ancMasterDataServiceImpl.getCommonDoctorMasterDataForGenopdAncNcdcarePnc(anyInt(), anyInt(), + anyString(), any(), any())).thenReturn("shared-doctor-master"); + + assertEquals("shared-doctor-master", service.getMasterDataForDoctor(visitCategoryID, + PROVIDER_SERVICE_MAP_ID, GENDER, FACILITY_ID, VAN_ID)); + } + + @Test + @DisplayName("getMasterDataForDoctor should reject a visit category it does not know") + void getMasterDataForDoctor_shouldRejectUnknownCategory() { + assertEquals("Invalid VisitCategoryID", + service.getMasterDataForDoctor(99, PROVIDER_SERVICE_MAP_ID, GENDER, FACILITY_ID, VAN_ID)); + } + + @Test + @DisplayName("getMasterDataForDoctor should reject a request with no visit category") + void getMasterDataForDoctor_shouldRejectMissingCategory() { + assertEquals("Invalid VisitCategoryID", + service.getMasterDataForDoctor(null, PROVIDER_SERVICE_MAP_ID, GENDER, FACILITY_ID, VAN_ID)); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/common/master/DoctorMasterDataServiceImplTest.java b/src/test/java/com/iemr/tm/service/common/master/DoctorMasterDataServiceImplTest.java new file mode 100644 index 00000000..6062ae7f --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/master/DoctorMasterDataServiceImplTest.java @@ -0,0 +1,59 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.common.master; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.masterrepo.anc.ServiceMasterRepo; +import com.iemr.tm.repo.masterrepo.doctor.InstituteRepo; +import com.iemr.tm.repo.masterrepo.doctor.PreMalignantLesionMasterRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("DoctorMasterDataServiceImpl Test Suite") +class DoctorMasterDataServiceImplTest { + + @Mock + private PreMalignantLesionMasterRepo preMalignantLesionMasterRepo; + @Mock + private InstituteRepo instituteRepo; + @Mock + private ServiceMasterRepo serviceMasterRepo; + + @InjectMocks + private DoctorMasterDataServiceImpl service; + + @Test + @DisplayName("getCancerScreeningMasterDataForDoctor should answer for a well formed request") + void getCancerScreeningMasterDataForDoctor_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getCancerScreeningMasterDataForDoctor(9)); + } +} diff --git a/src/test/java/com/iemr/tm/service/common/master/NCDCareMasterDataServiceImplTest.java b/src/test/java/com/iemr/tm/service/common/master/NCDCareMasterDataServiceImplTest.java new file mode 100644 index 00000000..ef9f120f --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/master/NCDCareMasterDataServiceImplTest.java @@ -0,0 +1,59 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.common.master; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.masterrepo.ncdCare.NCDCareTypeRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NCDCareMasterDataServiceImpl Test Suite") +class NCDCareMasterDataServiceImplTest { + + @Mock + private NCDScreeningMasterServiceImpl ncdScreeningMasterServiceImpl; + @Mock + private NCDCareTypeRepo ncdCareTypeRepo; + + @InjectMocks + private NCDCareMasterDataServiceImpl service; + + @Test + @DisplayName("getNCDCareMasterData should answer for a well formed request") + void getNCDCareMasterData_shouldAnswerForWellFormedRequest() throws Exception { + when(ncdScreeningMasterServiceImpl.getNCDScreeningConditions()).thenReturn(new java.util.ArrayList<>()); + when(ncdCareTypeRepo.getNCDCareTypes()).thenReturn(new java.util.ArrayList<>()); + + assertDoesNotThrow(() -> service.getNCDCareMasterData()); + } +} diff --git a/src/test/java/com/iemr/tm/service/common/master/NCDScreeningMasterServiceImplTest.java b/src/test/java/com/iemr/tm/service/common/master/NCDScreeningMasterServiceImplTest.java new file mode 100644 index 00000000..17240d7d --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/master/NCDScreeningMasterServiceImplTest.java @@ -0,0 +1,116 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.common.master; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.doctor.ChiefComplaintMasterRepo; +import com.iemr.tm.repo.doctor.LabTestMasterRepo; +import com.iemr.tm.repo.labModule.ProcedureRepo; +import com.iemr.tm.repo.masterrepo.anc.AllergicReactionTypesRepo; +import com.iemr.tm.repo.masterrepo.anc.DiseaseTypeRepo; +import com.iemr.tm.repo.masterrepo.anc.PersonalHabitTypeRepo; +import com.iemr.tm.repo.masterrepo.ncdScreening.BPAndDiabeticStatusRepo; +import com.iemr.tm.repo.masterrepo.ncdScreening.IDRS_ScreenQuestionsRepo; +import com.iemr.tm.repo.masterrepo.ncdScreening.NCDScreeningConditionRepo; +import com.iemr.tm.repo.masterrepo.ncdScreening.NCDScreeningReasonRepo; +import com.iemr.tm.repo.masterrepo.ncdScreening.PhysicalActivityRepo; +import com.iemr.tm.repo.masterrepo.nurse.FamilyMemberMasterRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NCDScreeningMasterServiceImpl Test Suite") +class NCDScreeningMasterServiceImplTest { + + @Mock + private NCDScreeningConditionRepo ncdScreeningConditionRepo; + @Mock + private NCDScreeningReasonRepo ncdScreeningReasonRepo; + @Mock + private BPAndDiabeticStatusRepo bpAndDiabeticStatusRepo; + @Mock + private LabTestMasterRepo labTestMasterRepo; + @Mock + private ChiefComplaintMasterRepo chiefComplaintMasterRepo; + @Mock + private ProcedureRepo procedureRepo; + @Mock + private IDRS_ScreenQuestionsRepo iDRS_ScreenQuestionsRepo; + @Mock + private PhysicalActivityRepo physicalActivityRepo; + @Mock + private DiseaseTypeRepo diseaseTypeRepo; + @Mock + private FamilyMemberMasterRepo familyMemberMasterRepo; + @Mock + private PersonalHabitTypeRepo personalHabitTypeRepo; + @Mock + private AllergicReactionTypesRepo allergicReactionTypesRepo; + + @InjectMocks + private NCDScreeningMasterServiceImpl service; + + @Test + @DisplayName("getNCDScreeningConditions should answer for a well formed request") + void getNCDScreeningConditions_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getNCDScreeningConditions()); + } + + @Test + @DisplayName("getNCDScreeningReasons should answer for a well formed request") + void getNCDScreeningReasons_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getNCDScreeningReasons()); + } + + @Test + @DisplayName("getBPAndDiabeticStatus should answer for a well formed request") + void getBPAndDiabeticStatus_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBPAndDiabeticStatus(Boolean.FALSE)); + } + + @Test + @DisplayName("getNCDTest should answer for a well formed request") + void getNCDTest_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getNCDTest()); + } + + @Test + @DisplayName("getChiefComplaintMaster should answer for a well formed request") + void getChiefComplaintMaster_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getChiefComplaintMaster()); + } + + @Test + @DisplayName("getNCDScreeningMasterData should answer for a well formed request") + void getNCDScreeningMasterData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getNCDScreeningMasterData(9, 9, "{}")); + } +} diff --git a/src/test/java/com/iemr/tm/service/common/master/NurseMasterDataServiceImplTest.java b/src/test/java/com/iemr/tm/service/common/master/NurseMasterDataServiceImplTest.java new file mode 100644 index 00000000..8d842105 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/master/NurseMasterDataServiceImplTest.java @@ -0,0 +1,71 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.common.master; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.masterrepo.nurse.CancerDiseaseMasterRepo; +import com.iemr.tm.repo.masterrepo.nurse.CancerPersonalHabitMasterRepo; +import com.iemr.tm.repo.masterrepo.nurse.FamilyMemberMasterRepo; +import com.iemr.tm.repo.masterrepo.nurse.VisitCategoryMasterRepo; +import com.iemr.tm.repo.masterrepo.nurse.VisitReasonMasterRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NurseMasterDataServiceImpl Test Suite") +class NurseMasterDataServiceImplTest { + + @Mock + private CancerDiseaseMasterRepo cancerDiseaseMasterRepo; + @Mock + private CancerPersonalHabitMasterRepo cancerPersonalHabitMasterRepo; + @Mock + private FamilyMemberMasterRepo familyMemberMasterRepo; + @Mock + private VisitCategoryMasterRepo visitCategoryMasterRepo; + @Mock + private VisitReasonMasterRepo visitReasonMasterRepo; + + @InjectMocks + private NurseMasterDataServiceImpl service; + + @Test + @DisplayName("GetVisitReasonAndCategories should answer for a well formed request") + void GetVisitReasonAndCategories_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.GetVisitReasonAndCategories()); + } + + @Test + @DisplayName("getCancerScreeningMasterDataForNurse should answer for a well formed request") + void getCancerScreeningMasterDataForNurse_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getCancerScreeningMasterDataForNurse()); + } +} diff --git a/src/test/java/com/iemr/tm/service/common/master/QCMasterDataServiceImplTest.java b/src/test/java/com/iemr/tm/service/common/master/QCMasterDataServiceImplTest.java new file mode 100644 index 00000000..82dc1575 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/master/QCMasterDataServiceImplTest.java @@ -0,0 +1,74 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.common.master; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.doctor.ChiefComplaintMasterRepo; +import com.iemr.tm.repo.doctor.DrugDoseMasterRepo; +import com.iemr.tm.repo.doctor.DrugDurationUnitMasterRepo; +import com.iemr.tm.repo.doctor.DrugFormMasterRepo; +import com.iemr.tm.repo.doctor.DrugFrequencyMasterRepo; +import com.iemr.tm.repo.doctor.LabTestMasterRepo; +import com.iemr.tm.repo.doctor.TempMasterDrugRepo; +import com.iemr.tm.repo.labModule.ProcedureRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("QCMasterDataServiceImpl Test Suite") +class QCMasterDataServiceImplTest { + + @Mock + private ChiefComplaintMasterRepo chiefComplaintMasterRepo; + @Mock + private DrugDoseMasterRepo drugDoseMasterRepo; + @Mock + private DrugDurationUnitMasterRepo drugDurationUnitMasterRepo; + @Mock + private DrugFormMasterRepo drugFormMasterRepo; + @Mock + private DrugFrequencyMasterRepo drugFrequencyMasterRepo; + @Mock + private LabTestMasterRepo labTestMasterRepo; + @Mock + private TempMasterDrugRepo tempMasterDrugRepo; + @Mock + private ProcedureRepo procedureRepo; + + @InjectMocks + private QCMasterDataServiceImpl service; + + @Test + @DisplayName("getQuickConsultMasterData should answer for a well formed request") + void getQuickConsultMasterData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getQuickConsultMasterData(9, "{}")); + } +} diff --git a/src/test/java/com/iemr/tm/service/common/master/RegistrarServiceMasterDataImplTest.java b/src/test/java/com/iemr/tm/service/common/master/RegistrarServiceMasterDataImplTest.java new file mode 100644 index 00000000..27dbcfbe --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/master/RegistrarServiceMasterDataImplTest.java @@ -0,0 +1,94 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.common.master; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo; +import com.iemr.tm.repo.masterrepo.CommunityMasterRepo; +import com.iemr.tm.repo.masterrepo.GenderMasterRepo; +import com.iemr.tm.repo.masterrepo.GovIdEntityTypeRepo; +import com.iemr.tm.repo.masterrepo.IncomeStatusMasterRepo; +import com.iemr.tm.repo.masterrepo.MaritalStatusMasterRepo; +import com.iemr.tm.repo.masterrepo.OccupationMasterRepo; +import com.iemr.tm.repo.masterrepo.QualificationMasterRepo; +import com.iemr.tm.repo.masterrepo.ReligionMasterRepo; +import com.iemr.tm.repo.nurse.anc.ANCCareRepo; +import com.iemr.tm.repo.registrar.BeneficiaryImageRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("RegistrarServiceMasterDataImpl Test Suite") +class RegistrarServiceMasterDataImplTest { + + @Mock + private CommunityMasterRepo communityMasterRepo; + @Mock + private GenderMasterRepo genderMasterRepo; + @Mock + private GovIdEntityTypeRepo govIdEntityTypeRepo; + @Mock + private IncomeStatusMasterRepo incomeStatusMasterRepo; + @Mock + private MaritalStatusMasterRepo maritalStatusMasterRepo; + @Mock + private OccupationMasterRepo occupationMasterRepo; + @Mock + private QualificationMasterRepo qualificationMasterRepo; + @Mock + private ReligionMasterRepo religionMasterRepo; + @Mock + private BeneficiaryImageRepo beneficiaryImageRepo; + @Mock + private BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private ANCCareRepo aNCCareRepo; + + @Mock + private com.iemr.tm.repo.registrar.ReistrarRepoBenSearch reistrarRepoBenSearch; + + @InjectMocks + private RegistrarServiceMasterDataImpl service; + + @Test + @DisplayName("getRegMasterData should answer for a well formed request") + void getRegMasterData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getRegMasterData()); + } + + + @Test + @DisplayName("getBenDetailsForLeftSideByRegIDNew should answer for a well formed request") + void getBenDetailsForLeftSideByRegIDNew_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDetailsForLeftSideByRegIDNew(11L, 11L, "{}", "{}")); + } + +} diff --git a/src/test/java/com/iemr/tm/service/common/transaction/CommonDoctorServiceImplTest.java b/src/test/java/com/iemr/tm/service/common/transaction/CommonDoctorServiceImplTest.java new file mode 100644 index 00000000..6f919a46 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/transaction/CommonDoctorServiceImplTest.java @@ -0,0 +1,755 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.common.transaction; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyShort; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Collections; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.test.util.ReflectionTestUtils; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.iemr.tm.data.anc.WrapperAncFindings; +import com.iemr.tm.data.quickConsultation.BenClinicalObservations; +import com.iemr.tm.data.login.Users; +import com.iemr.tm.data.quickConsultation.BenChiefComplaint; +import com.iemr.tm.data.snomedct.SCTDescription; +import com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo; +import com.iemr.tm.repo.quickConsultation.BenClinicalObservationsRepo; +import com.iemr.tm.repo.doctor.BenReferDetailsRepo; +import com.iemr.tm.repo.login.UserLoginRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.quickConsultation.BenChiefComplaintRepo; +import com.iemr.tm.repo.quickConsultation.LabTestOrderDetailRepo; +import com.iemr.tm.repo.quickConsultation.PrescribedDrugDetailRepo; +import com.iemr.tm.repo.quickConsultation.PrescriptionDetailRepo; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; +import com.iemr.tm.service.snomedct.SnomedServiceImpl; +import com.iemr.tm.service.tele_consultation.SMSGatewayServiceImpl; +import com.iemr.tm.utils.CookieUtil; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CommonDoctorServiceImpl Test Suite") +class CommonDoctorServiceImplTest { + + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_CODE = 22L; + + @Mock + private BenClinicalObservationsRepo benClinicalObservationsRepo; + @Mock + private BenChiefComplaintRepo benChiefComplaintRepo; + @Mock + private com.iemr.tm.repo.doctor.DocWorkListRepo docWorkListRepo; + @Mock + private BenReferDetailsRepo benReferDetailsRepo; + @Mock + private LabTestOrderDetailRepo labTestOrderDetailRepo; + @Mock + private PrescribedDrugDetailRepo prescribedDrugDetailRepo; + @Mock + private SnomedServiceImpl snomedServiceImpl; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private com.iemr.tm.repo.tc_consultation.TCRequestModelRepo tCRequestModelRepo; + @Mock + private com.iemr.tm.repo.nurse.pnc.PNCDiagnosisRepo pNCDiagnosisRepo; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private UserLoginRepo userLoginRepo; + @Mock + private PrescriptionDetailRepo prescriptionDetailRepo; + @Mock + private com.iemr.tm.repo.nurse.ncdcare.NCDCareDiagnosisRepo NCDCareDiagnosisRepo; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + @Mock + private com.iemr.tm.repo.foetalmonitor.FoetalMonitorRepo foetalMonitorRepo; + @Mock + private CookieUtil cookieUtil; + @Mock + private com.iemr.tm.repo.tc_consultation.TeleconsultationStatsRepo teleconsultationStatsRepo; + + @InjectMocks + private CommonDoctorServiceImpl service; + + private static org.mockito.stubbing.Answer echoList() { + return invocation -> invocation.getArgument(0); + } + + private JsonObject json(String raw) { + return JsonParser.parseString(raw).getAsJsonObject(); + } + + private WrapperAncFindings findings(String complaint) { + ArrayList complaints = new ArrayList<>(); + BenChiefComplaint chiefComplaint = new BenChiefComplaint(); + chiefComplaint.setChiefComplaint(complaint); + complaints.add(chiefComplaint); + WrapperAncFindings wrapper = new WrapperAncFindings(BEN_REG_ID, 3L, 9, "Clinical observation", "Fever", + "Significant findings", complaints, Boolean.FALSE, VISIT_CODE); + wrapper.setCreatedBy("doctor1"); + return wrapper; + } + + private ArrayList oneEmptyRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[60]); + return rows; + } + + @Nested + @DisplayName("findings") + class FindingsTests { + + @Test + @DisplayName("saveFindings should confirm the stored clinical observation") + void saveFindings_shouldConfirmStoredObservation() throws Exception { + when(benClinicalObservationsRepo.save(any())).thenReturn(new BenClinicalObservations()); + + assertEquals(1, service.saveFindings(json("{\"beneficiaryRegID\":11}"))); + } + + @Test + @DisplayName("saveFindings should report no change when nothing was stored") + void saveFindings_shouldReportNoChangeWhenNothingStored() throws Exception { + when(benClinicalObservationsRepo.save(any())).thenReturn(null); + + assertEquals(0, service.saveFindings(json("{\"beneficiaryRegID\":11}"))); + } + + @Test + @DisplayName("saveDocFindings should store the observation and the named chief complaints") + void saveDocFindings_shouldStoreObservationAndComplaints() { + when(benClinicalObservationsRepo.save(any())).thenReturn(new BenClinicalObservations()); + when(benChiefComplaintRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.saveDocFindings(findings("Fever"))); + verify(benChiefComplaintRepo).saveAll(any()); + } + + @Test + @DisplayName("saveDocFindings should skip an unnamed chief complaint") + void saveDocFindings_shouldSkipUnnamedComplaint() { + when(benClinicalObservationsRepo.save(any())).thenReturn(new BenClinicalObservations()); + + assertEquals(1, service.saveDocFindings(findings(null))); + verify(benChiefComplaintRepo, never()).saveAll(any()); + } + + @Test + @DisplayName("saveDocFindings should report no change when the observation was not stored") + void saveDocFindings_shouldReportNoChangeWhenObservationNotStored() { + when(benClinicalObservationsRepo.save(any())).thenReturn(null); + + assertEquals(0, service.saveDocFindings(findings(null))); + } + + @Test + @DisplayName("updateDocFindings should update the observation and replace the chief complaints") + void updateDocFindings_shouldUpdateObservationAndReplaceComplaints() { + when(benClinicalObservationsRepo.getBenClinicalObservationStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + when(benClinicalObservationsRepo.updateBenClinicalObservations(any(), any(), any(), any(), any(), any(), + any(), any(), any(), any())).thenReturn(1); + when(benChiefComplaintRepo.saveAll(any())).thenAnswer(echoList()); + + assertNotNull(service.updateDocFindings(findings("Fever"))); + } + + @Test + @DisplayName("fetchBenPreviousSignificantFindings should render the earlier findings") + void fetchPreviousFindings_shouldRenderEarlierFindings() { + when(benClinicalObservationsRepo.getPreviousSignificantFindings(BEN_REG_ID)) + .thenReturn(new ArrayList<>()); + + assertNotNull(service.fetchBenPreviousSignificantFindings(BEN_REG_ID)); + } + + @Test + @DisplayName("getFindingsDetails should assemble the observation and the chief complaints") + void getFindingsDetails_shouldAssembleFindings() { + when(benClinicalObservationsRepo.getFindingsData(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + when(benChiefComplaintRepo.getBenChiefComplaints(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getFindingsDetails(BEN_REG_ID, VISIT_CODE)); + } + } + + @Nested + @DisplayName("SNOMED lookups") + class SnomedTests { + + @Test + @DisplayName("getSnomedCTcode should map every term of a comma separated list") + void getSnomedCTcode_shouldMapEveryTerm() { + SCTDescription fever = new SCTDescription(); + fever.setConceptID("386661006"); + fever.setTerm("Fever"); + when(snomedServiceImpl.findSnomedCTRecordFromTerm(anyString())).thenReturn(fever); + + String[] result = service.getSnomedCTcode("Fever,Cough"); + + assertEquals("386661006,386661006", result[0]); + assertEquals("Fever,Fever", result[1]); + } + + @Test + @DisplayName("getSnomedCTcode should leave an unmatched term blank") + void getSnomedCTcode_shouldLeaveUnmatchedTermBlank() { + when(snomedServiceImpl.findSnomedCTRecordFromTerm(anyString())).thenReturn(null); + + assertNotNull(service.getSnomedCTcode("Fever")); + } + + @Test + @DisplayName("getSnomedCTcode should return empty codes for a blank request") + void getSnomedCTcode_shouldReturnEmptyCodesForBlankRequest() { + assertNotNull(service.getSnomedCTcode("")); + } + } + + @Nested + @DisplayName("worklists") + class WorklistTests { + + @Test + @DisplayName("getDocWorkList should render the doctor worklist") + void getDocWorkList_shouldRenderWorklist() { + when(docWorkListRepo.getDocWorkList()).thenReturn(new ArrayList<>()); + + assertNotNull(service.getDocWorkList()); + } + + @Test + @DisplayName("getDocWorkListNew should render the general OPD doctor worklist") + void getDocWorkListNew_shouldRenderGeneralWorklist() { + when(beneficiaryFlowStatusRepo.getDocWorkListNew(9)).thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getDocWorkListNew(9, 2, 7)); + } + + @Test + @DisplayName("getDocWorkListNew should render the teleconsultation doctor worklist") + void getDocWorkListNew_shouldRenderTeleconsultationWorklist() { + ReflectionTestUtils.setField(service, "docWL", 7); + when(beneficiaryFlowStatusRepo.getDocWorkListNewTC(any(), any(), any())).thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getDocWorkListNew(9, 4, 7)); + } + + @Test + @DisplayName("getDocWorkListNew should render an empty worklist for an unknown service") + void getDocWorkListNew_shouldRenderEmptyWorklistForUnknownService() { + assertEquals("[]", service.getDocWorkListNew(9, 99, 7)); + } + + @Test + @DisplayName("getDocWorkListNewFutureScheduledForTM should render the future scheduled worklist") + void getDocWorkListFutureScheduled_shouldRenderWorklist() { + when(beneficiaryFlowStatusRepo.getDocWorkListNewFutureScheduledTC(any(), any())) + .thenReturn(new ArrayList<>()); + + assertNotNull(service.getDocWorkListNewFutureScheduledForTM(9, 4, 7)); + } + + @Test + @DisplayName("getTCSpecialistWorkListNewForTMPatientApp should render the patient app worklist") + void getTCSpecialistWorkListPatientApp_shouldRenderWorklist() { + ReflectionTestUtils.setField(service, "tcSpeclistWL", 7); + when(beneficiaryFlowStatusRepo.getTCSpecialistWorkListNewPatientApp(any(), any(), any(), any())) + .thenReturn(new ArrayList<>()); + + assertNotNull(service.getTCSpecialistWorkListNewForTMPatientApp(9, 42, 4, 7)); + } + + @Test + @DisplayName("getTCSpecialistWorkListNewForTM should render the specialist worklist") + void getTCSpecialistWorkList_shouldRenderWorklist() { + when(beneficiaryFlowStatusRepo.getTCSpecialistWorkListNew(any(), any(), any())) + .thenReturn(new ArrayList<>()); + + assertNotNull(service.getTCSpecialistWorkListNewForTM(9, 42, 4)); + } + + @Test + @DisplayName("getTCSpecialistWorkListNewFutureScheduledForTM should render the future specialist worklist") + void getTCSpecialistWorkListFutureScheduled_shouldRenderWorklist() { + when(beneficiaryFlowStatusRepo.getTCSpecialistWorkListNewFutureScheduled(any(), any())) + .thenReturn(new ArrayList<>()); + + assertNotNull(service.getTCSpecialistWorkListNewFutureScheduledForTM(9, 42, 4)); + } + } + + @Nested + @DisplayName("referrals and case record reads") + class ReferralAndReadTests { + + @Test + @DisplayName("saveBenReferDetails should store the referral for every additional service") + void saveReferDetails_shouldStoreReferralPerService() throws Exception { + when(benReferDetailsRepo.saveAll(any())).thenAnswer(echoList()); + + Long result = service.saveBenReferDetails(json("{\"beneficiaryRegID\":11,\"visitCode\":22," + + "\"refrredToAdditionalServiceList\":[{\"serviceName\":\"Radiology\"}]," + + "\"referredToInstituteName\":\"District Hospital\"}")); + + assertNotNull(result); + } + + @Test + @DisplayName("saveBenReferDetails should store a referral without any additional service") + void saveReferDetails_shouldStoreReferralWithoutAdditionalService() throws Exception { + when(benReferDetailsRepo.saveAll(any())).thenAnswer(echoList()); + + assertNotNull(service.saveBenReferDetails(json("{\"beneficiaryRegID\":11,\"visitCode\":22," + + "\"referredToInstituteName\":\"District Hospital\"}"))); + } + + @Test + @DisplayName("getInvestigationDetails should render the ordered lab tests") + void getInvestigationDetails_shouldRenderOrderedTests() { + when(labTestOrderDetailRepo.getLabTestOrderDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(new ArrayList<>()); + + assertNotNull(service.getInvestigationDetails(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getPrescribedDrugs should render the prescribed drugs") + void getPrescribedDrugs_shouldRenderPrescribedDrugs() { + when(prescribedDrugDetailRepo.getBenPrescribedDrugDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new ArrayList<>()); + + assertNotNull(service.getPrescribedDrugs(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getReferralDetails should render the referral") + void getReferralDetails_shouldRenderReferral() { + when(benReferDetailsRepo.getBenReferDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(new ArrayList<>()); + + assertNotNull(service.getReferralDetails(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getFoetalMonitorData should render the foetal monitor readings") + void getFoetalMonitorData_shouldRenderReadings() { + when(foetalMonitorRepo.getFoetalMonitorDetailsForCaseRecord(BEN_REG_ID, VISIT_CODE)).thenReturn(new ArrayList<>()); + + assertNotNull(service.getFoetalMonitorData(BEN_REG_ID, VISIT_CODE)); + } + } + + @Nested + @DisplayName("updates") + class UpdateTests { + + @Test + @DisplayName("updateDoctorBenChiefComplaints should replace the stored complaints") + void updateChiefComplaints_shouldReplaceStoredComplaints() { + BenChiefComplaint complaint = new BenChiefComplaint(); + complaint.setBeneficiaryRegID(BEN_REG_ID); + complaint.setVisitCode(VISIT_CODE); + when(benChiefComplaintRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.updateDoctorBenChiefComplaints(Collections.singletonList(complaint))); + } + + @Test + @DisplayName("updateDoctorBenChiefComplaints should succeed for an empty list") + void updateChiefComplaints_shouldSucceedForEmptyList() { + assertEquals(1, service.updateDoctorBenChiefComplaints(Collections.emptyList())); + } + + @Test + @DisplayName("updateBenClinicalObservations should mark an already processed observation as updated") + void updateClinicalObservations_shouldMarkProcessedAsUpdated() { + BenClinicalObservations observations = new BenClinicalObservations(); + observations.setBeneficiaryRegID(BEN_REG_ID); + observations.setVisitCode(VISIT_CODE); + when(benClinicalObservationsRepo.getBenClinicalObservationStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateBenClinicalObservations(observations)); + } + + @Test + @DisplayName("updateBenClinicalObservations should report no change for a null payload") + void updateClinicalObservations_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateBenClinicalObservations(null)); + } + + @Test + @DisplayName("updateBenReferDetails should update the referral for the visit") + void updateReferDetails_shouldUpdateReferral() throws Exception { + when(benReferDetailsRepo.saveAll(any())).thenAnswer(echoList()); + + assertNotNull(service.updateBenReferDetails(json("{\"beneficiaryRegID\":11,\"visitCode\":22," + + "\"referredToInstituteName\":\"District Hospital\"}"))); + } + + @Test + @DisplayName("deletePrescribedMedicine should confirm the deletion") + void deletePrescribedMedicine_shouldConfirmDeletion() throws Exception { + when(prescribedDrugDetailRepo.deletePrescribedmedicine(4L)).thenReturn(1); + + assertEquals("record deleted successfully", + service.deletePrescribedMedicine(new org.json.JSONObject().put("id", 4L))); + } + + @Test + @DisplayName("deletePrescribedMedicine should return nothing when no row was deleted") + void deletePrescribedMedicine_shouldReturnNothingWhenNoRowDeleted() throws Exception { + when(prescribedDrugDetailRepo.deletePrescribedmedicine(4L)).thenReturn(0); + + assertNull(service.deletePrescribedMedicine(new org.json.JSONObject().put("id", 4L))); + } + + @Test + @DisplayName("deletePrescribedMedicine should return nothing for a request without an id") + void deletePrescribedMedicine_shouldReturnNothingWithoutId() { + assertNull(service.deletePrescribedMedicine(new org.json.JSONObject())); + } + } + + @Nested + @DisplayName("beneficiary flow after doctor data") + class BeneficiaryFlowTests { + + private com.iemr.tm.data.nurse.CommonUtilityClass utility(boolean isSpecialist) { + com.iemr.tm.data.nurse.CommonUtilityClass utility = new com.iemr.tm.data.nurse.CommonUtilityClass(); + utility.setBenFlowID(5L); + utility.setBeneficiaryID(7L); + utility.setBenVisitID(3L); + utility.setBeneficiaryRegID(BEN_REG_ID); + utility.setVisitCode(VISIT_CODE); + utility.setVisitCategoryID(1); + utility.setIsSpecialist(isSpecialist); + utility.setCreatedBy("doctor1"); + utility.setAuthorization("Bearer session-token"); + return utility; + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataSave should route a doctor visit that ordered a test to the lab") + void updateFlowAfterSave_shouldRouteTestToLab() throws Exception { + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocData(any(), any(), any(), any(), anyShort(), + anyShort(), anyShort(), anyShort(), anyInt(), any(), anyShort(), any())).thenReturn(1); + + assertEquals(1, service.updateBenFlowtableAfterDocDataSave(utility(false), true, true, null, false)); + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataSave should close the visit when nothing was prescribed") + void updateFlowAfterSave_shouldCloseVisitWithoutPrescriptions() throws Exception { + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocData(any(), any(), any(), any(), anyShort(), + anyShort(), anyShort(), anyShort(), anyInt(), any(), anyShort(), any())).thenReturn(1); + + assertEquals(1, service.updateBenFlowtableAfterDocDataSave(utility(false), false, false, null, false)); + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataSave should route a specialist visit through the specialist flow") + void updateFlowAfterSave_shouldRouteSpecialistVisit() throws Exception { + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocDataFromSpecialist(any(), any(), any(), any(), + anyShort(), anyShort(), anyShort(), anyShort(), anyShort(), any())).thenReturn(1); + + assertEquals(1, service.updateBenFlowtableAfterDocDataSave(utility(true), false, false, null, false)); + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataSave should read the foetal monitor state for an ANC visit") + void updateFlowAfterSave_shouldReadFoetalMonitorStateForAncVisit() throws Exception { + com.iemr.tm.data.nurse.CommonUtilityClass utility = utility(false); + utility.setVisitCategoryID(4); + com.iemr.tm.data.foetalmonitor.FoetalMonitor monitor = new com.iemr.tm.data.foetalmonitor.FoetalMonitor(); + monitor.setResultState(false); + when(foetalMonitorRepo.getFoetalMonitorDetailsByFlowId(5L)) + .thenReturn(new ArrayList<>(Collections.singletonList(monitor))); + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocData(any(), any(), any(), any(), anyShort(), + anyShort(), anyShort(), anyShort(), anyInt(), any(), anyShort(), any())).thenReturn(1); + + assertEquals(1, service.updateBenFlowtableAfterDocDataSave(utility, false, false, null, false)); + verify(foetalMonitorRepo).updateVisitCode(VISIT_CODE, 5L); + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataUpdate should advance the flow for a doctor visit") + void updateFlowAfterUpdate_shouldAdvanceFlow() throws Exception { + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocDataUpdate(any(), any(), any(), any(), anyShort(), + anyShort(), anyShort(), anyShort(), anyInt(), any(), anyShort(), any())).thenReturn(1); + + assertEquals(1, service.updateBenFlowtableAfterDocDataUpdate(utility(false), true, true, null, false)); + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataUpdate should advance the flow for a specialist visit") + void updateFlowAfterUpdate_shouldAdvanceSpecialistFlow() throws Exception { + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocDataUpdateTCSpecialist(any(), any(), any(), any(), + anyShort(), anyShort(), anyShort(), anyShort(), anyInt(), any(), anyShort(), any())).thenReturn(1); + + assertEquals(1, service.updateBenFlowtableAfterDocDataUpdate(utility(true), false, false, null, false)); + } + + @Test + @DisplayName("createTMPrescriptionSms should do nothing when the prescription has no drug") + void createPrescriptionSms_shouldDoNothingWithoutDrugs() throws Exception { + when(prescribedDrugDetailRepo.getPrescriptionDetails(any())).thenReturn(new ArrayList<>()); + + service.createTMPrescriptionSms(utility(false)); + + verify(sMSGatewayServiceImpl, never()).smsSenderGateway2(anyString(), any(), any(), any(), any(), any()); + } + } + + @Nested + @DisplayName("teleconsultation flow and prescription SMS") + class TeleconsultationFlowTests { + + private com.iemr.tm.data.nurse.CommonUtilityClass utility(boolean isSpecialist, Integer visitCategoryID) { + com.iemr.tm.data.nurse.CommonUtilityClass utility = new com.iemr.tm.data.nurse.CommonUtilityClass(); + utility.setBenFlowID(5L); + utility.setBeneficiaryID(7L); + utility.setBenVisitID(3L); + utility.setBeneficiaryRegID(BEN_REG_ID); + utility.setVisitCode(VISIT_CODE); + utility.setVisitCategoryID(visitCategoryID); + utility.setIsSpecialist(isSpecialist); + utility.setCreatedBy("doctor1"); + utility.setPrescriptionID(31L); + utility.setAuthorization("Bearer session-token"); + return utility; + } + + private com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ tcRequest() { + com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ tcRequest = + new com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ(); + tcRequest.setUserID(41); + tcRequest.setSpecializationID(3); + tcRequest.setAllocationDate(new java.sql.Timestamp(System.currentTimeMillis())); + return tcRequest; + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataSave should hand a scheduled visit to the specialist") + void updateFlowAfterSave_shouldHandScheduledVisitToSpecialist() throws Exception { + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocData(any(), any(), any(), any(), anyShort(), + anyShort(), anyShort(), anyShort(), anyInt(), any(), anyShort(), any())).thenReturn(1); + + assertEquals(1, service.updateBenFlowtableAfterDocDataSave(utility(false, 1), false, true, tcRequest(), + true)); + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataUpdate should hand a scheduled visit to the specialist") + void updateFlowAfterUpdate_shouldHandScheduledVisitToSpecialist() throws Exception { + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocDataUpdate(any(), any(), any(), any(), anyShort(), + anyShort(), anyShort(), anyShort(), anyInt(), any(), anyShort(), any())).thenReturn(1); + + assertEquals(1, service.updateBenFlowtableAfterDocDataUpdate(utility(false, 1), false, true, tcRequest(), + true)); + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataUpdate should close the specialist consultation and stamp its end time") + void updateFlowAfterUpdate_shouldCloseSpecialistConsultation() throws Exception { + com.iemr.tm.data.tele_consultation.TeleconsultationStats stats = + new com.iemr.tm.data.tele_consultation.TeleconsultationStats(); + when(teleconsultationStatsRepo.getLatestStartTime(BEN_REG_ID, VISIT_CODE)).thenReturn(stats); + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocDataUpdateTCSpecialist(any(), any(), any(), any(), + anyShort(), anyShort(), anyShort(), anyShort(), anyInt(), any(), anyShort(), any())).thenReturn(1); + when(prescribedDrugDetailRepo.getPrescriptionDetails(any())).thenReturn(new ArrayList<>()); + + assertEquals(1, service.updateBenFlowtableAfterDocDataUpdate(utility(true, 1), false, true, null, true)); + + verify(teleconsultationStatsRepo).save(stats); + verify(tCRequestModelRepo).updateStatusIfConsultationCompleted(BEN_REG_ID, VISIT_CODE, "D"); + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataUpdate should start a fresh stats entry when the last one is closed") + void updateFlowAfterUpdate_shouldStartFreshStatsEntry() throws Exception { + com.iemr.tm.data.tele_consultation.TeleconsultationStats stats = + new com.iemr.tm.data.tele_consultation.TeleconsultationStats(); + stats.settMStatsID(8L); + stats.setEndTime(new java.sql.Timestamp(System.currentTimeMillis())); + when(teleconsultationStatsRepo.getLatestStartTime(BEN_REG_ID, VISIT_CODE)).thenReturn(stats); + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocDataUpdateTCSpecialist(any(), any(), any(), any(), + anyShort(), anyShort(), anyShort(), anyShort(), anyInt(), any(), anyShort(), any())).thenReturn(1); + + assertEquals(1, service.updateBenFlowtableAfterDocDataUpdate(utility(true, 1), true, false, null, true)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.tele_consultation.TeleconsultationStats.class); + verify(teleconsultationStatsRepo).save(captor.capture()); + org.junit.jupiter.api.Assertions.assertNull(captor.getValue().gettMStatsID()); + } + + @Test + @DisplayName("updateBenFlowtableAfterDocDataUpdate should read the foetal monitor state for an ANC visit") + void updateFlowAfterUpdate_shouldReadFoetalMonitorStateForAncVisit() throws Exception { + com.iemr.tm.data.foetalmonitor.FoetalMonitor monitor = new com.iemr.tm.data.foetalmonitor.FoetalMonitor(); + monitor.setResultState(true); + when(foetalMonitorRepo.getFoetalMonitorDetailsByFlowId(5L)) + .thenReturn(new ArrayList<>(Collections.singletonList(monitor))); + when(commonBenStatusFlowServiceImpl.updateBenFlowAfterDocDataUpdate(any(), any(), any(), any(), anyShort(), + anyShort(), anyShort(), anyShort(), anyInt(), any(), anyShort(), any())).thenReturn(1); + when(prescribedDrugDetailRepo.getPrescriptionDetails(any())).thenReturn(new ArrayList<>()); + + assertEquals(1, service.updateBenFlowtableAfterDocDataUpdate(utility(false, 4), false, false, null, true)); + verify(foetalMonitorRepo).updateVisitCode(VISIT_CODE, 5L); + } + + private java.util.List onePrescribedDrug() { + com.iemr.tm.data.quickConsultation.PrescribedDrugDetail drug = new com.iemr.tm.data.quickConsultation.PrescribedDrugDetail(); + drug.setDrugName("Paracetamol"); + return new ArrayList<>(Collections.singletonList(drug)); + } + + @Test + @DisplayName("createTMPrescriptionSms should read the provisional diagnosis for a general OPD prescription") + void createPrescriptionSms_shouldReadProvisionalDiagnosis() throws Exception { + when(prescribedDrugDetailRepo.getPrescriptionDetails(31L)).thenReturn(onePrescribedDrug()); + when(prescriptionDetailRepo.getProvisionalDiagnosis(VISIT_CODE, 31L)).thenReturn(new ArrayList<>()); + when(sMSGatewayServiceImpl.smsSenderGateway2(anyString(), any(), any(), any(), any(), any())) + .thenReturn(1); + + service.createTMPrescriptionSms(utility(false, 6)); + + verify(sMSGatewayServiceImpl).smsSenderGateway2(eq("prescription"), any(), anyString(), eq(BEN_REG_ID), + anyString(), any()); + } + + @Test + @DisplayName("createTMPrescriptionSms should read the NCD condition for an NCD care prescription") + void createPrescriptionSms_shouldReadNcdCondition() throws Exception { + when(prescribedDrugDetailRepo.getPrescriptionDetails(31L)).thenReturn(onePrescribedDrug()); + when(NCDCareDiagnosisRepo.getNCDcondition(VISIT_CODE, 31L)).thenReturn(new ArrayList<>()); + + service.createTMPrescriptionSms(utility(false, 3)); + + verify(NCDCareDiagnosisRepo).getNCDcondition(VISIT_CODE, 31L); + } + + @Test + @DisplayName("createTMPrescriptionSms should read the PNC diagnosis for a PNC prescription") + void createPrescriptionSms_shouldReadPncDiagnosis() throws Exception { + when(prescribedDrugDetailRepo.getPrescriptionDetails(31L)).thenReturn(onePrescribedDrug()); + when(pNCDiagnosisRepo.getProvisionalDiagnosis(VISIT_CODE, 31L)).thenReturn(new ArrayList<>()); + + service.createTMPrescriptionSms(utility(false, 5)); + + verify(pNCDiagnosisRepo).getProvisionalDiagnosis(VISIT_CODE, 31L); + } + + @Test + @DisplayName("createTMPrescriptionSms should still send when the diagnosis lookup fails") + void createPrescriptionSms_shouldStillSendWhenDiagnosisLookupFails() throws Exception { + when(prescribedDrugDetailRepo.getPrescriptionDetails(31L)).thenReturn(onePrescribedDrug()); + when(prescriptionDetailRepo.getProvisionalDiagnosis(VISIT_CODE, 31L)) + .thenThrow(new RuntimeException("diagnosis unavailable")); + + service.createTMPrescriptionSms(utility(false, 7)); + + verify(sMSGatewayServiceImpl).smsSenderGateway2(eq("prescription"), any(), anyString(), eq(BEN_REG_ID), + anyString(), any()); + } + + @Test + @DisplayName("createTMPrescriptionSms should absorb a failure from the SMS gateway") + void createPrescriptionSms_shouldAbsorbGatewayFailure() throws Exception { + when(prescribedDrugDetailRepo.getPrescriptionDetails(31L)).thenReturn(onePrescribedDrug()); + when(sMSGatewayServiceImpl.smsSenderGateway2(anyString(), any(), any(), any(), any(), any())) + .thenThrow(new RuntimeException("gateway down")); + + org.junit.jupiter.api.Assertions.assertDoesNotThrow( + () -> service.createTMPrescriptionSms(utility(false, 8))); + } + + @Test + @DisplayName("callTmForSpecialistSlotBook should confirm a booked slot") + void callTmForSlotBook_shouldConfirmBookedSlot() { + new com.iemr.tm.utils.mapper.OutputMapper(); + org.springframework.test.util.ReflectionTestUtils.setField(service, "tcSpecialistSlotBook", + "http://common/tc/specialistSlotBook"); + + try (org.mockito.MockedConstruction ignored = + org.mockito.Mockito.mockConstruction(org.springframework.web.client.RestTemplate.class, + (restTemplate, context) -> when(restTemplate.exchange(anyString(), + any(org.springframework.http.HttpMethod.class), any(), + org.mockito.ArgumentMatchers.>any())) + .thenReturn(new org.springframework.http.ResponseEntity<>( + "{\"statusCode\":200}", org.springframework.http.HttpStatus.OK)))) { + assertEquals(1, service.callTmForSpecialistSlotBook( + new com.iemr.tm.data.tele_consultation.TcSpecialistSlotBookingRequestOBJ(), + "Bearer session-token")); + } + } + + @Test + @DisplayName("callTmForSpecialistSlotBook should report a slot the scheduler refused") + void callTmForSlotBook_shouldReportRefusedSlot() { + new com.iemr.tm.utils.mapper.OutputMapper(); + org.springframework.test.util.ReflectionTestUtils.setField(service, "tcSpecialistSlotBook", + "http://common/tc/specialistSlotBook"); + + try (org.mockito.MockedConstruction ignored = + org.mockito.Mockito.mockConstruction(org.springframework.web.client.RestTemplate.class, + (restTemplate, context) -> when(restTemplate.exchange(anyString(), + any(org.springframework.http.HttpMethod.class), any(), + org.mockito.ArgumentMatchers.>any())) + .thenReturn(new org.springframework.http.ResponseEntity<>( + "{\"statusCode\":5000}", org.springframework.http.HttpStatus.OK)))) { + assertEquals(0, service.callTmForSpecialistSlotBook( + new com.iemr.tm.data.tele_consultation.TcSpecialistSlotBookingRequestOBJ(), + "Bearer session-token")); + } + } + } +} diff --git a/src/test/java/com/iemr/tm/service/common/transaction/CommonNurseServiceImplTest.java b/src/test/java/com/iemr/tm/service/common/transaction/CommonNurseServiceImplTest.java new file mode 100644 index 00000000..4125616d --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/transaction/CommonNurseServiceImplTest.java @@ -0,0 +1,3233 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.common.transaction; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyShort; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.data.anc.BenAdherence; +import com.iemr.tm.data.anc.BenAllergyHistory; +import com.iemr.tm.data.anc.BenChildDevelopmentHistory; +import com.iemr.tm.data.anc.ChildFeedingDetails; +import com.iemr.tm.data.anc.PerinatalHistory; +import com.iemr.tm.data.anc.WrapperBenInvestigationANC; +import com.iemr.tm.data.anc.BenFamilyHistory; +import com.iemr.tm.data.anc.BenMedHistory; +import com.iemr.tm.data.anc.BenMedicationHistory; +import com.iemr.tm.data.anc.BenMenstrualDetails; +import com.iemr.tm.data.anc.BenPersonalHabit; +import com.iemr.tm.data.anc.BencomrbidityCondDetails; +import com.iemr.tm.data.anc.ChildOptionalVaccineDetail; +import com.iemr.tm.data.anc.ChildVaccineDetail1; +import com.iemr.tm.data.anc.FemaleObstetricHistory; +import com.iemr.tm.data.anc.PhyGeneralExamination; +import com.iemr.tm.data.anc.PhyHeadToToeExamination; +import com.iemr.tm.data.anc.SysCardiovascularExamination; +import com.iemr.tm.data.anc.SysCentralNervousExamination; +import com.iemr.tm.data.anc.SysGastrointestinalExamination; +import com.iemr.tm.data.anc.SysGenitourinarySystemExamination; +import com.iemr.tm.data.anc.SysMusculoskeletalSystemExamination; +import com.iemr.tm.data.anc.SysRespiratoryExamination; +import com.iemr.tm.data.anc.WrapperChildOptionalVaccineDetail; +import com.iemr.tm.data.anc.WrapperComorbidCondDetails; +import com.iemr.tm.data.anc.WrapperFemaleObstetricHistory; +import com.iemr.tm.data.anc.WrapperImmunizationHistory; +import com.iemr.tm.data.anc.WrapperMedicationHistory; +import com.iemr.tm.data.login.Users; +import com.iemr.tm.data.ncdScreening.IDRSData; +import com.iemr.tm.data.nurse.BenAnthropometryDetail; +import com.iemr.tm.data.nurse.BenPhysicalVitalDetail; +import com.iemr.tm.data.nurse.BeneficiaryVisitDetail; +import com.iemr.tm.data.ncdScreening.PhysicalActivityType; +import com.iemr.tm.data.quickConsultation.BenChiefComplaint; +import com.iemr.tm.data.quickConsultation.PrescribedDrugDetail; +import com.iemr.tm.data.quickConsultation.PrescriptionDetail; +import com.iemr.tm.data.snomedct.SCTDescription; +import com.iemr.tm.repo.bmiCalculation.BMICalculationRepo; +import com.iemr.tm.repo.login.UserLoginRepo; +import com.iemr.tm.repo.nurse.BenAnthropometryRepo; +import com.iemr.tm.repo.nurse.BenPhysicalVitalRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.nurse.anc.BenAdherenceRepo; +import com.iemr.tm.repo.nurse.anc.BenAllergyHistoryRepo; +import com.iemr.tm.repo.nurse.anc.BenChildDevelopmentHistoryRepo; +import com.iemr.tm.repo.nurse.anc.BenFamilyHistoryRepo; +import com.iemr.tm.repo.nurse.anc.BenMedHistoryRepo; +import com.iemr.tm.repo.nurse.anc.BenMedicationHistoryRepo; +import com.iemr.tm.repo.nurse.anc.BenMenstrualDetailsRepo; +import com.iemr.tm.repo.nurse.anc.BenPersonalHabitRepo; +import com.iemr.tm.repo.nurse.anc.BencomrbidityCondRepo; +import com.iemr.tm.repo.nurse.anc.ChildFeedingDetailsRepo; +import com.iemr.tm.repo.nurse.anc.ChildOptionalVaccineDetailRepo; +import com.iemr.tm.repo.nurse.anc.ChildVaccineDetail1Repo; +import com.iemr.tm.repo.nurse.anc.FemaleObstetricHistoryRepo; +import com.iemr.tm.repo.nurse.anc.PerinatalHistoryRepo; +import com.iemr.tm.repo.nurse.anc.PhyGeneralExaminationRepo; +import com.iemr.tm.repo.nurse.anc.PhyHeadToToeExaminationRepo; +import com.iemr.tm.repo.nurse.anc.SysCardiovascularExaminationRepo; +import com.iemr.tm.repo.nurse.anc.SysCentralNervousExaminationRepo; +import com.iemr.tm.repo.nurse.anc.SysGastrointestinalExaminationRepo; +import com.iemr.tm.repo.nurse.anc.SysGenitourinarySystemExaminationRepo; +import com.iemr.tm.repo.nurse.anc.SysMusculoskeletalSystemExaminationRepo; +import com.iemr.tm.repo.nurse.anc.SysRespiratoryExaminationRepo; +import com.iemr.tm.utils.exception.IEMRException; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CommonNurseServiceImpl Test Suite") +class CommonNurseServiceImplTest { + + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_CODE = 22L; + + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private UserLoginRepo userLoginRepo; + @Mock + private com.iemr.tm.repo.quickConsultation.BenChiefComplaintRepo benChiefComplaintRepo; + @Mock + private BenMedHistoryRepo benMedHistoryRepo; + @Mock + private BencomrbidityCondRepo bencomrbidityCondRepo; + @Mock + private BenMedicationHistoryRepo benMedicationHistoryRepo; + @Mock + private FemaleObstetricHistoryRepo femaleObstetricHistoryRepo; + @Mock + private BenMenstrualDetailsRepo benMenstrualDetailsRepo; + @Mock + private BenFamilyHistoryRepo benFamilyHistoryRepo; + @Mock + private BenPersonalHabitRepo benPersonalHabitRepo; + @Mock + private BenAllergyHistoryRepo benAllergyHistoryRepo; + @Mock + private ChildOptionalVaccineDetailRepo childOptionalVaccineDetailRepo; + @Mock + private ChildVaccineDetail1Repo childVaccineDetail1Repo; + @Mock + private BenAnthropometryRepo benAnthropometryRepo; + @Mock + private BenPhysicalVitalRepo benPhysicalVitalRepo; + @Mock + private PhyGeneralExaminationRepo phyGeneralExaminationRepo; + @Mock + private PhyHeadToToeExaminationRepo phyHeadToToeExaminationRepo; + @Mock + private SysGastrointestinalExaminationRepo sysGastrointestinalExaminationRepo; + @Mock + private SysCardiovascularExaminationRepo sysCardiovascularExaminationRepo; + @Mock + private SysRespiratoryExaminationRepo sysRespiratoryExaminationRepo; + @Mock + private SysCentralNervousExaminationRepo sysCentralNervousExaminationRepo; + @Mock + private SysMusculoskeletalSystemExaminationRepo sysMusculoskeletalSystemExaminationRepo; + @Mock + private SysGenitourinarySystemExaminationRepo sysGenitourinarySystemExaminationRepo; + @Mock + private com.iemr.tm.repo.registrar.RegistrarRepoBenData registrarRepoBenData; + @Mock + private com.iemr.tm.repo.quickConsultation.PrescriptionDetailRepo prescriptionDetailRepo; + @Mock + private com.iemr.tm.repo.quickConsultation.LabTestOrderDetailRepo labTestOrderDetailRepo; + @Mock + private com.iemr.tm.repo.quickConsultation.PrescribedDrugDetailRepo prescribedDrugDetailRepo; + @Mock + private com.iemr.tm.repo.registrar.ReistrarRepoBenSearch reistrarRepoBenSearch; + @Mock + private BenAdherenceRepo benAdherenceRepo; + @Mock + private BenChildDevelopmentHistoryRepo benChildDevelopmentHistoryRepo; + @Mock + private ChildFeedingDetailsRepo childFeedingDetailsRepo; + @Mock + private PerinatalHistoryRepo perinatalHistoryRepo; + @Mock + private com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private com.iemr.tm.repo.nurse.ncdscreening.PhysicalActivityTypeRepo physicalActivityTypeRepo; + @Mock + private com.iemr.tm.repo.nurse.ncdscreening.PhysicalActivityTypeRepo physicalActivityaRepo; + @Mock + private com.iemr.tm.repo.nurse.ncdscreening.IDRSDataRepo iDRSDataRepo; + @Mock + private com.iemr.tm.repo.nurse.ncdscreening.IDRSDataRepo iDrsDataRepo; + @Mock + private com.iemr.tm.repo.nurse.BenCancerVitalDetailRepo benCancerVitalDetailRepo; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private com.iemr.tm.repo.doctor.BenReferDetailsRepo benReferDetailsRepo; + @Mock + private BMICalculationRepo bmiCalculationRepo; + + @InjectMocks + private CommonNurseServiceImpl service; + + /** Echoes the entities handed to a {@code saveAll} call back to the caller. */ + private static org.mockito.stubbing.Answer echoList() { + return invocation -> invocation.getArgument(0); + } + + private BeneficiaryVisitDetail visitDetail() { + BeneficiaryVisitDetail detail = new BeneficiaryVisitDetail(); + detail.setBeneficiaryRegID(BEN_REG_ID); + detail.setVisitCode(VISIT_CODE); + detail.setCreatedBy("nurse1"); + return detail; + } + + @Nested + @DisplayName("beneficiary visit details") + class VisitDetailTests { + + @Test + @DisplayName("updateBeneficiaryStatus should delegate the flow status change to the registrar repository") + void updateBeneficiaryStatus_shouldDelegateToRegistrarRepo() { + when(registrarRepoBenData.updateBenFlowStatus('N', BEN_REG_ID)).thenReturn(1); + + assertEquals(1, service.updateBeneficiaryStatus('N', BEN_REG_ID)); + } + + @Test + @DisplayName("saveBeneficiaryVisitDetails should increment the visit number and return the new visit id") + void saveBeneficiaryVisitDetails_shouldIncrementVisitNumber() { + BeneficiaryVisitDetail detail = visitDetail(); + detail.setFileIDs(new Integer[] { 7, 8 }); + when(benVisitDetailRepo.getVisitCountForBeneficiary(BEN_REG_ID)).thenReturn((short) 2); + BeneficiaryVisitDetail saved = visitDetail(); + saved.setBenVisitID(99L); + when(benVisitDetailRepo.save(any(BeneficiaryVisitDetail.class))).thenReturn(saved); + Users user = new Users(); + user.setUserID(42L); + when(userLoginRepo.getUserByUsername("nurse1")).thenReturn(user); + + assertEquals(99L, service.saveBeneficiaryVisitDetails(detail)); + + ArgumentCaptor captor = ArgumentCaptor.forClass(BeneficiaryVisitDetail.class); + verify(benVisitDetailRepo).save(captor.capture()); + assertEquals((short) 3, captor.getValue().getVisitNo()); + assertEquals("7,8,", captor.getValue().getReportFilePath()); + assertEquals(42L, captor.getValue().getNurseID()); + } + + @Test + @DisplayName("saveBeneficiaryVisitDetails should start the visit numbering at one for a first visit") + void saveBeneficiaryVisitDetails_shouldStartVisitNumberingAtOne() { + when(benVisitDetailRepo.getVisitCountForBeneficiary(BEN_REG_ID)).thenReturn(null); + BeneficiaryVisitDetail saved = visitDetail(); + saved.setBenVisitID(99L); + when(benVisitDetailRepo.save(any(BeneficiaryVisitDetail.class))).thenReturn(saved); + + assertEquals(99L, service.saveBeneficiaryVisitDetails(visitDetail())); + + ArgumentCaptor captor = ArgumentCaptor.forClass(BeneficiaryVisitDetail.class); + verify(benVisitDetailRepo).save(captor.capture()); + assertEquals((short) 1, captor.getValue().getVisitNo()); + assertEquals("", captor.getValue().getReportFilePath()); + assertNull(captor.getValue().getNurseID()); + } + + @Test + @DisplayName("saveBeneficiaryVisitDetails should return null when the save does not come back") + void saveBeneficiaryVisitDetails_shouldReturnNullWhenSaveFails() { + when(benVisitDetailRepo.getVisitCountForBeneficiary(BEN_REG_ID)).thenReturn((short) 0); + when(benVisitDetailRepo.save(any(BeneficiaryVisitDetail.class))).thenReturn(null); + + assertNull(service.saveBeneficiaryVisitDetails(visitDetail())); + } + + @Test + @DisplayName("saveBeneficiaryVisitDetails should leave the nurse unresolved for a blank createdBy") + void saveBeneficiaryVisitDetails_shouldLeaveNurseUnresolvedForBlankCreatedBy() { + BeneficiaryVisitDetail detail = visitDetail(); + detail.setCreatedBy(" "); + BeneficiaryVisitDetail saved = visitDetail(); + saved.setBenVisitID(99L); + when(benVisitDetailRepo.save(any(BeneficiaryVisitDetail.class))).thenReturn(saved); + + service.saveBeneficiaryVisitDetails(detail); + + verify(userLoginRepo, never()).getUserByUsername(anyString()); + } + + @Test + @DisplayName("saveBeneficiaryVisitDetails should leave the nurse unresolved for an unknown user") + void saveBeneficiaryVisitDetails_shouldLeaveNurseUnresolvedForUnknownUser() { + when(userLoginRepo.getUserByUsername("nurse1")).thenReturn(null); + BeneficiaryVisitDetail saved = visitDetail(); + saved.setBenVisitID(99L); + when(benVisitDetailRepo.save(any(BeneficiaryVisitDetail.class))).thenReturn(saved); + + service.saveBeneficiaryVisitDetails(visitDetail()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(BeneficiaryVisitDetail.class); + verify(benVisitDetailRepo).save(captor.capture()); + assertNull(captor.getValue().getNurseID()); + } + + @Test + @DisplayName("getMaxCurrentdate should report a recent visit as still within the ten minute window") + void getMaxCurrentdate_shouldReportRecentVisitWithinWindow() throws Exception { + String now = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date(System.currentTimeMillis())); + when(benVisitDetailRepo.getMaxCreatedDate(BEN_REG_ID, "New", "ANC")).thenReturn(now + ".0"); + + assertEquals(1, service.getMaxCurrentdate(BEN_REG_ID, "New", "ANC")); + } + + @Test + @DisplayName("getMaxCurrentdate should report an old visit as outside the ten minute window") + void getMaxCurrentdate_shouldReportOldVisitOutsideWindow() throws Exception { + when(benVisitDetailRepo.getMaxCreatedDate(BEN_REG_ID, "New", "ANC")) + .thenReturn("2020-01-01 10:00:00.0"); + + assertEquals(-1, service.getMaxCurrentdate(BEN_REG_ID, "New", "ANC")); + } + + @Test + @DisplayName("getMaxCurrentdate should report zero when the beneficiary has no earlier visit") + void getMaxCurrentdate_shouldReportZeroWithoutEarlierVisit() throws Exception { + when(benVisitDetailRepo.getMaxCreatedDate(BEN_REG_ID, "New", "ANC")).thenReturn(null); + + assertEquals(0, service.getMaxCurrentdate(BEN_REG_ID, "New", "ANC")); + } + + @Test + @DisplayName("getMaxCurrentdate should fail for an unparseable created date") + void getMaxCurrentdate_shouldFailForUnparseableDate() { + when(benVisitDetailRepo.getMaxCreatedDate(BEN_REG_ID, "New", "ANC")).thenReturn("not-a-date.0"); + + IEMRException thrown = assertThrows(IEMRException.class, + () -> service.getMaxCurrentdate(BEN_REG_ID, "New", "ANC")); + + assertTrue(thrown.getMessage().contains("Error while parseing created date")); + } + + @Test + @DisplayName("generateVisitCode should build a zero padded visit code and return it once stored") + void generateVisitCode_shouldBuildZeroPaddedVisitCode() { + when(benVisitDetailRepo.updateVisitCode(10000100123456L, 123456L)).thenReturn(1); + + assertEquals(10000100123456L, service.generateVisitCode(123456L, 1, 1)); + } + + @Test + @DisplayName("generateVisitCode should return zero when the visit code could not be stored") + void generateVisitCode_shouldReturnZeroWhenNotStored() { + when(benVisitDetailRepo.updateVisitCode(anyLong(), anyLong())).thenReturn(0); + + assertEquals(0L, service.generateVisitCode(123456L, 1, 1)); + } + + @Test + @DisplayName("updateVisitCodeInVisitDetailsTable should delegate to the visit detail repository") + void updateVisitCodeInVisitDetailsTable_shouldDelegateToRepo() { + when(benVisitDetailRepo.updateVisitCode(55L, 66L)).thenReturn(1); + + assertEquals(1, service.updateVisitCodeInVisitDetailsTable(55L, 66L)); + } + + @Test + @DisplayName("getBenVisitCount should return the next visit number for a returning beneficiary") + void getBenVisitCount_shouldReturnNextVisitNumber() { + when(benVisitDetailRepo.getVisitCountForBeneficiary(BEN_REG_ID)).thenReturn((short) 4); + + assertEquals((short) 5, service.getBenVisitCount(BEN_REG_ID)); + } + + @Test + @DisplayName("getBenVisitCount should return one for a first time beneficiary") + void getBenVisitCount_shouldReturnOneForFirstVisit() { + when(benVisitDetailRepo.getVisitCountForBeneficiary(BEN_REG_ID)).thenReturn(null); + + assertEquals((short) 1, service.getBenVisitCount(BEN_REG_ID)); + } + + @Test + @DisplayName("updateBeneficiaryVisitDetails should return the number of rows changed") + void updateBeneficiaryVisitDetails_shouldReturnRowsChanged() { + when(benVisitDetailRepo.updateBeneficiaryVisitDetail(any(), any(), any(), any(), any(), any(), any(), any(), + any(), any())).thenReturn(1); + + assertEquals(1, service.updateBeneficiaryVisitDetails(visitDetail())); + } + + @Test + @DisplayName("updateBeneficiaryVisitDetails should swallow a repository failure and report no change") + void updateBeneficiaryVisitDetails_shouldSwallowRepositoryFailure() { + when(benVisitDetailRepo.updateBeneficiaryVisitDetail(any(), any(), any(), any(), any(), any(), any(), any(), + any(), any())).thenThrow(new IllegalStateException("db down")); + + assertEquals(0, service.updateBeneficiaryVisitDetails(visitDetail())); + } + + @Test + @DisplayName("getCSVisitDetails should copy the stored visit and expand the report file ids") + void getCSVisitDetails_shouldCopyVisitAndExpandFileIds() { + BeneficiaryVisitDetail stored = visitDetail(); + stored.setBenVisitID(99L); + stored.setReportFilePath("7, 8,"); + when(benVisitDetailRepo.getVisitDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(stored); + + BeneficiaryVisitDetail result = service.getCSVisitDetails(BEN_REG_ID, VISIT_CODE); + + assertNotNull(result); + assertEquals(99L, result.getBenVisitID()); + assertEquals(2, result.getFileIDs().length); + assertEquals(7, result.getFileIDs()[0]); + assertEquals(8, result.getFileIDs()[1]); + } + + @Test + @DisplayName("getCSVisitDetails should leave the file ids empty when no report path is stored") + void getCSVisitDetails_shouldLeaveFileIdsEmptyWithoutReportPath() { + BeneficiaryVisitDetail stored = visitDetail(); + stored.setBenVisitID(99L); + when(benVisitDetailRepo.getVisitDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(stored); + + BeneficiaryVisitDetail result = service.getCSVisitDetails(BEN_REG_ID, VISIT_CODE); + + assertEquals(0, result.getFileIDs().length); + } + + @Test + @DisplayName("getCSVisitDetails should return null when the visit is unknown") + void getCSVisitDetails_shouldReturnNullForUnknownVisit() { + when(benVisitDetailRepo.getVisitDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(null); + + assertNull(service.getCSVisitDetails(BEN_REG_ID, VISIT_CODE)); + } + } + + @Nested + @DisplayName("history saves") + class HistorySaveTests { + + @Test + @DisplayName("saveBenChiefComplaints should store only the complaints that carry an id") + void saveBenChiefComplaints_shouldStoreOnlyIdentifiedComplaints() { + BenChiefComplaint identified = new BenChiefComplaint(); + identified.setChiefComplaintID(3); + BenChiefComplaint unidentified = new BenChiefComplaint(); + when(benChiefComplaintRepo.saveAll(any())).thenReturn(Collections.singletonList(identified)); + + assertEquals(1, service.saveBenChiefComplaints(Arrays.asList(identified, unidentified))); + } + + @Test + @DisplayName("saveBenChiefComplaints should succeed without touching the repository when nothing is identified") + void saveBenChiefComplaints_shouldSucceedWithoutIdentifiedComplaints() { + assertEquals(1, service.saveBenChiefComplaints(Collections.singletonList(new BenChiefComplaint()))); + + verify(benChiefComplaintRepo, never()).saveAll(any()); + } + + @Test + @DisplayName("saveBenChiefComplaints should report a failure when not every complaint was stored") + void saveBenChiefComplaints_shouldReportPartialSaveAsFailure() { + BenChiefComplaint identified = new BenChiefComplaint(); + identified.setChiefComplaintID(3); + when(benChiefComplaintRepo.saveAll(any())).thenReturn(Collections.emptyList()); + + assertEquals(0, service.saveBenChiefComplaints(Collections.singletonList(identified))); + } + + @Test + @DisplayName("saveBenPastHistory should store the derived past illness and surgery entries") + void saveBenPastHistory_shouldStoreDerivedEntries() { + BenMedHistory history = new BenMedHistory(); + ArrayList> illnesses = new ArrayList<>(); + illnesses.add(illness()); + history.setPastIllness(illnesses); + history.setPastSurgery(new ArrayList<>()); + when(benMedHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1L, service.saveBenPastHistory(history)); + } + + @Test + @DisplayName("saveBenPastHistory should succeed for an empty past history") + void saveBenPastHistory_shouldSucceedForEmptyHistory() { + BenMedHistory history = new BenMedHistory(); + history.setPastIllness(new ArrayList<>()); + history.setPastSurgery(new ArrayList<>()); + + assertEquals(1L, service.saveBenPastHistory(history)); + verify(benMedHistoryRepo, never()).saveAll(any()); + } + + @Test + @DisplayName("saveBenPastHistory should report a failure when not every entry was stored") + void saveBenPastHistory_shouldReportPartialSaveAsFailure() { + BenMedHistory history = new BenMedHistory(); + ArrayList> illnesses = new ArrayList<>(); + illnesses.add(illness()); + history.setPastIllness(illnesses); + history.setPastSurgery(new ArrayList<>()); + when(benMedHistoryRepo.saveAll(any())).thenReturn(new ArrayList()); + + assertNull(service.saveBenPastHistory(history)); + } + + @Test + @DisplayName("saveBenComorbidConditions should return the id of the first stored condition") + void saveBenComorbidConditions_shouldReturnFirstStoredId() { + WrapperComorbidCondDetails wrapper = new WrapperComorbidCondDetails(); + BencomrbidityCondDetails condition = new BencomrbidityCondDetails(); + condition.setComorbidCondition("Diabetes"); + condition.setID(5L); + wrapper.setComorbidityConcurrentConditionsList( + new ArrayList<>(Collections.singletonList(condition))); + when(bencomrbidityCondRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(5L, service.saveBenComorbidConditions(wrapper)); + } + + @Test + @DisplayName("saveBenComorbidConditions should succeed when no condition carries a name") + void saveBenComorbidConditions_shouldSucceedWithoutNamedConditions() { + WrapperComorbidCondDetails wrapper = new WrapperComorbidCondDetails(); + wrapper.setComorbidityConcurrentConditionsList( + new ArrayList<>(Collections.singletonList(new BencomrbidityCondDetails()))); + + assertEquals(1L, service.saveBenComorbidConditions(wrapper)); + verify(bencomrbidityCondRepo, never()).saveAll(any()); + } + + @Test + @DisplayName("saveBenComorbidConditions should report a failure when not every condition was stored") + void saveBenComorbidConditions_shouldReportPartialSaveAsFailure() { + WrapperComorbidCondDetails wrapper = new WrapperComorbidCondDetails(); + BencomrbidityCondDetails condition = new BencomrbidityCondDetails(); + condition.setComorbidCondition("Diabetes"); + wrapper.setComorbidityConcurrentConditionsList( + new ArrayList<>(Collections.singletonList(condition))); + when(bencomrbidityCondRepo.saveAll(any())).thenReturn(new ArrayList()); + + assertNull(service.saveBenComorbidConditions(wrapper)); + } + + @Test + @DisplayName("saveBenMedicationHistory should return the id of the first stored entry") + void saveBenMedicationHistory_shouldReturnFirstStoredId() { + WrapperMedicationHistory wrapper = new WrapperMedicationHistory(); + BenMedicationHistory entry = new BenMedicationHistory(); + entry.setCurrentMedication("Metformin"); + entry.setID(6L); + wrapper.setMedicationHistoryList(new ArrayList<>(Collections.singletonList(entry))); + when(benMedicationHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(6L, service.saveBenMedicationHistory(wrapper)); + } + + @Test + @DisplayName("saveBenMedicationHistory should succeed when no entry names a medication") + void saveBenMedicationHistory_shouldSucceedWithoutNamedMedication() { + WrapperMedicationHistory wrapper = new WrapperMedicationHistory(); + wrapper.setMedicationHistoryList(new ArrayList<>(Collections.singletonList(new BenMedicationHistory()))); + + assertEquals(1L, service.saveBenMedicationHistory(wrapper)); + verify(benMedicationHistoryRepo, never()).saveAll(any()); + } + + @Test + @DisplayName("saveFemaleObstetricHistory should flatten the complication lists before storing") + void saveFemaleObstetricHistory_shouldFlattenComplicationLists() { + FemaleObstetricHistory history = new FemaleObstetricHistory(); + history.setPregComplicationList(complications("pregComplicationID", "pregComplicationType")); + history.setDeliveryComplicationList( + complications("deliveryComplicationID", "deliveryComplicationType")); + history.setPostpartumComplicationList( + complications("postpartumComplicationID", "postpartumComplicationType")); + ArrayList> postAbortion = new ArrayList<>(); + postAbortion.add(complication("complicationID", 1d, "complicationValue", "Sepsis")); + postAbortion.add(complication("complicationID", 2d, "complicationValue", "Bleeding")); + history.setPostAbortionComplication(postAbortion); + history.setAbortionType(complication("complicationID", 3d, "complicationValue", "Induced")); + history.setTypeofFacility(complication("serviceFacilityID", 4d, "facilityName", "PHC")); + + WrapperFemaleObstetricHistory wrapper = new WrapperFemaleObstetricHistory(); + wrapper.setFemaleObstetricHistoryList(new ArrayList<>(Collections.singletonList(history))); + when(femaleObstetricHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1L, service.saveFemaleObstetricHistory(wrapper)); + assertEquals("1,2", history.getPregComplicationID()); + assertEquals("A,B", history.getPregComplicationType()); + assertEquals("1,2", history.getDeliveryComplicationID()); + assertEquals("1,2", history.getPostpartumComplicationID()); + assertEquals("1,2", history.getPostAbortionComplication_db()); + assertEquals("Sepsis,Bleeding", history.getPostAbortionComplicationsValues()); + assertEquals(3, history.getAbortionTypeID()); + assertEquals("Induced", history.getTypeOfAbortionValue()); + assertEquals(4, history.getTypeofFacilityID()); + assertEquals("PHC", history.getServiceFacilityValue()); + } + + @Test + @DisplayName("saveFemaleObstetricHistory should succeed for an entry without any complications") + void saveFemaleObstetricHistory_shouldSucceedWithoutComplications() { + WrapperFemaleObstetricHistory wrapper = new WrapperFemaleObstetricHistory(); + wrapper.setFemaleObstetricHistoryList( + new ArrayList<>(Collections.singletonList(new FemaleObstetricHistory()))); + when(femaleObstetricHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1L, service.saveFemaleObstetricHistory(wrapper)); + } + + @Test + @DisplayName("saveFemaleObstetricHistory should succeed for an empty obstetric history") + void saveFemaleObstetricHistory_shouldSucceedForEmptyHistory() { + WrapperFemaleObstetricHistory wrapper = new WrapperFemaleObstetricHistory(); + wrapper.setFemaleObstetricHistoryList(new ArrayList<>()); + + assertEquals(1L, service.saveFemaleObstetricHistory(wrapper)); + verify(femaleObstetricHistoryRepo, never()).saveAll(any()); + } + + @Test + @DisplayName("saveBenMenstrualHistory should flatten the menstrual problem list before storing") + void saveBenMenstrualHistory_shouldFlattenProblemList() { + BenMenstrualDetails details = new BenMenstrualDetails(); + ArrayList> problems = new ArrayList<>(); + problems.add(complication("menstrualProblemID", 1, "problemName", "Cramps")); + problems.add(complication("menstrualProblemID", 2, "problemName", "Irregular")); + details.setMenstrualProblemList(problems); + BenMenstrualDetails saved = new BenMenstrualDetails(); + saved.setBenMenstrualID(9); + when(benMenstrualDetailsRepo.save(details)).thenReturn(saved); + + assertEquals(9, service.saveBenMenstrualHistory(details)); + assertEquals("1,2", details.getMenstrualProblemID()); + assertEquals("Cramps,Irregular", details.getProblemName()); + } + + @Test + @DisplayName("saveBenMenstrualHistory should return null when the stored row carries no id") + void saveBenMenstrualHistory_shouldReturnNullWithoutStoredId() { + BenMenstrualDetails details = new BenMenstrualDetails(); + BenMenstrualDetails saved = new BenMenstrualDetails(); + saved.setBenMenstrualID(0); + when(benMenstrualDetailsRepo.save(details)).thenReturn(saved); + + assertNull(service.saveBenMenstrualHistory(details)); + } + + @Test + @DisplayName("saveBenFamilyHistory should store the derived family history entries") + void saveBenFamilyHistory_shouldStoreDerivedEntries() { + BenFamilyHistory history = new BenFamilyHistory(); + history.setFamilyDiseaseList(familyDiseaseList()); + when(benFamilyHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1L, service.saveBenFamilyHistory(history)); + } + + @Test + @DisplayName("saveBenFamilyHistory should succeed for an empty family history") + void saveBenFamilyHistory_shouldSucceedForEmptyHistory() { + BenFamilyHistory history = new BenFamilyHistory(); + history.setFamilyDiseaseList(new ArrayList<>()); + + assertEquals(1L, service.saveBenFamilyHistory(history)); + verify(benFamilyHistoryRepo, never()).saveAll(any()); + } + + @Test + @DisplayName("savePersonalHistory should store the derived personal habit entries") + void savePersonalHistory_shouldStoreDerivedEntries() { + BenPersonalHabit habit = new BenPersonalHabit(); + List> tobacco = new ArrayList<>(); + Map entry = new HashMap<>(); + entry.put("habit", "Smoking"); + tobacco.add(entry); + habit.setTobaccoList(tobacco); + habit.setAlcoholList(new ArrayList<>()); + habit.setAllergicList(new ArrayList<>()); + when(benPersonalHabitRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.savePersonalHistory(habit)); + } + + @Test + @DisplayName("savePersonalHistory should succeed for an empty personal history") + void savePersonalHistory_shouldSucceedForEmptyHistory() { + BenPersonalHabit habit = new BenPersonalHabit(); + habit.setTobaccoList(new ArrayList<>()); + habit.setAlcoholList(new ArrayList<>()); + habit.setAllergicList(new ArrayList<>()); + + assertEquals(1, service.savePersonalHistory(habit)); + } + + @Test + @DisplayName("saveAllergyHistory should store the derived allergy entries") + void saveAllergyHistory_shouldStoreDerivedEntries() { + BenAllergyHistory allergy = new BenAllergyHistory(); + List> allergies = new ArrayList<>(); + Map entry = new HashMap<>(); + entry.put("allergyType", "Food"); + allergies.add(entry); + allergy.setAllergicList(allergies); + when(benAllergyHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1L, service.saveAllergyHistory(allergy)); + } + + @Test + @DisplayName("saveAllergyHistory should succeed for an empty allergy history") + void saveAllergyHistory_shouldSucceedForEmptyHistory() { + BenAllergyHistory allergy = new BenAllergyHistory(); + allergy.setAllergicList(new ArrayList<>()); + + assertEquals(1L, service.saveAllergyHistory(allergy)); + verify(benAllergyHistoryRepo, never()).saveAll(any()); + } + + @Test + @DisplayName("saveChildOptionalVaccineDetail should store the optional vaccine entries") + void saveChildOptionalVaccineDetail_shouldStoreEntries() { + WrapperChildOptionalVaccineDetail wrapper = new WrapperChildOptionalVaccineDetail(); + wrapper.setChildOptionalVaccineList( + new ArrayList<>(Collections.singletonList(new ChildOptionalVaccineDetail()))); + when(childOptionalVaccineDetailRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1L, service.saveChildOptionalVaccineDetail(wrapper)); + } + + @Test + @DisplayName("saveChildOptionalVaccineDetail should succeed for an empty vaccine list") + void saveChildOptionalVaccineDetail_shouldSucceedForEmptyList() { + WrapperChildOptionalVaccineDetail wrapper = new WrapperChildOptionalVaccineDetail(); + wrapper.setChildOptionalVaccineList(new ArrayList<>()); + + assertEquals(1L, service.saveChildOptionalVaccineDetail(wrapper)); + } + + @Test + @DisplayName("saveImmunizationHistory should expand each dose of the immunization list") + void saveImmunizationHistory_shouldExpandEachDose() { + WrapperImmunizationHistory wrapper = new WrapperImmunizationHistory(); + ChildVaccineDetail1 vaccine = new ChildVaccineDetail1(); + List> vaccines = new ArrayList<>(); + Map dose = new HashMap<>(); + dose.put("vaccine", "BCG"); + dose.put("status", Boolean.TRUE); + vaccines.add(dose); + vaccine.setVaccines(vaccines); + wrapper.setImmunizationList(new ArrayList<>(Collections.singletonList(vaccine))); + when(childVaccineDetail1Repo.saveAll(any())).thenAnswer(echoList()); + + assertNull(service.saveImmunizationHistory(wrapper)); + } + + @Test + @DisplayName("saveImmunizationHistory should store a placeholder row for an empty immunization list") + void saveImmunizationHistory_shouldStorePlaceholderForEmptyList() { + WrapperImmunizationHistory wrapper = new WrapperImmunizationHistory(); + wrapper.setImmunizationList(new ArrayList<>()); + when(childVaccineDetail1Repo.saveAll(any())).thenAnswer(echoList()); + + assertNull(service.saveImmunizationHistory(wrapper)); + } + + @Test + @DisplayName("saveBenFamilyHistoryNCDScreening should store the derived screening family history") + void saveBenFamilyHistoryNCDScreening_shouldStoreDerivedEntries() { + BenFamilyHistory history = new BenFamilyHistory(); + history.setFamilyDiseaseList(familyDiseaseList()); + when(benFamilyHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1L, service.saveBenFamilyHistoryNCDScreening(history)); + } + + @Test + @DisplayName("saveBenFamilyHistoryNCDScreening should succeed for an empty screening history") + void saveBenFamilyHistoryNCDScreening_shouldSucceedForEmptyHistory() { + BenFamilyHistory history = new BenFamilyHistory(); + history.setFamilyDiseaseList(new ArrayList<>()); + + assertEquals(1L, service.saveBenFamilyHistoryNCDScreening(history)); + verify(benFamilyHistoryRepo, never()).saveAll(any()); + } + + private Map illness() { + Map map = new HashMap<>(); + map.put("illnessType", "Asthma"); + map.put("illnessTypeID", 1); + return map; + } + + private List> familyDiseaseList() { + List> list = new ArrayList<>(); + Map disease = new HashMap<>(); + disease.put("diseaseType", "Diabetes"); + disease.put("diseaseTypeID", 1); + disease.put("deleted", "false"); + disease.put("familyMembers", Arrays.asList("Mother", "Father")); + list.add(disease); + return list; + } + + private ArrayList> complications(String idKey, String nameKey) { + ArrayList> list = new ArrayList<>(); + list.add(complication(idKey, 1, nameKey, "A")); + list.add(complication(idKey, 2, nameKey, "B")); + return list; + } + + private Map complication(String idKey, Object id, String nameKey, Object name) { + Map map = new HashMap<>(); + map.put(idKey, id); + map.put(nameKey, name); + return map; + } + } + + @Nested + @DisplayName("vitals and examinations") + class VitalsAndExaminationTests { + + @Test + @DisplayName("saveBeneficiaryPhysicalAnthropometryDetails should return the stored row id") + void saveAnthropometry_shouldReturnStoredId() { + BenAnthropometryDetail detail = new BenAnthropometryDetail(); + BenAnthropometryDetail saved = new BenAnthropometryDetail(); + saved.setID(4L); + when(benAnthropometryRepo.save(detail)).thenReturn(saved); + + assertEquals(4L, service.saveBeneficiaryPhysicalAnthropometryDetails(detail)); + } + + @Test + @DisplayName("saveBeneficiaryPhysicalAnthropometryDetails should return null when nothing was stored") + void saveAnthropometry_shouldReturnNullWhenNothingStored() { + BenAnthropometryDetail detail = new BenAnthropometryDetail(); + when(benAnthropometryRepo.save(detail)).thenReturn(null); + + assertNull(service.saveBeneficiaryPhysicalAnthropometryDetails(detail)); + } + + @Test + @DisplayName("saveIDRS should return the stored row id") + void saveIDRS_shouldReturnStoredId() { + IDRSData detail = new IDRSData(); + IDRSData saved = new IDRSData(); + saved.setId(4L); + when(iDrsDataRepo.save(detail)).thenReturn(saved); + + assertEquals(4L, service.saveIDRS(detail)); + } + + @Test + @DisplayName("saveIDRS should return null when nothing was stored") + void saveIDRS_shouldReturnNullWhenNothingStored() { + IDRSData detail = new IDRSData(); + when(iDrsDataRepo.save(detail)).thenReturn(null); + + assertNull(service.saveIDRS(detail)); + } + + @Test + @DisplayName("savePhysicalActivity should return the stored row id") + void savePhysicalActivity_shouldReturnStoredId() { + PhysicalActivityType detail = new PhysicalActivityType(); + PhysicalActivityType saved = new PhysicalActivityType(); + saved.setpAID(4L); + when(physicalActivityaRepo.save(detail)).thenReturn(saved); + + assertEquals(4L, service.savePhysicalActivity(detail)); + } + + @Test + @DisplayName("savePhysicalActivity should return null when nothing was stored") + void savePhysicalActivity_shouldReturnNullWhenNothingStored() { + PhysicalActivityType detail = new PhysicalActivityType(); + when(physicalActivityaRepo.save(detail)).thenReturn(null); + + assertNull(service.savePhysicalActivity(detail)); + } + + @Test + @DisplayName("saveBeneficiaryPhysicalVitalDetails should average the three blood pressure readings") + void savePhysicalVitals_shouldAverageThreeReadings() { + BenPhysicalVitalDetail detail = new BenPhysicalVitalDetail(); + detail.setSystolicBP_1stReading((short) 120); + detail.setDiastolicBP_1stReading((short) 80); + detail.setSystolicBP_2ndReading((short) 130); + detail.setDiastolicBP_2ndReading((short) 90); + detail.setSystolicBP_3rdReading((short) 110); + detail.setDiastolicBP_3rdReading((short) 70); + BenPhysicalVitalDetail saved = new BenPhysicalVitalDetail(); + saved.setID(4L); + when(benPhysicalVitalRepo.save(detail)).thenReturn(saved); + + assertEquals(4L, service.saveBeneficiaryPhysicalVitalDetails(detail)); + assertEquals((short) 120, detail.getAverageSystolicBP()); + assertEquals((short) 80, detail.getAverageDiastolicBP()); + } + + @Test + @DisplayName("saveBeneficiaryPhysicalVitalDetails should leave the average unset without any reading") + void savePhysicalVitals_shouldLeaveAverageUnsetWithoutReadings() { + BenPhysicalVitalDetail detail = new BenPhysicalVitalDetail(); + BenPhysicalVitalDetail saved = new BenPhysicalVitalDetail(); + saved.setID(4L); + when(benPhysicalVitalRepo.save(detail)).thenReturn(saved); + + assertEquals(4L, service.saveBeneficiaryPhysicalVitalDetails(detail)); + assertNull(detail.getAverageSystolicBP()); + } + + @Test + @DisplayName("saveBeneficiaryPhysicalVitalDetails should return null when nothing was stored") + void savePhysicalVitals_shouldReturnNullWhenNothingStored() { + BenPhysicalVitalDetail detail = new BenPhysicalVitalDetail(); + when(benPhysicalVitalRepo.save(detail)).thenReturn(null); + + assertNull(service.saveBeneficiaryPhysicalVitalDetails(detail)); + } + + @Test + @DisplayName("getBeneficiaryPhysicalAnthropometryDetails should return the stored row as JSON") + void getAnthropometry_shouldReturnJson() { + BenAnthropometryDetail detail = new BenAnthropometryDetail(); + detail.setID(4L); + when(benAnthropometryRepo.getBenAnthropometryDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(detail); + + assertTrue(service.getBeneficiaryPhysicalAnthropometryDetails(BEN_REG_ID, VISIT_CODE).contains("\"ID\"")); + } + + @Test + @DisplayName("getBeneficiaryPhysicalVitalDetails should return the stored row as JSON") + void getPhysicalVitals_shouldReturnJson() { + BenPhysicalVitalDetail detail = new BenPhysicalVitalDetail(); + detail.setID(4L); + when(benPhysicalVitalRepo.getBenPhysicalVitalDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(detail); + + assertTrue(service.getBeneficiaryPhysicalVitalDetails(BEN_REG_ID, VISIT_CODE).contains("\"ID\"")); + } + + @Test + @DisplayName("updateANCAnthropometryDetails should mark an already processed row as updated") + void updateAnthropometry_shouldMarkProcessedRowAsUpdated() { + BenAnthropometryDetail detail = new BenAnthropometryDetail(); + detail.setBeneficiaryRegID(BEN_REG_ID); + detail.setVisitCode(VISIT_CODE); + when(benAnthropometryRepo.getBenAnthropometryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + when(benAnthropometryRepo.updateANCCareDetails(any(), any(), any(), any(), any(), any(), any(), any(), + any(), eq("U"), eq(BEN_REG_ID), eq(VISIT_CODE))).thenReturn(1); + + assertEquals(1, service.updateANCAnthropometryDetails(detail)); + } + + @Test + @DisplayName("updateANCAnthropometryDetails should keep a fresh row marked as new") + void updateAnthropometry_shouldKeepFreshRowAsNew() { + BenAnthropometryDetail detail = new BenAnthropometryDetail(); + detail.setBeneficiaryRegID(BEN_REG_ID); + detail.setVisitCode(VISIT_CODE); + when(benAnthropometryRepo.getBenAnthropometryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("N"); + when(benAnthropometryRepo.updateANCCareDetails(any(), any(), any(), any(), any(), any(), any(), any(), + any(), eq("N"), eq(BEN_REG_ID), eq(VISIT_CODE))).thenReturn(1); + + assertEquals(1, service.updateANCAnthropometryDetails(detail)); + } + + @Test + @DisplayName("updateANCAnthropometryDetails should report no change for a null payload") + void updateAnthropometry_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateANCAnthropometryDetails(null)); + } + + @Test + @DisplayName("updateANCPhysicalVitalDetails should copy the first reading into the averages") + void updatePhysicalVitals_shouldCopyFirstReadingIntoAverages() { + BenPhysicalVitalDetail detail = new BenPhysicalVitalDetail(); + detail.setBeneficiaryRegID(BEN_REG_ID); + detail.setVisitCode(VISIT_CODE); + detail.setSystolicBP_1stReading((short) 118); + detail.setDiastolicBP_1stReading((short) 78); + when(benPhysicalVitalRepo.getBenPhysicalVitalStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + when(benPhysicalVitalRepo.updatePhysicalVitalDetails(any(), any(), any(), any(), any(), any(), any(), any(), + any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), + eq("U"), any(), any(), any(), eq(BEN_REG_ID), eq(VISIT_CODE))).thenReturn(1); + + assertEquals(1, service.updateANCPhysicalVitalDetails(detail)); + assertEquals((short) 118, detail.getAverageSystolicBP()); + assertEquals((short) 78, detail.getAverageDiastolicBP()); + } + + @Test + @DisplayName("updateANCPhysicalVitalDetails should report no change for a null payload") + void updatePhysicalVitals_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateANCPhysicalVitalDetails(null)); + } + + @Test + @DisplayName("savePhyGeneralExamination should flatten the danger sign list before storing") + void saveGeneralExamination_shouldFlattenDangerSigns() { + PhyGeneralExamination examination = new PhyGeneralExamination(); + examination.setTypeOfDangerSigns(new ArrayList<>(Arrays.asList("Fever", "Bleeding"))); + PhyGeneralExamination saved = new PhyGeneralExamination(); + saved.setID(4L); + when(phyGeneralExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.savePhyGeneralExamination(examination)); + assertEquals("Fever,Bleeding,", examination.getTypeOfDangerSign()); + } + + @Test + @DisplayName("savePhyGeneralExamination should return null when nothing was stored") + void saveGeneralExamination_shouldReturnNullWhenNothingStored() { + PhyGeneralExamination examination = new PhyGeneralExamination(); + when(phyGeneralExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.savePhyGeneralExamination(examination)); + } + + @Test + @DisplayName("savePhyHeadToToeExamination should return the stored row id") + void saveHeadToToeExamination_shouldReturnStoredId() { + PhyHeadToToeExamination examination = new PhyHeadToToeExamination(); + PhyHeadToToeExamination saved = new PhyHeadToToeExamination(); + saved.setID(4L); + when(phyHeadToToeExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.savePhyHeadToToeExamination(examination)); + } + + @Test + @DisplayName("savePhyHeadToToeExamination should return null when nothing was stored") + void saveHeadToToeExamination_shouldReturnNullWhenNothingStored() { + PhyHeadToToeExamination examination = new PhyHeadToToeExamination(); + when(phyHeadToToeExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.savePhyHeadToToeExamination(examination)); + } + + @Test + @DisplayName("saveSysGastrointestinalExamination should return the stored row id") + void saveGastrointestinalExamination_shouldReturnStoredId() { + SysGastrointestinalExamination examination = new SysGastrointestinalExamination(); + SysGastrointestinalExamination saved = new SysGastrointestinalExamination(); + saved.setID(4L); + when(sysGastrointestinalExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.saveSysGastrointestinalExamination(examination)); + } + + @Test + @DisplayName("saveSysGastrointestinalExamination should return null when nothing was stored") + void saveGastrointestinalExamination_shouldReturnNullWhenNothingStored() { + SysGastrointestinalExamination examination = new SysGastrointestinalExamination(); + when(sysGastrointestinalExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.saveSysGastrointestinalExamination(examination)); + } + + @Test + @DisplayName("saveSysCardiovascularExamination should return the stored row id") + void saveCardiovascularExamination_shouldReturnStoredId() { + SysCardiovascularExamination examination = new SysCardiovascularExamination(); + SysCardiovascularExamination saved = new SysCardiovascularExamination(); + saved.setID(4L); + when(sysCardiovascularExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.saveSysCardiovascularExamination(examination)); + } + + @Test + @DisplayName("saveSysCardiovascularExamination should return null when nothing was stored") + void saveCardiovascularExamination_shouldReturnNullWhenNothingStored() { + SysCardiovascularExamination examination = new SysCardiovascularExamination(); + when(sysCardiovascularExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.saveSysCardiovascularExamination(examination)); + } + + @Test + @DisplayName("saveSysRespiratoryExamination should return the stored row id") + void saveRespiratoryExamination_shouldReturnStoredId() { + SysRespiratoryExamination examination = new SysRespiratoryExamination(); + SysRespiratoryExamination saved = new SysRespiratoryExamination(); + saved.setID(4L); + when(sysRespiratoryExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.saveSysRespiratoryExamination(examination)); + } + + @Test + @DisplayName("saveSysRespiratoryExamination should return null when nothing was stored") + void saveRespiratoryExamination_shouldReturnNullWhenNothingStored() { + SysRespiratoryExamination examination = new SysRespiratoryExamination(); + when(sysRespiratoryExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.saveSysRespiratoryExamination(examination)); + } + + @Test + @DisplayName("saveSysCentralNervousExamination should return the stored row id") + void saveCentralNervousExamination_shouldReturnStoredId() { + SysCentralNervousExamination examination = new SysCentralNervousExamination(); + SysCentralNervousExamination saved = new SysCentralNervousExamination(); + saved.setID(4L); + when(sysCentralNervousExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.saveSysCentralNervousExamination(examination)); + } + + @Test + @DisplayName("saveSysCentralNervousExamination should return null when nothing was stored") + void saveCentralNervousExamination_shouldReturnNullWhenNothingStored() { + SysCentralNervousExamination examination = new SysCentralNervousExamination(); + when(sysCentralNervousExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.saveSysCentralNervousExamination(examination)); + } + + @Test + @DisplayName("saveSysMusculoskeletalSystemExamination should return the stored row id") + void saveMusculoskeletalExamination_shouldReturnStoredId() { + SysMusculoskeletalSystemExamination examination = new SysMusculoskeletalSystemExamination(); + SysMusculoskeletalSystemExamination saved = new SysMusculoskeletalSystemExamination(); + saved.setID(4L); + when(sysMusculoskeletalSystemExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.saveSysMusculoskeletalSystemExamination(examination)); + } + + @Test + @DisplayName("saveSysMusculoskeletalSystemExamination should return null when nothing was stored") + void saveMusculoskeletalExamination_shouldReturnNullWhenNothingStored() { + SysMusculoskeletalSystemExamination examination = new SysMusculoskeletalSystemExamination(); + when(sysMusculoskeletalSystemExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.saveSysMusculoskeletalSystemExamination(examination)); + } + + @Test + @DisplayName("saveSysGenitourinarySystemExamination should return the stored row id") + void saveGenitourinaryExamination_shouldReturnStoredId() { + SysGenitourinarySystemExamination examination = new SysGenitourinarySystemExamination(); + SysGenitourinarySystemExamination saved = new SysGenitourinarySystemExamination(); + saved.setID(4L); + when(sysGenitourinarySystemExaminationRepo.save(examination)).thenReturn(saved); + + assertEquals(4L, service.saveSysGenitourinarySystemExamination(examination)); + } + + @Test + @DisplayName("saveSysGenitourinarySystemExamination should return null when nothing was stored") + void saveGenitourinaryExamination_shouldReturnNullWhenNothingStored() { + SysGenitourinarySystemExamination examination = new SysGenitourinarySystemExamination(); + when(sysGenitourinarySystemExaminationRepo.save(examination)).thenReturn(null); + + assertNull(service.saveSysGenitourinarySystemExamination(examination)); + } + } + + @Nested + @DisplayName("history report fetches") + class HistoryFetchTests { + + /** A single result row whose every column is empty, as an unfilled visit returns. */ + private ArrayList oneEmptyRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[40]); + return rows; + } + + @Test + @DisplayName("fetchBenPastMedicalHistory should render the stored rows with the report columns") + void fetchBenPastMedicalHistory_shouldRenderRowsWithColumns() { + when(benMedHistoryRepo.getBenPastHistory(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + String result = service.fetchBenPastMedicalHistory(BEN_REG_ID); + + assertTrue(result.contains("Illness Type")); + assertTrue(result.contains("\"data\"")); + } + + @Test + @DisplayName("fetchBenPastMedicalHistory should render only the columns when nothing is stored") + void fetchBenPastMedicalHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(benMedHistoryRepo.getBenPastHistory(BEN_REG_ID)).thenReturn(new ArrayList<>()); + + assertTrue(service.fetchBenPastMedicalHistory(BEN_REG_ID).contains("Year of Surgery")); + } + + @Test + @DisplayName("fetchBenPersonalTobaccoHistory should render the stored rows with the report columns") + void fetchBenPersonalTobaccoHistory_shouldRenderRowsWithColumns() { + when(benPersonalHabitRepo.getBenPersonalTobaccoHabitDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenPersonalTobaccoHistory(BEN_REG_ID).contains("Tobacco Use Type")); + } + + @Test + @DisplayName("fetchBenPersonalTobaccoHistory should render only the columns when nothing is stored") + void fetchBenPersonalTobaccoHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(benPersonalHabitRepo.getBenPersonalTobaccoHabitDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenPersonalTobaccoHistory(BEN_REG_ID).contains("Tobacco Use Status")); + } + + @Test + @DisplayName("fetchBenPersonalAlcoholHistory should render the stored rows with the report columns") + void fetchBenPersonalAlcoholHistory_shouldRenderRowsWithColumns() { + when(benPersonalHabitRepo.getBenPersonalAlcoholHabitDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenPersonalAlcoholHistory(BEN_REG_ID).contains("Alcohol Type")); + } + + @Test + @DisplayName("fetchBenPersonalAlcoholHistory should render only the columns when nothing is stored") + void fetchBenPersonalAlcoholHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(benPersonalHabitRepo.getBenPersonalAlcoholHabitDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenPersonalAlcoholHistory(BEN_REG_ID).contains("Alcohol Intake Status")); + } + + @Test + @DisplayName("fetchBenPersonalAllergyHistory should render the stored rows with the report columns") + void fetchBenPersonalAllergyHistory_shouldRenderRowsWithColumns() { + when(benAllergyHistoryRepo.getBenPersonalAllergyDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenPersonalAllergyHistory(BEN_REG_ID).contains("Allergy Name")); + } + + @Test + @DisplayName("fetchBenPersonalAllergyHistory should render only the columns when nothing is stored") + void fetchBenPersonalAllergyHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(benAllergyHistoryRepo.getBenPersonalAllergyDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenPersonalAllergyHistory(BEN_REG_ID).contains("Allergy Status")); + } + + @Test + @DisplayName("fetchBenPersonalMedicationHistory should render the stored rows with the report columns") + void fetchBenPersonalMedicationHistory_shouldRenderRowsWithColumns() { + when(benMedicationHistoryRepo.getBenMedicationHistoryDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenPersonalMedicationHistory(BEN_REG_ID).contains("Current Medication")); + } + + @Test + @DisplayName("fetchBenPersonalMedicationHistory should render only the columns when nothing is stored") + void fetchBenPersonalMedicationHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(benMedicationHistoryRepo.getBenMedicationHistoryDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenPersonalMedicationHistory(BEN_REG_ID).contains("Date of Capture")); + } + + @Test + @DisplayName("fetchBenPersonalFamilyHistory should render the stored rows with the report columns") + void fetchBenPersonalFamilyHistory_shouldRenderRowsWithColumns() { + when(benFamilyHistoryRepo.getBenFamilyHistoryDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenPersonalFamilyHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenPersonalFamilyHistory should render only the columns when nothing is stored") + void fetchBenPersonalFamilyHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(benFamilyHistoryRepo.getBenFamilyHistoryDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenPersonalFamilyHistory(BEN_REG_ID).contains("Date of Capture")); + } + + @Test + @DisplayName("fetchBenPhysicalHistory should render the stored rows with the report columns") + void fetchBenPhysicalHistory_shouldRenderRowsWithColumns() { + when(physicalActivityTypeRepo.getBenPhysicalHistoryDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenPhysicalHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenPhysicalHistory should render only the columns when nothing is stored") + void fetchBenPhysicalHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(physicalActivityTypeRepo.getBenPhysicalHistoryDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenPhysicalHistory(BEN_REG_ID).contains("Date of Capture")); + } + + @Test + @DisplayName("fetchBenMenstrualHistory should render the stored rows with the report columns") + void fetchBenMenstrualHistory_shouldRenderRowsWithColumns() { + when(benMenstrualDetailsRepo.getBenMenstrualDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenMenstrualHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenMenstrualHistory should render only the columns when nothing is stored") + void fetchBenMenstrualHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(benMenstrualDetailsRepo.getBenMenstrualDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenMenstrualHistory(BEN_REG_ID).contains("Date of Capture")); + } + + @Test + @DisplayName("fetchBenPastObstetricHistory should render the stored rows with the report columns") + void fetchBenPastObstetricHistory_shouldRenderRowsWithColumns() { + when(femaleObstetricHistoryRepo.getBenFemaleObstetricHistoryDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenPastObstetricHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenPastObstetricHistory should render only the columns when nothing is stored") + void fetchBenPastObstetricHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(femaleObstetricHistoryRepo.getBenFemaleObstetricHistoryDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenPastObstetricHistory(BEN_REG_ID).contains("Date of Capture")); + } + + @Test + @DisplayName("fetchBenComorbidityHistory should render the stored rows with the report columns") + void fetchBenComorbidityHistory_shouldRenderRowsWithColumns() { + when(bencomrbidityCondRepo.getBencomrbidityCondDetails(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenComorbidityHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenComorbidityHistory should render only the columns when nothing is stored") + void fetchBenComorbidityHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(bencomrbidityCondRepo.getBencomrbidityCondDetails(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenComorbidityHistory(BEN_REG_ID).contains("Date of Capture")); + } + + @Test + @DisplayName("fetchBenImmunizationHistory should render the stored rows with the report columns") + void fetchBenImmunizationHistory_shouldRenderRowsWithColumns() { + when(childVaccineDetail1Repo.getBenChildVaccineDetails(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenImmunizationHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenImmunizationHistory should render only the columns when nothing is stored") + void fetchBenImmunizationHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(childVaccineDetail1Repo.getBenChildVaccineDetails(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenImmunizationHistory(BEN_REG_ID).contains("Date of Capture")); + } + + @Test + @DisplayName("fetchBenOptionalVaccineHistory should render the stored rows with the report columns") + void fetchBenOptionalVaccineHistory_shouldRenderRowsWithColumns() { + when(childOptionalVaccineDetailRepo.getBenOptionalVaccineDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenOptionalVaccineHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenOptionalVaccineHistory should render only the columns when nothing is stored") + void fetchBenOptionalVaccineHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(childOptionalVaccineDetailRepo.getBenOptionalVaccineDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenOptionalVaccineHistory(BEN_REG_ID).contains("Date of Capture")); + } + } + + @Nested + @DisplayName("per visit history lookups") + class PerVisitLookupTests { + + private ArrayList oneEmptyRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[60]); + return rows; + } + + @Test + @DisplayName("getBenChiefComplaints should render the stored complaints as JSON") + void getBenChiefComplaints_shouldRenderJson() { + when(benChiefComplaintRepo.getBenChiefComplaints(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getBenChiefComplaints(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getFamilyHistoryDetail should return the stored family history for the visit") + void getFamilyHistoryDetail_shouldReturnStoredHistory() { + when(benFamilyHistoryRepo.getBenFamilyHisDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getFamilyHistoryDetail(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getPhysicalActivityType should delegate to the physical activity repository") + void getPhysicalActivityType_shouldDelegateToRepo() { + PhysicalActivityType stored = new PhysicalActivityType(); + when(physicalActivityTypeRepo.getBenPhysicalHistoryDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(stored); + + assertEquals(stored, service.getPhysicalActivityType(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getBeneficiaryIdrsDetails should return the stored IDRS row for the visit") + void getBeneficiaryIdrsDetails_shouldReturnStoredRow() { + when(iDRSDataRepo.getBenIdrsDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getBeneficiaryIdrsDetails(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getPastHistoryData should return the stored past history for the visit") + void getPastHistoryData_shouldReturnStoredHistory() { + when(benMedHistoryRepo.getBenPastHistory(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getPastHistoryData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getComorbidityConditionsHistory should return the stored comorbid conditions for the visit") + void getComorbidityConditionsHistory_shouldReturnStoredConditions() { + when(bencomrbidityCondRepo.getBencomrbidityCondDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getComorbidityConditionsHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getMedicationHistory should return the stored medication history for the visit") + void getMedicationHistory_shouldReturnStoredHistory() { + when(benMedicationHistoryRepo.getBenMedicationHistoryDetail(BEN_REG_ID, VISIT_CODE)) + .thenReturn(oneEmptyRow()); + + assertNotNull(service.getMedicationHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getPersonalHistory should merge the stored habits with the allergy history") + void getPersonalHistory_shouldMergeHabitsAndAllergies() { + when(benPersonalHabitRepo.getBenPersonalHabitDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + when(benAllergyHistoryRepo.getBenPersonalAllergyDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getPersonalHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getPersonalHistory should build an empty habit record when nothing is stored") + void getPersonalHistory_shouldBuildEmptyRecordWhenNothingStored() { + when(benPersonalHabitRepo.getBenPersonalHabitDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(new ArrayList<>()); + when(benAllergyHistoryRepo.getBenPersonalAllergyDetail(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new ArrayList<>()); + + assertNotNull(service.getPersonalHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getFamilyHistory should return the stored family history for the visit") + void getFamilyHistory_shouldReturnStoredHistory() { + when(benFamilyHistoryRepo.getBenFamilyHistoryDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getFamilyHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getMenstrualHistory should return the stored menstrual history for the visit") + void getMenstrualHistory_shouldReturnStoredHistory() { + when(benMenstrualDetailsRepo.getBenMenstrualDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getMenstrualHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getFemaleObstetricHistory should return the stored obstetric history for the visit") + void getFemaleObstetricHistory_shouldReturnStoredHistory() { + when(femaleObstetricHistoryRepo.getBenFemaleObstetricHistoryDetail(BEN_REG_ID, VISIT_CODE)) + .thenReturn(oneEmptyRow()); + + assertNotNull(service.getFemaleObstetricHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getChildOptionalVaccineHistory should return the stored optional vaccines for the visit") + void getChildOptionalVaccineHistory_shouldReturnStoredVaccines() { + when(childOptionalVaccineDetailRepo.getBenOptionalVaccineDetail(BEN_REG_ID, VISIT_CODE)) + .thenReturn(oneEmptyRow()); + + assertNotNull(service.getChildOptionalVaccineHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getImmunizationHistory should return the stored immunisations for the visit") + void getImmunizationHistory_shouldReturnStoredImmunisations() { + when(childVaccineDetail1Repo.getBenChildVaccineDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getImmunizationHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getGeneralExaminationData should expand the comma separated danger signs") + void getGeneralExaminationData_shouldExpandDangerSigns() { + PhyGeneralExamination stored = new PhyGeneralExamination(); + stored.setTypeOfDangerSign("Fever,Bleeding"); + when(phyGeneralExaminationRepo.getPhyGeneralExaminationData(BEN_REG_ID, VISIT_CODE)).thenReturn(stored); + + PhyGeneralExamination result = service.getGeneralExaminationData(BEN_REG_ID, VISIT_CODE); + + assertNotNull(result.getTypeOfDangerSigns()); + assertEquals(2, result.getTypeOfDangerSigns().size()); + } + + @Test + @DisplayName("getGeneralExaminationData should leave the danger signs alone when none are stored") + void getGeneralExaminationData_shouldLeaveDangerSignsAloneWhenNoneStored() { + PhyGeneralExamination stored = new PhyGeneralExamination(); + when(phyGeneralExaminationRepo.getPhyGeneralExaminationData(BEN_REG_ID, VISIT_CODE)).thenReturn(stored); + + assertNotNull(service.getGeneralExaminationData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getGeneralExaminationData should return null when the visit has no general examination") + void getGeneralExaminationData_shouldReturnNullWithoutExamination() { + when(phyGeneralExaminationRepo.getPhyGeneralExaminationData(BEN_REG_ID, VISIT_CODE)).thenReturn(null); + + assertNull(service.getGeneralExaminationData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getHeadToToeExaminationData should delegate to the head to toe repository") + void getHeadToToeExaminationData_shouldDelegateToRepo() { + PhyHeadToToeExamination stored = new PhyHeadToToeExamination(); + when(phyHeadToToeExaminationRepo.getPhyHeadToToeExaminationData(BEN_REG_ID, VISIT_CODE)).thenReturn(stored); + + assertEquals(stored, service.getHeadToToeExaminationData(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getSysGastrointestinalExamination should delegate to the gastrointestinal repository") + void getSysGastrointestinalExamination_shouldDelegateToRepo() { + SysGastrointestinalExamination stored = new SysGastrointestinalExamination(); + when(sysGastrointestinalExaminationRepo.getSSysGastrointestinalExamination(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getSysGastrointestinalExamination(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getCardiovascularExamination should delegate to the cardiovascular repository") + void getCardiovascularExamination_shouldDelegateToRepo() { + SysCardiovascularExamination stored = new SysCardiovascularExamination(); + when(sysCardiovascularExaminationRepo.getSysCardiovascularExaminationData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getCardiovascularExamination(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getRespiratoryExamination should delegate to the respiratory repository") + void getRespiratoryExamination_shouldDelegateToRepo() { + SysRespiratoryExamination stored = new SysRespiratoryExamination(); + when(sysRespiratoryExaminationRepo.getSysRespiratoryExaminationData(BEN_REG_ID, VISIT_CODE)).thenReturn(stored); + + assertEquals(stored, service.getRespiratoryExamination(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getSysCentralNervousExamination should delegate to the central nervous repository") + void getSysCentralNervousExamination_shouldDelegateToRepo() { + SysCentralNervousExamination stored = new SysCentralNervousExamination(); + when(sysCentralNervousExaminationRepo.getSysCentralNervousExaminationData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getSysCentralNervousExamination(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getMusculoskeletalExamination should delegate to the musculoskeletal repository") + void getMusculoskeletalExamination_shouldDelegateToRepo() { + SysMusculoskeletalSystemExamination stored = new SysMusculoskeletalSystemExamination(); + when(sysMusculoskeletalSystemExaminationRepo.getSysMusculoskeletalSystemExamination(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getMusculoskeletalExamination(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getGenitourinaryExamination should delegate to the genitourinary repository") + void getGenitourinaryExamination_shouldDelegateToRepo() { + SysGenitourinarySystemExamination stored = new SysGenitourinarySystemExamination(); + when(sysGenitourinarySystemExaminationRepo.getSysGenitourinarySystemExaminationData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(stored); + + assertEquals(stored, service.getGenitourinaryExamination(BEN_REG_ID, VISIT_CODE)); + } + } + + @Nested + @DisplayName("history updates") + class HistoryUpdateTests { + + /** One already-processed row and one fresh row, as a re-edited visit returns. */ + private ArrayList statuses() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { 1L, "P" }); + rows.add(new Object[] { 2L, "N" }); + return rows; + } + + private ArrayList integerKeyedStatuses() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { 1, "P" }); + rows.add(new Object[] { 2, "N" }); + return rows; + } + + @Test + @DisplayName("updateBenChiefComplaints should replace the stored complaints and report the row count") + void updateBenChiefComplaints_shouldReplaceStoredComplaints() { + BenChiefComplaint complaint = new BenChiefComplaint(); + complaint.setBeneficiaryRegID(BEN_REG_ID); + complaint.setVisitCode(VISIT_CODE); + List complaints = Collections.singletonList(complaint); + when(benChiefComplaintRepo.saveAll(complaints)).thenReturn(complaints); + + assertEquals(1, service.updateBenChiefComplaints(complaints)); + verify(benChiefComplaintRepo).deleteExistingBenChiefComplaints(BEN_REG_ID, VISIT_CODE); + } + + @Test + @DisplayName("updateBenChiefComplaints should report no change for an empty complaint list") + void updateBenChiefComplaints_shouldReportNoChangeForEmptyList() { + assertEquals(0, service.updateBenChiefComplaints(Collections.emptyList())); + } + + @Test + @DisplayName("updateBenChiefComplaints should report no change for a null complaint list") + void updateBenChiefComplaints_shouldReportNoChangeForNullList() { + assertEquals(0, service.updateBenChiefComplaints(null)); + } + + @Test + @DisplayName("updateBenPastHistoryDetails should soft delete the stored rows before writing the new ones") + void updateBenPastHistoryDetails_shouldSoftDeleteBeforeWriting() throws Exception { + BenMedHistory history = new BenMedHistory(); + history.setBeneficiaryRegID(BEN_REG_ID); + history.setVisitCode(VISIT_CODE); + ArrayList> illnesses = new ArrayList<>(); + Map illness = new HashMap<>(); + illness.put("illnessType", "Asthma"); + illnesses.add(illness); + history.setPastIllness(illnesses); + history.setPastSurgery(new ArrayList<>()); + when(benMedHistoryRepo.getBenMedHistoryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn(statuses()); + when(benMedHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.updateBenPastHistoryDetails(history)); + verify(benMedHistoryRepo).deleteExistingBenMedHistory(1L, "U"); + verify(benMedHistoryRepo).deleteExistingBenMedHistory(2L, "N"); + } + + @Test + @DisplayName("updateBenPastHistoryDetails should report no change for a null payload") + void updateBenPastHistoryDetails_shouldReportNoChangeForNullPayload() throws Exception { + assertEquals(0, service.updateBenPastHistoryDetails(null)); + } + + @Test + @DisplayName("updateBenComorbidConditions should soft delete the stored rows before writing the new ones") + void updateBenComorbidConditions_shouldSoftDeleteBeforeWriting() { + WrapperComorbidCondDetails wrapper = new WrapperComorbidCondDetails(); + wrapper.setBeneficiaryRegID(BEN_REG_ID); + wrapper.setVisitCode(VISIT_CODE); + BencomrbidityCondDetails condition = new BencomrbidityCondDetails(); + condition.setComorbidCondition("Diabetes"); + wrapper.setComorbidityConcurrentConditionsList( + new ArrayList<>(Collections.singletonList(condition))); + when(bencomrbidityCondRepo.getBenComrbidityCondHistoryStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn(statuses()); + when(bencomrbidityCondRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.updateBenComorbidConditions(wrapper)); + verify(bencomrbidityCondRepo).deleteExistingBenComrbidityCondDetails(1L, "U"); + verify(bencomrbidityCondRepo).deleteExistingBenComrbidityCondDetails(2L, "N"); + } + + @Test + @DisplayName("updateBenComorbidConditions should report no change for a null payload") + void updateBenComorbidConditions_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateBenComorbidConditions(null)); + } + + @Test + @DisplayName("updateBenMedicationHistory should soft delete the stored rows before writing the new ones") + void updateBenMedicationHistory_shouldSoftDeleteBeforeWriting() { + WrapperMedicationHistory wrapper = new WrapperMedicationHistory(); + wrapper.setBeneficiaryRegID(BEN_REG_ID); + wrapper.setVisitCode(VISIT_CODE); + BenMedicationHistory entry = new BenMedicationHistory(); + entry.setCurrentMedication("Metformin"); + wrapper.setMedicationHistoryList(new ArrayList<>(Collections.singletonList(entry))); + when(benMedicationHistoryRepo.getBenMedicationHistoryStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn(statuses()); + when(benMedicationHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.updateBenMedicationHistory(wrapper)); + } + + @Test + @DisplayName("updateBenMedicationHistory should report no change for a null payload") + void updateBenMedicationHistory_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateBenMedicationHistory(null)); + } + + @Test + @DisplayName("updateBenPersonalHistory should soft delete the stored rows before writing the new ones") + void updateBenPersonalHistory_shouldSoftDeleteBeforeWriting() { + BenPersonalHabit habit = new BenPersonalHabit(); + habit.setBeneficiaryRegID(BEN_REG_ID); + habit.setVisitCode(VISIT_CODE); + habit.setTobaccoList(new ArrayList<>()); + habit.setAlcoholList(new ArrayList<>()); + habit.setAllergicList(new ArrayList<>()); + when(benPersonalHabitRepo.getBenPersonalHistoryStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn(integerKeyedStatuses()); + + assertEquals(1, service.updateBenPersonalHistory(habit)); + verify(benPersonalHabitRepo).deleteExistingBenPersonalHistory(1, "U"); + verify(benPersonalHabitRepo).deleteExistingBenPersonalHistory(2, "N"); + } + + @Test + @DisplayName("updateBenPersonalHistory should report no change for a null payload") + void updateBenPersonalHistory_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateBenPersonalHistory(null)); + } + + @Test + @DisplayName("updateBenAllergicHistory should soft delete the stored rows before writing the new ones") + void updateBenAllergicHistory_shouldSoftDeleteBeforeWriting() { + BenAllergyHistory allergy = new BenAllergyHistory(); + allergy.setBeneficiaryRegID(BEN_REG_ID); + allergy.setVisitCode(VISIT_CODE); + allergy.setAllergicList(new ArrayList<>()); + when(benAllergyHistoryRepo.getBenAllergyHistoryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn(statuses()); + + assertEquals(1, service.updateBenAllergicHistory(allergy)); + verify(benAllergyHistoryRepo).deleteExistingBenAllergyHistory(1L, "U"); + } + + @Test + @DisplayName("updateBenAllergicHistory should report no change for a null payload") + void updateBenAllergicHistory_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateBenAllergicHistory(null)); + } + + @Test + @DisplayName("updateBenFamilyHistory should soft delete the stored rows before writing the new ones") + void updateBenFamilyHistory_shouldSoftDeleteBeforeWriting() { + BenFamilyHistory history = new BenFamilyHistory(); + history.setBeneficiaryRegID(BEN_REG_ID); + history.setVisitCode(VISIT_CODE); + history.setFamilyDiseaseList(new ArrayList<>()); + when(benFamilyHistoryRepo.getBenFamilyHistoryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn(statuses()); + + assertEquals(1, service.updateBenFamilyHistory(history)); + verify(benFamilyHistoryRepo).deleteExistingBenFamilyHistory(1L, "U"); + } + + @Test + @DisplayName("updateBenFamilyHistory should report no change for a null payload") + void updateBenFamilyHistory_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateBenFamilyHistory(null)); + } + + @Test + @DisplayName("updateMenstrualHistory should update the stored row when one already exists") + void updateMenstrualHistory_shouldUpdateExistingRow() { + BenMenstrualDetails details = new BenMenstrualDetails(); + details.setBeneficiaryRegID(BEN_REG_ID); + details.setVisitCode(VISIT_CODE); + ArrayList> problems = new ArrayList<>(); + Map problem = new HashMap<>(); + problem.put("menstrualProblemID", 1); + problem.put("problemName", "Cramps"); + problems.add(problem); + details.setMenstrualProblemList(problems); + when(benMenstrualDetailsRepo.getBenMenstrualDetailStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateMenstrualHistory(details)); + assertEquals("1", details.getMenstrualProblemID()); + } + + @Test + @DisplayName("updateMenstrualHistory should insert a new row when the visit has none") + void updateMenstrualHistory_shouldInsertNewRow() { + BenMenstrualDetails details = new BenMenstrualDetails(); + details.setBeneficiaryRegID(BEN_REG_ID); + details.setVisitCode(VISIT_CODE); + details.setModifiedBy("nurse1"); + when(benMenstrualDetailsRepo.getBenMenstrualDetailStatus(BEN_REG_ID, VISIT_CODE)).thenReturn(null); + BenMenstrualDetails saved = new BenMenstrualDetails(); + saved.setBenMenstrualID(9); + when(benMenstrualDetailsRepo.save(details)).thenReturn(saved); + + assertEquals(1, service.updateMenstrualHistory(details)); + assertEquals("nurse1", details.getCreatedBy()); + } + + @Test + @DisplayName("updateMenstrualHistory should report no change for a null payload") + void updateMenstrualHistory_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateMenstrualHistory(null)); + } + + @Test + @DisplayName("updatePastObstetricHistory should soft delete the stored rows before writing the new ones") + void updatePastObstetricHistory_shouldSoftDeleteBeforeWriting() { + WrapperFemaleObstetricHistory wrapper = new WrapperFemaleObstetricHistory(); + wrapper.setBeneficiaryRegID(BEN_REG_ID); + wrapper.setVisitCode(VISIT_CODE); + wrapper.setFemaleObstetricHistoryList(new ArrayList<>()); + when(femaleObstetricHistoryRepo.getBenObstetricHistoryStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn(statuses()); + + assertEquals(1, service.updatePastObstetricHistory(wrapper)); + verify(femaleObstetricHistoryRepo).deleteExistingObstetricHistory(1L, "U"); + } + + @Test + @DisplayName("updatePastObstetricHistory should report no change for a null payload") + void updatePastObstetricHistory_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updatePastObstetricHistory(null)); + } + + @Test + @DisplayName("updateChildOptionalVaccineDetail should soft delete the stored rows before writing the new ones") + void updateChildOptionalVaccineDetail_shouldSoftDeleteBeforeWriting() { + WrapperChildOptionalVaccineDetail wrapper = new WrapperChildOptionalVaccineDetail(); + wrapper.setBeneficiaryRegID(BEN_REG_ID); + wrapper.setVisitCode(VISIT_CODE); + wrapper.setChildOptionalVaccineList(new ArrayList<>()); + when(childOptionalVaccineDetailRepo.getBenChildOptionalVaccineHistoryStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn(statuses()); + + assertEquals(1, service.updateChildOptionalVaccineDetail(wrapper)); + } + + @Test + @DisplayName("updateChildOptionalVaccineDetail should report no change for a null payload") + void updateChildOptionalVaccineDetail_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateChildOptionalVaccineDetail(null)); + } + + @Test + @DisplayName("updateChildImmunizationDetail should report no change for an empty immunisation list") + void updateChildImmunizationDetail_shouldReportNoChangeForEmptyList() { + WrapperImmunizationHistory wrapper = new WrapperImmunizationHistory(); + wrapper.setImmunizationList(new ArrayList<>()); + + assertEquals(0, service.updateChildImmunizationDetail(wrapper)); + } + + @Test + @DisplayName("updatePhyGeneralExamination should mark an already processed row as updated") + void updatePhyGeneralExamination_shouldMarkProcessedRowAsUpdated() { + PhyGeneralExamination examination = new PhyGeneralExamination(); + examination.setBeneficiaryRegID(BEN_REG_ID); + examination.setVisitCode(VISIT_CODE); + examination.setTypeOfDangerSigns(new ArrayList<>(Arrays.asList("Fever"))); + when(phyGeneralExaminationRepo.getBenGeneralExaminationStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updatePhyGeneralExamination(examination)); + assertEquals("Fever,", examination.getTypeOfDangerSign()); + } + + @Test + @DisplayName("updatePhyGeneralExamination should report no change for a null payload") + void updatePhyGeneralExamination_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updatePhyGeneralExamination(null)); + } + + @Test + @DisplayName("updatePhyHeadToToeExamination should read the processed flag before updating") + void updatePhyHeadToToeExamination_shouldReadProcessedFlag() { + PhyHeadToToeExamination examination = new PhyHeadToToeExamination(); + examination.setBeneficiaryRegID(BEN_REG_ID); + examination.setVisitCode(VISIT_CODE); + when(phyHeadToToeExaminationRepo.getBenHeadToToeExaminationStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("N"); + + assertEquals(0, service.updatePhyHeadToToeExamination(examination)); + } + + @Test + @DisplayName("updatePhyHeadToToeExamination should report no change for a null payload") + void updatePhyHeadToToeExamination_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updatePhyHeadToToeExamination(null)); + } + + @Test + @DisplayName("updateSysCardiovascularExamination should read the processed flag before updating") + void updateSysCardiovascularExamination_shouldReadProcessedFlag() { + SysCardiovascularExamination examination = new SysCardiovascularExamination(); + examination.setBeneficiaryRegID(BEN_REG_ID); + examination.setVisitCode(VISIT_CODE); + when(sysCardiovascularExaminationRepo.getBenCardiovascularExaminationStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn("P"); + + assertEquals(0, service.updateSysCardiovascularExamination(examination)); + } + + @Test + @DisplayName("updateSysCardiovascularExamination should report no change for a null payload") + void updateSysCardiovascularExamination_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateSysCardiovascularExamination(null)); + } + + @Test + @DisplayName("updateSysRespiratoryExamination should report no change for a null payload") + void updateSysRespiratoryExamination_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateSysRespiratoryExamination(null)); + } + + @Test + @DisplayName("updateSysCentralNervousExamination should read the processed flag before updating") + void updateSysCentralNervousExamination_shouldReadProcessedFlag() { + SysCentralNervousExamination examination = new SysCentralNervousExamination(); + examination.setBeneficiaryRegID(BEN_REG_ID); + examination.setVisitCode(VISIT_CODE); + when(sysCentralNervousExaminationRepo.getBenCentralNervousExaminationStatus(BEN_REG_ID, VISIT_CODE)) + .thenReturn("P"); + + assertEquals(0, service.updateSysCentralNervousExamination(examination)); + } + + @Test + @DisplayName("updateSysCentralNervousExamination should report no change for a null payload") + void updateSysCentralNervousExamination_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateSysCentralNervousExamination(null)); + } + + @Test + @DisplayName("updateSysMusculoskeletalSystemExamination should read the processed flag before updating") + void updateSysMusculoskeletalSystemExamination_shouldReadProcessedFlag() { + SysMusculoskeletalSystemExamination examination = new SysMusculoskeletalSystemExamination(); + examination.setBeneficiaryRegID(BEN_REG_ID); + examination.setVisitCode(VISIT_CODE); + when(sysMusculoskeletalSystemExaminationRepo + .getBenMusculoskeletalSystemExaminationStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateSysMusculoskeletalSystemExamination(examination)); + } + + @Test + @DisplayName("updateSysMusculoskeletalSystemExamination should report no change for a null payload") + void updateSysMusculoskeletalSystemExamination_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateSysMusculoskeletalSystemExamination(null)); + } + + @Test + @DisplayName("updateSysGenitourinarySystemExamination should read the processed flag before updating") + void updateSysGenitourinarySystemExamination_shouldReadProcessedFlag() { + SysGenitourinarySystemExamination examination = new SysGenitourinarySystemExamination(); + examination.setBeneficiaryRegID(BEN_REG_ID); + examination.setVisitCode(VISIT_CODE); + when(sysGenitourinarySystemExaminationRepo + .getBenGenitourinarySystemExaminationStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateSysGenitourinarySystemExamination(examination)); + } + + @Test + @DisplayName("updateSysGenitourinarySystemExamination should report no change for a null payload") + void updateSysGenitourinarySystemExamination_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateSysGenitourinarySystemExamination(null)); + } + + @Test + @DisplayName("updateSysGastrointestinalExamination should report no change for a null payload") + void updateSysGastrointestinalExamination_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateSysGastrointestinalExamination(null)); + } + } + + @Nested + @DisplayName("prescriptions and prescribed drugs") + class PrescriptionTests { + + private SCTDescription diagnosis(String term, String conceptId) { + SCTDescription description = new SCTDescription(); + description.setTerm(term); + description.setConceptID(conceptId); + return description; + } + + private PrescriptionDetail stored(Long prescriptionId) { + PrescriptionDetail stored = new PrescriptionDetail(); + stored.setPrescriptionID(prescriptionId); + return stored; + } + + @Test + @DisplayName("savePrescriptionDetailsAndGetPrescriptionID should build the prescription and return its id") + void savePrescriptionDetailsAndGetPrescriptionID_shouldBuildAndReturnId() { + when(prescriptionDetailRepo.save(any(PrescriptionDetail.class))).thenReturn(stored(5L)); + + Long result = service.savePrescriptionDetailsAndGetPrescriptionID(BEN_REG_ID, 3L, 9, "nurse1", + "X-Ray", VISIT_CODE, 7, 8, "after food", + new ArrayList<>(Collections.singletonList(diagnosis("Fever", "386661006")))); + + assertEquals(5L, result); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PrescriptionDetail.class); + verify(prescriptionDetailRepo).save(captor.capture()); + assertEquals(BEN_REG_ID, captor.getValue().getBeneficiaryRegID()); + assertEquals("after food", captor.getValue().getInstruction()); + assertEquals("Fever", captor.getValue().getDiagnosisProvided()); + assertEquals("386661006", captor.getValue().getDiagnosisProvided_SCTCode()); + } + + @Test + @DisplayName("savePrescriptionDetailsAndGetPrescriptionID should omit an absent instruction and diagnosis list") + void savePrescriptionDetailsAndGetPrescriptionID_shouldOmitAbsentOptionalFields() { + when(prescriptionDetailRepo.save(any(PrescriptionDetail.class))).thenReturn(stored(5L)); + + assertEquals(5L, service.savePrescriptionDetailsAndGetPrescriptionID(BEN_REG_ID, 3L, 9, "nurse1", + "X-Ray", VISIT_CODE, 7, 8, null, null)); + } + + @Test + @DisplayName("savePrescriptionCovid should carry the doctor diagnosis onto the prescription") + void savePrescriptionCovid_shouldCarryDoctorDiagnosis() { + when(prescriptionDetailRepo.save(any(PrescriptionDetail.class))).thenReturn(stored(5L)); + + assertEquals(5L, service.savePrescriptionCovid(BEN_REG_ID, 3L, 9, "nurse1", "X-Ray", VISIT_CODE, 7, 8, + "after food", "Covid positive")); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PrescriptionDetail.class); + verify(prescriptionDetailRepo).save(captor.capture()); + assertEquals("Covid positive", captor.getValue().getDiagnosisProvided()); + } + + @Test + @DisplayName("savePrescriptionCovid should omit an absent instruction and diagnosis") + void savePrescriptionCovid_shouldOmitAbsentOptionalFields() { + when(prescriptionDetailRepo.save(any(PrescriptionDetail.class))).thenReturn(stored(5L)); + + assertEquals(5L, service.savePrescriptionCovid(BEN_REG_ID, 3L, 9, "nurse1", "X-Ray", VISIT_CODE, 7, 8, + null, null)); + } + + @Test + @DisplayName("saveBeneficiaryPrescription should map the case sheet onto a prescription and store it") + void saveBeneficiaryPrescription_shouldMapCaseSheetAndStore() throws Exception { + when(prescriptionDetailRepo.save(any(PrescriptionDetail.class))).thenReturn(stored(5L)); + + com.google.gson.JsonObject caseSheet = new com.google.gson.JsonObject(); + caseSheet.addProperty("beneficiaryRegID", BEN_REG_ID); + + assertEquals(5L, service.saveBeneficiaryPrescription(caseSheet)); + } + + @Test + @DisplayName("saveBenPrescription should join several provisional diagnoses into one field") + void saveBenPrescription_shouldJoinSeveralDiagnoses() { + PrescriptionDetail prescription = new PrescriptionDetail(); + prescription.setProvisionalDiagnosisList(new ArrayList<>( + Arrays.asList(diagnosis("Fever", "1"), diagnosis("Cough", null), diagnosis(null, "3")))); + when(prescriptionDetailRepo.save(prescription)).thenReturn(stored(5L)); + + assertEquals(5L, service.saveBenPrescription(prescription)); + assertEquals("Fever || Cough", prescription.getDiagnosisProvided()); + assertEquals("1 || N/A", prescription.getDiagnosisProvided_SCTCode()); + } + + @Test + @DisplayName("saveBenPrescription should record N/A when the first diagnosis carries no concept id") + void saveBenPrescription_shouldRecordNotAvailableForMissingConceptId() { + PrescriptionDetail prescription = new PrescriptionDetail(); + prescription.setProvisionalDiagnosisList( + new ArrayList<>(Collections.singletonList(diagnosis("Fever", null)))); + when(prescriptionDetailRepo.save(prescription)).thenReturn(stored(5L)); + + assertEquals(5L, service.saveBenPrescription(prescription)); + assertEquals("N/A", prescription.getDiagnosisProvided_SCTCode()); + } + + @Test + @DisplayName("saveBenPrescription should return null when the stored prescription carries no id") + void saveBenPrescription_shouldReturnNullWithoutStoredId() { + PrescriptionDetail prescription = new PrescriptionDetail(); + when(prescriptionDetailRepo.save(prescription)).thenReturn(stored(0L)); + + assertNull(service.saveBenPrescription(prescription)); + } + + @Test + @DisplayName("updatePrescription should mark an already processed prescription as updated") + void updatePrescription_shouldMarkProcessedPrescriptionAsUpdated() { + PrescriptionDetail prescription = new PrescriptionDetail(); + prescription.setBeneficiaryRegID(BEN_REG_ID); + prescription.setVisitCode(VISIT_CODE); + prescription.setPrescriptionID(5L); + prescription.setProvisionalDiagnosisList(new ArrayList<>( + Arrays.asList(diagnosis("Fever", "1"), diagnosis("Cough", null)))); + PrescriptionDetail existing = stored(5L); + existing.setProcessed("P"); + existing.setInstruction("after food"); + when(prescriptionDetailRepo.getGeneralOPDDiagnosisStatus(BEN_REG_ID, VISIT_CODE, 5L)).thenReturn(existing); + when(prescriptionDetailRepo.save(prescription)).thenReturn(stored(5L)); + + assertEquals(1, service.updatePrescription(prescription)); + assertEquals("U", prescription.getProcessed()); + assertEquals("after food", prescription.getInstruction()); + assertEquals("Fever || Cough", prescription.getDiagnosisProvided()); + } + + @Test + @DisplayName("updatePrescription should keep a fresh prescription marked as new") + void updatePrescription_shouldKeepFreshPrescriptionAsNew() { + PrescriptionDetail prescription = new PrescriptionDetail(); + prescription.setBeneficiaryRegID(BEN_REG_ID); + prescription.setVisitCode(VISIT_CODE); + prescription.setPrescriptionID(5L); + PrescriptionDetail existing = stored(5L); + existing.setProcessed("N"); + existing.setDiagnosisProvided("Fever"); + when(prescriptionDetailRepo.getGeneralOPDDiagnosisStatus(BEN_REG_ID, VISIT_CODE, 5L)).thenReturn(existing); + when(prescriptionDetailRepo.save(prescription)).thenReturn(stored(5L)); + + assertEquals(1, service.updatePrescription(prescription)); + assertEquals("N", prescription.getProcessed()); + assertEquals("Fever", prescription.getDiagnosisProvided()); + } + + @Test + @DisplayName("updatePrescription should treat an unknown prescription as new") + void updatePrescription_shouldTreatUnknownPrescriptionAsNew() { + PrescriptionDetail prescription = new PrescriptionDetail(); + prescription.setBeneficiaryRegID(BEN_REG_ID); + prescription.setVisitCode(VISIT_CODE); + prescription.setPrescriptionID(5L); + when(prescriptionDetailRepo.getGeneralOPDDiagnosisStatus(BEN_REG_ID, VISIT_CODE, 5L)).thenReturn(null); + when(prescriptionDetailRepo.save(prescription)).thenReturn(stored(0L)); + + assertEquals(0, service.updatePrescription(prescription)); + assertEquals("N", prescription.getProcessed()); + } + + @Test + @DisplayName("saveBeneficiaryLabTestOrderDetails should succeed when the case sheet orders no test") + void saveBeneficiaryLabTestOrderDetails_shouldSucceedWithoutOrders() { + assertEquals(1L, service.saveBeneficiaryLabTestOrderDetails(new com.google.gson.JsonObject(), 5L)); + } + + @Test + @DisplayName("saveBenPrescribedDrugsList should succeed for an empty drug list") + void saveBenPrescribedDrugsList_shouldSucceedForEmptyList() { + assertEquals(1, service.saveBenPrescribedDrugsList(new ArrayList<>())); + } + + @Test + @DisplayName("saveBenPrescribedDrugsList should store a drug that needs no quantity calculation") + void saveBenPrescribedDrugsList_shouldStoreDrugWithoutQuantityCalculation() { + PrescribedDrugDetail drug = new PrescribedDrugDetail(); + drug.setFormName("Syrup"); + List drugs = new ArrayList<>(Collections.singletonList(drug)); + when(prescribedDrugDetailRepo.saveAll(drugs)).thenReturn(drugs); + + assertEquals(1, service.saveBenPrescribedDrugsList(drugs)); + assertNull(drug.getQtyPrescribed()); + } + + @Test + @DisplayName("saveBenPrescribedDrugsList should report a failure when not every drug was stored") + void saveBenPrescribedDrugsList_shouldReportPartialSaveAsFailure() { + PrescribedDrugDetail drug = new PrescribedDrugDetail(); + drug.setFormName("Syrup"); + List drugs = new ArrayList<>(Collections.singletonList(drug)); + when(prescribedDrugDetailRepo.saveAll(drugs)).thenReturn(new ArrayList<>()); + + assertEquals(0, service.saveBenPrescribedDrugsList(drugs)); + } + + @org.junit.jupiter.params.ParameterizedTest(name = "{0} {1} {2} for {3} {4} should be {5} units") + @org.junit.jupiter.params.provider.CsvSource({ + "Tablet, Half Tab, Once Daily(OD), 10, Day(s), 5", + "Tablet, One Tab, Once Daily(OD) Before Food, 10, Day(s), 10", + "Tablet, One & Half Tab, Once Daily(OD) After Food, 2, Week(s), 21", + "Tablet, Two Tabs, Once Daily(OD) At Bedtime, 1, Month(s), 60", + "Capsule, One Tab, Once Daily(OD), 10, Day(s), 10", + "Tablet, Half Tab, Twice Daily(BD), 10, Day(s), 10", + "Tablet, One Tab, Twice Daily(BD) Before Food, 10, Day(s), 20", + "Tablet, One & Half Tab, Twice Daily(BD) After Food, 10, Day(s), 30", + "Tablet, Two Tabs, Twice Daily(BD), 10, Day(s), 40", + "Capsule, One Tab, Twice Daily(BD), 10, Day(s), 20", + "Tablet, Half Tab, Thrice Daily (TID), 10, Day(s), 15", + "Tablet, One Tab, Thrice Daily (TID) After Food, 10, Day(s), 30", + "Tablet, One & Half Tab, Thrice Daily (TID) Before Food, 10, Day(s), 45", + "Tablet, Two Tabs, Thrice Daily (TID), 10, Day(s), 60", + "Tablet, Half Tab, Four Times in a Day (QID), 10, Day(s), 20", + "Tablet, One Tab, Four Times in a Day AF, 10, Day(s), 40", + "Tablet, One & Half Tab, Four Times in a Day BF, 10, Day(s), 60", + "Tablet, Two Tabs, Four Times in a Day (QID), 10, Day(s), 80", + "Tablet, Half Tab, Single Dose, 10, Day(s), 1", + "Tablet, One Tab, Stat Dose, 10, Day(s), 1", + "Tablet, Two Tabs, Single Dose After Food, 10, Day(s), 2", + "Tablet, One Tab, Once in a Week, 4, Week(s), 4", + "Tablet, Half Tab, SOS, 10, Day(s), 5", + "Tablet, One Tab, SOS, 10, Day(s), 10" }) + @DisplayName("saveBenPrescribedDrugsList should derive the dispensed quantity from the dosage") + void saveBenPrescribedDrugsList_shouldDeriveDispensedQuantity(String form, String dose, String frequency, + String duration, String unit, int expectedQuantity) { + PrescribedDrugDetail drug = new PrescribedDrugDetail(); + drug.setFormName(form); + drug.setDose(dose); + drug.setFrequency(frequency); + drug.setDuration(duration); + drug.setUnit(unit); + List drugs = new ArrayList<>(Collections.singletonList(drug)); + when(prescribedDrugDetailRepo.saveAll(drugs)).thenReturn(drugs); + + service.saveBenPrescribedDrugsList(drugs); + + assertEquals(expectedQuantity, drug.getQtyPrescribed()); + } + + @Test + @DisplayName("saveBenPrescribedDrugsList should leave the quantity at zero when the dosage is incomplete") + void saveBenPrescribedDrugsList_shouldLeaveQuantityAtZeroForIncompleteDosage() { + PrescribedDrugDetail drug = new PrescribedDrugDetail(); + drug.setFormName("Tablet"); + List drugs = new ArrayList<>(Collections.singletonList(drug)); + when(prescribedDrugDetailRepo.saveAll(drugs)).thenReturn(drugs); + + service.saveBenPrescribedDrugsList(drugs); + + assertEquals(0, drug.getQtyPrescribed()); + } + } + + @Nested + @DisplayName("investigations, worklists and child histories") + class WorklistAndChildHistoryTests { + + private ArrayList oneEmptyRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[60]); + return rows; + } + + @Test + @DisplayName("saveBenInvestigationDetails should create the prescription then store the ordered tests") + void saveBenInvestigationDetails_shouldCreatePrescriptionThenStoreTests() { + WrapperBenInvestigationANC wrapper = new WrapperBenInvestigationANC(); + wrapper.setBeneficiaryRegID(BEN_REG_ID); + wrapper.setVisitCode(VISIT_CODE); + PrescriptionDetail stored = new PrescriptionDetail(); + stored.setPrescriptionID(5L); + when(prescriptionDetailRepo.save(any(PrescriptionDetail.class))).thenReturn(stored); + + assertEquals(1, service.saveBenInvestigationDetails(wrapper)); + assertEquals(5L, wrapper.getPrescriptionID()); + } + + @Test + @DisplayName("saveBenInvestigationDetails should report no change for a null payload") + void saveBenInvestigationDetails_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.saveBenInvestigationDetails(null)); + } + + @Test + @DisplayName("saveBenInvestigation should stamp the visit details onto every ordered test") + void saveBenInvestigation_shouldStampVisitDetailsOntoTests() { + WrapperBenInvestigationANC wrapper = new WrapperBenInvestigationANC(); + wrapper.setBeneficiaryRegID(BEN_REG_ID); + wrapper.setVisitCode(VISIT_CODE); + wrapper.setPrescriptionID(5L); + com.iemr.tm.data.quickConsultation.LabTestOrderDetail test = + new com.iemr.tm.data.quickConsultation.LabTestOrderDetail(); + wrapper.setLaboratoryList(new ArrayList<>(Collections.singletonList(test))); + when(labTestOrderDetailRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1L, service.saveBenInvestigation(wrapper)); + assertEquals(5L, test.getPrescriptionID()); + assertEquals(BEN_REG_ID, test.getBeneficiaryRegID()); + } + + @Test + @DisplayName("saveBenInvestigation should succeed when no test was ordered") + void saveBenInvestigation_shouldSucceedWithoutOrderedTests() { + WrapperBenInvestigationANC wrapper = new WrapperBenInvestigationANC(); + + assertEquals(1L, service.saveBenInvestigation(wrapper)); + } + + @Test + @DisplayName("updateBenVisitStatusFlag should confirm the flag change") + void updateBenVisitStatusFlag_shouldConfirmFlagChange() { + when(benVisitDetailRepo.updateBenFlowStatus("C", 3L)).thenReturn(1); + + assertTrue(service.updateBenVisitStatusFlag(3L, "C").contains("Updated Successfully")); + } + + @Test + @DisplayName("updateBenStatus should leave the response empty when no row was changed") + void updateBenStatus_shouldLeaveResponseEmptyWhenNothingChanged() { + when(benVisitDetailRepo.updateBenFlowStatus("C", 3L)).thenReturn(0); + + assertEquals("{}", service.updateBenStatus(3L, "C")); + } + + @Test + @DisplayName("getNurseWorkList should render the registrar worklist") + void getNurseWorkList_shouldRenderRegistrarWorklist() { + when(reistrarRepoBenSearch.getNurseWorkList()).thenReturn(new ArrayList<>()); + + assertNotNull(service.getNurseWorkList()); + } + + @Test + @DisplayName("getNurseWorkListNew should render the worklist for the configured lookback window") + void getNurseWorkListNew_shouldRenderWorklistForConfiguredWindow() { + org.springframework.test.util.ReflectionTestUtils.setField(service, "nurseWL", 10); + when(beneficiaryFlowStatusRepo.getNurseWorklistNew(eq(9), eq(7), any(java.sql.Timestamp.class))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getNurseWorkListNew(9, 7)); + } + + @Test + @DisplayName("getNurseWorkListNew should fall back to a seven day window when the setting is out of range") + void getNurseWorkListNew_shouldFallBackToSevenDayWindow() { + org.springframework.test.util.ReflectionTestUtils.setField(service, "nurseWL", 90); + when(beneficiaryFlowStatusRepo.getNurseWorklistNew(eq(9), eq(7), any(java.sql.Timestamp.class))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getNurseWorkListNew(9, 7)); + } + + @Test + @DisplayName("getNurseWorkListTcCurrentDate should render the same day teleconsultation worklist") + void getNurseWorkListTcCurrentDate_shouldRenderWorklist() { + org.springframework.test.util.ReflectionTestUtils.setField(service, "nurseTCWL", 5); + when(beneficiaryFlowStatusRepo.getNurseWorklistCurrentDate(eq(9), any(java.sql.Timestamp.class), eq(7))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getNurseWorkListTcCurrentDate(9, 7)); + } + + @Test + @DisplayName("getNurseWorkListTcCurrentDate should fall back to a seven day window when unset") + void getNurseWorkListTcCurrentDate_shouldFallBackToSevenDayWindow() { + when(beneficiaryFlowStatusRepo.getNurseWorklistCurrentDate(eq(9), any(java.sql.Timestamp.class), eq(7))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getNurseWorkListTcCurrentDate(9, 7)); + } + + @Test + @DisplayName("getNurseWorkListTcFutureDate should render the future teleconsultation worklist") + void getNurseWorkListTcFutureDate_shouldRenderWorklist() { + when(beneficiaryFlowStatusRepo.getNurseWorklistFutureDate(9, 7)).thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getNurseWorkListTcFutureDate(9, 7)); + } + + @Test + @DisplayName("getLabWorkListNew should render the laboratory worklist") + void getLabWorkListNew_shouldRenderWorklist() { + org.springframework.test.util.ReflectionTestUtils.setField(service, "labWL", 5); + when(beneficiaryFlowStatusRepo.getLabWorklistNew(eq(9), any(java.sql.Timestamp.class), eq(7))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getLabWorkListNew(9, 7)); + } + + @Test + @DisplayName("getLabWorkListNew should fall back to a seven day window when unset") + void getLabWorkListNew_shouldFallBackToSevenDayWindow() { + when(beneficiaryFlowStatusRepo.getLabWorklistNew(eq(9), any(java.sql.Timestamp.class), eq(7))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getLabWorkListNew(9, 7)); + } + + @Test + @DisplayName("getRadiologistWorkListNew should render the radiology worklist") + void getRadiologistWorkListNew_shouldRenderWorklist() { + org.springframework.test.util.ReflectionTestUtils.setField(service, "radioWL", 5); + when(beneficiaryFlowStatusRepo.getRadiologistWorkListNew(eq(9), any(java.sql.Timestamp.class), eq(7))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getRadiologistWorkListNew(9, 7)); + } + + @Test + @DisplayName("getRadiologistWorkListNew should fall back to a seven day window when unset") + void getRadiologistWorkListNew_shouldFallBackToSevenDayWindow() { + when(beneficiaryFlowStatusRepo.getRadiologistWorkListNew(eq(9), any(java.sql.Timestamp.class), eq(7))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getRadiologistWorkListNew(9, 7)); + } + + @Test + @DisplayName("getOncologistWorkListNew should render the oncology worklist") + void getOncologistWorkListNew_shouldRenderWorklist() { + org.springframework.test.util.ReflectionTestUtils.setField(service, "oncoWL", 5); + when(beneficiaryFlowStatusRepo.getOncologistWorkListNew(eq(9), any(java.sql.Timestamp.class), eq(7))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getOncologistWorkListNew(9, 7)); + } + + @Test + @DisplayName("getOncologistWorkListNew should fall back to a seven day window when unset") + void getOncologistWorkListNew_shouldFallBackToSevenDayWindow() { + when(beneficiaryFlowStatusRepo.getOncologistWorkListNew(eq(9), any(java.sql.Timestamp.class), eq(7))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getOncologistWorkListNew(9, 7)); + } + + @Test + @DisplayName("getPharmaWorkListNew should render the pharmacy worklist") + void getPharmaWorkListNew_shouldRenderWorklist() { + org.springframework.test.util.ReflectionTestUtils.setField(service, "pharmaWL", 5); + when(beneficiaryFlowStatusRepo.getPharmaWorkListNew(eq(9), any(java.sql.Timestamp.class), eq(7))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getPharmaWorkListNew(9, 7)); + } + + @Test + @DisplayName("getPharmaWorkListNew should fall back to a seven day window when unset") + void getPharmaWorkListNew_shouldFallBackToSevenDayWindow() { + when(beneficiaryFlowStatusRepo.getPharmaWorkListNew(eq(9), any(java.sql.Timestamp.class), eq(7))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getPharmaWorkListNew(9, 7)); + } + + @Test + @DisplayName("saveBenAdherenceDetails should confirm the stored adherence row") + void saveBenAdherenceDetails_shouldConfirmStoredRow() { + BenAdherence adherence = new BenAdherence(); + when(benAdherenceRepo.save(adherence)).thenReturn(adherence); + + assertEquals(1, service.saveBenAdherenceDetails(adherence)); + } + + @Test + @DisplayName("saveBenAdherenceDetails should report no change when nothing was stored") + void saveBenAdherenceDetails_shouldReportNoChangeWhenNothingStored() { + BenAdherence adherence = new BenAdherence(); + when(benAdherenceRepo.save(adherence)).thenReturn(null); + + assertEquals(0, service.saveBenAdherenceDetails(adherence)); + } + + @Test + @DisplayName("saveChildFeedingHistory should return the stored row id") + void saveChildFeedingHistory_shouldReturnStoredId() { + ChildFeedingDetails details = new ChildFeedingDetails(); + ChildFeedingDetails saved = new ChildFeedingDetails(); + saved.setID(4L); + when(childFeedingDetailsRepo.save(details)).thenReturn(saved); + + assertEquals(4L, service.saveChildFeedingHistory(details)); + } + + @Test + @DisplayName("saveChildFeedingHistory should return null when nothing was stored") + void saveChildFeedingHistory_shouldReturnNullWhenNothingStored() { + ChildFeedingDetails details = new ChildFeedingDetails(); + when(childFeedingDetailsRepo.save(details)).thenReturn(null); + + assertNull(service.saveChildFeedingHistory(details)); + } + + @Test + @DisplayName("savePerinatalHistory should return the stored row id") + void savePerinatalHistory_shouldReturnStoredId() { + PerinatalHistory history = new PerinatalHistory(); + PerinatalHistory saved = new PerinatalHistory(); + saved.setID(4L); + when(perinatalHistoryRepo.save(history)).thenReturn(saved); + + assertEquals(4L, service.savePerinatalHistory(history)); + } + + @Test + @DisplayName("savePerinatalHistory should return null when nothing was stored") + void savePerinatalHistory_shouldReturnNullWhenNothingStored() { + PerinatalHistory history = new PerinatalHistory(); + when(perinatalHistoryRepo.save(history)).thenReturn(null); + + assertNull(service.savePerinatalHistory(history)); + } + + @Test + @DisplayName("getBenAdherence should render the stored adherence for the visit") + void getBenAdherence_shouldRenderStoredAdherence() { + when(benAdherenceRepo.getBenAdherence(BEN_REG_ID, VISIT_CODE)).thenReturn(oneEmptyRow()); + + assertNotNull(service.getBenAdherence(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getLabTestOrders should render the stored lab test orders for the visit") + void getLabTestOrders_shouldRenderStoredOrders() { + when(labTestOrderDetailRepo.getLabTestOrderDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(new ArrayList<>()); + + assertNotNull(service.getLabTestOrders(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getPerinatalHistory should return the stored perinatal history for the visit") + void getPerinatalHistory_shouldReturnStoredHistory() { + when(perinatalHistoryRepo.getBenPerinatalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(new ArrayList<>()); + + assertNull(service.getPerinatalHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getFeedingHistory should return the stored feeding history for the visit") + void getFeedingHistory_shouldReturnStoredHistory() { + when(childFeedingDetailsRepo.getBenFeedingDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(new ArrayList<>()); + + assertNull(service.getFeedingHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("fetchBenPerinatalHistory should render the stored rows with the report columns") + void fetchBenPerinatalHistory_shouldRenderRowsWithColumns() { + when(perinatalHistoryRepo.getBenPerinatalDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenPerinatalHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenPerinatalHistory should render only the columns when nothing is stored") + void fetchBenPerinatalHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(perinatalHistoryRepo.getBenPerinatalDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenPerinatalHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenFeedingHistory should render the stored rows with the report columns") + void fetchBenFeedingHistory_shouldRenderRowsWithColumns() { + when(childFeedingDetailsRepo.getBenFeedingHistoryDetail(BEN_REG_ID)).thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenFeedingHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenFeedingHistory should render only the columns when nothing is stored") + void fetchBenFeedingHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(childFeedingDetailsRepo.getBenFeedingHistoryDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenFeedingHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("updateChildFeedingHistory should report no change for a null payload") + void updateChildFeedingHistory_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateChildFeedingHistory(null)); + } + + @Test + @DisplayName("updatePerinatalHistory should report no change for a null payload") + void updatePerinatalHistory_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updatePerinatalHistory(null)); + } + + @Test + @DisplayName("updateChildDevelopmentHistory should report no change for a null payload") + void updateChildDevelopmentHistory_shouldReportNoChangeForNullPayload() { + assertEquals(0, service.updateChildDevelopmentHistory(null)); + } + } + + @Nested + @DisplayName("trends, screening summaries and BMI status") + class TrendAndSummaryTests { + + private ArrayList visitRows(String visitCategory) { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[] { 1L, visitCategory, 22L }); + return rows; + } + + @Test + @DisplayName("getGraphicalTrendData should return the weight, blood pressure and blood glucose series") + void getGraphicalTrendData_shouldReturnAllSeries() { + when(benVisitDetailRepo.getLastSixVisitDetailsForBeneficiary(BEN_REG_ID)) + .thenReturn(visitRows("General OPD")); + when(benAnthropometryRepo.getBenAnthropometryDetailForGraphtrends(any())).thenReturn(new ArrayList<>()); + when(benPhysicalVitalRepo.getBenPhysicalVitalDetailForGraphTrends(any())).thenReturn(new ArrayList<>()); + + Map result = service.getGraphicalTrendData(BEN_REG_ID, "General OPD"); + + assertTrue(result.containsKey("weightList")); + assertTrue(result.containsKey("bpList")); + assertTrue(result.containsKey("bgList")); + } + + @Test + @DisplayName("getGraphicalTrendData should read the cancer vitals for a cancer screening visit") + void getGraphicalTrendData_shouldReadCancerVitals() { + when(benVisitDetailRepo.getLastSixVisitDetailsForBeneficiary(BEN_REG_ID)) + .thenReturn(visitRows("Cancer Screening")); + when(benCancerVitalDetailRepo.getBenCancerVitalDetailForGraph(any())).thenReturn(new ArrayList<>()); + + assertNotNull(service.getGraphicalTrendData(BEN_REG_ID, "Cancer Screening")); + verify(benCancerVitalDetailRepo).getBenCancerVitalDetailForGraph(any()); + } + + @Test + @DisplayName("getGraphicalTrendData should return empty series when the beneficiary has no earlier visit") + void getGraphicalTrendData_shouldReturnEmptySeriesWithoutVisits() { + when(benVisitDetailRepo.getLastSixVisitDetailsForBeneficiary(BEN_REG_ID)).thenReturn(new ArrayList<>()); + + assertNotNull(service.getGraphicalTrendData(BEN_REG_ID, "General OPD")); + } + + @Test + @DisplayName("updateBenFamilyHistoryNCDScreening should confirm the update when entries were stored") + void updateBenFamilyHistoryNCDScreening_shouldConfirmUpdate() { + BenFamilyHistory history = new BenFamilyHistory(); + List> diseases = new ArrayList<>(); + Map disease = new HashMap<>(); + disease.put("diseaseType", "Diabetes"); + disease.put("deleted", "false"); + diseases.add(disease); + history.setFamilyDiseaseList(diseases); + when(benFamilyHistoryRepo.saveAll(any())).thenAnswer(echoList()); + + assertEquals(1, service.updateBenFamilyHistoryNCDScreening(history)); + } + + @Test + @DisplayName("updateBenFamilyHistoryNCDScreening should report no change for an empty history") + void updateBenFamilyHistoryNCDScreening_shouldReportNoChangeForEmptyHistory() { + BenFamilyHistory history = new BenFamilyHistory(); + history.setFamilyDiseaseList(new ArrayList<>()); + + assertEquals(0, service.updateBenFamilyHistoryNCDScreening(history)); + } + + @Test + @DisplayName("updateBenPhysicalActivityHistoryNCDScreening should mark an existing row as updated") + void updatePhysicalActivityNCDScreening_shouldMarkExistingRowAsUpdated() { + PhysicalActivityType activity = new PhysicalActivityType(); + activity.setID(3L); + when(physicalActivityTypeRepo.save(activity)).thenReturn(activity); + + assertEquals(1, service.updateBenPhysicalActivityHistoryNCDScreening(activity)); + assertEquals("U", activity.getProcessed()); + assertEquals(Boolean.FALSE, activity.getDeleted()); + } + + @Test + @DisplayName("updateBenPhysicalActivityHistoryNCDScreening should mark a new row as new") + void updatePhysicalActivityNCDScreening_shouldMarkNewRowAsNew() { + PhysicalActivityType activity = new PhysicalActivityType(); + when(physicalActivityTypeRepo.save(activity)).thenReturn(null); + + assertEquals(0, service.updateBenPhysicalActivityHistoryNCDScreening(activity)); + assertEquals("N", activity.getProcessed()); + } + + @Test + @DisplayName("getBenSymptomaticData should summarise the confirmed and suspected diseases") + void getBenSymptomaticData_shouldSummariseDiseases() throws Exception { + when(iDRSDataRepo.getBenIdrsDetailsLast_3_Month(eq(BEN_REG_ID), any(java.sql.Timestamp.class))) + .thenReturn(new ArrayList<>()); + when(iDRSDataRepo.isDiabeticCheck(BEN_REG_ID)).thenReturn(1); + when(iDRSDataRepo.isEpilepsyCheck(BEN_REG_ID)).thenReturn(1); + when(iDRSDataRepo.isDefectiveVisionCheck(BEN_REG_ID)).thenReturn(1); + when(iDRSDataRepo.isHypertensionCheck(BEN_REG_ID)).thenReturn(1); + + assertNotNull(service.getBenSymptomaticData(BEN_REG_ID)); + } + + @Test + @DisplayName("getBenSymptomaticData should summarise nothing when no screening was recorded") + void getBenSymptomaticData_shouldSummariseNothingWithoutScreening() throws Exception { + when(iDRSDataRepo.getBenIdrsDetailsLast_3_Month(eq(BEN_REG_ID), any(java.sql.Timestamp.class))) + .thenReturn(new ArrayList<>()); + + assertNotNull(service.getBenSymptomaticData(BEN_REG_ID)); + } + + @Test + @DisplayName("getBenPreviousDiabetesData should render the earlier diabetes screening") + void getBenPreviousDiabetesData_shouldRenderEarlierScreening() throws Exception { + when(iDRSDataRepo.getBenPreviousDiabetesDetails(BEN_REG_ID)).thenReturn(new ArrayList<>()); + + assertNotNull(service.getBenPreviousDiabetesData(BEN_REG_ID)); + } + + @Test + @DisplayName("getBenPreviousReferralData should render the earlier referrals") + void getBenPreviousReferralData_shouldRenderEarlierReferrals() throws Exception { + assertNotNull(service.getBenPreviousReferralData(BEN_REG_ID)); + } + + @Test + @DisplayName("getMmuNurseWorkListNew should render the MMU referred worklist") + void getMmuNurseWorkListNew_shouldRenderWorklist() { + org.springframework.test.util.ReflectionTestUtils.setField(service, "TMReferredWL", 7); + when(beneficiaryFlowStatusRepo.getMmuNurseWorklistNew(eq(9), eq(7), any(java.sql.Timestamp.class))) + .thenReturn(new ArrayList<>()); + + assertEquals("[]", service.getMmuNurseWorkListNew(9, 7)); + } + + @Test + @DisplayName("fetchProviderSpecificdata should render the referral data for a referral request") + void fetchProviderSpecificdata_shouldRenderReferralData() throws Exception { + when(benReferDetailsRepo.getBenReferDetails(anyLong(), any())).thenReturn(new ArrayList<>()); + + String result = service.fetchProviderSpecificdata( + "{\"benRegID\":11,\"visitCode\":22,\"fetchMMUDataFor\":\"referral\"}"); + + assertNotNull(result); + } + + @Test + @DisplayName("fetchProviderSpecificdata should render the prescription data for a prescription request") + void fetchProviderSpecificdata_shouldRenderPrescriptionData() throws Exception { + when(prescribedDrugDetailRepo.getBenPrescribedDrugDetails(anyLong(), any())).thenReturn(new ArrayList<>()); + + assertNotNull(service.fetchProviderSpecificdata( + "{\"benRegID\":11,\"visitCode\":22,\"fetchMMUDataFor\":\"prescription\"}")); + } + + @Test + @DisplayName("fetchProviderSpecificdata should reject an unknown data category") + void fetchProviderSpecificdata_shouldRejectUnknownCategory() throws Exception { + assertEquals("Invalid master param to fetch data", service.fetchProviderSpecificdata( + "{\"benRegID\":11,\"visitCode\":22,\"fetchMMUDataFor\":\"unknown\"}")); + } + + @Test + @DisplayName("fetchProviderSpecificdata should fail for a request without a data category") + void fetchProviderSpecificdata_shouldFailWithoutCategory() { + assertThrows(IEMRException.class, + () -> service.fetchProviderSpecificdata("{\"benRegID\":11,\"visitCode\":22}")); + } + + @org.junit.jupiter.params.ParameterizedTest(name = "a BMI of {0} should read as {1}") + @org.junit.jupiter.params.provider.CsvSource({ "16.0, Normal", "14.5, Mild malnourished", + "12.5, Moderately Malnourished", "11.0, Severely Malnourished", "18.5, Overweight", "20.5, Obese", + "23.0, Severely Obese" }) + @DisplayName("calculateBMIStatus should classify the BMI against the age and gender reference") + void calculateBMIStatus_shouldClassifyAgainstReference(double bmi, String expectedStatus) throws Exception { + com.iemr.tm.data.bmi.BmiCalculation reference = new com.iemr.tm.data.bmi.BmiCalculation(); + reference.setN3SD(12d); + reference.setN2SD(13d); + reference.setN1SD(15d); + reference.setP1SD(18d); + reference.setP2SD(20d); + reference.setP3SD(22d); + when(bmiCalculationRepo.getBMIDetails(30, "Male")).thenReturn(reference); + + String result = service.calculateBMIStatus( + "{\"yearMonth\":\"2 years and 6 months\",\"gender\":\"Male\",\"bmi\":" + bmi + "}"); + + assertTrue(result.contains(expectedStatus)); + } + + @Test + @DisplayName("calculateBMIStatus should fail when no reference exists for the age and gender") + void calculateBMIStatus_shouldFailWithoutReference() { + when(bmiCalculationRepo.getBMIDetails(30, "Male")).thenReturn(null); + + IEMRException thrown = assertThrows(IEMRException.class, () -> service.calculateBMIStatus( + "{\"yearMonth\":\"2 years and 6 months\",\"gender\":\"Male\",\"bmi\":16.0}")); + + assertTrue(thrown.getMessage().contains("No data found for this category")); + } + + @Test + @DisplayName("calculateBMIStatus should report an empty status for a request without a BMI") + void calculateBMIStatus_shouldReportEmptyStatusWithoutBmi() throws Exception { + assertTrue(service.calculateBMIStatus("{\"yearMonth\":\"2 years and 6 months\",\"gender\":\"Male\"}") + .contains("\"bmiStatus\":\"\"")); + } + } + + @Nested + @DisplayName("child history reports and graph series") + class ChildHistoryAndGraphTests { + + private ArrayList oneEmptyRow() { + ArrayList rows = new ArrayList<>(); + rows.add(new Object[40]); + return rows; + } + + @Test + @DisplayName("fetchBenDevelopmentHistory should render the stored rows with the report columns") + void fetchDevelopmentHistory_shouldRenderRowsWithColumns() { + when(benChildDevelopmentHistoryRepo.getBenDevelopmentHistoryDetail(BEN_REG_ID)) + .thenReturn(oneEmptyRow()); + + assertTrue(service.fetchBenDevelopmentHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("fetchBenDevelopmentHistory should render only the columns when nothing is stored") + void fetchDevelopmentHistory_shouldRenderColumnsOnlyWhenEmpty() { + when(benChildDevelopmentHistoryRepo.getBenDevelopmentHistoryDetail(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.fetchBenDevelopmentHistory(BEN_REG_ID).contains("\"columns\"")); + } + + @Test + @DisplayName("updateChildDevelopmentHistory should update the stored development history") + void updateDevelopmentHistory_shouldUpdateStoredHistory() { + BenChildDevelopmentHistory history = new BenChildDevelopmentHistory(); + history.setBeneficiaryRegID(BEN_REG_ID); + history.setVisitCode(VISIT_CODE); + when(benChildDevelopmentHistoryRepo.getDevelopmentHistoryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateChildDevelopmentHistory(history)); + } + + @Test + @DisplayName("updateChildDevelopmentHistory should insert a new row when the visit has none") + void updateDevelopmentHistory_shouldInsertNewRow() { + BenChildDevelopmentHistory history = new BenChildDevelopmentHistory(); + history.setBeneficiaryRegID(BEN_REG_ID); + history.setVisitCode(VISIT_CODE); + history.setModifiedBy("nurse1"); + when(benChildDevelopmentHistoryRepo.getDevelopmentHistoryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn(null); + BenChildDevelopmentHistory saved = new BenChildDevelopmentHistory(); + saved.setID(4L); + when(benChildDevelopmentHistoryRepo.save(any())).thenReturn(saved); + + assertEquals(1, service.updateChildDevelopmentHistory(history)); + } + + @Test + @DisplayName("updateChildFeedingHistory should update the stored feeding history") + void updateFeedingHistory_shouldUpdateStoredHistory() { + ChildFeedingDetails details = new ChildFeedingDetails(); + details.setBeneficiaryRegID(BEN_REG_ID); + details.setVisitCode(VISIT_CODE); + when(childFeedingDetailsRepo.getBenChildFeedingDetailStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updateChildFeedingHistory(details)); + } + + @Test + @DisplayName("updateChildFeedingHistory should insert a new row when the visit has none") + void updateFeedingHistory_shouldInsertNewRow() { + ChildFeedingDetails details = new ChildFeedingDetails(); + details.setBeneficiaryRegID(BEN_REG_ID); + details.setVisitCode(VISIT_CODE); + details.setModifiedBy("nurse1"); + when(childFeedingDetailsRepo.getBenChildFeedingDetailStatus(BEN_REG_ID, VISIT_CODE)).thenReturn(null); + ChildFeedingDetails saved = new ChildFeedingDetails(); + saved.setID(4L); + when(childFeedingDetailsRepo.save(any())).thenReturn(saved); + + assertEquals(1, service.updateChildFeedingHistory(details)); + } + + @Test + @DisplayName("updatePerinatalHistory should update the stored perinatal history") + void updatePerinatalHistory_shouldUpdateStoredHistory() { + PerinatalHistory history = new PerinatalHistory(); + history.setBeneficiaryRegID(BEN_REG_ID); + history.setVisitCode(VISIT_CODE); + when(perinatalHistoryRepo.getPerinatalHistoryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn("P"); + + assertEquals(0, service.updatePerinatalHistory(history)); + } + + @Test + @DisplayName("updatePerinatalHistory should insert a new row when the visit has none") + void updatePerinatalHistory_shouldInsertNewRow() { + PerinatalHistory history = new PerinatalHistory(); + history.setBeneficiaryRegID(BEN_REG_ID); + history.setVisitCode(VISIT_CODE); + history.setModifiedBy("nurse1"); + when(perinatalHistoryRepo.getPerinatalHistoryStatus(BEN_REG_ID, VISIT_CODE)).thenReturn(null); + PerinatalHistory saved = new PerinatalHistory(); + saved.setID(4L); + when(perinatalHistoryRepo.save(any())).thenReturn(saved); + + assertEquals(1, service.updatePerinatalHistory(history)); + } + + @Test + @DisplayName("getGraphicalTrendData should build the weight, blood pressure and blood glucose series") + void getGraphicalTrendData_shouldBuildEverySeries() { + ArrayList visits = new ArrayList<>(); + visits.add(new Object[] { 1L, "General OPD", 22L }); + when(benVisitDetailRepo.getLastSixVisitDetailsForBeneficiary(BEN_REG_ID)).thenReturn(visits); + + ArrayList anthro = new ArrayList<>(); + anthro.add(new Object[] { 60d, new java.sql.Timestamp(1_700_000_000_000L) }); + when(benAnthropometryRepo.getBenAnthropometryDetailForGraphtrends(any())).thenReturn(anthro); + + ArrayList vitals = new ArrayList<>(); + vitals.add(new Object[] { (short) 120, (short) 80, 90d, 140d, 120d, new java.sql.Timestamp( + 1_700_000_000_000L) }); + when(benPhysicalVitalRepo.getBenPhysicalVitalDetailForGraphTrends(any())).thenReturn(vitals); + + Map result = service.getGraphicalTrendData(BEN_REG_ID, "General OPD"); + + assertNotNull(result.get("weightList")); + assertNotNull(result.get("bpList")); + assertNotNull(result.get("bgList")); + } + + @Test + @DisplayName("getGraphicalTrendData should build the series from the cancer screening vitals") + void getGraphicalTrendData_shouldBuildSeriesFromCancerVitals() { + ArrayList visits = new ArrayList<>(); + visits.add(new Object[] { 1L, "Cancer Screening", 22L }); + when(benVisitDetailRepo.getLastSixVisitDetailsForBeneficiary(BEN_REG_ID)).thenReturn(visits); + com.iemr.tm.data.nurse.BenCancerVitalDetail cancerVital = + new com.iemr.tm.data.nurse.BenCancerVitalDetail(); + cancerVital.setSystolicBP_1stReading((short) 120); + cancerVital.setDiastolicBP_1stReading((short) 80); + cancerVital.setWeight_Kg(60d); + cancerVital.setCreatedDate(new java.sql.Timestamp(1_700_000_000_000L)); + when(benCancerVitalDetailRepo.getBenCancerVitalDetailForGraph(any())) + .thenReturn(new ArrayList<>(Collections.singletonList(cancerVital))); + + assertNotNull(service.getGraphicalTrendData(BEN_REG_ID, "Cancer Screening")); + } + } + + @Nested + @DisplayName("obstetric, menstrual and trend assembly") + class HistoryAssemblyTests { + + /** + * One obstetric history row as the native query returns it: identifiers, + * the pregnancy count and the comma separated complication columns. + */ + private Object[] obstetricRow() { + Object[] values = new Object[38]; + values[0] = 11L; + values[1] = 3L; + values[2] = 9; + values[3] = (short) 1; + values[4] = (short) 2; + for (int i = 5; i < 38; i++) { + values[i] = "1,2"; + } + values[8] = (short) 1; + values[10] = (short) 1; + values[12] = (short) 1; + values[18] = (short) 1; + values[23] = (short) 1; + values[27] = (short) 1; + values[30] = 5L; + values[31] = 3; + values[33] = 3; + values[34] = 3; + return values; + } + + @Test + @DisplayName("getFemaleObstetricHistory should expand the complications of every pregnancy") + void getFemaleObstetricHistory_shouldExpandComplications() { + java.util.ArrayList rows = new java.util.ArrayList<>(); + rows.add(obstetricRow()); + when(femaleObstetricHistoryRepo.getBenFemaleObstetricHistoryDetail(BEN_REG_ID, VISIT_CODE)) + .thenReturn(rows); + + com.iemr.tm.data.anc.WrapperFemaleObstetricHistory history = + service.getFemaleObstetricHistory(BEN_REG_ID, VISIT_CODE); + + assertEquals(1, history.getFemaleObstetricHistoryList().size()); + com.iemr.tm.data.anc.FemaleObstetricHistory pregnancy = history.getFemaleObstetricHistoryList().get(0); + assertNotNull(pregnancy.getPregComplicationList()); + assertNotNull(pregnancy.getDeliveryComplicationList()); + assertNotNull(pregnancy.getPostpartumComplicationList()); + assertNotNull(pregnancy.getAbortionType()); + assertNotNull(pregnancy.getTypeofFacility()); + assertNotNull(pregnancy.getPostAbortionComplication()); + } + + @Test + @DisplayName("getFemaleObstetricHistory should answer for a beneficiary with no recorded pregnancy") + void getFemaleObstetricHistory_shouldAnswerWithoutRecordedPregnancy() { + when(femaleObstetricHistoryRepo.getBenFemaleObstetricHistoryDetail(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new java.util.ArrayList<>()); + + assertNotNull(service.getFemaleObstetricHistory(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getMenstrualHistory should expand the recorded menstrual problems") + void getMenstrualHistory_shouldExpandRecordedProblems() { + java.util.ArrayList rows = new java.util.ArrayList<>(); + rows.add(new Object[] { 11L, 3L, 9, (short) 1, "1,2", "1,2", (short) 5, "1,2", (short) 28, + "1,2", "1,2", "1,2", new java.sql.Timestamp(System.currentTimeMillis()), 22L }); + when(benMenstrualDetailsRepo.getBenMenstrualDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(rows); + + com.iemr.tm.data.anc.BenMenstrualDetails details = + service.getMenstrualHistory(BEN_REG_ID, VISIT_CODE); + + assertNotNull(details); + } + + @Test + @DisplayName("getMenstrualHistory should answer for a beneficiary with no recorded menstrual history") + void getMenstrualHistory_shouldAnswerWithoutRecordedHistory() { + when(benMenstrualDetailsRepo.getBenMenstrualDetail(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertDoesNotThrow( + () -> service.getMenstrualHistory(BEN_REG_ID, VISIT_CODE)); + } + + private com.iemr.tm.data.nurse.BenCancerVitalDetail cancerVitals() { + com.iemr.tm.data.nurse.BenCancerVitalDetail vitals = + new com.iemr.tm.data.nurse.BenCancerVitalDetail(); + vitals.setWeight_Kg(62.0); + vitals.setSystolicBP_1stReading((short) 120); + vitals.setSystolicBP_2ndReading((short) 122); + vitals.setSystolicBP_3rdReading((short) 118); + vitals.setDiastolicBP_1stReading((short) 80); + vitals.setDiastolicBP_2ndReading((short) 82); + vitals.setDiastolicBP_3rdReading((short) 78); + vitals.setBloodGlucose_Fasting((short) 90); + vitals.setBloodGlucose_Random((short) 120); + vitals.setBloodGlucose_2HrPostPrandial((short) 140); + vitals.setCreatedDate(new java.sql.Timestamp(System.currentTimeMillis())); + return vitals; + } + + @Test + @DisplayName("getGraphicalTrendData should chart the weight, blood pressure and blood glucose readings") + void getGraphicalTrendData_shouldChartWeightBpAndGlucose() { + java.util.ArrayList visits = new java.util.ArrayList<>(); + visits.add(new Object[] { 3L, "Cancer Screening", "22" }); + visits.add(new Object[] { 4L, "ANC", "23" }); + when(benVisitDetailRepo.getLastSixVisitDetailsForBeneficiary(BEN_REG_ID)).thenReturn(visits); + when(benCancerVitalDetailRepo.getBenCancerVitalDetailForGraph(org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>( + java.util.Collections.singletonList(cancerVitals()))); + java.util.ArrayList anthro = new java.util.ArrayList<>(); + anthro.add(new Object[] { "60", java.sql.Date.valueOf("2026-08-01") }); + when(benAnthropometryRepo.getBenAnthropometryDetailForGraphtrends(org.mockito.ArgumentMatchers.any())) + .thenReturn(anthro); + java.util.ArrayList vital = new java.util.ArrayList<>(); + vital.add(new Object[] { (short) 120, (short) 80, "90", "120", "140", + java.sql.Date.valueOf("2026-08-01") }); + when(benPhysicalVitalRepo.getBenPhysicalVitalDetailForGraphTrends(org.mockito.ArgumentMatchers.any())) + .thenReturn(vital); + + java.util.Map trend = service.getGraphicalTrendData(BEN_REG_ID, "ANC"); + + assertNotNull(trend); + assertTrue(trend.containsKey("bpList") || !trend.isEmpty()); + } + + @Test + @DisplayName("getGraphicalTrendData should answer for a beneficiary with no earlier visit") + void getGraphicalTrendData_shouldAnswerWithoutEarlierVisit() { + when(benVisitDetailRepo.getLastSixVisitDetailsForBeneficiary(BEN_REG_ID)) + .thenReturn(new java.util.ArrayList<>()); + + assertNotNull(service.getGraphicalTrendData(BEN_REG_ID, "ANC")); + } + + private com.iemr.tm.data.ncdScreening.IDRSData idrsAnswer(Long visitCode, Integer questionID) { + com.iemr.tm.data.ncdScreening.IDRSData answer = new com.iemr.tm.data.ncdScreening.IDRSData(); + answer.setVisitCode(visitCode); + answer.setIdrsQuestionID(questionID); + answer.setAnswer("Yes"); + answer.setSuspectedDisease("Diabetes"); + answer.setConfirmedDisease("Diabetes"); + return answer; + } + + @Test + @DisplayName("getBenSymptomaticData should collect the answers of the most recent screening") + void getBenSymptomaticData_shouldCollectRecentAnswers() throws Exception { + when(iDRSDataRepo.getBenIdrsDetailsLast_3_Month(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>(java.util.Arrays.asList( + idrsAnswer(22L, 1), idrsAnswer(22L, 2), idrsAnswer(23L, 3)))); + + String result = service.getBenSymptomaticData(BEN_REG_ID); + + assertNotNull(result); + assertTrue(result.contains("Diabetes")); + } + + @Test + @DisplayName("getBenSymptomaticData should answer for a beneficiary with no recent screening") + void getBenSymptomaticData_shouldAnswerWithoutRecentScreening() throws Exception { + when(iDRSDataRepo.getBenIdrsDetailsLast_3_Month(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.any())).thenReturn(new java.util.ArrayList<>()); + + assertNotNull(service.getBenSymptomaticData(BEN_REG_ID)); + } + + @Test + @DisplayName("getBenPreviousReferralData should render the earlier referrals with the report columns") + void getBenPreviousReferralData_shouldRenderEarlierReferrals() throws Exception { + java.util.ArrayList rows = new java.util.ArrayList<>(); + rows.add(new Object[] { java.math.BigInteger.valueOf(22L), + new java.sql.Timestamp(System.currentTimeMillis()), "Diabetes" }); + when(iDRSDataRepo.getBenPreviousReferredDetails(BEN_REG_ID)).thenReturn(rows); + + String result = service.getBenPreviousReferralData(BEN_REG_ID); + + assertTrue(result.contains("columns")); + assertTrue(result.contains("Diabetes")); + } + + @Test + @DisplayName("getBenPreviousReferralData should render only the columns when there is no earlier referral") + void getBenPreviousReferralData_shouldRenderColumnsOnly() throws Exception { + when(iDRSDataRepo.getBenPreviousReferredDetails(BEN_REG_ID)).thenReturn(null); + + assertTrue(service.getBenPreviousReferralData(BEN_REG_ID).contains("columns")); + } + } + + @Nested + @DisplayName("provider specific data") + class ProviderSpecificDataTests { + + private String request(String fetchFor) { + return "{\"benRegID\":11,\"visitCode\":22,\"fetchMMUDataFor\":\"" + fetchFor + "\"}"; + } + + @Test + @DisplayName("fetchProviderSpecificdata should render the prescribed drugs") + void fetchProviderSpecificdata_shouldRenderPrescribedDrugs() throws Exception { + when(prescribedDrugDetailRepo.getBenPrescribedDrugDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new java.util.ArrayList<>()); + + assertTrue(service.fetchProviderSpecificdata(request("prescription")).contains("columns")); + } + + @Test + @DisplayName("fetchProviderSpecificdata should render the ordered tests and the RBS test from the vitals") + void fetchProviderSpecificdata_shouldRenderOrderedTestsAndRbsFromVitals() throws Exception { + java.util.ArrayList orders = new java.util.ArrayList<>(); + orders.add(new Object[] { 11L, 3L, 9, 4, "CBC", "1,2", 22L }); + when(labTestOrderDetailRepo.getLabTestOrderDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(orders); + com.iemr.tm.data.nurse.BenPhysicalVitalDetail vitals = + new com.iemr.tm.data.nurse.BenPhysicalVitalDetail(); + vitals.setRbsTestResult("110"); + when(benPhysicalVitalRepo.getBenPhysicalVitalDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(vitals); + + String result = service.fetchProviderSpecificdata(request("investigation")); + + assertTrue(result.contains("CBC")); + assertTrue(result.contains("RBS Test")); + } + + @Test + @DisplayName("fetchProviderSpecificdata should render the RBS test alone when no test was ordered") + void fetchProviderSpecificdata_shouldRenderRbsTestAlone() throws Exception { + when(labTestOrderDetailRepo.getLabTestOrderDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new java.util.ArrayList<>()); + com.iemr.tm.data.nurse.BenPhysicalVitalDetail vitals = + new com.iemr.tm.data.nurse.BenPhysicalVitalDetail(); + vitals.setRbsTestResult("110"); + when(benPhysicalVitalRepo.getBenPhysicalVitalDetail(BEN_REG_ID, VISIT_CODE)).thenReturn(vitals); + + assertTrue(service.fetchProviderSpecificdata(request("investigation")).contains("RBS Test")); + } + + @Test + @DisplayName("fetchProviderSpecificdata should skip the RBS test when it was already ordered") + void fetchProviderSpecificdata_shouldSkipAlreadyOrderedRbsTest() throws Exception { + java.util.ArrayList orders = new java.util.ArrayList<>(); + orders.add(new Object[] { 11L, 3L, 9, 4, "RBS Test", "1,2", 22L }); + when(labTestOrderDetailRepo.getLabTestOrderDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(orders); + + assertTrue(service.fetchProviderSpecificdata(request("investigation")).contains("RBS Test")); + verify(benPhysicalVitalRepo, never()).getBenPhysicalVitalDetail(BEN_REG_ID, VISIT_CODE); + } + + @Test + @DisplayName("fetchProviderSpecificdata should render the referrals") + void fetchProviderSpecificdata_shouldRenderReferrals() throws Exception { + when(benReferDetailsRepo.getBenReferDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new java.util.ArrayList<>()); + + assertTrue(service.fetchProviderSpecificdata(request("referral")).contains("columns")); + } + + @Test + @DisplayName("fetchProviderSpecificdata should reject a master it does not know") + void fetchProviderSpecificdata_shouldRejectUnknownMaster() throws Exception { + assertEquals("Invalid master param to fetch data", service.fetchProviderSpecificdata(request("unknown"))); + } + + @Test + @DisplayName("fetchProviderSpecificdata should report a request it cannot act on") + void fetchProviderSpecificdata_shouldReportRequestItCannotActOn() { + org.junit.jupiter.api.Assertions.assertThrows( + com.iemr.tm.utils.exception.IEMRException.class, + () -> service.fetchProviderSpecificdata("{\"benRegID\":11}")); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/common/transaction/CommonServiceImplTest.java b/src/test/java/com/iemr/tm/service/common/transaction/CommonServiceImplTest.java new file mode 100644 index 00000000..b021eb25 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/common/transaction/CommonServiceImplTest.java @@ -0,0 +1,349 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.common.transaction; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.provider.ProviderServiceMappingRepo; +import com.iemr.tm.service.anc.ANCServiceImpl; +import com.iemr.tm.service.cancerScreening.CSNurseServiceImpl; +import com.iemr.tm.service.cancerScreening.CSServiceImpl; +import com.iemr.tm.service.covid19.Covid19ServiceImpl; +import com.iemr.tm.service.generalOPD.GeneralOPDServiceImpl; +import com.iemr.tm.service.ncdCare.NCDCareServiceImpl; +import com.iemr.tm.service.ncdscreening.NCDScreeningServiceImpl; +import com.iemr.tm.service.pnc.PNCServiceImpl; +import com.iemr.tm.service.quickConsultation.QuickConsultationServiceImpl; +import com.iemr.tm.service.tele_consultation.TeleConsultationServiceImpl; +import com.iemr.tm.utils.CookieUtil; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CommonServiceImpl Test Suite") +class CommonServiceImplTest { + + @Mock + private Covid19ServiceImpl covid19ServiceImpl; + @Mock + private BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private ANCServiceImpl ancServiceImpl; + @Mock + private PNCServiceImpl pncServiceImpl; + @Mock + private GeneralOPDServiceImpl generalOPDServiceImpl; + @Mock + private NCDCareServiceImpl ncdCareServiceImpl; + @Mock + private QuickConsultationServiceImpl quickConsultationServiceImpl; + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private CSNurseServiceImpl cSNurseServiceImpl; + @Mock + private CSServiceImpl csServiceImpl; + @Mock + private NCDScreeningServiceImpl ncdScreeningServiceImpl; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private TeleConsultationServiceImpl teleConsultationServiceImpl; + @Mock + private ProviderServiceMappingRepo providerServiceMappingRepo; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private CookieUtil cookieUtil; + + @InjectMocks + private CommonServiceImpl service; + + @Test + @DisplayName("getCaseSheetPrintDataForBeneficiary should answer for a well formed request") + void getCaseSheetPrintDataForBeneficiary_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getCaseSheetPrintDataForBeneficiary(new com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus(), "{}")); + } + + @Test + @DisplayName("getBenPastHistoryData should answer for a well formed request") + void getBenPastHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenPastHistoryData(11L)); + } + + @Test + @DisplayName("getComorbidHistoryData should answer for a well formed request") + void getComorbidHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getComorbidHistoryData(11L)); + } + + @Test + @DisplayName("getMedicationHistoryData should answer for a well formed request") + void getMedicationHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getMedicationHistoryData(11L)); + } + + @Test + @DisplayName("getPersonalTobaccoHistoryData should answer for a well formed request") + void getPersonalTobaccoHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getPersonalTobaccoHistoryData(11L)); + } + + @Test + @DisplayName("getPersonalAlcoholHistoryData should answer for a well formed request") + void getPersonalAlcoholHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getPersonalAlcoholHistoryData(11L)); + } + + @Test + @DisplayName("getPersonalAllergyHistoryData should answer for a well formed request") + void getPersonalAllergyHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getPersonalAllergyHistoryData(11L)); + } + + @Test + @DisplayName("getFamilyHistoryData should answer for a well formed request") + void getFamilyHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getFamilyHistoryData(11L)); + } + + @Test + @DisplayName("getProviderSpecificData should answer for a well formed request") + void getProviderSpecificData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getProviderSpecificData("{}")); + } + + @Test + @DisplayName("getBenPhysicalHistory should answer for a well formed request") + void getBenPhysicalHistory_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenPhysicalHistory(11L)); + } + + @Test + @DisplayName("getMenstrualHistoryData should answer for a well formed request") + void getMenstrualHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getMenstrualHistoryData(11L)); + } + + @Test + @DisplayName("getObstetricHistoryData should answer for a well formed request") + void getObstetricHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getObstetricHistoryData(11L)); + } + + @Test + @DisplayName("getImmunizationHistoryData should answer for a well formed request") + void getImmunizationHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getImmunizationHistoryData(11L)); + } + + @Test + @DisplayName("getChildVaccineHistoryData should answer for a well formed request") + void getChildVaccineHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getChildVaccineHistoryData(11L)); + } + + @Test + @DisplayName("getBenPerinatalHistoryData should answer for a well formed request") + void getBenPerinatalHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenPerinatalHistoryData(11L)); + } + + @Test + @DisplayName("getBenFeedingHistoryData should answer for a well formed request") + void getBenFeedingHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenFeedingHistoryData(11L)); + } + + @Test + @DisplayName("getBenDevelopmentHistoryData should answer for a well formed request") + void getBenDevelopmentHistoryData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDevelopmentHistoryData(11L)); + } + + @Test + @DisplayName("getBenPreviousVisitDataForCaseRecord should answer for a well formed request") + void getBenPreviousVisitDataForCaseRecord_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenPreviousVisitDataForCaseRecord("{}")); + } + + @Test + @DisplayName("createTcRequest should answer for a well formed request") + void createTcRequest_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.createTcRequest(new com.google.gson.JsonObject(), org.mockito.Mockito.mock(com.iemr.tm.data.nurse.CommonUtilityClass.class), "{}")); + } + + @Test + @DisplayName("getOpenKMDocURL should reject a request it cannot act on") + void getOpenKMDocURL_shouldRejectRequestItCannotActOn() { + assertThrows(NullPointerException.class, () -> service.getOpenKMDocURL("{}", "{}")); + } + + @Test + @DisplayName("getBenSymptomaticQuestionnaireDetailsData should answer for a well formed request") + void getBenSymptomaticQuestionnaireDetailsData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenSymptomaticQuestionnaireDetailsData(11L)); + } + + @Test + @DisplayName("getBenPreviousDiabetesData should answer for a well formed request") + void getBenPreviousDiabetesData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenPreviousDiabetesData(11L)); + } + + @Test + @DisplayName("getBenPreviousReferralData should answer for a well formed request") + void getBenPreviousReferralData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenPreviousReferralData(11L)); + } + + @org.junit.jupiter.api.Nested + @DisplayName("case sheet assembly") + class CaseSheetTests { + + private com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus flow(String visitCategory) { + com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus flow = + new com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus(); + flow.setBeneficiaryRegID(11L); + flow.setBenVisitCode(22L); + flow.setBenFlowID(5L); + flow.setVisitCategory(visitCategory); + return flow; + } + + @org.junit.jupiter.api.BeforeEach + void stubBeneficiaryPanel() { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.getBenDetailsForLeftSidePanel( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong())) + .thenReturn(new java.util.ArrayList<>()); + } + + @Test + @DisplayName("getCaseSheetPrintDataForBeneficiary should assemble the ANC case sheet") + void getCaseSheet_shouldAssembleAncCaseSheet() throws Exception { + org.mockito.Mockito.when(ancServiceImpl.getBenANCNurseData(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + org.mockito.Mockito.when(ancServiceImpl.getBenCaseRecordFromDoctorANC(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + + org.junit.jupiter.api.Assertions.assertNotNull( + service.getCaseSheetPrintDataForBeneficiary(flow("ANC"), "Bearer session-token")); + } + + @Test + @DisplayName("getCaseSheetPrintDataForBeneficiary should assemble the PNC case sheet") + void getCaseSheet_shouldAssemblePncCaseSheet() throws Exception { + org.mockito.Mockito.when(pncServiceImpl.getBenPNCNurseData(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + org.mockito.Mockito.when(pncServiceImpl.getBenCaseRecordFromDoctorPNC(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + + org.junit.jupiter.api.Assertions.assertNotNull( + service.getCaseSheetPrintDataForBeneficiary(flow("PNC"), "Bearer session-token")); + } + + @Test + @DisplayName("getCaseSheetPrintDataForBeneficiary should assemble the General OPD case sheet") + void getCaseSheet_shouldAssembleGeneralOpdCaseSheet() throws Exception { + org.mockito.Mockito.when(generalOPDServiceImpl.getBenGeneralOPDNurseData(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + org.mockito.Mockito.when(generalOPDServiceImpl.getBenCaseRecordFromDoctorGeneralOPD(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + + org.junit.jupiter.api.Assertions.assertNotNull( + service.getCaseSheetPrintDataForBeneficiary(flow("General OPD"), "Bearer session-token")); + } + + @Test + @DisplayName("getCaseSheetPrintDataForBeneficiary should assemble the NCD care case sheet") + void getCaseSheet_shouldAssembleNcdCareCaseSheet() throws Exception { + org.mockito.Mockito.when(ncdCareServiceImpl.getBenNCDCareNurseData(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + org.mockito.Mockito.when(ncdCareServiceImpl.getBenCaseRecordFromDoctorNCDCare(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + + org.junit.jupiter.api.Assertions.assertNotNull( + service.getCaseSheetPrintDataForBeneficiary(flow("NCD care"), "Bearer session-token")); + } + + @Test + @DisplayName("getCaseSheetPrintDataForBeneficiary should assemble the General OPD (QC) case sheet") + void getCaseSheet_shouldAssembleGeneralOpdQcCaseSheet() throws Exception { + org.mockito.Mockito.when(quickConsultationServiceImpl.getBenQuickConsultNurseData(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + org.mockito.Mockito.when(quickConsultationServiceImpl.getBenCaseRecordFromDoctorQuickConsult(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + + org.junit.jupiter.api.Assertions.assertNotNull( + service.getCaseSheetPrintDataForBeneficiary(flow("General OPD (QC)"), "Bearer session-token")); + } + + @Test + @DisplayName("getCaseSheetPrintDataForBeneficiary should assemble the COVID-19 Screening case sheet") + void getCaseSheet_shouldAssembleCovid19ScreeningCaseSheet() throws Exception { + org.mockito.Mockito.when(covid19ServiceImpl.getBenCovidNurseData(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + org.mockito.Mockito.when(covid19ServiceImpl.getBenCaseRecordFromDoctorCovid19(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + + org.junit.jupiter.api.Assertions.assertNotNull( + service.getCaseSheetPrintDataForBeneficiary(flow("COVID-19 Screening"), "Bearer session-token")); + } + + @Test + @DisplayName("getCaseSheetPrintDataForBeneficiary should assemble the NCD screening case sheet") + void getCaseSheet_shouldAssembleNcdScreeningCaseSheet() throws Exception { + org.mockito.Mockito.when(ncdScreeningServiceImpl.getBenNCDScreeningNurseData(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + org.mockito.Mockito.when(ncdScreeningServiceImpl.getBenCaseRecordFromDoctorNCDScreening(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn("{}"); + + org.junit.jupiter.api.Assertions.assertNotNull( + service.getCaseSheetPrintDataForBeneficiary(flow("NCD screening"), "Bearer session-token")); + } + + @Test + @DisplayName("getCaseSheetPrintDataForBeneficiary should answer for an unknown visit category") + void getCaseSheet_shouldAnswerForUnknownVisitCategory() throws Exception { + org.junit.jupiter.api.Assertions.assertNotNull( + service.getCaseSheetPrintDataForBeneficiary(flow("Unknown"), "Bearer session-token")); + } + + @Test + @DisplayName("getCaseSheetPrintDataForBeneficiary should answer for a visit without a category") + void getCaseSheet_shouldAnswerWithoutVisitCategory() throws Exception { + org.junit.jupiter.api.Assertions.assertDoesNotThrow( + () -> service.getCaseSheetPrintDataForBeneficiary(flow(null), "Bearer session-token")); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/covid19/Covid19ServiceImplTest.java b/src/test/java/com/iemr/tm/service/covid19/Covid19ServiceImplTest.java new file mode 100644 index 00000000..6479a995 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/covid19/Covid19ServiceImplTest.java @@ -0,0 +1,534 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.covid19; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyShort; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.iemr.tm.data.covid19.Covid19BenFeedback; +import com.iemr.tm.data.nurse.CommonUtilityClass; +import com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ; +import com.iemr.tm.repo.nurse.covid19.Covid19BenFeedbackRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.quickConsultation.PrescriptionDetailRepo; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.service.labtechnician.LabTechnicianServiceImpl; +import com.iemr.tm.service.tele_consultation.SMSGatewayServiceImpl; +import com.iemr.tm.service.tele_consultation.TeleConsultationServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("Covid19ServiceImpl Test Suite") +class Covid19ServiceImplTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_ID = 3L; + private static final Long VISIT_CODE = 22L; + + private static final String FULL_HISTORY = "{\"pastHistory\":{},\"comorbidConditions\":{}," + + "\"medicationHistory\":{\"medicationHistoryList\":[{\"currentMedication\":\"Iron\"}]}," + + "\"personalHistory\":{},\"familyHistory\":{},\"menstrualHistory\":{}," + + "\"femaleObstetricHistory\":{},\"immunizationHistory\":{},\"childVaccineDetails\":{}," + + "\"developmentHistory\":{},\"feedingHistory\":{},\"perinatalHistroy\":{}}"; + + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private LabTechnicianServiceImpl labTechnicianServiceImpl; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private TeleConsultationServiceImpl teleConsultationServiceImpl; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + @Mock + private Covid19BenFeedbackRepo covid19BenFeedbackRepo; + @Mock + private PrescriptionDetailRepo prescriptionDetailRepo; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + + @InjectMocks + private Covid19ServiceImpl service; + + private JsonObject json(String raw) { + return JsonParser.parseString(raw).getAsJsonObject(); + } + + private JsonObject nurseRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"providerServiceMapID\":9,\"vanID\":7,\"sessionID\":1," + + "\"benFlowID\":5,\"createdBy\":\"nurse1\"," + + "\"visitDetails\":{" + + " \"visitDetails\":{\"beneficiaryRegID\":11,\"visitReason\":\"New Chief Complaint\"," + + " \"visitCategory\":\"COVID-19 Screening\"}," + + " \"covidDetails\":{\"symptoms\":[\"Fever\",\"Cough\"],\"contactStatus\":[\"Yes\"]," + + " \"suspectedStatusUI\":\"Suspected\"}" + + "}," + + "\"historyDetails\":" + FULL_HISTORY + ",\"vitalDetails\":{\"height_cm\":170}}"); + } + + private CommonUtilityClass utility() { + CommonUtilityClass utility = new CommonUtilityClass(); + utility.setBeneficiaryRegID(BEN_REG_ID); + utility.setVanID(7); + utility.setSessionID(1); + utility.setProviderServiceMapID(9); + utility.setCreatedBy("nurse1"); + return utility; + } + + private TeleconsultationRequestOBJ teleconsultationRequest(boolean walkIn) { + TeleconsultationRequestOBJ request = new TeleconsultationRequestOBJ(); + request.setWalkIn(walkIn); + request.setUserID(42); + request.setSpecializationID(2); + request.setTmRequestID(8L); + request.setAllocationDate(new Timestamp(1_700_000_000_000L)); + return request; + } + + private Covid19BenFeedback storedFeedback() { + Covid19BenFeedback stored = new Covid19BenFeedback(); + stored.setcOVID19ID(5L); + return stored; + } + + @BeforeEach + @DisplayName("Wire the collaborators that every save path needs") + void setUp() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(0); + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(VISIT_ID); + when(commonNurseServiceImpl.generateVisitCode(anyLong(), any(), any())).thenReturn(VISIT_CODE); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalAnthropometryDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPastHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenComorbidConditions(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMedicationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePersonalHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveAllergyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenFamilyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMenstrualHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveFemaleObstetricHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveImmunizationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildOptionalVaccineDetail(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildDevelopmentHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildFeedingHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePerinatalHistory(any())).thenReturn(1L); + when(covid19BenFeedbackRepo.save(any())).thenReturn(storedFeedback()); + when(commonBenStatusFlowServiceImpl.updateBenFlowNurseAfterNurseActivity(any(), anyLong(), anyLong(), + anyString(), anyString(), anyShort(), anyShort(), anyShort(), anyShort(), anyShort(), anyLong(), any(), + anyShort(), any(), any())).thenReturn(1); + } + + @Nested + @DisplayName("saveCovid19NurseData") + class SaveNurseDataTests { + + @Test + @DisplayName("saveCovid19NurseData should save the visit, screening feedback, history and vitals") + void saveNurseData_shouldSaveEverySection() throws Exception { + String result = service.saveCovid19NurseData(nurseRequest(), AUTHORIZATION); + + assertTrue(result.contains("Data saved successfully")); + assertTrue(result.contains("\"visitCode\":\"22\"")); + verify(covid19BenFeedbackRepo).save(any()); + } + + @Test + @DisplayName("saveCovid19NurseData should save the visit when no screening feedback was captured") + void saveNurseData_shouldSaveWithoutScreeningFeedback() throws Exception { + JsonObject request = nurseRequest(); + request.getAsJsonObject("visitDetails").remove("covidDetails"); + + assertTrue(service.saveCovid19NurseData(request, AUTHORIZATION).contains("Data saved successfully")); + verify(covid19BenFeedbackRepo, never()).save(any()); + } + + @Test + @DisplayName("saveCovid19NurseData should report an already saved visit") + void saveNurseData_shouldReportAlreadySavedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.saveCovid19NurseData(nurseRequest(), AUTHORIZATION).contains("Data already saved")); + } + + @Test + @DisplayName("saveCovid19NurseData should reject a request without visit details") + void saveNurseData_shouldRejectRequestWithoutVisitDetails() { + assertThrows(Exception.class, () -> service.saveCovid19NurseData(json("{}"), AUTHORIZATION)); + } + + @Test + @DisplayName("saveCovid19NurseData should fail when the visit could not be created") + void saveNurseData_shouldFailWhenVisitNotCreated() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(0L); + + assertThrows(RuntimeException.class, () -> service.saveCovid19NurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveCovid19NurseData should fail when the screening feedback could not be stored") + void saveNurseData_shouldFailWhenFeedbackNotStored() throws Exception { + when(covid19BenFeedbackRepo.save(any())).thenReturn(null); + + assertThrows(RuntimeException.class, () -> service.saveCovid19NurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveCovid19NurseData should notify a scheduled teleconsultation by SMS") + void saveNurseData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.saveCovid19NurseData(nurseRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), + anyString(), anyString(), any(), eq(AUTHORIZATION)); + } + } + + @Nested + @DisplayName("deleteVisitDetails") + class DeleteVisitDetailsTests { + + @Test + @DisplayName("deleteVisitDetails should remove the visit and the screening feedback") + void deleteVisitDetails_shouldRemoveVisitRows() throws Exception { + when(benVisitDetailRepo.getVisitCode(BEN_REG_ID, 9)).thenReturn(VISIT_CODE); + + service.deleteVisitDetails(nurseRequest()); + + verify(covid19BenFeedbackRepo).deleteVisitDetails(VISIT_CODE); + verify(benVisitDetailRepo).deleteVisitDetails(VISIT_CODE); + } + + @Test + @DisplayName("deleteVisitDetails should do nothing for a request without visit details") + void deleteVisitDetails_shouldDoNothingWithoutVisitDetails() throws Exception { + service.deleteVisitDetails(json("{}")); + + verify(benVisitDetailRepo, never()).deleteVisitDetails(anyLong()); + } + } + + @Nested + @DisplayName("nurse section saves") + class SectionSaveTests { + + @Test + @DisplayName("saveBenVisitDetails should return the new visit id and code") + void saveBenVisitDetails_shouldReturnVisitIdAndCode() throws Exception { + Map result = service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), + utility()); + + assertEquals(VISIT_ID, result.get("visitID")); + assertEquals(VISIT_CODE, result.get("visitCode")); + } + + @Test + @DisplayName("saveBenVisitDetails should return nothing when the visit was already recorded") + void saveBenVisitDetails_shouldReturnNothingForAlreadyRecordedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), utility()) + .isEmpty()); + } + + @Test + @DisplayName("saveBenCovid19HistoryDetails should store every captured history section") + void saveHistory_shouldStoreCapturedSections() throws Exception { + assertEquals(1L, service.saveBenCovid19HistoryDetails(json(FULL_HISTORY), VISIT_ID, VISIT_CODE)); + verify(commonNurseServiceImpl).saveBenPastHistory(any()); + } + + @Test + @DisplayName("saveBenCovid19VitalDetails should store the anthropometry and the physical vitals") + void saveVitals_shouldStoreAnthropometryAndPhysicalVitals() throws Exception { + assertEquals(1L, service.saveBenCovid19VitalDetails(json("{\"height_cm\":170}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenCovid19VitalDetails should report a failure when the physical vitals were not stored") + void saveVitals_shouldReportFailureWhenPhysicalVitalsNotStored() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(null); + + assertNull(service.saveBenCovid19VitalDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveCovidDetails should flatten the symptom, contact and travel lists before storing") + void saveCovidDetails_shouldFlattenLists() { + Covid19BenFeedback feedback = new Covid19BenFeedback(); + feedback.setSuspectedStatusUI("Suspected"); + feedback.setSymptoms(new String[] { "Fever", "Cough" }); + feedback.setContactStatus(new String[] { "Yes", "No" }); + + assertEquals(1, service.saveCovidDetails(feedback)); + assertEquals("Fever||Cough", feedback.getSymptoms_db()); + assertEquals("Yes||No", feedback.getcOVID19_contact_history()); + } + + @Test + @DisplayName("saveCovidDetails should store a feedback without any list") + void saveCovidDetails_shouldStoreFeedbackWithoutLists() { + Covid19BenFeedback feedback = new Covid19BenFeedback(); + feedback.setSuspectedStatusUI("Not Suspected"); + + assertEquals(1, service.saveCovidDetails(feedback)); + } + + @Test + @DisplayName("saveCovidDetails should report a failure when nothing was stored") + void saveCovidDetails_shouldReportFailureWhenNothingStored() { + when(covid19BenFeedbackRepo.save(any())).thenReturn(null); + Covid19BenFeedback feedback = new Covid19BenFeedback(); + feedback.setSuspectedStatusUI("Not Suspected"); + + assertNull(service.saveCovidDetails(feedback)); + } + } + + @Nested + @DisplayName("read endpoints") + class ReadTests { + + @Test + @DisplayName("getBenVisitDetailsFrmNurseCovid19 should assemble the visit and the screening feedback") + void getVisitDetails_shouldAssembleVisitSections() { + when(covid19BenFeedbackRepo.findByBeneficiaryRegIDAndVisitCode(BEN_REG_ID, VISIT_CODE)) + .thenReturn(storedFeedback()); + + String result = service.getBenVisitDetailsFrmNurseCovid19(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("covid19NurseVisitDetail")); + assertTrue(result.contains("covidDetails")); + } + + @Test + @DisplayName("getBenCovid19HistoryDetails should assemble every stored history section") + void getHistoryDetails_shouldAssembleStoredSections() { + when(commonNurseServiceImpl.getPastHistoryData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.BenMedHistory()); + + assertTrue(service.getBenCovid19HistoryDetails(BEN_REG_ID, VISIT_CODE).contains("PastHistory")); + } + + @Test + @DisplayName("getBeneficiaryVitalDetails should assemble the anthropometry and the physical vitals") + void getVitalDetails_shouldAssembleVitalSections() { + when(commonNurseServiceImpl.getBeneficiaryPhysicalAnthropometryDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn("{}"); + when(commonNurseServiceImpl.getBeneficiaryPhysicalVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + + assertTrue(service.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE).contains("benAnthropometryDetail")); + } + + @Test + @DisplayName("getBenCovidNurseData should assemble the nurse captured sections") + void getNurseData_shouldAssembleNurseSections() { + assertTrue(service.getBenCovidNurseData(BEN_REG_ID, VISIT_CODE).contains("covidDetails")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorCovid19 should assemble the whole doctor case record") + void getCaseRecord_shouldAssembleDoctorCaseRecord() throws Exception { + when(commonNurseServiceImpl.getGraphicalTrendData(anyLong(), anyString())).thenReturn(new HashMap<>()); + + String result = service.getBenCaseRecordFromDoctorCovid19(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("findings")); + assertTrue(result.contains("LabReport")); + } + } + + @Nested + @DisplayName("saveDoctorData") + class SaveDoctorDataTests { + + private JsonObject doctorRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22,\"providerServiceMapID\":9," + + "\"createdBy\":\"doctor1\",\"doctorSignatureFlag\":true,\"findings\":{}," + + "\"diagnosis\":{\"specialistDiagnosis\":\"Covid suspect\",\"doctorDiagnosis\":\"Covid\"}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + } + + @BeforeEach + void stubDoctorCollaborators() throws Exception { + when(commonDoctorServiceImpl.saveDocFindings(any())).thenReturn(1); + when(commonNurseServiceImpl.savePrescriptionCovid(any(), any(), any(), any(), any(), any(), any(), any(), + any(), any())).thenReturn(4L); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.saveBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + } + + @Test + @DisplayName("saveDoctorData should save the findings, prescription, tests, drugs and referral") + void saveDoctorData_shouldSaveWholeCaseRecord() throws Exception { + assertEquals(1L, service.saveDoctorData(doctorRequest(), AUTHORIZATION)); + + verify(commonDoctorServiceImpl).saveDocFindings(any()); + verify(commonNurseServiceImpl).saveBenInvestigation(any()); + } + + @Test + @DisplayName("saveDoctorData should succeed for a case record with only an investigation section") + void saveDoctorData_shouldSucceedForMinimalCaseRecord() throws Exception { + assertEquals(1L, + service.saveDoctorData(json("{\"beneficiaryRegID\":11,\"investigation\":{}}"), AUTHORIZATION)); + } + + @Test + @DisplayName("saveDoctorData should return nothing for a null request") + void saveDoctorData_shouldReturnNothingForNullRequest() throws Exception { + assertNull(service.saveDoctorData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("saveDoctorData should fail when the beneficiary flow could not be advanced") + void saveDoctorData_shouldFailWhenFlowNotAdvanced() throws Exception { + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(0); + + assertThrows(RuntimeException.class, () -> service.saveDoctorData(doctorRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveDoctorData should notify a scheduled teleconsultation by SMS") + void saveDoctorData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.saveDoctorData(doctorRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), any(), + anyString(), any(), eq(AUTHORIZATION)); + } + } + + @Nested + @DisplayName("update endpoints") + class UpdateTests { + + @Test + @DisplayName("updateBenHistoryDetails should succeed when no history section was captured") + void updateHistory_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1, service.updateBenHistoryDetails(json("{}"))); + } + + @Test + @DisplayName("updateBenVitalDetails should confirm the update when both sections changed") + void updateVitals_shouldConfirmUpdate() throws Exception { + when(commonNurseServiceImpl.updateANCAnthropometryDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.updateANCPhysicalVitalDetails(any())).thenReturn(1); + + assertEquals(1, service.updateBenVitalDetails(json("{\"height_cm\":170}"))); + } + + @Test + @DisplayName("updateBenVitalDetails should report no change when a section did not change") + void updateVitals_shouldReportNoChangeWhenSectionUnchanged() throws Exception { + when(commonNurseServiceImpl.updateANCAnthropometryDetails(any())).thenReturn(0); + + assertEquals(0, service.updateBenVitalDetails(json("{}"))); + } + + @Test + @DisplayName("updateCovid19DoctorData should return nothing for a null request") + void updateDoctorData_shouldReturnNothingForNullRequest() throws Exception { + assertNull(service.updateCovid19DoctorData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("updateCovid19DoctorData should update the whole case record") + void updateDoctorData_shouldUpdateWholeCaseRecord() throws Exception { + when(commonDoctorServiceImpl.updateDocFindings(any())).thenReturn(1); + when(commonNurseServiceImpl.updatePrescription(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.updateBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataUpdate(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + + JsonObject request = json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22," + + "\"isSpecialist\":false,\"findings\":{},\"diagnosis\":{\"prescriptionID\":4}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + + assertEquals(1L, service.updateCovid19DoctorData(request, AUTHORIZATION)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("COVID-19 captured-section updates") + class CapturedSectionUpdateTests { + + @Test + @DisplayName("updateBenHistoryDetails should update every captured history section") + void updateBenHistoryDetails_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertDoesNotThrow(() -> service.updateBenHistoryDetails( + com.google.gson.JsonParser.parseString("{\"pastHistory\":{},\"comorbidConditions\":{},\"medicationHistory\":{},\"personalHistory\":{},\"familyHistory\":{},\"menstrualHistory\":{},\"femaleObstetricHistory\":{},\"immunizationHistory\":{},\"childVaccineDetails\":{},\"developmentHistory\":{},\"feedingHistory\":{},\"perinatalHistroy\":{},\"allergyHistory\":{}}").getAsJsonObject())); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/dataSyncActivity/DownloadDataFromServerImplTest.java b/src/test/java/com/iemr/tm/service/dataSyncActivity/DownloadDataFromServerImplTest.java new file mode 100644 index 00000000..ee516c1a --- /dev/null +++ b/src/test/java/com/iemr/tm/service/dataSyncActivity/DownloadDataFromServerImplTest.java @@ -0,0 +1,71 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.dataSyncActivity; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.syncActivity_syncLayer.SyncDownloadMasterRepo; +import com.iemr.tm.repo.syncActivity_syncLayer.TempVanRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("DownloadDataFromServerImpl Test Suite") +class DownloadDataFromServerImplTest { + + @Mock + private SyncDownloadMasterRepo syncDownloadMasterRepo; + @Mock + private DataSyncRepository dataSyncRepository; + @Mock + private TempVanRepo tempVanRepo; + + @InjectMocks + private DownloadDataFromServerImpl service; + + @Test + @DisplayName("downloadMasterDataFromServer should answer for a well formed request") + void downloadMasterDataFromServer_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.downloadMasterDataFromServer("{}", 9, 9)); + } + + @Test + @DisplayName("getVanDetailsForMasterDownload should reject a request it cannot act on") + void getVanDetailsForMasterDownload_shouldRejectRequestItCannotActOn() { + assertThrows(Exception.class, () -> service.getVanDetailsForMasterDownload()); + } + + @Test + @DisplayName("getDownloadStatus should reject a request it cannot act on") + void getDownloadStatus_shouldRejectRequestItCannotActOn() { + assertThrows(ArithmeticException.class, () -> service.getDownloadStatus()); + } +} diff --git a/src/test/java/com/iemr/tm/service/dataSyncActivity/UploadDataToServerImplTest.java b/src/test/java/com/iemr/tm/service/dataSyncActivity/UploadDataToServerImplTest.java new file mode 100644 index 00000000..5397ea07 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/dataSyncActivity/UploadDataToServerImplTest.java @@ -0,0 +1,80 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.dataSyncActivity; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.syncActivity_syncLayer.DataSyncGroupsRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("UploadDataToServerImpl Test Suite") +class UploadDataToServerImplTest { + + @Mock + private DataSyncRepository dataSyncRepository; + @Mock + private DataSyncGroupsRepo dataSyncGroupsRepo; + + @InjectMocks + private UploadDataToServerImpl service; + + @Test + @DisplayName("getDataToSyncToServer should answer for a well formed request") + void getDataToSyncToServer_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getDataToSyncToServer(9, "{}", "{}")); + } + + @Test + @DisplayName("syncIntercepter should answer for a well formed request") + void syncIntercepter_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.syncIntercepter(9, "{}", "{}")); + } + + @Test + @DisplayName("syncDataToServer should reject a request it cannot act on") + void syncDataToServer_shouldRejectRequestItCannotActOn() { + assertThrows(IllegalArgumentException.class, () -> service.syncDataToServer("{}", "{}", "{}", "{}", new java.util.ArrayList<>(), "user", "{}")); + } + + @Test + @DisplayName("getVanSerialNoListForSyncedData should answer for a well formed request") + void getVanSerialNoListForSyncedData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getVanSerialNoListForSyncedData("{}", new java.util.ArrayList<>())); + } + + @Test + @DisplayName("getDataSyncGroupDetails should answer for a well formed request") + void getDataSyncGroupDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getDataSyncGroupDetails()); + } +} diff --git a/src/test/java/com/iemr/tm/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImplTest.java b/src/test/java/com/iemr/tm/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImplTest.java new file mode 100644 index 00000000..da9258b5 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImplTest.java @@ -0,0 +1,72 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.dataSyncLayerCentral; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + + + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("GetDataFromVanAndSyncToDBImpl Test Suite") +class GetDataFromVanAndSyncToDBImplTest { + + @Mock + private DataSyncRepositoryCentral dataSyncRepositoryCentral; + + @InjectMocks + private GetDataFromVanAndSyncToDBImpl service; + + @Test + @DisplayName("syncDataToServer should reject a request it cannot act on") + void syncDataToServer_shouldRejectRequestItCannotActOn() { + assertThrows(NullPointerException.class, () -> service.syncDataToServer("{}", "{}")); + } + + @Test + @DisplayName("update_M_BeneficiaryRegIdMapping_for_provisioned_benID should answer for a well formed request") + void update_M_BeneficiaryRegIdMapping_for_provisioned_benID_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.update_M_BeneficiaryRegIdMapping_for_provisioned_benID(org.mockito.Mockito.mock(com.iemr.tm.data.syncActivity_syncLayer.SyncUploadDataDigester.class))); + } + + @Test + @DisplayName("getQueryToInsertDataToServerDB should answer for a well formed request") + void getQueryToInsertDataToServerDB_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getQueryToInsertDataToServerDB("{}", "{}", "{}")); + } + + @Test + @DisplayName("getQueryToUpdateDataToServerDB should answer for a well formed request") + void getQueryToUpdateDataToServerDB_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getQueryToUpdateDataToServerDB("{}", "{}", "{}")); + } +} diff --git a/src/test/java/com/iemr/tm/service/foetalmonitor/FoetalMonitorServiceImplTest.java b/src/test/java/com/iemr/tm/service/foetalmonitor/FoetalMonitorServiceImplTest.java new file mode 100644 index 00000000..21071b6a --- /dev/null +++ b/src/test/java/com/iemr/tm/service/foetalmonitor/FoetalMonitorServiceImplTest.java @@ -0,0 +1,156 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.foetalmonitor; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo; +import com.iemr.tm.repo.foetalmonitor.FoetalMonitorDeviceIDRepo; +import com.iemr.tm.repo.foetalmonitor.FoetalMonitorRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("FoetalMonitorServiceImpl Test Suite") +class FoetalMonitorServiceImplTest { + + @Mock + private FoetalMonitorRepo foetalMonitorRepo; + @Mock + private BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private FoetalMonitorDeviceIDRepo foetalMonitorDeviceIDRepo; + + @InjectMocks + private FoetalMonitorServiceImpl service; + + @Test + @DisplayName("updateFoetalMonitorData should reject a request it cannot act on") + void updateFoetalMonitorData_shouldRejectRequestItCannotActOn() { + assertThrows(com.iemr.tm.utils.exception.IEMRException.class, () -> service.updateFoetalMonitorData(new com.iemr.tm.data.foetalmonitor.FoetalMonitor())); + } + + @Test + @DisplayName("readPDFANDGetBase64 should reject a request it cannot act on") + void readPDFANDGetBase64_shouldRejectRequestItCannotActOn() { + assertThrows(IllegalArgumentException.class, () -> service.readPDFANDGetBase64("{}")); + } + + @Test + @DisplayName("sendFoetalMonitorTestDetails should reject a request it cannot act on") + void sendFoetalMonitorTestDetails_shouldRejectRequestItCannotActOn() { + assertThrows(Exception.class, () -> service.sendFoetalMonitorTestDetails(new com.iemr.tm.data.foetalmonitor.FoetalMonitor(), "{}")); + } + + @Test + @DisplayName("getFoetalMonitorDetails should answer for a well formed request") + void getFoetalMonitorDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getFoetalMonitorDetails(11L)); + } + + @org.junit.jupiter.api.Nested + @DisplayName("foetal monitor test results") + class TestResultTests { + + private com.iemr.tm.data.foetalmonitor.FoetalMonitor deviceResult() { + com.iemr.tm.data.foetalmonitor.FoetalMonitor result = new com.iemr.tm.data.foetalmonitor.FoetalMonitor(); + result.setFoetalMonitorID(4L); + result.setAccelerationsList(new java.util.ArrayList<>()); + result.setDecelerationsList(new java.util.ArrayList<>()); + result.setMovementEntries(new java.util.ArrayList<>()); + result.setAutoFetalMovement(new java.util.ArrayList<>()); + java.util.Map mother = new java.util.HashMap<>(); + mother.put("cmMotherId", "M1"); + mother.put("partnerId", "P1"); + mother.put("partnerName", "Partner"); + result.setMother(mother); + return result; + } + + @Test + @DisplayName("updateFoetalMonitorData should reject a result for an unknown device test") + void updateResult_shouldRejectUnknownDeviceTest() { + org.mockito.Mockito.when(foetalMonitorRepo.getFoetalMonitorDetails(4L)).thenReturn(null); + + org.junit.jupiter.api.Assertions.assertThrows(com.iemr.tm.utils.exception.IEMRException.class, + () -> service.updateFoetalMonitorData(deviceResult())); + } + + @Test + @DisplayName("updateFoetalMonitorData should fail when the report file cannot be written") + void updateResult_shouldFailWhenReportCannotBeWritten() throws Exception { + com.iemr.tm.data.foetalmonitor.FoetalMonitor stored = new com.iemr.tm.data.foetalmonitor.FoetalMonitor(); + stored.setFoetalMonitorID(4L); + stored.setBeneficiaryID(7L); + stored.setBeneficiaryRegID(11L); + stored.setVisitCode(22L); + stored.setBenFlowID(5L); + org.mockito.Mockito.when(foetalMonitorRepo.getFoetalMonitorDetails(4L)).thenReturn(stored); + org.mockito.Mockito.when(foetalMonitorRepo.save(org.mockito.ArgumentMatchers.any())).thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertThrows(com.iemr.tm.utils.exception.IEMRException.class, + () -> service.updateFoetalMonitorData(deviceResult())); + } + + @Test + @DisplayName("getFoetalMonitorDetails should render the tests recorded against the flow") + void getDetails_shouldRenderTestsForFlow() throws Exception { + com.iemr.tm.data.foetalmonitor.FoetalMonitor stored = new com.iemr.tm.data.foetalmonitor.FoetalMonitor(); + stored.setFoetalMonitorID(4L); + stored.setBeneficiaryRegID(11L); + stored.setBenFlowID(5L); + stored.setVisitCode(22L); + stored.setResultState(true); + org.mockito.Mockito.when(foetalMonitorRepo.getFoetalMonitorDetailsByFlowId(5L)) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(stored))); + + org.junit.jupiter.api.Assertions.assertTrue( + service.getFoetalMonitorDetails(5L).contains("benFetosenseData")); + } + + @Test + @DisplayName("getFoetalMonitorDetails should render an empty list when the flow has no test") + void getDetails_shouldRenderEmptyListWithoutTests() throws Exception { + org.mockito.Mockito.when(foetalMonitorRepo.getFoetalMonitorDetailsByFlowId(5L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertTrue( + service.getFoetalMonitorDetails(5L).contains("benFetosenseData")); + } + + @Test + @DisplayName("readPDFANDGetBase64 should reject a path that does not exist") + void readReport_shouldRejectMissingPath() { + org.junit.jupiter.api.Assertions.assertThrows(Exception.class, + () -> service.readPDFANDGetBase64("/no/such/report.pdf")); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/generalOPD/GeneralOPDDoctorServiceImplTest.java b/src/test/java/com/iemr/tm/service/generalOPD/GeneralOPDDoctorServiceImplTest.java new file mode 100644 index 00000000..73370ad5 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/generalOPD/GeneralOPDDoctorServiceImplTest.java @@ -0,0 +1,53 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.generalOPD; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.quickConsultation.PrescriptionDetailRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("GeneralOPDDoctorServiceImpl Test Suite") +class GeneralOPDDoctorServiceImplTest { + + @Mock + private PrescriptionDetailRepo prescriptionDetailRepo; + + @InjectMocks + private GeneralOPDDoctorServiceImpl service; + + @Test + @DisplayName("getGeneralOPDDiagnosisDetails should answer for a well formed request") + void getGeneralOPDDiagnosisDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getGeneralOPDDiagnosisDetails(11L, 11L)); + } +} diff --git a/src/test/java/com/iemr/tm/service/generalOPD/GeneralOPDServiceImplTest.java b/src/test/java/com/iemr/tm/service/generalOPD/GeneralOPDServiceImplTest.java new file mode 100644 index 00000000..1acf83d6 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/generalOPD/GeneralOPDServiceImplTest.java @@ -0,0 +1,627 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.generalOPD; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyShort; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.iemr.tm.data.nurse.CommonUtilityClass; +import com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.nurse.anc.BenAdherenceRepo; +import com.iemr.tm.repo.quickConsultation.BenChiefComplaintRepo; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.service.labtechnician.LabTechnicianServiceImpl; +import com.iemr.tm.service.tele_consultation.SMSGatewayServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("GeneralOPDServiceImpl Test Suite") +class GeneralOPDServiceImplTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_ID = 3L; + private static final Long VISIT_CODE = 22L; + + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private GeneralOPDDoctorServiceImpl generalOPDDoctorServiceImpl; + @Mock + private LabTechnicianServiceImpl labTechnicianServiceImpl; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private BenChiefComplaintRepo benChiefComplaintRepo; + @Mock + private BenAdherenceRepo benAdherenceRepo; + + @InjectMocks + private GeneralOPDServiceImpl service; + + private JsonObject json(String raw) { + return JsonParser.parseString(raw).getAsJsonObject(); + } + + private JsonObject nurseRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"providerServiceMapID\":9,\"vanID\":7,\"sessionID\":1," + + "\"benFlowID\":5,\"createdBy\":\"nurse1\"," + + "\"visitDetails\":{" + + " \"visitDetails\":{\"beneficiaryRegID\":11,\"visitReason\":\"New Chief Complaint\"," + + " \"visitCategory\":\"General OPD\"}," + + " \"chiefComplaints\":[{\"chiefComplaintID\":3}]" + + "}," + + "\"historyDetails\":{},\"vitalDetails\":{\"height_cm\":170}," + + "\"examinationDetails\":{\"generalExamination\":{}}" + "}"); + } + + private CommonUtilityClass utility() { + CommonUtilityClass utility = new CommonUtilityClass(); + utility.setBeneficiaryRegID(BEN_REG_ID); + utility.setVanID(7); + utility.setSessionID(1); + utility.setProviderServiceMapID(9); + utility.setCreatedBy("nurse1"); + return utility; + } + + private TeleconsultationRequestOBJ teleconsultationRequest(boolean walkIn) { + TeleconsultationRequestOBJ request = new TeleconsultationRequestOBJ(); + request.setWalkIn(walkIn); + request.setUserID(42); + request.setSpecializationID(2); + request.setTmRequestID(8L); + request.setAllocationDate(new Timestamp(1_700_000_000_000L)); + return request; + } + + @BeforeEach + @DisplayName("Wire the collaborators that every save path needs") + void setUp() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(0); + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(VISIT_ID); + when(commonNurseServiceImpl.generateVisitCode(anyLong(), any(), any())).thenReturn(VISIT_CODE); + when(commonNurseServiceImpl.saveBenChiefComplaints(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalAnthropometryDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePhyGeneralExamination(any())).thenReturn(1L); + when(commonBenStatusFlowServiceImpl.updateBenFlowNurseAfterNurseActivity(any(), anyLong(), anyLong(), + anyString(), anyString(), anyShort(), anyShort(), anyShort(), anyShort(), anyShort(), anyLong(), any(), + anyShort(), any(), any())).thenReturn(1); + } + + @Nested + @DisplayName("saveNurseData") + class SaveNurseDataTests { + + @Test + @DisplayName("saveNurseData should save the visit, history, vitals and examination") + void saveNurseData_shouldSaveEverySection() throws Exception { + String result = service.saveNurseData(nurseRequest(), AUTHORIZATION); + + assertTrue(result.contains("Data saved successfully")); + assertTrue(result.contains("\"visitCode\":\"22\"")); + verify(commonNurseServiceImpl).saveBenChiefComplaints(any()); + } + + @Test + @DisplayName("saveNurseData should report an already saved visit without touching the history") + void saveNurseData_shouldReportAlreadySavedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.saveNurseData(nurseRequest(), AUTHORIZATION).contains("Data already saved")); + } + + @Test + @DisplayName("saveNurseData should reject a request without visit details") + void saveNurseData_shouldRejectRequestWithoutVisitDetails() { + assertThrows(Exception.class, () -> service.saveNurseData(json("{}"), AUTHORIZATION)); + } + + @Test + @DisplayName("saveNurseData should reject a null request") + void saveNurseData_shouldRejectNullRequest() { + assertThrows(Exception.class, () -> service.saveNurseData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("saveNurseData should fail when the visit could not be created") + void saveNurseData_shouldFailWhenVisitNotCreated() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(0L); + + assertThrows(RuntimeException.class, () -> service.saveNurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveNurseData should fail when a section was not captured") + void saveNurseData_shouldFailWhenSectionNotCaptured() throws Exception { + JsonObject request = nurseRequest(); + request.remove("examinationDetails"); + + assertThrows(RuntimeException.class, () -> service.saveNurseData(request, AUTHORIZATION)); + } + + @Test + @DisplayName("saveNurseData should notify a scheduled teleconsultation by SMS") + void saveNurseData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.saveNurseData(nurseRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), + anyString(), anyString(), any(), eq(AUTHORIZATION)); + } + + @Test + @DisplayName("saveNurseData should not notify a walk-in teleconsultation") + void saveNurseData_shouldNotNotifyWalkInTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())).thenReturn(teleconsultationRequest(true)); + + service.saveNurseData(nurseRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl, never()).smsSenderGateway(anyString(), anyLong(), anyInt(), anyLong(), any(), + anyString(), anyString(), any(), anyString()); + } + } + + @Nested + @DisplayName("deleteVisitDetails") + class DeleteVisitDetailsTests { + + @Test + @DisplayName("deleteVisitDetails should remove the visit rows for a created visit") + void deleteVisitDetails_shouldRemoveVisitRows() throws Exception { + when(benVisitDetailRepo.getVisitCode(BEN_REG_ID, 9)).thenReturn(VISIT_CODE); + + service.deleteVisitDetails(nurseRequest()); + + verify(benVisitDetailRepo).deleteVisitDetails(VISIT_CODE); + } + + @Test + @DisplayName("deleteVisitDetails should do nothing for a request without visit details") + void deleteVisitDetails_shouldDoNothingWithoutVisitDetails() throws Exception { + service.deleteVisitDetails(json("{}")); + + verify(benVisitDetailRepo, never()).deleteVisitDetails(anyLong()); + } + } + + @Nested + @DisplayName("nurse section saves") + class SectionSaveTests { + + @Test + @DisplayName("saveBenVisitDetails should return the new visit id and code") + void saveBenVisitDetails_shouldReturnVisitIdAndCode() throws Exception { + Map result = service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), + utility()); + + assertEquals(VISIT_ID, result.get("visitID")); + assertEquals(VISIT_CODE, result.get("visitCode")); + } + + @Test + @DisplayName("saveBenVisitDetails should return nothing when the visit was already recorded") + void saveBenVisitDetails_shouldReturnNothingForAlreadyRecordedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), utility()) + .isEmpty()); + } + + @Test + @DisplayName("saveBenVisitDetails should return nothing for a payload without visit details") + void saveBenVisitDetails_shouldReturnNothingWithoutVisitDetails() throws Exception { + assertTrue(service.saveBenVisitDetails(json("{}"), utility()).isEmpty()); + } + + @Test + @DisplayName("saveBenGeneralOPDHistoryDetails should succeed when no history section was captured") + void saveHistory_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1L, service.saveBenGeneralOPDHistoryDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenGeneralOPDHistoryDetails should store every captured history section") + void saveHistory_shouldStoreCapturedSections() throws Exception { + when(commonNurseServiceImpl.saveBenPastHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenComorbidConditions(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMedicationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveFemaleObstetricHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMenstrualHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenFamilyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePersonalHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveAllergyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildOptionalVaccineDetail(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveImmunizationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildDevelopmentHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildFeedingHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePerinatalHistory(any())).thenReturn(1L); + + JsonObject history = json("{\"pastHistory\":{},\"comorbidConditions\":{}," + + "\"medicationHistory\":{\"medicationHistoryList\":[{\"currentMedication\":\"Metformin\"}]}," + + "\"femaleObstetricHistory\":{},\"menstrualHistory\":{},\"familyHistory\":{}," + + "\"personalHistory\":{},\"childVaccineDetails\":{},\"immunizationHistory\":{}," + + "\"developmentHistory\":{},\"feedingHistory\":{},\"perinatalHistroy\":{}}"); + + assertEquals(1L, service.saveBenGeneralOPDHistoryDetails(history, VISIT_ID, VISIT_CODE)); + verify(commonNurseServiceImpl).saveBenPastHistory(any()); + } + + @Test + @DisplayName("saveBenGeneralOPDHistoryDetails should report a failure when a section could not be stored") + void saveHistory_shouldReportFailureWhenSectionNotStored() throws Exception { + when(commonNurseServiceImpl.saveBenPastHistory(any())).thenReturn(null); + + assertNull(service.saveBenGeneralOPDHistoryDetails(json("{\"pastHistory\":{}}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenVitalDetails should store the anthropometry and the physical vitals") + void saveVitals_shouldStoreAnthropometryAndPhysicalVitals() throws Exception { + assertEquals(1L, service.saveBenVitalDetails(json("{\"height_cm\":170}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenVitalDetails should report a failure when the physical vitals were not stored") + void saveVitals_shouldReportFailureWhenPhysicalVitalsNotStored() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(null); + + assertNull(service.saveBenVitalDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenVitalDetails should succeed for a null payload") + void saveVitals_shouldSucceedForNullPayload() throws Exception { + assertEquals(1L, service.saveBenVitalDetails(null, VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenExaminationDetails should succeed when no examination section was captured") + void saveExamination_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1L, service.saveBenExaminationDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenExaminationDetails should store every captured examination section") + void saveExamination_shouldStoreCapturedSections() throws Exception { + when(commonNurseServiceImpl.savePhyHeadToToeExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysGastrointestinalExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysCardiovascularExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysRespiratoryExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysCentralNervousExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysMusculoskeletalSystemExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysGenitourinarySystemExamination(any())).thenReturn(1L); + + JsonObject examination = json("{\"generalExamination\":{},\"headToToeExamination\":{}," + + "\"gastroIntestinalExamination\":{},\"cardioVascularExamination\":{}," + + "\"respiratorySystemExamination\":{},\"centralNervousSystemExamination\":{}," + + "\"musculoskeletalSystemExamination\":{},\"genitoUrinarySystemExamination\":{}}"); + + assertEquals(1L, service.saveBenExaminationDetails(examination, VISIT_ID, VISIT_CODE)); + verify(commonNurseServiceImpl).savePhyHeadToToeExamination(any()); + } + + @Test + @DisplayName("saveBenExaminationDetails should report a failure when a section could not be stored") + void saveExamination_shouldReportFailureWhenSectionNotStored() throws Exception { + when(commonNurseServiceImpl.savePhyGeneralExamination(any())).thenReturn(null); + + assertNull(service.saveBenExaminationDetails(json("{\"generalExamination\":{}}"), VISIT_ID, VISIT_CODE)); + } + } + + @Nested + @DisplayName("read endpoints") + class ReadTests { + + @Test + @DisplayName("getBenVisitDetailsFrmNurseGOPD should assemble the visit and the chief complaints") + void getVisitDetails_shouldAssembleVisitSections() { + when(commonNurseServiceImpl.getBenChiefComplaints(BEN_REG_ID, VISIT_CODE)).thenReturn("[]"); + + String result = service.getBenVisitDetailsFrmNurseGOPD(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("GOPDNurseVisitDetail")); + assertTrue(result.contains("BenChiefComplaints")); + } + + @Test + @DisplayName("getBenHistoryDetails should assemble every stored history section") + void getHistoryDetails_shouldAssembleStoredSections() { + when(commonNurseServiceImpl.getPastHistoryData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.BenMedHistory()); + + assertTrue(service.getBenHistoryDetails(BEN_REG_ID, VISIT_CODE).contains("PastHistory")); + } + + @Test + @DisplayName("getBeneficiaryVitalDetails should assemble the anthropometry and the physical vitals") + void getVitalDetails_shouldAssembleVitalSections() { + when(commonNurseServiceImpl.getBeneficiaryPhysicalAnthropometryDetails(BEN_REG_ID, VISIT_ID)) + .thenReturn("{}"); + when(commonNurseServiceImpl.getBeneficiaryPhysicalVitalDetails(BEN_REG_ID, VISIT_ID)).thenReturn("{}"); + + String result = service.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_ID); + + assertTrue(result.contains("benAnthropometryDetail")); + assertTrue(result.contains("benPhysicalVitalDetail")); + } + + @Test + @DisplayName("getExaminationDetailsData should assemble every stored examination section") + void getExaminationDetails_shouldAssembleStoredExaminationSections() { + when(commonNurseServiceImpl.getGeneralExaminationData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.PhyGeneralExamination()); + when(commonNurseServiceImpl.getGenitourinaryExamination(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.SysGenitourinarySystemExamination()); + + String result = service.getExaminationDetailsData(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("generalExamination")); + assertTrue(result.contains("genitourinaryExamination")); + } + + @Test + @DisplayName("getBenGeneralOPDNurseData should assemble the nurse captured sections") + void getNurseData_shouldAssembleNurseSections() { + assertTrue(service.getBenGeneralOPDNurseData(BEN_REG_ID, VISIT_CODE).contains("history")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorGeneralOPD should assemble the whole doctor case record") + void getCaseRecord_shouldAssembleDoctorCaseRecord() throws Exception { + when(commonNurseServiceImpl.getGraphicalTrendData(BEN_REG_ID, "genOPD")).thenReturn(new HashMap<>()); + + String result = service.getBenCaseRecordFromDoctorGeneralOPD(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("findings")); + assertTrue(result.contains("LabReport")); + assertTrue(result.contains("GraphData")); + } + } + + @Nested + @DisplayName("saveDoctorData") + class SaveDoctorDataTests { + + private JsonObject doctorRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22,\"providerServiceMapID\":9," + + "\"createdBy\":\"doctor1\",\"doctorSignatureFlag\":true,\"findings\":{}," + + "\"diagnosis\":{\"provisionalDiagnosisList\":[{\"term\":\"Fever\",\"conceptID\":\"1\"}]}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + } + + @BeforeEach + void stubDoctorCollaborators() throws Exception { + when(commonDoctorServiceImpl.saveDocFindings(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenPrescription(any())).thenReturn(4L); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.saveBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + } + + @Test + @DisplayName("saveDoctorData should save the findings, prescription, tests, drugs and referral") + void saveDoctorData_shouldSaveWholeCaseRecord() throws Exception { + assertEquals(1L, service.saveDoctorData(doctorRequest(), AUTHORIZATION)); + + verify(commonDoctorServiceImpl).saveDocFindings(any()); + verify(commonNurseServiceImpl).saveBenInvestigation(any()); + verify(commonNurseServiceImpl).saveBenPrescribedDrugsList(any()); + verify(commonDoctorServiceImpl).saveBenReferDetails(any()); + } + + @Test + @DisplayName("saveDoctorData should succeed for a case record with only an investigation section") + void saveDoctorData_shouldSucceedForMinimalCaseRecord() throws Exception { + assertEquals(1L, + service.saveDoctorData(json("{\"beneficiaryRegID\":11,\"investigation\":{}}"), AUTHORIZATION)); + + verify(commonDoctorServiceImpl, never()).saveDocFindings(any()); + } + + @Test + @DisplayName("saveDoctorData should return nothing for a null request") + void saveDoctorData_shouldReturnNothingForNullRequest() throws Exception { + assertNull(service.saveDoctorData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("saveDoctorData should fail when the beneficiary flow could not be advanced") + void saveDoctorData_shouldFailWhenFlowNotAdvanced() throws Exception { + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(0); + + assertThrows(RuntimeException.class, () -> service.saveDoctorData(doctorRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveDoctorData should notify a scheduled teleconsultation by SMS") + void saveDoctorData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.saveDoctorData(doctorRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), any(), + anyString(), any(), eq(AUTHORIZATION)); + } + } + + @Nested + @DisplayName("update endpoints") + class UpdateTests { + + @Test + @DisplayName("UpdateVisitDetails should update the chief complaints") + void updateVisitDetails_shouldUpdateChiefComplaints() throws Exception { + when(commonNurseServiceImpl.updateBenChiefComplaints(any())).thenReturn(1); + + assertEquals(1, service.UpdateVisitDetails( + json("{\"visitDetails\":{},\"chiefComplaints\":[{\"chiefComplaintID\":3}]}"))); + } + + @Test + @DisplayName("UpdateVisitDetails should report no change for a payload without chief complaints") + void updateVisitDetails_shouldReportNoChangeWithoutChiefComplaints() throws Exception { + assertEquals(0, service.UpdateVisitDetails(json("{\"visitDetails\":{}}"))); + } + + @Test + @DisplayName("UpdateVisitDetails should report no change for a payload without visit details") + void updateVisitDetails_shouldReportNoChangeWithoutVisitDetails() throws Exception { + assertEquals(0, service.UpdateVisitDetails(json("{}"))); + } + + @Test + @DisplayName("updateBenHistoryDetails should succeed when no history section was captured") + void updateHistory_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1, service.updateBenHistoryDetails(json("{}"))); + } + + @Test + @DisplayName("updateBenVitalDetails should confirm the update when both sections changed") + void updateVitals_shouldConfirmUpdate() throws Exception { + when(commonNurseServiceImpl.updateANCAnthropometryDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.updateANCPhysicalVitalDetails(any())).thenReturn(1); + + assertEquals(1, service.updateBenVitalDetails(json("{\"height_cm\":170}"))); + } + + @Test + @DisplayName("updateBenVitalDetails should report no change when a section did not change") + void updateVitals_shouldReportNoChangeWhenSectionUnchanged() throws Exception { + when(commonNurseServiceImpl.updateANCAnthropometryDetails(any())).thenReturn(0); + + assertEquals(0, service.updateBenVitalDetails(json("{}"))); + } + + @Test + @DisplayName("updateBenExaminationDetails should succeed when no examination section was captured") + void updateExamination_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1, service.updateBenExaminationDetails(json("{}"))); + } + + @Test + @DisplayName("updateGeneralOPDDoctorData should return nothing for a null request") + void updateDoctorData_shouldReturnNothingForNullRequest() throws Exception { + assertNull(service.updateGeneralOPDDoctorData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("updateGeneralOPDDoctorData should update the whole case record") + void updateDoctorData_shouldUpdateWholeCaseRecord() throws Exception { + when(commonDoctorServiceImpl.updateDocFindings(any())).thenReturn(1); + when(commonNurseServiceImpl.updatePrescription(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.updateBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataUpdate(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + + JsonObject request = json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22," + + "\"findings\":{},\"diagnosis\":{\"prescriptionID\":4}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + + assertEquals(1L, service.updateGeneralOPDDoctorData(request, AUTHORIZATION)); + } + + @Test + @DisplayName("updateGeneralOPDDoctorData should fail when the beneficiary flow could not be advanced") + void updateDoctorData_shouldFailWhenFlowNotAdvanced() throws Exception { + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataUpdate(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(0); + + assertThrows(RuntimeException.class, () -> service.updateGeneralOPDDoctorData( + json("{\"beneficiaryRegID\":11,\"investigation\":{}}"), AUTHORIZATION)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("general OPD captured-section updates") + class CapturedSectionUpdateTests { + + @Test + @DisplayName("updateBenHistoryDetails should update every captured history section") + void updateBenHistoryDetails_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertDoesNotThrow(() -> service.updateBenHistoryDetails( + com.google.gson.JsonParser.parseString("{\"pastHistory\":{},\"comorbidConditions\":{},\"medicationHistory\":{},\"personalHistory\":{},\"familyHistory\":{},\"menstrualHistory\":{},\"femaleObstetricHistory\":{},\"immunizationHistory\":{},\"childVaccineDetails\":{},\"developmentHistory\":{},\"feedingHistory\":{},\"perinatalHistroy\":{},\"allergyHistory\":{}}").getAsJsonObject())); + } + + @Test + @DisplayName("updateBenExaminationDetails should update every captured examination section") + void updateBenExaminationDetails_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertDoesNotThrow(() -> service.updateBenExaminationDetails( + com.google.gson.JsonParser.parseString("{\"generalExamination\":{},\"headToToeExamination\":{},\"gastroIntestinalExamination\":{},\"cardioVascularExamination\":{},\"respiratorySystemExamination\":{},\"centralNervousSystemExamination\":{},\"musculoskeletalSystemExamination\":{},\"genitoUrinarySystemExamination\":{},\"obstetricExamination\":{}}").getAsJsonObject())); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/health/HealthServiceTest.java b/src/test/java/com/iemr/tm/service/health/HealthServiceTest.java new file mode 100644 index 00000000..d30f0134 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/health/HealthServiceTest.java @@ -0,0 +1,218 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.health; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; + +import javax.sql.DataSource; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("HealthService Test Suite") +class HealthServiceTest { + + @Mock + private DataSource dataSource; + @Mock + private RedisTemplate redisTemplate; + @Mock + private Connection connection; + @Mock + private PreparedStatement statement; + @Mock + private ResultSet resultSet; + + private HealthService service; + + @BeforeEach + @DisplayName("Wire a service over a mocked datasource and Redis template") + void setUp() throws Exception { + when(dataSource.getConnection()).thenReturn(connection); + when(connection.prepareStatement(any(String.class))).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getInt(1)).thenReturn(0); + service = new HealthService(dataSource, redisTemplate); + } + + @AfterEach + @DisplayName("Shut the health check executor down after each test") + void tearDown() { + service.shutdown(); + } + + @SuppressWarnings("unchecked") + private void redisReplies(String reply) { + when(redisTemplate.execute(any(RedisCallback.class))).thenReturn(reply); + } + + @Nested + @DisplayName("checkHealth") + class CheckHealthTests { + + @Test + @DisplayName("checkHealth should report both components up when MySQL and Redis answer") + void checkHealth_shouldReportBothComponentsUp() { + redisReplies("PONG"); + + Map result = service.checkHealth(); + + assertEquals("UP", result.get("status")); + assertNotNull(result.get("timestamp")); + assertNotNull(result.get("components")); + } + + @Test + @DisplayName("checkHealth should report Redis down when the ping is not answered") + void checkHealth_shouldReportRedisDownWhenPingUnanswered() { + redisReplies("NOPE"); + + assertEquals("DOWN", service.checkHealth().get("status")); + } + + @Test + @DisplayName("checkHealth should report Redis down when the ping fails") + void checkHealth_shouldReportRedisDownWhenPingFails() { + when(redisTemplate.execute(any(RedisCallback.class))).thenThrow(new IllegalStateException("redis down")); + + assertEquals("DOWN", service.checkHealth().get("status")); + } + + @Test + @DisplayName("checkHealth should report MySQL down when the connection fails") + void checkHealth_shouldReportMysqlDownWhenConnectionFails() throws Exception { + when(dataSource.getConnection()).thenThrow(new SQLException("connection refused")); + redisReplies("PONG"); + + Map result = service.checkHealth(); + + assertEquals("DOWN", result.get("status")); + } + + @Test + @DisplayName("checkHealth should report MySQL down when the probe query returns no row") + void checkHealth_shouldReportMysqlDownWithoutProbeRow() throws Exception { + when(resultSet.next()).thenReturn(false); + redisReplies("PONG"); + + assertEquals("DOWN", service.checkHealth().get("status")); + } + + @Test + @DisplayName("checkHealth should skip Redis when no Redis template is configured") + void checkHealth_shouldSkipRedisWhenNotConfigured() { + HealthService withoutRedis = new HealthService(dataSource, null); + try { + Map result = withoutRedis.checkHealth(); + + assertEquals("UP", result.get("status")); + } finally { + withoutRedis.shutdown(); + } + } + + @Test + @DisplayName("checkHealth should degrade MySQL when the pool reports lock waits") + void checkHealth_shouldDegradeMysqlOnLockWaits() throws Exception { + when(resultSet.getInt(1)).thenReturn(3); + redisReplies("PONG"); + + assertEquals("DEGRADED", service.checkHealth().get("status")); + } + + @Test + @DisplayName("checkHealth should degrade MySQL when the pool reports slow queries") + void checkHealth_shouldDegradeMysqlOnSlowQueries() throws Exception { + when(resultSet.getInt(1)).thenReturn(0, 5); + redisReplies("PONG"); + + assertNotNull(service.checkHealth().get("status")); + } + + @Test + @DisplayName("checkHealth should still answer when the advanced diagnostics fail") + void checkHealth_shouldStillAnswerWhenDiagnosticsFail() throws Exception { + when(statement.executeQuery()).thenReturn(resultSet).thenThrow(new SQLException("diagnostics failed")); + redisReplies("PONG"); + + assertNotNull(service.checkHealth().get("status")); + } + + @Test + @DisplayName("checkHealth should reuse the throttled diagnostics result on a second call") + void checkHealth_shouldReuseThrottledDiagnostics() { + redisReplies("PONG"); + + service.checkHealth(); + + assertEquals("UP", service.checkHealth().get("status")); + } + } + + @Nested + @DisplayName("shutdown") + class ShutdownTests { + + @Test + @DisplayName("shutdown should stop the executor so later checks report the components down") + void shutdown_shouldStopExecutor() { + service.shutdown(); + + Map result = service.checkHealth(); + + assertEquals("DOWN", result.get("status")); + } + + @Test + @DisplayName("shutdown should be safe to call twice") + void shutdown_shouldBeSafeToCallTwice() { + service.shutdown(); + service.shutdown(); + + assertTrue(true); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/labtechnician/LabTechnicianServiceImplTest.java b/src/test/java/com/iemr/tm/service/labtechnician/LabTechnicianServiceImplTest.java new file mode 100644 index 00000000..fea40a35 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/labtechnician/LabTechnicianServiceImplTest.java @@ -0,0 +1,464 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.labtechnician; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.labModule.ECGAbnormalFindingMasterRepo; +import com.iemr.tm.repo.labModule.LabResultEntryRepo; +import com.iemr.tm.repo.labtechnician.V_benLabTestOrderedDetailsRepo; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("LabTechnicianServiceImpl Test Suite") +class LabTechnicianServiceImplTest { + + @Mock + private V_benLabTestOrderedDetailsRepo v_benLabTestOrderedDetailsRepo; + @Mock + private LabResultEntryRepo labResultEntryRepo; + @Mock + private ECGAbnormalFindingMasterRepo ecgAbnormalFindingMasterRepo; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private com.iemr.tm.repo.nurse.BenVisitDetailRepo benVisitDetailRepo; + @Mock + private com.iemr.tm.repo.login.UserLoginRepo userLoginRepo; + + @InjectMocks + private LabTechnicianServiceImpl service; + + @Test + @DisplayName("getBenePrescribedProcedureDetails should answer for a well formed request") + void getBenePrescribedProcedureDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenePrescribedProcedureDetails(11L, 11L)); + } + + @Test + @DisplayName("getLabResultDataForBen should answer for a well formed request") + void getLabResultDataForBen_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getLabResultDataForBen(11L, 11L)); + } + + @Test + @DisplayName("saveLabTestResult should answer for a well formed request") + void saveLabTestResult_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveLabTestResult(new com.google.gson.JsonObject())); + } + + + @Test + @DisplayName("getLast_3_ArchivedTestVisitList should answer for a well formed request") + void getLast_3_ArchivedTestVisitList_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getLast_3_ArchivedTestVisitList(11L, 11L)); + } + + @Test + @DisplayName("getLabResultForVisitcode should answer for a well formed request") + void getLabResultForVisitcode_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getLabResultForVisitcode(11L, 11L)); + } + + @Test + @DisplayName("getECGAbnormalFindings should answer for a well formed request") + void getECGAbnormalFindings_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getECGAbnormalFindings()); + } + + @org.junit.jupiter.api.Nested + @DisplayName("lab result capture") + class LabResultCaptureTests { + + private static final String LAB_REQUEST = "{\"beneficiaryRegID\":11,\"visitCode\":22,\"benVisitID\":3," + + "\"benFlowID\":5,\"createdBy\":\"lab1\",\"providerServiceMapID\":9,\"labCompleted\":true," + + "\"nurseFlag\":2,\"doctorFlag\":2,\"specialist_flag\":1," + + "\"labTestResults\":[{\"procedureID\":1,\"procedureName\":\"CBC\"," + + " \"compResult\":[{\"testComponentID\":\"2\",\"testResultValue\":\"12\"," + + " \"testResultUnit\":\"g/dL\",\"remarks\":\"normal\"}]}]," + + "\"radiologyTestResults\":[]}"; + + private com.google.gson.JsonObject request() { + return com.google.gson.JsonParser.parseString(LAB_REQUEST).getAsJsonObject(); + } + + @Test + @DisplayName("saveLabTestResult should store the entered results and stamp the lab technician") + void saveLabTestResult_shouldStoreResultsAndStampTechnician() throws Exception { + com.iemr.tm.data.login.Users user = new com.iemr.tm.data.login.Users(); + user.setUserID(42L); + org.mockito.Mockito.when(userLoginRepo.getUserByUsername("lab1")).thenReturn(user); + org.mockito.Mockito.when(labResultEntryRepo.saveAll(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateFlowAfterLabResultEntryForTCSpecialist( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateFlowAfterLabResultEntry( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.saveLabTestResult(request())); + org.mockito.Mockito.verify(benVisitDetailRepo).updateLabTechnicianID(42L, 22L); + } + + @Test + @DisplayName("saveLabTestResult should succeed when the request carries no result to store") + void saveLabTestResult_shouldSucceedWithoutResults() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(1, service.saveLabTestResult( + com.google.gson.JsonParser.parseString("{\"beneficiaryRegID\":11}").getAsJsonObject())); + } + + @Test + @DisplayName("saveLabTestResult should store the entered results for a wrapper payload") + void saveLabTestResult_shouldStoreWrapperResults() throws Exception { + com.iemr.tm.data.labModule.WrapperLabResultEntry wrapper = com.iemr.tm.utils.mapper.InputMapper.gson() + .fromJson(request(), com.iemr.tm.data.labModule.WrapperLabResultEntry.class); + org.mockito.Mockito.when(labResultEntryRepo.saveAll(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + + org.junit.jupiter.api.Assertions.assertNotNull(service.saveLabTestResult(wrapper)); + } + + @Test + @DisplayName("getLabResultDataForBen should expand the stored result components") + void getLabResultDataForBen_shouldExpandStoredComponents() { + com.iemr.tm.data.labModule.LabResultEntry entry = new com.iemr.tm.data.labModule.LabResultEntry(); + entry.setBeneficiaryRegID(11L); + entry.setVisitCode(22L); + com.iemr.tm.data.labModule.ProcedureData procedure = new com.iemr.tm.data.labModule.ProcedureData(); + procedure.setProcedureName("CBC"); + procedure.setProcedureType("Laboratory"); + entry.setProcedureData(procedure); + com.iemr.tm.data.labModule.TestComponentMaster component = + new com.iemr.tm.data.labModule.TestComponentMaster(); + component.setTestComponentName("Haemoglobin"); + entry.setTestComponentMaster(component); + org.mockito.Mockito.when(labResultEntryRepo + .findByBeneficiaryRegIDAndVisitCodeOrderByProcedureIDAsc(11L, 22L)) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(entry))); + + org.junit.jupiter.api.Assertions.assertFalse(service.getLabResultDataForBen(11L, 22L).isEmpty()); + } + + @Test + @DisplayName("getLabResultForVisitcode should render the stored results for the visit") + void getLabResultForVisitcode_shouldRenderStoredResults() { + org.mockito.Mockito.when(labResultEntryRepo + .findByBeneficiaryRegIDAndVisitCodeOrderByProcedureIDAsc(11L, 22L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getLabResultForVisitcode(11L, 22L)); + } + + @Test + @DisplayName("getLast_3_ArchivedTestVisitList should render the three most recent tested visits") + void getArchivedVisitList_shouldRenderRecentVisits() throws Exception { + org.mockito.Mockito.when(labResultEntryRepo.getLast_3_visitForLabTestDone(11L, 22L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getLast_3_ArchivedTestVisitList(11L, 22L)); + } + + @Test + @DisplayName("getECGAbnormalFindings should render the ECG findings master") + void getECGAbnormalFindings_shouldRenderFindingsMaster() { + org.mockito.Mockito.when(ecgAbnormalFindingMasterRepo.findByDeleted(false)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getECGAbnormalFindings()); + } + + @Test + @DisplayName("getBenePrescribedProcedureDetails should render the prescribed procedures") + void getPrescribedProcedures_shouldRenderProcedures() { + org.mockito.Mockito.when(v_benLabTestOrderedDetailsRepo + .findDistinctByBeneficiaryRegIDAndVisitCodeAndProcedureTypeAndProcedureIDNotInOrderByProcedureIDAscTestComponentIDAscResultValueAsc( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getBenePrescribedProcedureDetails(11L, 22L)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("prescribed procedure assembly") + class PrescribedProcedureTests { + + private com.iemr.tm.data.labtechnician.V_benLabTestOrderedDetails orderedTest(String procedureType, + Integer procedureId, Integer componentId) { + com.iemr.tm.data.labtechnician.V_benLabTestOrderedDetails ordered = + new com.iemr.tm.data.labtechnician.V_benLabTestOrderedDetails(); + ordered.setBeneficiaryRegID(11L); + ordered.setVisitCode(22L); + ordered.setProcedureID(procedureId); + ordered.setProcedureName("CBC"); + ordered.setProcedureType(procedureType); + ordered.setPrescriptionID(4L); + ordered.setIsMandatory(true); + ordered.setTestComponentID(componentId); + ordered.setTestComponentName("Haemoglobin"); + ordered.setInputType("Text"); + ordered.setMeasurementUnit("g/dL"); + return ordered; + } + + private void orderedTestsAre(String procedureType, + java.util.ArrayList ordered) { + org.mockito.Mockito.when(v_benLabTestOrderedDetailsRepo + .findDistinctByBeneficiaryRegIDAndVisitCodeAndProcedureTypeAndProcedureIDNotInOrderByProcedureIDAscTestComponentIDAscResultValueAsc( + org.mockito.ArgumentMatchers.eq(11L), org.mockito.ArgumentMatchers.eq(22L), + org.mockito.ArgumentMatchers.eq(procedureType), org.mockito.ArgumentMatchers.any())) + .thenReturn(ordered); + } + + @Test + @DisplayName("getBenePrescribedProcedureDetails should group the components of an ordered laboratory test") + void getPrescribedProcedures_shouldGroupLaboratoryComponents() { + java.util.ArrayList ordered = + new java.util.ArrayList<>(); + ordered.add(orderedTest("Laboratory", 1, 2)); + ordered.add(orderedTest("Laboratory", 1, 3)); + ordered.add(orderedTest("Laboratory", 5, 6)); + orderedTestsAre("Laboratory", ordered); + orderedTestsAre("Radiology", + new java.util.ArrayList()); + org.mockito.Mockito.when(labResultEntryRepo + .findByBeneficiaryRegIDAndVisitCodeOrderByProcedureIDAsc(11L, 22L)) + .thenReturn(new java.util.ArrayList<>()); + + String result = service.getBenePrescribedProcedureDetails(11L, 22L); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("CBC")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Haemoglobin")); + } + + @Test + @DisplayName("getBenePrescribedProcedureDetails should list an ordered radiology test") + void getPrescribedProcedures_shouldListRadiologyTest() { + java.util.ArrayList ordered = + new java.util.ArrayList<>(); + ordered.add(orderedTest("Radiology", 7, null)); + orderedTestsAre("Radiology", ordered); + orderedTestsAre("Laboratory", + new java.util.ArrayList()); + org.mockito.Mockito.when(labResultEntryRepo + .findByBeneficiaryRegIDAndVisitCodeOrderByProcedureIDAsc(11L, 22L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getBenePrescribedProcedureDetails(11L, 22L)); + } + + @Test + @DisplayName("getBenePrescribedProcedureDetails should skip procedures whose result is already entered") + void getPrescribedProcedures_shouldSkipAlreadyEnteredResults() { + com.iemr.tm.data.labModule.LabResultEntry entered = new com.iemr.tm.data.labModule.LabResultEntry(); + entered.setProcedureID(1); + com.iemr.tm.data.labModule.ProcedureData procedure = new com.iemr.tm.data.labModule.ProcedureData(); + procedure.setProcedureName("CBC"); + procedure.setProcedureType("Laboratory"); + entered.setProcedureData(procedure); + com.iemr.tm.data.labModule.TestComponentMaster component = + new com.iemr.tm.data.labModule.TestComponentMaster(); + component.setTestComponentName("Haemoglobin"); + entered.setTestComponentMaster(component); + org.mockito.Mockito.when(labResultEntryRepo + .findByBeneficiaryRegIDAndVisitCodeOrderByProcedureIDAsc(11L, 22L)) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(entered))); + orderedTestsAre("Laboratory", + new java.util.ArrayList()); + orderedTestsAre("Radiology", + new java.util.ArrayList()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getBenePrescribedProcedureDetails(11L, 22L)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("lab result entry payload") + class LabResultPayloadTests { + + private static final String WITH_COMPONENTS = "{\"beneficiaryRegID\":11,\"visitCode\":22,\"visitID\":3," + + "\"benFlowID\":5,\"createdBy\":\"lab1\",\"providerServiceMapID\":9,\"vanID\":7," + + "\"parkingPlaceID\":2,\"labCompleted\":true,\"nurseFlag\":2,\"doctorFlag\":2,\"specialist_flag\":1," + + "\"labTestResults\":[{\"prescriptionID\":4,\"procedureID\":1,\"abnormalFindings\":[3]," + + " \"compList\":[" + + " {\"testComponentID\":\"2\",\"testResultValue\":\"12\",\"testResultUnit\":\"g/dL\"," + + " \"remarks\":\"normal\"}," + + " {\"testComponentID\":\"3\",\"stripsNotAvailable\":\"true\"}," + + " {\"testComponentID\":\"4\",\"testResultValue\":\"\"}," + + " {\"testComponentID\":\"\",\"testResultValue\":\"9\"}]}]," + + "\"radiologyTestResults\":[{\"prescriptionID\":4,\"procedureID\":7," + + " \"testResultValue\":\"No abnormality\",\"fileIDs\":[81,82]}]}"; + + private com.iemr.tm.data.labModule.WrapperLabResultEntry wrapper(String raw) throws Exception { + return com.iemr.tm.utils.mapper.InputMapper.gson().fromJson( + com.google.gson.JsonParser.parseString(raw).getAsJsonObject(), + com.iemr.tm.data.labModule.WrapperLabResultEntry.class); + } + + @Test + @DisplayName("saveLabTestResult should store one row per measured component and attach the report files") + void saveLabTestResult_shouldStoreOneRowPerMeasuredComponent() throws Exception { + org.mockito.Mockito.when(labResultEntryRepo.saveAll(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.saveLabTestResult(wrapper(WITH_COMPONENTS))); + + @SuppressWarnings("unchecked") + org.mockito.ArgumentCaptor> captor = + org.mockito.ArgumentCaptor.forClass(java.util.List.class); + org.mockito.Mockito.verify(labResultEntryRepo).saveAll(captor.capture()); + java.util.List stored = captor.getValue(); + org.junit.jupiter.api.Assertions.assertEquals(3, stored.size()); + org.junit.jupiter.api.Assertions.assertEquals("12", stored.get(0).getTestResultValue()); + org.junit.jupiter.api.Assertions.assertEquals("g/dL", stored.get(0).getTestResultUnit()); + org.junit.jupiter.api.Assertions.assertEquals("normal", stored.get(0).getRemarks()); + org.junit.jupiter.api.Assertions.assertEquals(Boolean.TRUE, stored.get(1).getStripsNotAvailable()); + org.junit.jupiter.api.Assertions.assertEquals("81,82,", stored.get(2).getTestReportFilePath()); + org.junit.jupiter.api.Assertions.assertEquals(7, stored.get(2).getProcedureID()); + } + + @Test + @DisplayName("saveLabTestResult should report a failure when the stored rows do not match") + void saveLabTestResult_shouldReportStoreMismatch() throws Exception { + org.mockito.Mockito.when(labResultEntryRepo.saveAll(org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNull(service.saveLabTestResult(wrapper(WITH_COMPONENTS))); + } + + @Test + @DisplayName("saveLabTestResult should succeed when the wrapper carries no result at all") + void saveLabTestResult_shouldSucceedForEmptyWrapper() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(1, service.saveLabTestResult(wrapper( + "{\"beneficiaryRegID\":11,\"labTestResults\":[],\"radiologyTestResults\":[]}"))); + } + + @Test + @DisplayName("saveLabTestResult should succeed when every component was left blank") + void saveLabTestResult_shouldSucceedWhenEveryComponentBlank() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(1, service.saveLabTestResult(wrapper( + "{\"beneficiaryRegID\":11,\"labTestResults\":[{\"procedureID\":1," + + "\"compList\":[{\"testComponentID\":\"2\",\"testResultValue\":\"\"}]}]," + + "\"radiologyTestResults\":[]}"))); + } + + @Test + @DisplayName("saveLabTestResult should send a completed specialist visit back to the specialist") + void saveLabTestResult_shouldReturnCompletedSpecialistVisit() throws Exception { + org.mockito.Mockito.when(labResultEntryRepo.saveAll(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateFlowAfterLabResultEntryForTCSpecialist( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.saveLabTestResult( + com.google.gson.JsonParser.parseString( + WITH_COMPONENTS.replace("\"specialist_flag\":1", "\"specialist_flag\":2")) + .getAsJsonObject())); + + org.mockito.Mockito.verify(commonBenStatusFlowServiceImpl) + .updateFlowAfterLabResultEntryForTCSpecialist(5L, 11L, (short) 3); + } + + @Test + @DisplayName("saveLabTestResult should keep a partly tested specialist visit with the lab") + void saveLabTestResult_shouldKeepPartlyTestedSpecialistVisitWithLab() throws Exception { + org.mockito.Mockito.when(labResultEntryRepo.saveAll(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateFlowAfterLabResultEntryForTCSpecialist( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.saveLabTestResult( + com.google.gson.JsonParser.parseString(WITH_COMPONENTS + .replace("\"specialist_flag\":1", "\"specialist_flag\":2") + .replace("\"labCompleted\":true", "\"labCompleted\":false")).getAsJsonObject())); + + org.mockito.Mockito.verify(commonBenStatusFlowServiceImpl) + .updateFlowAfterLabResultEntryForTCSpecialist(5L, 11L, (short) 2); + } + + @Test + @DisplayName("saveLabTestResult should hand a completed nurse visit to the doctor") + void saveLabTestResult_shouldHandCompletedNurseVisitToDoctor() throws Exception { + org.mockito.Mockito.when(labResultEntryRepo.saveAll(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateFlowAfterLabResultEntry( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.saveLabTestResult( + com.google.gson.JsonParser.parseString(WITH_COMPONENTS).getAsJsonObject())); + + org.mockito.Mockito.verify(commonBenStatusFlowServiceImpl).updateFlowAfterLabResultEntry(5L, 11L, 3L, + (short) 3, (short) 1, (short) 1); + } + + @Test + @DisplayName("saveLabTestResult should hand a completed doctor visit back to the doctor") + void saveLabTestResult_shouldHandCompletedDoctorVisitBackToDoctor() throws Exception { + org.mockito.Mockito.when(labResultEntryRepo.saveAll(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateFlowAfterLabResultEntry( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.saveLabTestResult( + com.google.gson.JsonParser.parseString( + WITH_COMPONENTS.replace("\"nurseFlag\":2", "\"nurseFlag\":3")).getAsJsonObject())); + + org.mockito.Mockito.verify(commonBenStatusFlowServiceImpl).updateFlowAfterLabResultEntry(5L, 11L, 3L, + (short) 3, (short) 3, (short) 1); + } + + @Test + @DisplayName("saveLabTestResult should keep a partly tested visit with the lab") + void saveLabTestResult_shouldKeepPartlyTestedVisitWithLab() throws Exception { + org.mockito.Mockito.when(labResultEntryRepo.saveAll(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateFlowAfterLabResultEntry( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.saveLabTestResult( + com.google.gson.JsonParser.parseString( + WITH_COMPONENTS.replace("\"labCompleted\":true", "\"labCompleted\":false")) + .getAsJsonObject())); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/location/LocationServiceImplTest.java b/src/test/java/com/iemr/tm/service/location/LocationServiceImplTest.java new file mode 100644 index 00000000..7b1b85a1 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/location/LocationServiceImplTest.java @@ -0,0 +1,300 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.location; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.location.CountryCityMasterRepo; +import com.iemr.tm.repo.location.CountryMasterRepo; +import com.iemr.tm.repo.location.DistrictBlockMasterRepo; +import com.iemr.tm.repo.location.DistrictBranchMasterRepo; +import com.iemr.tm.repo.location.DistrictMasterRepo; +import com.iemr.tm.repo.location.ParkingPlaceMasterRepo; +import com.iemr.tm.repo.location.ServicePointMasterRepo; +import com.iemr.tm.repo.location.StateMasterRepo; +import com.iemr.tm.repo.location.V_GetLocDetailsFromSPidAndPSMidRepo; +import com.iemr.tm.repo.location.V_getVanLocDetailsRepo; +import com.iemr.tm.repo.location.V_get_prkngplc_dist_zone_state_from_spidRepo; +import com.iemr.tm.repo.location.ZoneMasterRepo; +import com.iemr.tm.repo.login.ServicePointVillageMappingRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("LocationServiceImpl Test Suite") +class LocationServiceImplTest { + + @Mock + private CountryMasterRepo countryMasterRepo; + @Mock + private CountryCityMasterRepo countryCityMasterRepo; + @Mock + private StateMasterRepo stateMasterRepo; + @Mock + private ZoneMasterRepo zoneMasterRepo; + @Mock + private DistrictMasterRepo districtMasterRepo; + @Mock + private DistrictBlockMasterRepo districtBlockMasterRepo; + @Mock + private ParkingPlaceMasterRepo parkingPlaceMasterRepo; + @Mock + private ServicePointMasterRepo servicePointMasterRepo; + @Mock + private V_GetLocDetailsFromSPidAndPSMidRepo v_GetLocDetailsFromSPidAndPSMidRepo; + @Mock + private ServicePointVillageMappingRepo servicePointVillageMappingRepo; + @Mock + private DistrictBranchMasterRepo districtBranchMasterRepo; + @Mock + private V_get_prkngplc_dist_zone_state_from_spidRepo v_get_prkngplc_dist_zone_state_from_spidRepo; + @Mock + private V_getVanLocDetailsRepo v_getVanLocDetailsRepo; + + @InjectMocks + private LocationServiceImpl service; + + @Test + @DisplayName("getCountryList should answer for a well formed request") + void getCountryList_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getCountryList()); + } + + @Test + @DisplayName("getCountryCityList should answer for a well formed request") + void getCountryCityList_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getCountryCityList(9)); + } + + @Test + @DisplayName("getStateList should answer for a well formed request") + void getStateList_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getStateList()); + } + + @Test + @DisplayName("getZoneList should answer for a well formed request") + void getZoneList_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getZoneList(9)); + } + + @Test + @DisplayName("getDistrictList should answer for a well formed request") + void getDistrictList_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getDistrictList(9)); + } + + @Test + @DisplayName("getDistrictBlockList should answer for a well formed request") + void getDistrictBlockList_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getDistrictBlockList(9)); + } + + @Test + @DisplayName("getParkingPlaceList should answer for a well formed request") + void getParkingPlaceList_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getParkingPlaceList(9)); + } + + @Test + @DisplayName("getServicePointPlaceList should answer for a well formed request") + void getServicePointPlaceList_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getServicePointPlaceList(9)); + } + + @Test + @DisplayName("getVillageMasterFromBlockID should answer for a well formed request") + void getVillageMasterFromBlockID_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getVillageMasterFromBlockID(9)); + } + + @Test + @DisplayName("getLocDetailsNew should answer for a well formed request") + void getLocDetailsNew_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getLocDetailsNew(9, 9)); + } + + @org.junit.jupiter.api.Nested + @DisplayName("location master lookups") + class LocationMasterTests { + + private java.util.ArrayList rows(Object[]... values) { + return new java.util.ArrayList<>(java.util.Arrays.asList(values)); + } + + @Test + @DisplayName("getStateList should name every configured state") + void getStateList_shouldNameEveryState() { + org.mockito.Mockito.when(stateMasterRepo.getStateMaster()) + .thenReturn(rows(new Object[] { 21, "Maharashtra" })); + + org.junit.jupiter.api.Assertions.assertTrue(service.getStateList().contains("Maharashtra")); + } + + @Test + @DisplayName("getStateList should answer with an empty list when no state is configured") + void getStateList_shouldAnswerWithEmptyList() { + org.mockito.Mockito.when(stateMasterRepo.getStateMaster()).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getStateList()); + } + + @Test + @DisplayName("getZoneList should name every zone of the provider") + void getZoneList_shouldNameEveryZone() { + org.mockito.Mockito.when(zoneMasterRepo.getZoneMaster(9)) + .thenReturn(rows(new Object[] { 5, "West Zone" })); + + org.junit.jupiter.api.Assertions.assertTrue(service.getZoneList(9).contains("West Zone")); + } + + @Test + @DisplayName("getZoneList should answer with an empty list when the provider has no zone") + void getZoneList_shouldAnswerWithEmptyList() { + org.mockito.Mockito.when(zoneMasterRepo.getZoneMaster(9)).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getZoneList(9)); + } + + @Test + @DisplayName("getDistrictList should name every district of the state") + void getDistrictList_shouldNameEveryDistrict() { + org.mockito.Mockito.when(districtMasterRepo.getDistrictMaster(21)) + .thenReturn(rows(new Object[] { 31, "Nagpur", 21, 5 })); + + org.junit.jupiter.api.Assertions.assertTrue(service.getDistrictList(21).contains("Nagpur")); + } + + @Test + @DisplayName("getDistrictList should answer with an empty list when the state has no district") + void getDistrictList_shouldAnswerWithEmptyList() { + org.mockito.Mockito.when(districtMasterRepo.getDistrictMaster(21)).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getDistrictList(21)); + } + + @Test + @DisplayName("getDistrictBlockList should name every block of the district") + void getDistrictBlockList_shouldNameEveryBlock() { + org.mockito.Mockito.when(districtBlockMasterRepo.getDistrictBlockMaster(31)) + .thenReturn(rows(new Object[] { 41, "Kamptee" })); + + org.junit.jupiter.api.Assertions.assertTrue(service.getDistrictBlockList(31).contains("Kamptee")); + } + + @Test + @DisplayName("getDistrictBlockList should answer with an empty list when the district has no block") + void getDistrictBlockList_shouldAnswerWithEmptyList() { + org.mockito.Mockito.when(districtBlockMasterRepo.getDistrictBlockMaster(31)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getDistrictBlockList(31)); + } + + @Test + @DisplayName("getParkingPlaceList should name every parking place of the provider") + void getParkingPlaceList_shouldNameEveryParkingPlace() { + org.mockito.Mockito.when(parkingPlaceMasterRepo.getParkingPlaceMaster(9)) + .thenReturn(rows(new Object[] { 2, "Kamptee Depot" })); + + org.junit.jupiter.api.Assertions.assertTrue(service.getParkingPlaceList(9).contains("Kamptee Depot")); + } + + @Test + @DisplayName("getParkingPlaceList should answer with an empty list when the provider has no parking place") + void getParkingPlaceList_shouldAnswerWithEmptyList() { + org.mockito.Mockito.when(parkingPlaceMasterRepo.getParkingPlaceMaster(9)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getParkingPlaceList(9)); + } + + @Test + @DisplayName("getServicePointPlaceList should name every service point of the parking place") + void getServicePointPlaceList_shouldNameEveryServicePoint() { + org.mockito.Mockito.when(servicePointMasterRepo.getServicePointMaster(2)) + .thenReturn(rows(new Object[] { 51, "PHC Kamptee" })); + + org.junit.jupiter.api.Assertions.assertTrue(service.getServicePointPlaceList(2).contains("PHC Kamptee")); + } + + @Test + @DisplayName("getServicePointPlaceList should answer with an empty list when the parking place has no service point") + void getServicePointPlaceList_shouldAnswerWithEmptyList() { + org.mockito.Mockito.when(servicePointMasterRepo.getServicePointMaster(2)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getServicePointPlaceList(2)); + } + + @Test + @DisplayName("getCountryCityList should list the cities of the country") + void getCountryCityList_shouldListCities() { + org.mockito.Mockito.when(countryCityMasterRepo.findByCountryIDAndDeleted(1, false)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getCountryCityList(1)); + } + + @Test + @DisplayName("getVillageMasterFromBlockID should list the villages of the block") + void getVillageMasterFromBlockID_shouldListVillages() { + org.mockito.Mockito.when(districtBranchMasterRepo.findByBlockID(41)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getVillageMasterFromBlockID(41)); + } + + @Test + @DisplayName("getLocDetailsNew should answer with the van location and the state master") + void getLocDetailsNew_shouldAnswerWithVanLocationAndStateMaster() { + org.mockito.Mockito.when(v_getVanLocDetailsRepo.getVanLocDetails(7)) + .thenReturn(rows(new Object[] { 21, 2, 31, "Nagpur" }, new Object[] { 21, 2, 32, "Wardha" })); + org.mockito.Mockito.when(stateMasterRepo.getStateMaster()) + .thenReturn(rows(new Object[] { 21, "Maharashtra", 1 })); + + String result = service.getLocDetailsNew(7, 9); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Maharashtra")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Nagpur")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("parkingPlaceID")); + } + + @Test + @DisplayName("getLocDetailsNew should answer with an empty location when the van is not mapped") + void getLocDetailsNew_shouldAnswerWithEmptyLocation() { + org.mockito.Mockito.when(v_getVanLocDetailsRepo.getVanLocDetails(7)) + .thenReturn(new java.util.ArrayList<>()); + org.mockito.Mockito.when(stateMasterRepo.getStateMaster()).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertTrue(service.getLocDetailsNew(7, 9).contains("otherLoc")); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/login/IemrMmuLoginServiceImplTest.java b/src/test/java/com/iemr/tm/service/login/IemrMmuLoginServiceImplTest.java new file mode 100644 index 00000000..9310bfa0 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/login/IemrMmuLoginServiceImplTest.java @@ -0,0 +1,207 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.login; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.login.MasterVanRepo; +import com.iemr.tm.repo.login.ServicePointVillageMappingRepo; +import com.iemr.tm.repo.login.UserParkingplaceMappingRepo; +import com.iemr.tm.repo.login.UserVanSpDetails_View_Repo; +import com.iemr.tm.repo.login.VanServicepointMappingRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("IemrMmuLoginServiceImpl Test Suite") +class IemrMmuLoginServiceImplTest { + + @Mock + private UserParkingplaceMappingRepo userParkingplaceMappingRepo; + @Mock + private MasterVanRepo masterVanRepo; + @Mock + private VanServicepointMappingRepo vanServicepointMappingRepo; + @Mock + private ServicePointVillageMappingRepo servicePointVillageMappingRepo; + @Mock + private UserVanSpDetails_View_Repo userVanSpDetails_View_Repo; + + @InjectMocks + private IemrMmuLoginServiceImpl service; + + @Test + @DisplayName("getUserServicePointVanDetails should answer for a well formed request") + void getUserServicePointVanDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getUserServicePointVanDetails(9)); + } + + @Test + @DisplayName("getServicepointVillages should answer for a well formed request") + void getServicepointVillages_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getServicepointVillages(9)); + } + + @Test + @DisplayName("getUserVanSpDetails should answer for a well formed request") + void getUserVanSpDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getUserVanSpDetails(9, 9)); + } + + @Test + @DisplayName("getUserSpokeDetails should answer for a well formed request") + void getUserSpokeDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getUserSpokeDetails(9)); + } + + @org.junit.jupiter.api.Nested + @DisplayName("van and service point lookup") + class VanAndServicePointTests { + + /** parkingPlaceID, stateID, stateName, districtID, districtName, blockID, blockName. */ + private java.util.List parkingPlaceRows() { + return new java.util.ArrayList<>(java.util.Collections.singletonList( + new Object[] { 2, 21, "Maharashtra", 31, "Nagpur", 41, "Kamptee" })); + } + + @Test + @DisplayName("getUserServicePointVanDetails should assemble the vans and service points for the parking place") + void getUserServicePointVanDetails_shouldAssembleVansAndServicePoints() { + org.mockito.Mockito.when(userParkingplaceMappingRepo.getUserParkingPlce(41)) + .thenReturn(parkingPlaceRows()); + org.mockito.Mockito.when(masterVanRepo.getUserVanDatails(org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList( + new Object[] { 7, "MH-31-AB-1234" }))); + org.mockito.Mockito.when(vanServicepointMappingRepo + .getuserSpSessionDetails(org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList( + new Object[] { 51, "PHC Kamptee", "Morning" }))); + + String result = service.getUserServicePointVanDetails(41); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("MH-31-AB-1234")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("PHC Kamptee")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Maharashtra")); + } + + @Test + @DisplayName("getUserServicePointVanDetails should answer with placeholders when no van or service point is mapped") + void getUserServicePointVanDetails_shouldAnswerWithPlaceholders() { + org.mockito.Mockito.when(userParkingplaceMappingRepo.getUserParkingPlce(41)) + .thenReturn(parkingPlaceRows()); + org.mockito.Mockito.when(masterVanRepo.getUserVanDatails(org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>()); + org.mockito.Mockito.when(vanServicepointMappingRepo + .getuserSpSessionDetails(org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>()); + + String result = service.getUserServicePointVanDetails(41); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("userVanDetails")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("userSpDetails")); + } + + @Test + @DisplayName("getUserServicePointVanDetails should answer with nothing when no parking place is mapped") + void getUserServicePointVanDetails_shouldAnswerWithNothingWithoutParkingPlace() { + org.mockito.Mockito.when(userParkingplaceMappingRepo.getUserParkingPlce(41)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("{}", service.getUserServicePointVanDetails(41)); + } + + @Test + @DisplayName("getServicepointVillages should list the villages mapped to the service point") + void getServicepointVillages_shouldListMappedVillages() { + org.mockito.Mockito.when(servicePointVillageMappingRepo.getServicePointVillages(51)) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList( + new Object[] { 61, "Kanhan" }))); + + org.junit.jupiter.api.Assertions.assertTrue(service.getServicepointVillages(51).contains("Kanhan")); + } + + @Test + @DisplayName("getServicepointVillages should answer with an empty list when no village is mapped") + void getServicepointVillages_shouldAnswerWithEmptyList() { + org.mockito.Mockito.when(servicePointVillageMappingRepo.getServicePointVillages(51)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getServicepointVillages(51)); + } + + @Test + @DisplayName("getUserVanSpDetails should assemble the van, service point and location for the user") + void getUserVanSpDetails_shouldAssembleVanServicePointAndLocation() { + org.mockito.Mockito.when(userVanSpDetails_View_Repo.getUserVanSpDetails_View(41, 9)) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList( + new Object[] { 41, 7, "MH-31-AB-1234", (short) 1, 51, "PHC Kamptee", 2, 9 }))); + org.mockito.Mockito.when(userParkingplaceMappingRepo.getUserParkingPlce(41)) + .thenReturn(parkingPlaceRows()); + + String result = service.getUserVanSpDetails(41, 9); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("UserVanSpDetails")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Nagpur")); + } + + @Test + @DisplayName("getUserVanSpDetails should answer with empty sections when nothing is mapped to the user") + void getUserVanSpDetails_shouldAnswerWithEmptySections() { + org.mockito.Mockito.when(userVanSpDetails_View_Repo.getUserVanSpDetails_View(41, 9)) + .thenReturn(new java.util.ArrayList<>()); + org.mockito.Mockito.when(userParkingplaceMappingRepo.getUserParkingPlce(41)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertTrue( + service.getUserVanSpDetails(41, 9).contains("UserLocDetails")); + } + + @Test + @DisplayName("getUserSpokeDetails should list every van after the all option") + void getUserSpokeDetails_shouldListEveryVanAfterAllOption() { + org.mockito.Mockito.when(masterVanRepo.getVanMaster(9)) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList( + new Object[] { 7, "MH-31-AB-1234" }))); + + String result = service.getUserSpokeDetails(9); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("All")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("MH-31-AB-1234")); + } + + @Test + @DisplayName("getUserSpokeDetails should offer only the all option when no van is configured") + void getUserSpokeDetails_shouldOfferOnlyAllOption() { + org.mockito.Mockito.when(masterVanRepo.getVanMaster(9)).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertTrue(service.getUserSpokeDetails(9).contains("All")); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/ncdCare/NCDCareDoctorServiceImplTest.java b/src/test/java/com/iemr/tm/service/ncdCare/NCDCareDoctorServiceImplTest.java new file mode 100644 index 00000000..a1ddd424 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/ncdCare/NCDCareDoctorServiceImplTest.java @@ -0,0 +1,110 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.ncdCare; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.nurse.ncdcare.NCDCareDiagnosisRepo; +import com.iemr.tm.repo.quickConsultation.PrescriptionDetailRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NCDCareDoctorServiceImpl Test Suite") +class NCDCareDoctorServiceImplTest { + + @Mock + private NCDCareDiagnosisRepo ncdCareDiagnosisRepo; + @Mock + private PrescriptionDetailRepo prescriptionDetailRepo; + + @InjectMocks + private NCDCareDoctorServiceImpl service; + + @Test + @DisplayName("saveNCDDiagnosisData should answer for a well formed request") + void saveNCDDiagnosisData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveNCDDiagnosisData(org.mockito.Mockito.mock(com.iemr.tm.data.ncdcare.NCDCareDiagnosis.class))); + } + + @Test + @DisplayName("getNCDCareDiagnosisDetails should answer for a well formed request") + void getNCDCareDiagnosisDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getNCDCareDiagnosisDetails(11L, 11L)); + } + + @Test + @DisplayName("updateBenNCDCareDiagnosis should answer for a well formed request") + void updateBenNCDCareDiagnosis_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenNCDCareDiagnosis(org.mockito.Mockito.mock(com.iemr.tm.data.ncdcare.NCDCareDiagnosis.class))); + } + + @org.junit.jupiter.api.Nested + @DisplayName("NCD care diagnosis") + class DiagnosisTests { + + private com.iemr.tm.data.ncdcare.NCDCareDiagnosis diagnosis() throws Exception { + com.iemr.tm.data.ncdcare.NCDCareDiagnosis diagnosis = com.iemr.tm.utils.mapper.InputMapper.gson().fromJson( + "{\"beneficiaryRegID\":11,\"visitCode\":22,\"benVisitID\":3," + + "\"ncdScreeningConditionArray\":[\"Diabetes\",\"Hypertension\"]}", + com.iemr.tm.data.ncdcare.NCDCareDiagnosis.class); + return diagnosis; + } + + @Test + @DisplayName("saveNCDDiagnosisData should flatten the screening conditions before storing") + void saveDiagnosis_shouldFlattenScreeningConditions() throws Exception { + com.iemr.tm.data.ncdcare.NCDCareDiagnosis stored = diagnosis(); + stored.setID(4L); + org.mockito.Mockito.when(ncdCareDiagnosisRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, service.saveNCDDiagnosisData(diagnosis())); + } + + @Test + @DisplayName("getNCDCareDiagnosisDetails should render the stored diagnosis for the visit") + void getDiagnosisDetails_shouldRenderStoredDiagnosis() { + org.mockito.Mockito.when(ncdCareDiagnosisRepo.getNCDCareDiagnosisDetails(11L, 22L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getNCDCareDiagnosisDetails(11L, 22L)); + } + + @Test + @DisplayName("updateBenNCDCareDiagnosis should mark an already processed diagnosis as updated") + void updateDiagnosis_shouldMarkProcessedAsUpdated() throws Exception { + org.mockito.Mockito.when(ncdCareDiagnosisRepo.getNCDCareDiagnosisStatus(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.any())).thenReturn("P"); + + org.junit.jupiter.api.Assertions.assertEquals(0, service.updateBenNCDCareDiagnosis(diagnosis())); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/ncdCare/NCDCareServiceImplTest.java b/src/test/java/com/iemr/tm/service/ncdCare/NCDCareServiceImplTest.java new file mode 100644 index 00000000..ae63cb41 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/ncdCare/NCDCareServiceImplTest.java @@ -0,0 +1,644 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.ncdCare; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyShort; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.iemr.tm.data.nurse.CommonUtilityClass; +import com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.nurse.anc.BenAdherenceRepo; +import com.iemr.tm.repo.quickConsultation.BenChiefComplaintRepo; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.service.labtechnician.LabTechnicianServiceImpl; +import com.iemr.tm.service.tele_consultation.SMSGatewayServiceImpl; +import com.iemr.tm.service.tele_consultation.TeleConsultationServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NCDCareServiceImpl Test Suite") +class NCDCareServiceImplTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_ID = 3L; + private static final Long VISIT_CODE = 22L; + + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private NCDCareDoctorServiceImpl ncdCareDoctorServiceImpl; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private LabTechnicianServiceImpl labTechnicianServiceImpl; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private TeleConsultationServiceImpl teleConsultationServiceImpl; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private BenChiefComplaintRepo benChiefComplaintRepo; + @Mock + private BenAdherenceRepo benAdherenceRepo; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + + @InjectMocks + private NCDCareServiceImpl service; + + private JsonObject json(String raw) { + return JsonParser.parseString(raw).getAsJsonObject(); + } + + /** A nurse request carrying a visit, an ordered lab test, history and vitals. */ + private JsonObject nurseRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"providerServiceMapID\":9,\"vanID\":7,\"sessionID\":1," + + "\"benFlowID\":5,\"createdBy\":\"nurse1\"," + + "\"visitDetails\":{" + + " \"visitDetails\":{\"beneficiaryRegID\":11,\"visitReason\":\"New Chief Complaint\"," + + " \"visitCategory\":\"NCD care\"}," + + " \"adherence\":{\"toDrugs\":true}," + + " \"investigation\":{\"laboratoryList\":[{\"testID\":1}]}" + + "}," + + "\"historyDetails\":{},\"vitalDetails\":{\"height_cm\":170}" + "}"); + } + + private CommonUtilityClass utility() { + CommonUtilityClass utility = new CommonUtilityClass(); + utility.setBeneficiaryRegID(BEN_REG_ID); + utility.setVisitCode(VISIT_CODE); + utility.setBenVisitID(VISIT_ID); + utility.setProviderServiceMapID(9); + utility.setCreatedBy("nurse1"); + return utility; + } + + private TeleconsultationRequestOBJ teleconsultationRequest(boolean walkIn) { + TeleconsultationRequestOBJ request = new TeleconsultationRequestOBJ(); + request.setWalkIn(walkIn); + request.setUserID(42); + request.setSpecializationID(2); + request.setTmRequestID(8L); + request.setAllocationDate(new Timestamp(1_700_000_000_000L)); + return request; + } + + @BeforeEach + @DisplayName("Wire the collaborators that every save path needs") + void setUp() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(0); + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(VISIT_ID); + when(commonNurseServiceImpl.generateVisitCode(anyLong(), any(), any())).thenReturn(VISIT_CODE); + when(commonNurseServiceImpl.saveBenAdherenceDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenInvestigationDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalAnthropometryDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(1L); + when(commonBenStatusFlowServiceImpl.updateBenFlowNurseAfterNurseActivity(any(), anyLong(), anyLong(), + anyString(), anyString(), anyShort(), anyShort(), anyShort(), anyShort(), anyShort(), anyLong(), any(), + anyShort(), any(), any())).thenReturn(1); + } + + @Nested + @DisplayName("saveNCDCareNurseData") + class SaveNurseDataTests { + + @Test + @DisplayName("saveNCDCareNurseData should save the visit, history and vitals and return the visit code") + void saveNurseData_shouldSaveVisitHistoryAndVitals() throws Exception { + String result = service.saveNCDCareNurseData(nurseRequest(), AUTHORIZATION); + + assertTrue(result.contains("Data saved successfully")); + assertTrue(result.contains("\"visitCode\":\"22\"")); + } + + @Test + @DisplayName("saveNCDCareNurseData should report an already saved visit without touching the history") + void saveNurseData_shouldReportAlreadySavedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + String result = service.saveNCDCareNurseData(nurseRequest(), AUTHORIZATION); + + assertTrue(result.contains("Data already saved")); + verify(commonNurseServiceImpl, never()).saveBeneficiaryVisitDetails(any()); + } + + @Test + @DisplayName("saveNCDCareNurseData should reject a request without visit details") + void saveNurseData_shouldRejectRequestWithoutVisitDetails() { + assertThrows(Exception.class, () -> service.saveNCDCareNurseData(json("{}"), AUTHORIZATION)); + } + + @Test + @DisplayName("saveNCDCareNurseData should reject a null request") + void saveNurseData_shouldRejectNullRequest() { + assertThrows(Exception.class, () -> service.saveNCDCareNurseData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("saveNCDCareNurseData should fail when the visit could not be created") + void saveNurseData_shouldFailWhenVisitNotCreated() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(0L); + + assertThrows(RuntimeException.class, () -> service.saveNCDCareNurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveNCDCareNurseData should fail when the vitals could not be stored") + void saveNurseData_shouldFailWhenVitalsNotStored() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(null); + + assertThrows(RuntimeException.class, () -> service.saveNCDCareNurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveNCDCareNurseData should fail when the beneficiary flow could not be advanced") + void saveNurseData_shouldFailWhenFlowNotAdvanced() throws Exception { + when(commonBenStatusFlowServiceImpl.updateBenFlowNurseAfterNurseActivity(any(), anyLong(), anyLong(), + anyString(), anyString(), anyShort(), anyShort(), anyShort(), anyShort(), anyShort(), anyLong(), + any(), anyShort(), any(), any())).thenReturn(0); + + assertThrows(RuntimeException.class, () -> service.saveNCDCareNurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveNCDCareNurseData should notify a scheduled teleconsultation by SMS") + void saveNurseData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.saveNCDCareNurseData(nurseRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), + anyString(), anyString(), any(), eq(AUTHORIZATION)); + } + + @Test + @DisplayName("saveNCDCareNurseData should not notify a walk-in teleconsultation") + void saveNurseData_shouldNotNotifyWalkInTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(true)); + + service.saveNCDCareNurseData(nurseRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl, never()).smsSenderGateway(anyString(), anyLong(), anyInt(), anyLong(), any(), + anyString(), anyString(), any(), anyString()); + } + + @Test + @DisplayName("saveNCDCareNurseData should route the beneficiary to the doctor only when no test was ordered") + void saveNurseData_shouldRouteToDoctorOnlyWithoutOrderedTest() throws Exception { + JsonObject request = nurseRequest(); + request.getAsJsonObject("visitDetails").getAsJsonObject("investigation").add("laboratoryList", + new com.google.gson.JsonArray()); + + service.saveNCDCareNurseData(request, AUTHORIZATION); + + verify(commonBenStatusFlowServiceImpl).updateBenFlowNurseAfterNurseActivity(any(), anyLong(), anyLong(), + anyString(), anyString(), eq((short) 9), eq((short) 1), eq((short) 0), anyShort(), anyShort(), + anyLong(), any(), anyShort(), any(), any()); + } + } + + @Nested + @DisplayName("deleteVisitDetails") + class DeleteVisitDetailsTests { + + @Test + @DisplayName("deleteVisitDetails should remove the complaint, adherence and visit rows") + void deleteVisitDetails_shouldRemoveVisitRows() throws Exception { + when(benVisitDetailRepo.getVisitCode(BEN_REG_ID, 9)).thenReturn(VISIT_CODE); + + service.deleteVisitDetails(nurseRequest()); + + verify(benChiefComplaintRepo).deleteVisitDetails(VISIT_CODE); + verify(benAdherenceRepo).deleteVisitDetails(VISIT_CODE); + verify(benVisitDetailRepo).deleteVisitDetails(VISIT_CODE); + } + + @Test + @DisplayName("deleteVisitDetails should do nothing when no visit was created") + void deleteVisitDetails_shouldDoNothingWithoutVisit() throws Exception { + when(benVisitDetailRepo.getVisitCode(BEN_REG_ID, 9)).thenReturn(null); + + service.deleteVisitDetails(nurseRequest()); + + verify(benVisitDetailRepo, never()).deleteVisitDetails(anyLong()); + } + + @Test + @DisplayName("deleteVisitDetails should do nothing for a request without visit details") + void deleteVisitDetails_shouldDoNothingWithoutVisitDetails() throws Exception { + service.deleteVisitDetails(json("{}")); + + verify(benVisitDetailRepo, never()).deleteVisitDetails(anyLong()); + } + } + + @Nested + @DisplayName("saveBenVisitDetails") + class SaveVisitDetailsTests { + + @Test + @DisplayName("saveBenVisitDetails should return the new visit id and code") + void saveBenVisitDetails_shouldReturnVisitIdAndCode() throws Exception { + Map result = service.saveBenVisitDetails( + nurseRequest().getAsJsonObject("visitDetails"), utility()); + + assertEquals(VISIT_ID, result.get("visitID")); + assertEquals(VISIT_CODE, result.get("visitCode")); + } + + @Test + @DisplayName("saveBenVisitDetails should return nothing when the visit was already recorded") + void saveBenVisitDetails_shouldReturnNothingForAlreadyRecordedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), utility()) + .isEmpty()); + } + + @Test + @DisplayName("saveBenVisitDetails should return nothing for a payload without visit details") + void saveBenVisitDetails_shouldReturnNothingWithoutVisitDetails() throws Exception { + assertTrue(service.saveBenVisitDetails(json("{}"), utility()).isEmpty()); + } + + @Test + @DisplayName("saveBenVisitDetails should skip the adherence and investigation when they are absent") + void saveBenVisitDetails_shouldSkipAbsentAdherenceAndInvestigation() throws Exception { + JsonObject payload = json("{\"visitDetails\":{\"beneficiaryRegID\":11,\"visitReason\":\"New\"," + + "\"visitCategory\":\"NCD care\"}}"); + + assertEquals(VISIT_ID, service.saveBenVisitDetails(payload, utility()).get("visitID")); + verify(commonNurseServiceImpl, never()).saveBenAdherenceDetails(any()); + } + } + + @Nested + @DisplayName("saveBenNCDCareHistoryDetails") + class SaveHistoryDetailsTests { + + @Test + @DisplayName("saveBenNCDCareHistoryDetails should succeed when no history section was captured") + void saveHistory_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1L, service.saveBenNCDCareHistoryDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenNCDCareHistoryDetails should store every captured history section") + void saveHistory_shouldStoreCapturedSections() throws Exception { + when(commonNurseServiceImpl.saveBenPastHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenComorbidConditions(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMedicationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveFemaleObstetricHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMenstrualHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenFamilyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePersonalHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveAllergyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildOptionalVaccineDetail(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveImmunizationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildDevelopmentHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildFeedingHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePerinatalHistory(any())).thenReturn(1L); + + JsonObject history = json("{\"pastHistory\":{},\"comorbidConditions\":{}," + + "\"medicationHistory\":{\"medicationHistoryList\":[{\"currentMedication\":\"Metformin\"}]}," + + "\"femaleObstetricHistory\":{},\"menstrualHistory\":{},\"familyHistory\":{}," + + "\"personalHistory\":{},\"allergyHistory\":{},\"childVaccineDetails\":{}," + + "\"immunizationHistory\":{},\"developmentHistory\":{},\"childFeedingDetails\":{}," + + "\"perinatalHistroy\":{}}"); + + assertEquals(1L, service.saveBenNCDCareHistoryDetails(history, VISIT_ID, VISIT_CODE)); + verify(commonNurseServiceImpl).saveBenPastHistory(any()); + verify(commonNurseServiceImpl).saveBenComorbidConditions(any()); + } + + @Test + @DisplayName("saveBenNCDCareHistoryDetails should report a failure when a section could not be stored") + void saveHistory_shouldReportFailureWhenSectionNotStored() throws Exception { + when(commonNurseServiceImpl.saveBenPastHistory(any())).thenReturn(null); + + assertNull(service.saveBenNCDCareHistoryDetails(json("{\"pastHistory\":{}}"), VISIT_ID, VISIT_CODE)); + } + } + + @Nested + @DisplayName("saveBenNCDCareVitalDetails") + class SaveVitalDetailsTests { + + @Test + @DisplayName("saveBenNCDCareVitalDetails should store the anthropometry and the physical vitals") + void saveVitals_shouldStoreAnthropometryAndPhysicalVitals() throws Exception { + assertEquals(1L, service.saveBenNCDCareVitalDetails(json("{\"height_cm\":170}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenNCDCareVitalDetails should report a failure when the anthropometry was not stored") + void saveVitals_shouldReportFailureWhenAnthropometryNotStored() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryPhysicalAnthropometryDetails(any())).thenReturn(null); + + assertNull(service.saveBenNCDCareVitalDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenNCDCareVitalDetails should report nothing for a null payload") + void saveVitals_shouldReportNothingForNullPayload() throws Exception { + assertNull(service.saveBenNCDCareVitalDetails(null, VISIT_ID, VISIT_CODE)); + } + } + + @Nested + @DisplayName("read endpoints") + class ReadTests { + + @Test + @DisplayName("getBenVisitDetailsFrmNurseNCDCare should assemble the visit, adherence and investigations") + void getVisitDetails_shouldAssembleVisitSections() { + when(commonNurseServiceImpl.getBenAdherence(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + when(commonNurseServiceImpl.getLabTestOrders(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + + String result = service.getBenVisitDetailsFrmNurseNCDCare(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("NCDCareNurseVisitDetail")); + assertTrue(result.contains("BenAdherence")); + assertTrue(result.contains("Investigation")); + } + + @Test + @DisplayName("getBenNCDCareHistoryDetails should assemble every stored history section") + void getHistoryDetails_shouldAssembleStoredHistorySections() { + when(commonNurseServiceImpl.getPastHistoryData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.BenMedHistory()); + when(commonNurseServiceImpl.getFeedingHistory(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.ChildFeedingDetails()); + + String result = service.getBenNCDCareHistoryDetails(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("PastHistory")); + assertTrue(result.contains("FeedingHistory")); + } + + @Test + @DisplayName("getBeneficiaryVitalDetails should assemble the anthropometry and the physical vitals") + void getVitalDetails_shouldAssembleVitalSections() { + when(commonNurseServiceImpl.getBeneficiaryPhysicalAnthropometryDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn("{}"); + when(commonNurseServiceImpl.getBeneficiaryPhysicalVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + + String result = service.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("benAnthropometryDetail")); + assertTrue(result.contains("benPhysicalVitalDetail")); + } + + @Test + @DisplayName("getBenNCDCareNurseData should assemble the vitals and the history") + void getNurseData_shouldAssembleVitalsAndHistory() { + String result = service.getBenNCDCareNurseData(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("vitals")); + assertTrue(result.contains("history")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorNCDCare should assemble the whole doctor case record") + void getCaseRecord_shouldAssembleDoctorCaseRecord() throws Exception { + when(commonNurseServiceImpl.getGraphicalTrendData(BEN_REG_ID, "ncdCare")).thenReturn(new HashMap<>()); + + String result = service.getBenCaseRecordFromDoctorNCDCare(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("findings")); + assertTrue(result.contains("diagnosis")); + assertTrue(result.contains("prescription")); + assertTrue(result.contains("LabReport")); + assertTrue(result.contains("GraphData")); + } + } + + @Nested + @DisplayName("saveDoctorData") + class SaveDoctorDataTests + + { + private JsonObject doctorRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22,\"providerServiceMapID\":9," + + "\"createdBy\":\"doctor1\",\"doctorSignatureFlag\":true," + + "\"findings\":{}," + + "\"diagnosis\":{\"specialistDiagnosis\":\"NCD follow up\"," + + " \"provisionalDiagnosisList\":[{\"term\":\"Diabetes\",\"conceptID\":\"73211009\"}]}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + } + + @BeforeEach + void stubDoctorCollaborators() throws Exception { + when(commonDoctorServiceImpl.saveDocFindings(any())).thenReturn(1); + when(commonNurseServiceImpl.savePrescriptionDetailsAndGetPrescriptionID(any(), any(), any(), any(), any(), + any(), any(), any(), any(), any())).thenReturn(4L); + when(ncdCareDoctorServiceImpl.saveNCDDiagnosisData(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.saveBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + } + + @Test + @DisplayName("saveDoctorData should save the findings, diagnosis, tests, drugs and referral") + void saveDoctorData_shouldSaveWholeCaseRecord() throws Exception { + assertEquals(1L, service.saveDoctorData(doctorRequest(), AUTHORIZATION)); + + verify(commonDoctorServiceImpl).saveDocFindings(any()); + verify(ncdCareDoctorServiceImpl).saveNCDDiagnosisData(any()); + verify(commonNurseServiceImpl).saveBenInvestigation(any()); + verify(commonNurseServiceImpl).saveBenPrescribedDrugsList(any()); + verify(commonDoctorServiceImpl).saveBenReferDetails(any()); + } + + @Test + @DisplayName("saveDoctorData should succeed for a case record with only an investigation section") + void saveDoctorData_shouldSucceedForMinimalCaseRecord() throws Exception { + assertEquals(1L, service.saveDoctorData(json("{\"beneficiaryRegID\":11,\"investigation\":{}}"), + AUTHORIZATION)); + + verify(commonDoctorServiceImpl, never()).saveDocFindings(any()); + verify(commonNurseServiceImpl, never()).saveBenPrescribedDrugsList(any()); + } + + @Test + @DisplayName("saveDoctorData should return nothing for a null request") + void saveDoctorData_shouldReturnNothingForNullRequest() throws Exception { + assertNull(service.saveDoctorData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("saveDoctorData should fail when a section could not be stored") + void saveDoctorData_shouldFailWhenSectionNotStored() throws Exception { + when(ncdCareDoctorServiceImpl.saveNCDDiagnosisData(any())).thenReturn(0L); + + assertThrows(RuntimeException.class, () -> service.saveDoctorData(doctorRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveDoctorData should fail when the beneficiary flow could not be advanced") + void saveDoctorData_shouldFailWhenFlowNotAdvanced() throws Exception { + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(0); + + assertThrows(RuntimeException.class, () -> service.saveDoctorData(doctorRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveDoctorData should notify a scheduled teleconsultation by SMS") + void saveDoctorData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.saveDoctorData(doctorRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), + any(), anyString(), any(), eq(AUTHORIZATION)); + } + } + + @Nested + @DisplayName("update endpoints") + class UpdateTests { + + @Test + @DisplayName("updateBenHistoryDetails should succeed when no history section was captured") + void updateHistory_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1, service.updateBenHistoryDetails(json("{}"))); + } + + @Test + @DisplayName("updateBenVitalDetails should confirm the update when both sections changed") + void updateVitals_shouldConfirmUpdate() throws Exception { + when(commonNurseServiceImpl.updateANCAnthropometryDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.updateANCPhysicalVitalDetails(any())).thenReturn(1); + + assertEquals(1, service.updateBenVitalDetails(json("{\"height_cm\":170}"))); + } + + @Test + @DisplayName("updateBenVitalDetails should report no change when a section did not change") + void updateVitals_shouldReportNoChangeWhenSectionUnchanged() throws Exception { + when(commonNurseServiceImpl.updateANCAnthropometryDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.updateANCPhysicalVitalDetails(any())).thenReturn(0); + + assertEquals(0, service.updateBenVitalDetails(json("{}"))); + } + + @Test + @DisplayName("updateBenVitalDetails should succeed for a null payload") + void updateVitals_shouldSucceedForNullPayload() throws Exception { + assertEquals(1, service.updateBenVitalDetails(null)); + } + + @Test + @DisplayName("updateNCDCareDoctorData should return nothing for a null request") + void updateDoctorData_shouldReturnNothingForNullRequest() throws Exception { + assertNull(service.updateNCDCareDoctorData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("updateNCDCareDoctorData should update the whole case record") + void updateDoctorData_shouldUpdateWholeCaseRecord() throws Exception { + when(commonDoctorServiceImpl.updateDocFindings(any())).thenReturn(1); + when(ncdCareDoctorServiceImpl.updateBenNCDCareDiagnosis(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.updateBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataUpdate(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + + JsonObject request = json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22," + + "\"findings\":{},\"diagnosis\":{\"prescriptionID\":4,\"specialistDiagnosis\":\"NCD\"}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + + assertEquals(1L, service.updateNCDCareDoctorData(request, AUTHORIZATION)); + } + + @Test + @DisplayName("updateNCDCareDoctorData should fail when the beneficiary flow could not be advanced") + void updateDoctorData_shouldFailWhenFlowNotAdvanced() throws Exception { + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataUpdate(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(0); + + assertThrows(RuntimeException.class, () -> service.updateNCDCareDoctorData( + json("{\"beneficiaryRegID\":11,\"investigation\":{}}"), AUTHORIZATION)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("NCD care captured-section updates") + class CapturedSectionUpdateTests { + + @Test + @DisplayName("updateBenHistoryDetails should update every captured history section") + void updateBenHistoryDetails_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertDoesNotThrow(() -> service.updateBenHistoryDetails( + com.google.gson.JsonParser.parseString("{\"pastHistory\":{},\"comorbidConditions\":{},\"medicationHistory\":{},\"personalHistory\":{},\"familyHistory\":{},\"menstrualHistory\":{},\"femaleObstetricHistory\":{},\"immunizationHistory\":{},\"childVaccineDetails\":{},\"developmentHistory\":{},\"feedingHistory\":{},\"perinatalHistroy\":{},\"allergyHistory\":{}}").getAsJsonObject())); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/ncdscreening/NCDSCreeningDoctorServiceImplTest.java b/src/test/java/com/iemr/tm/service/ncdscreening/NCDSCreeningDoctorServiceImplTest.java new file mode 100644 index 00000000..3a350c14 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/ncdscreening/NCDSCreeningDoctorServiceImplTest.java @@ -0,0 +1,75 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.ncdscreening; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.quickConsultation.PrescriptionDetailRepo; +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.service.tele_consultation.SMSGatewayServiceImpl; +import com.iemr.tm.service.tele_consultation.TeleConsultationServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NCDSCreeningDoctorServiceImpl Test Suite") +class NCDSCreeningDoctorServiceImplTest { + + @Mock + private PrescriptionDetailRepo prescriptionDetailRepo; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private TeleConsultationServiceImpl teleConsultationServiceImpl; + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + + @InjectMocks + private NCDSCreeningDoctorServiceImpl service; + + @Test + @DisplayName("updateDoctorData should reject a request that carries no beneficiary details") + void updateDoctorData_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(RuntimeException.class, () -> service.updateDoctorData(new com.google.gson.JsonObject(), "{}")); + } + + @Test + @DisplayName("getNCDDiagnosisData should answer for a well formed request") + void getNCDDiagnosisData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getNCDDiagnosisData(11L, 11L)); + } +} diff --git a/src/test/java/com/iemr/tm/service/ncdscreening/NCDScreeningNurseServiceImplTest.java b/src/test/java/com/iemr/tm/service/ncdscreening/NCDScreeningNurseServiceImplTest.java new file mode 100644 index 00000000..856f73a0 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/ncdscreening/NCDScreeningNurseServiceImplTest.java @@ -0,0 +1,65 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.ncdscreening; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.nurse.ncdscreening.NCDScreeningRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NCDScreeningNurseServiceImpl Test Suite") +class NCDScreeningNurseServiceImplTest { + + @Mock + private NCDScreeningRepo ncdScreeningRepo; + + @InjectMocks + private NCDScreeningNurseServiceImpl service; + + @Test + @DisplayName("saveNCDScreeningDetails should answer for a well formed request") + void saveNCDScreeningDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveNCDScreeningDetails(org.mockito.Mockito.mock(com.iemr.tm.data.ncdScreening.NCDScreening.class))); + } + + @Test + @DisplayName("getNCDScreeningDetails should answer for a well formed request") + void getNCDScreeningDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getNCDScreeningDetails(11L, 11L)); + } + + @Test + @DisplayName("updateNCDScreeningDetails should answer for a well formed request") + void updateNCDScreeningDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateNCDScreeningDetails(org.mockito.Mockito.mock(com.iemr.tm.data.ncdScreening.NCDScreening.class))); + } +} diff --git a/src/test/java/com/iemr/tm/service/ncdscreening/NCDScreeningServiceImplTest.java b/src/test/java/com/iemr/tm/service/ncdscreening/NCDScreeningServiceImplTest.java new file mode 100644 index 00000000..5d5cddfd --- /dev/null +++ b/src/test/java/com/iemr/tm/service/ncdscreening/NCDScreeningServiceImplTest.java @@ -0,0 +1,633 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.ncdscreening; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyShort; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.iemr.tm.data.nurse.CommonUtilityClass; +import com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ; +import com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.nurse.anc.BenAdherenceRepo; +import com.iemr.tm.repo.nurse.ncdscreening.IDRSDataRepo; +import com.iemr.tm.repo.quickConsultation.BenChiefComplaintRepo; +import com.iemr.tm.repo.quickConsultation.PrescriptionDetailRepo; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.service.labtechnician.LabTechnicianServiceImpl; +import com.iemr.tm.service.tele_consultation.SMSGatewayServiceImpl; +import com.iemr.tm.service.tele_consultation.TeleConsultationServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NCDScreeningServiceImpl Test Suite") +class NCDScreeningServiceImplTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_ID = 3L; + private static final Long VISIT_CODE = 22L; + + private static final String FULL_HISTORY = "{\"pastHistory\":{},\"comorbidConditions\":{}," + + "\"medicationHistory\":{\"medicationHistoryList\":[{\"currentMedication\":\"Iron\"}]}," + + "\"personalHistory\":{},\"familyHistory\":{},\"menstrualHistory\":{}," + + "\"femaleObstetricHistory\":{},\"immunizationHistory\":{},\"childVaccineDetails\":{}," + + "\"developmentHistory\":{},\"feedingHistory\":{},\"perinatalHistroy\":{}," + + "\"physicalActivityHistory\":{}}"; + + /** One answered IDRS question, as the screening form submits it. */ + private static final String IDRS_REQUEST = "{\"beneficiaryRegID\":11,\"questionArray\":" + + "[{\"idrsQuestionID\":1,\"answer\":\"Yes\",\"question\":\"Family history?\"," + + " \"diseaseQuestionType\":\"Diabetes\"}]}"; + + @Mock + private NCDScreeningNurseServiceImpl ncdScreeningNurseServiceImpl; + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private LabTechnicianServiceImpl labTechnicianServiceImpl; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private TeleConsultationServiceImpl teleConsultationServiceImpl; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + @Mock + private PrescriptionDetailRepo prescriptionDetailRepo; + @Mock + private NCDSCreeningDoctorServiceImpl ncdSCreeningDoctorServiceImpl; + @Mock + private IDRSDataRepo iDrsDataRepo; + @Mock + private BenChiefComplaintRepo benChiefComplaintRepo; + @Mock + private BenAdherenceRepo benAdherenceRepo; + + @InjectMocks + private NCDScreeningServiceImpl service; + + private JsonObject json(String raw) { + return JsonParser.parseString(raw).getAsJsonObject(); + } + + private JsonObject nurseRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"providerServiceMapID\":9,\"vanID\":7,\"sessionID\":1," + + "\"benFlowID\":5,\"createdBy\":\"nurse1\"," + + "\"visitDetails\":{" + + " \"visitDetails\":{\"beneficiaryRegID\":11,\"visitReason\":\"New Chief Complaint\"," + + " \"visitCategory\":\"NCD screening\"}," + + " \"chiefComplaints\":[{\"chiefComplaintID\":3}]" + + "}," + + "\"historyDetails\":" + FULL_HISTORY + ",\"vitalDetails\":{\"height_cm\":170}}"); + } + + private CommonUtilityClass utility() { + CommonUtilityClass utility = new CommonUtilityClass(); + utility.setBeneficiaryRegID(BEN_REG_ID); + utility.setVanID(7); + utility.setSessionID(1); + utility.setProviderServiceMapID(9); + utility.setCreatedBy("nurse1"); + return utility; + } + + private TeleconsultationRequestOBJ teleconsultationRequest(boolean walkIn) { + TeleconsultationRequestOBJ request = new TeleconsultationRequestOBJ(); + request.setWalkIn(walkIn); + request.setUserID(42); + request.setSpecializationID(2); + request.setTmRequestID(8L); + request.setAllocationDate(new Timestamp(1_700_000_000_000L)); + return request; + } + + @BeforeEach + @DisplayName("Wire the collaborators that every save path needs") + void setUp() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(0); + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(VISIT_ID); + when(commonNurseServiceImpl.generateVisitCode(anyLong(), any(), any())).thenReturn(VISIT_CODE); + when(commonNurseServiceImpl.saveBenChiefComplaints(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalAnthropometryDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPastHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenComorbidConditions(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMedicationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePersonalHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveAllergyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenFamilyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenFamilyHistoryNCDScreening(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMenstrualHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveFemaleObstetricHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveImmunizationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildOptionalVaccineDetail(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildDevelopmentHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildFeedingHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePerinatalHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePhysicalActivity(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveIDRS(any())).thenReturn(1L); + when(commonBenStatusFlowServiceImpl.updateBenFlowNurseAfterNurseActivity(any(), anyLong(), anyLong(), + anyString(), anyString(), anyShort(), anyShort(), anyShort(), anyShort(), anyShort(), anyLong(), any(), + anyShort(), any(), any())).thenReturn(1); + } + + @Nested + @DisplayName("saveNCDScreeningNurseData") + class SaveNurseDataTests { + + @Test + @DisplayName("saveNCDScreeningNurseData should save the visit, history and vitals") + void saveNurseData_shouldSaveEverySection() throws Exception { + String result = service.saveNCDScreeningNurseData(nurseRequest(), AUTHORIZATION); + + assertTrue(result.contains("Data saved successfully")); + assertTrue(result.contains("\"visitCode\":\"22\"")); + } + + @Test + @DisplayName("saveNCDScreeningNurseData should report an already saved visit") + void saveNurseData_shouldReportAlreadySavedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.saveNCDScreeningNurseData(nurseRequest(), AUTHORIZATION) + .contains("Data already saved")); + } + + @Test + @DisplayName("saveNCDScreeningNurseData should reject a request without visit details") + void saveNurseData_shouldRejectRequestWithoutVisitDetails() { + assertThrows(Exception.class, () -> service.saveNCDScreeningNurseData(json("{}"), AUTHORIZATION)); + } + + @Test + @DisplayName("saveNCDScreeningNurseData should fail when the visit could not be created") + void saveNurseData_shouldFailWhenVisitNotCreated() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(0L); + + assertThrows(RuntimeException.class, + () -> service.saveNCDScreeningNurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("saveNCDScreeningNurseData should notify a scheduled teleconsultation by SMS") + void saveNurseData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.saveNCDScreeningNurseData(nurseRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), + anyString(), anyString(), any(), eq(AUTHORIZATION)); + } + } + + @Nested + @DisplayName("deleteVisitDetails") + class DeleteVisitDetailsTests { + + @Test + @DisplayName("deleteVisitDetails should remove the visit rows for a created visit") + void deleteVisitDetails_shouldRemoveVisitRows() throws Exception { + when(benVisitDetailRepo.getVisitCode(BEN_REG_ID, 9)).thenReturn(VISIT_CODE); + + service.deleteVisitDetails(nurseRequest()); + + verify(benVisitDetailRepo).deleteVisitDetails(VISIT_CODE); + } + + @Test + @DisplayName("deleteVisitDetails should do nothing for a request without visit details") + void deleteVisitDetails_shouldDoNothingWithoutVisitDetails() throws Exception { + service.deleteVisitDetails(json("{}")); + + verify(benVisitDetailRepo, never()).deleteVisitDetails(anyLong()); + } + } + + @Nested + @DisplayName("nurse section saves") + class SectionSaveTests { + + @Test + @DisplayName("saveBenVisitDetails should return the new visit id and code") + void saveBenVisitDetails_shouldReturnVisitIdAndCode() throws Exception { + Map result = service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), + utility()); + + assertEquals(VISIT_ID, result.get("visitID")); + assertEquals(VISIT_CODE, result.get("visitCode")); + } + + @Test + @DisplayName("saveBenVisitDetails should return nothing when the visit was already recorded") + void saveBenVisitDetails_shouldReturnNothingForAlreadyRecordedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), utility()) + .isEmpty()); + } + + @Test + @DisplayName("saveNCDScreeningVitalDetails should store the anthropometry and the physical vitals") + void saveScreeningVitals_shouldStoreAnthropometryAndPhysicalVitals() throws Exception { + assertEquals(1L, service.saveNCDScreeningVitalDetails( + json("{\"ncdScreeningDetails\":{\"height_cm\":170}}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveNCDScreeningVitalDetails should report a failure when the physical vitals were not stored") + void saveScreeningVitals_shouldReportFailureWhenPhysicalVitalsNotStored() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(null); + + assertNull(service.saveNCDScreeningVitalDetails(json("{\"ncdScreeningDetails\":{}}"), VISIT_ID, + VISIT_CODE)); + } + + @Test + @DisplayName("saveBenNCDCareHistoryDetails should store every captured history section") + void saveHistory_shouldStoreCapturedSections() throws Exception { + assertEquals(1L, service.saveBenNCDCareHistoryDetails(json(FULL_HISTORY), VISIT_ID, VISIT_CODE)); + verify(commonNurseServiceImpl).saveBenPastHistory(any()); + } + + @Test + @DisplayName("saveidrsDetails should store the captured IDRS answers") + void saveIdrsDetails_shouldStoreCapturedAnswers() throws Exception { + assertEquals(1L, service.saveidrsDetails(json(IDRS_REQUEST), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveidrsDetails should succeed when no IDRS answer was captured") + void saveIdrsDetails_shouldSucceedWithoutCapturedAnswers() throws Exception { + assertEquals(1L, service.saveidrsDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("savePhysicalActivityDetails should store the captured physical activity") + void savePhysicalActivity_shouldStoreCapturedActivity() throws Exception { + assertEquals(1L, service.savePhysicalActivityDetails( + json("{\"physicalActivityHistory\":{\"physicalActivityType\":\"Moderate\"}}"), VISIT_ID, + VISIT_CODE)); + } + + @Test + @DisplayName("savePhysicalActivityDetails should succeed when no physical activity was captured") + void savePhysicalActivity_shouldSucceedWithoutCapturedActivity() throws Exception { + assertEquals(1L, service.savePhysicalActivityDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenNCDCareVitalDetails should store the anthropometry and the physical vitals") + void saveCareVitals_shouldStoreAnthropometryAndPhysicalVitals() throws Exception { + assertEquals(1L, service.saveBenNCDCareVitalDetails(json("{\"height_cm\":170}"), VISIT_ID, VISIT_CODE)); + } + } + + @Nested + @DisplayName("read endpoints") + class ReadTests { + + @Test + @DisplayName("getNCDScreeningDetails should assemble the screening, anthropometry and vitals") + void getScreeningDetails_shouldAssembleScreeningSections() { + when(ncdScreeningNurseServiceImpl.getNCDScreeningDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + when(commonNurseServiceImpl.getBeneficiaryPhysicalAnthropometryDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn("{}"); + when(commonNurseServiceImpl.getBeneficiaryPhysicalVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + + String result = service.getNCDScreeningDetails(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("ncdScreeningDetails")); + assertTrue(result.contains("anthropometryDetails")); + } + + @Test + @DisplayName("getNCDScreeningDetails should return an empty payload when a section is missing") + void getScreeningDetails_shouldReturnEmptyPayloadWhenSectionMissing() { + when(ncdScreeningNurseServiceImpl.getNCDScreeningDetails(BEN_REG_ID, VISIT_CODE)).thenReturn(null); + + assertEquals("{}", service.getNCDScreeningDetails(BEN_REG_ID, VISIT_CODE)); + } + + @Test + @DisplayName("getNcdScreeningVisitCnt should report the next screening visit number") + void getVisitCount_shouldReportNextVisitNumber() { + when(beneficiaryFlowStatusRepo.getNcdScreeningVisitCount(BEN_REG_ID)).thenReturn(2L); + + assertTrue(service.getNcdScreeningVisitCnt(BEN_REG_ID).contains("3")); + } + + @Test + @DisplayName("getBenVisitDetailsFrmNurseNCDScreening should assemble the visit and the chief complaints") + void getVisitDetails_shouldAssembleVisitSections() { + when(commonNurseServiceImpl.getBenChiefComplaints(BEN_REG_ID, VISIT_CODE)).thenReturn("[]"); + + assertTrue(service.getBenVisitDetailsFrmNurseNCDScreening(BEN_REG_ID, VISIT_CODE) + .contains("BenChiefComplaints")); + } + + @Test + @DisplayName("getBenHistoryDetails should assemble the stored screening history") + void getHistoryDetails_shouldAssembleStoredSections() { + when(commonNurseServiceImpl.getFamilyHistoryDetail(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.BenFamilyHistory()); + + assertTrue(service.getBenHistoryDetails(BEN_REG_ID, VISIT_CODE).contains("FamilyHistory")); + } + + @Test + @DisplayName("getBenIdrsDetailsFrmNurse should assemble the stored IDRS answers") + void getIdrsDetails_shouldAssembleStoredAnswers() { + when(commonNurseServiceImpl.getBeneficiaryIdrsDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.ncdScreening.IDRSData()); + + assertTrue(service.getBenIdrsDetailsFrmNurse(BEN_REG_ID, VISIT_CODE).contains("IDRSDetail")); + } + + @Test + @DisplayName("getBeneficiaryVitalDetails should assemble the anthropometry and the physical vitals") + void getVitalDetails_shouldAssembleVitalSections() { + when(commonNurseServiceImpl.getBeneficiaryPhysicalAnthropometryDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn("{}"); + when(commonNurseServiceImpl.getBeneficiaryPhysicalVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + + assertTrue(service.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE).contains("benAnthropometryDetail")); + } + + @Test + @DisplayName("getBenNCDScreeningNurseData should assemble the nurse captured sections") + void getNurseData_shouldAssembleNurseSections() { + assertTrue(service.getBenNCDScreeningNurseData(BEN_REG_ID, VISIT_CODE).length() > 0); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorNCDScreening should assemble the whole doctor case record") + void getCaseRecord_shouldAssembleDoctorCaseRecord() throws Exception { + when(commonNurseServiceImpl.getGraphicalTrendData(anyLong(), anyString())).thenReturn(new HashMap<>()); + + String result = service.getBenCaseRecordFromDoctorNCDScreening(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("findings")); + assertTrue(result.contains("LabReport")); + } + } + + @Nested + @DisplayName("saveDoctorData") + class SaveDoctorDataTests { + + private JsonObject doctorRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22,\"providerServiceMapID\":9," + + "\"createdBy\":\"doctor1\",\"doctorSignatureFlag\":true,\"findings\":{}," + + "\"diagnosis\":{\"specialistDiagnosis\":\"Diabetes suspect\"}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + } + + @BeforeEach + void stubDoctorCollaborators() throws Exception { + when(commonDoctorServiceImpl.saveDocFindings(any())).thenReturn(1); + when(commonNurseServiceImpl.savePrescriptionDetailsAndGetPrescriptionID(any(), any(), any(), any(), any(), + any(), any(), any(), any(), any())).thenReturn(4L); + when(commonNurseServiceImpl.saveBenPrescription(any())).thenReturn(4L); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.saveBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + } + + @Test + @DisplayName("saveDoctorData should save the findings, diagnosis, tests, drugs and referral") + void saveDoctorData_shouldSaveWholeCaseRecord() throws Exception { + assertEquals(1L, service.saveDoctorData(doctorRequest(), AUTHORIZATION)); + + verify(commonDoctorServiceImpl).saveDocFindings(any()); + verify(commonNurseServiceImpl).saveBenInvestigation(any()); + } + + @Test + @DisplayName("saveDoctorData should return nothing for a null request") + void saveDoctorData_shouldReturnNothingForNullRequest() throws Exception { + assertNull(service.saveDoctorData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("saveDoctorData should fail when the beneficiary flow could not be advanced") + void saveDoctorData_shouldFailWhenFlowNotAdvanced() throws Exception { + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(0); + + assertThrows(RuntimeException.class, () -> service.saveDoctorData(doctorRequest(), AUTHORIZATION)); + } + } + + @Nested + @DisplayName("update endpoints") + class UpdateTests { + + @Test + @DisplayName("updateNurseNCDScreeningDetails should update the captured screening details") + void updateScreeningDetails_shouldUpdateCapturedDetails() throws Exception { + when(ncdScreeningNurseServiceImpl.updateNCDScreeningDetails(any())).thenReturn(1); + + assertEquals(1, service.updateNurseNCDScreeningDetails(json("{\"beneficiaryRegID\":11}"))); + } + + @Test + @DisplayName("updateBenVitalDetails should confirm the update when both sections changed") + void updateVitals_shouldConfirmUpdate() throws Exception { + when(commonNurseServiceImpl.updateANCAnthropometryDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.updateANCPhysicalVitalDetails(any())).thenReturn(1); + + assertEquals(1, service.updateBenVitalDetails(json("{\"height_cm\":170}"))); + } + + @Test + @DisplayName("updateBenVitalDetails should report no change when a section did not change") + void updateVitals_shouldReportNoChangeWhenSectionUnchanged() throws Exception { + when(commonNurseServiceImpl.updateANCAnthropometryDetails(any())).thenReturn(0); + + assertEquals(0, service.updateBenVitalDetails(json("{}"))); + } + + @Test + @DisplayName("UpdateIDRSScreen should update the captured IDRS answers") + void updateIdrsScreen_shouldUpdateCapturedAnswers() throws Exception { + assertEquals(1L, service.UpdateIDRSScreen(json("{\"idrsDetails\":" + IDRS_REQUEST + "}"))); + } + + @Test + @DisplayName("UpdateIDRSScreen should return nothing when no IDRS answer was captured") + void updateIdrsScreen_shouldReturnNothingWithoutCapturedAnswers() throws Exception { + assertNull(service.UpdateIDRSScreen(json("{}"))); + } + + @Test + @DisplayName("UpdateNCDScreeningHistory should update the captured screening history") + void updateScreeningHistory_shouldUpdateCapturedHistory() throws Exception { + when(commonNurseServiceImpl.updateBenFamilyHistoryNCDScreening(any())).thenReturn(1); + when(commonNurseServiceImpl.updateBenPhysicalActivityHistoryNCDScreening(any())).thenReturn(1); + + assertEquals(1, service.UpdateNCDScreeningHistory( + json("{\"familyHistory\":{},\"physicalActivityHistory\":{},\"personalHistory\":{}}"))); + } + + @Test + @DisplayName("UpdateNCDScreeningHistory should report no change when no history section was captured") + void updateScreeningHistory_shouldReportNoChangeWithoutCapturedHistory() throws Exception { + assertEquals(0, service.UpdateNCDScreeningHistory(json("{}"))); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("IDRS suspected and confirmed diseases") + class IdrsDiseaseTests { + + private static final String WITH_DISEASES = "{\"beneficiaryRegID\":11,\"visitCode\":22,\"idrsScore\":40," + + "\"suspectArray\":[\"Diabetes\",\"Hypertension\"]," + + "\"confirmArray\":[\"Diabetes\"]," + + "\"questionArray\":[{\"idrsQuestionID\":1,\"answer\":\"Yes\",\"question\":\"Family history?\"," + + " \"diseaseQuestionType\":\"Diabetes\"}," + + " {\"idrsQuestionID\":2,\"answer\":\"No\",\"question\":\"Waist?\"," + + " \"diseaseQuestionType\":\"Diabetes\"}]}"; + + private static final String WITHOUT_QUESTIONS = "{\"beneficiaryRegID\":11,\"visitCode\":22,\"idrsScore\":40," + + "\"suspectArray\":[\"Diabetes\",\"Hypertension\"],\"confirmArray\":[\"Diabetes\"]}"; + + @Test + @DisplayName("saveidrsDetails should flatten the suspected and confirmed diseases per answered question") + void saveIdrs_shouldFlattenDiseasesPerQuestion() throws Exception { + assertEquals(1L, service.saveidrsDetails(json(WITH_DISEASES), VISIT_ID, VISIT_CODE)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.ncdScreening.IDRSData.class); + org.mockito.Mockito.verify(commonNurseServiceImpl, org.mockito.Mockito.atLeastOnce()) + .saveIDRS(captor.capture()); + com.iemr.tm.data.ncdScreening.IDRSData saved = captor.getValue(); + assertEquals("Diabetes,Hypertension", saved.getSuspectedDisease()); + assertEquals("Diabetes", saved.getConfirmedDisease()); + } + + @Test + @DisplayName("saveidrsDetails should flatten the diseases when no question was answered") + void saveIdrs_shouldFlattenDiseasesWithoutQuestions() throws Exception { + assertEquals(1L, service.saveidrsDetails(json(WITHOUT_QUESTIONS), VISIT_ID, VISIT_CODE)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.ncdScreening.IDRSData.class); + org.mockito.Mockito.verify(commonNurseServiceImpl).saveIDRS(captor.capture()); + assertEquals("Diabetes,Hypertension", captor.getValue().getSuspectedDisease()); + } + + @Test + @DisplayName("UpdateIDRSScreen should update the suspected diseases for each answered question") + void updateIdrs_shouldUpdateSuspectedDiseasesPerQuestion() throws Exception { + when(iDrsDataRepo.updateSuspectedDiseases(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(1); + + assertEquals(1L, service.UpdateIDRSScreen(json("{\"idrsDetails\":" + WITH_DISEASES + "}"))); + + org.mockito.Mockito.verify(iDrsDataRepo, org.mockito.Mockito.atLeastOnce()) + .updateSuspectedDiseases(11L, 22L, "Diabetes,Hypertension"); + } + + @Test + @DisplayName("UpdateIDRSScreen should update the diseases and the score when no question was answered") + void updateIdrs_shouldUpdateDiseasesAndScoreWithoutQuestions() throws Exception { + when(iDrsDataRepo.updateConfirmedDiseases(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(1); + when(iDrsDataRepo.updateSuspectedDiseases(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(1); + when(iDrsDataRepo.updateIdrsScore(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyInt())) + .thenReturn(1); + + assertEquals(1L, service.UpdateIDRSScreen(json("{\"idrsDetails\":" + WITHOUT_QUESTIONS + "}"))); + + org.mockito.Mockito.verify(iDrsDataRepo).updateConfirmedDiseases(11L, 22L, "Diabetes"); + org.mockito.Mockito.verify(iDrsDataRepo, org.mockito.Mockito.atLeastOnce()) + .updateIdrsScore(org.mockito.ArgumentMatchers.eq(11L), org.mockito.ArgumentMatchers.eq(22L), + org.mockito.ArgumentMatchers.anyInt()); + } + + @Test + @DisplayName("UpdateIDRSScreen should report nothing when no disease could be updated") + void updateIdrs_shouldReportNothingWhenNoDiseaseUpdated() throws Exception { + when(iDrsDataRepo.updateConfirmedDiseases(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(0); + when(iDrsDataRepo.updateSuspectedDiseases(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(0); + when(iDrsDataRepo.updateIdrsScore(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyInt())) + .thenReturn(0); + + assertNull(service.UpdateIDRSScreen(json("{\"idrsDetails\":" + WITHOUT_QUESTIONS + "}"))); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/nurse/NurseServiceImplTest.java b/src/test/java/com/iemr/tm/service/nurse/NurseServiceImplTest.java new file mode 100644 index 00000000..613f482c --- /dev/null +++ b/src/test/java/com/iemr/tm/service/nurse/NurseServiceImplTest.java @@ -0,0 +1,54 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.nurse; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("NurseServiceImpl Test Suite") +class NurseServiceImplTest { + + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + + @InjectMocks + private NurseServiceImpl service; + + + @Test + @DisplayName("getBeneficiaryVisitHistory should answer for a well formed request") + void getBeneficiaryVisitHistory_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBeneficiaryVisitHistory(11L)); + } +} diff --git a/src/test/java/com/iemr/tm/service/nurse/vitals/AnthropometryVitalsServiceImplTest.java b/src/test/java/com/iemr/tm/service/nurse/vitals/AnthropometryVitalsServiceImplTest.java new file mode 100644 index 00000000..9aa99640 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/nurse/vitals/AnthropometryVitalsServiceImplTest.java @@ -0,0 +1,53 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.nurse.vitals; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.nurse.BenAnthropometryRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("AnthropometryVitalsServiceImpl Test Suite") +class AnthropometryVitalsServiceImplTest { + + @Mock + private BenAnthropometryRepo benAnthropometryRepo; + + @InjectMocks + private AnthropometryVitalsServiceImpl service; + + @Test + @DisplayName("getBeneficiaryHeightDetails should answer for a well formed request") + void getBeneficiaryHeightDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBeneficiaryHeightDetails(11L)); + } +} diff --git a/src/test/java/com/iemr/tm/service/patientApp/master/CommonPatientAppMasterServiceImplTest.java b/src/test/java/com/iemr/tm/service/patientApp/master/CommonPatientAppMasterServiceImplTest.java new file mode 100644 index 00000000..705bc61a --- /dev/null +++ b/src/test/java/com/iemr/tm/service/patientApp/master/CommonPatientAppMasterServiceImplTest.java @@ -0,0 +1,427 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.patientApp.master; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo; +import com.iemr.tm.repo.doctor.ChiefComplaintMasterRepo; +import com.iemr.tm.repo.masterrepo.covid19.CovidContactHistoryMasterRepo; +import com.iemr.tm.repo.masterrepo.covid19.CovidRecommnedationMasterRepo; +import com.iemr.tm.repo.masterrepo.covid19.CovidSymptomsMasterRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.quickBlox.QuickBloxRepo; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.service.covid19.Covid19ServiceImpl; +import com.iemr.tm.service.generalOPD.GeneralOPDDoctorServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CommonPatientAppMasterServiceImpl Test Suite") +class CommonPatientAppMasterServiceImplTest { + + @Mock + private CovidSymptomsMasterRepo covidSymptomsMasterRepo; + @Mock + private CovidContactHistoryMasterRepo covidContactHistoryMasterRepo; + @Mock + private CovidRecommnedationMasterRepo covidRecommnedationMasterRepo; + @Mock + private ChiefComplaintMasterRepo chiefComplaintMasterRepo; + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private Covid19ServiceImpl covid19ServiceImpl; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private GeneralOPDDoctorServiceImpl generalOPDDoctorServiceImpl; + @Mock + private QuickBloxRepo quickBloxRepo; + + @InjectMocks + private CommonPatientAppMasterServiceImpl service; + + @Test + @DisplayName("getChiefComplaintsMaster should answer for a well formed request") + void getChiefComplaintsMaster_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getChiefComplaintsMaster(9, 9, "{}")); + } + + @Test + @DisplayName("getCovidMaster should answer for a well formed request") + void getCovidMaster_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getCovidMaster(9, 9, "{}")); + } + + @Test + @DisplayName("saveCovidScreeningData should reject a request that carries no beneficiary details") + void saveCovidScreeningData_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(RuntimeException.class, () -> service.saveCovidScreeningData("{}")); + } + + @Test + @DisplayName("savechiefComplaintsData should reject a request that carries no beneficiary details") + void savechiefComplaintsData_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(RuntimeException.class, () -> service.savechiefComplaintsData("{}")); + } + + @Test + @DisplayName("bookTCSlotData should reject a request that carries no beneficiary details") + void bookTCSlotData_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(RuntimeException.class, () -> service.bookTCSlotData("{}", "{}")); + } + + @Test + @DisplayName("getPatientEpisodeData should reject a request that carries no beneficiary details") + void getPatientEpisodeData_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(RuntimeException.class, () -> service.getPatientEpisodeData("{}")); + } + + @Test + @DisplayName("getPatientBookedSlots should reject a request that carries no beneficiary details") + void getPatientBookedSlots_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(RuntimeException.class, () -> service.getPatientBookedSlots("{}")); + } + + @Test + @DisplayName("saveSpecialistDiagnosisData should reject a request that carries no beneficiary details") + void saveSpecialistDiagnosisData_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(RuntimeException.class, () -> service.saveSpecialistDiagnosisData("{}")); + } + + @Test + @DisplayName("getSpecialistDiagnosisData should reject a request that carries no beneficiary details") + void getSpecialistDiagnosisData_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(RuntimeException.class, () -> service.getSpecialistDiagnosisData("{}")); + } + + @Test + @DisplayName("getPatientsLast_3_Episode should reject a request that carries no beneficiary details") + void getPatientsLast_3_Episode_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(RuntimeException.class, () -> service.getPatientsLast_3_Episode("{}")); + } + + @org.junit.jupiter.api.Nested + @DisplayName("patient app episodes and diagnosis") + class PatientAppEpisodeTests { + + private static final String BEN_REQUEST = "{\"beneficiaryRegID\":11,\"beneficiaryID\":7,\"vanID\":7," + + "\"visitCode\":22,\"benVisitID\":3,\"providerServiceMapID\":9,\"createdBy\":\"patient1\"," + + "\"parkingPlaceID\":8,\"specialistDiagnosis\":\"Covid suspect\",\"benFlowID\":5," + + "\"visitReason\":\"New Chief Complaint\",\"visitCategory\":\"COVID-19 Screening\"," + + "\"isCovidFlowDone\":true," + + "\"chiefComplaints\":{\"pastIllness\":[{\"illnessTypeID\":3,\"illnessType\":\"Fever\"}]}," + + "\"covidDetails\":{\"suspectedStatusUI\":\"Suspected\"}}"; + + @org.junit.jupiter.api.BeforeEach + void stubVisitCreation() { + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBeneficiaryVisitDetails(org.mockito.ArgumentMatchers.any())).thenReturn(3L); + org.mockito.Mockito.when(commonNurseServiceImpl.generateVisitCode(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(22L); + } + + @Test + @DisplayName("getChiefComplaintsMaster should render the chief complaint master") + void getChiefComplaintsMaster_shouldRenderMaster() { + org.mockito.Mockito.when(chiefComplaintMasterRepo.getChiefComplaintMaster()) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getChiefComplaintsMaster(1, 9, "Female")); + } + + @Test + @DisplayName("getCovidMaster should render the covid screening master") + void getCovidMaster_shouldRenderMaster() { + org.junit.jupiter.api.Assertions.assertNotNull(service.getCovidMaster(1, 9, "Female")); + } + + @Test + @DisplayName("saveCovidScreeningData should create the episode and store the screening feedback") + void saveCovidScreening_shouldCreateEpisodeAndStoreFeedback() throws Exception { + org.mockito.Mockito.when(covid19ServiceImpl.saveCovidDetails(org.mockito.ArgumentMatchers.any())) + .thenReturn(1); + + org.junit.jupiter.api.Assertions.assertNotNull(service.saveCovidScreeningData(BEN_REQUEST)); + } + + @Test + @DisplayName("saveCovidScreeningData should still answer when the screening feedback was not stored") + void saveCovidScreening_shouldStillAnswerWhenFeedbackNotStored() throws Exception { + org.mockito.Mockito.when(covid19ServiceImpl.saveCovidDetails(org.mockito.ArgumentMatchers.any())) + .thenReturn(null); + + org.junit.jupiter.api.Assertions.assertNotNull(service.saveCovidScreeningData(BEN_REQUEST)); + } + + @Test + @DisplayName("saveCovidScreeningData should fail when the episode could not be created") + void saveCovidScreening_shouldFailWhenEpisodeNotCreated() { + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBeneficiaryVisitDetails(org.mockito.ArgumentMatchers.any())).thenReturn(null); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.saveCovidScreeningData(BEN_REQUEST)); + } + + @Test + @DisplayName("savechiefComplaintsData should create the episode and store the complaints") + void saveChiefComplaints_shouldCreateEpisodeAndStoreComplaints() throws Exception { + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBenChiefComplaints(org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertNotNull(service.savechiefComplaintsData(BEN_REQUEST)); + } + + + @Test + @DisplayName("getPatientEpisodeData should assemble the covid details and the complaints") + void getPatientEpisodeData_shouldAssembleEpisode() throws Exception { + org.mockito.Mockito.when(covid19ServiceImpl.getBenVisitDetailsFrmNurseCovid19(11L, 22L)) + .thenReturn("{\"covidDetails\":\"{}\"}"); + org.mockito.Mockito.when(commonNurseServiceImpl.getBenChiefComplaints(11L, 22L)).thenReturn("[]"); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getPatientEpisodeData(BEN_REQUEST)); + } + + @Test + @DisplayName("saveSpecialistDiagnosisData should store the diagnosis and advance the flow") + void saveSpecialistDiagnosis_shouldStoreAndAdvanceFlow() throws Exception { + org.mockito.Mockito.when(commonNurseServiceImpl.saveBenPrescription(org.mockito.ArgumentMatchers.any())) + .thenReturn(4L); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateBenFlowStatusAfterSpecialistMobileAPP( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(4L, service.saveSpecialistDiagnosisData(BEN_REQUEST)); + } + + @Test + @DisplayName("saveSpecialistDiagnosisData should fail when the flow could not be advanced") + void saveSpecialistDiagnosis_shouldFailWhenFlowNotAdvanced() { + org.mockito.Mockito.when(commonNurseServiceImpl.saveBenPrescription(org.mockito.ArgumentMatchers.any())) + .thenReturn(4L); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateBenFlowStatusAfterSpecialistMobileAPP( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.saveSpecialistDiagnosisData(BEN_REQUEST)); + } + + @Test + @DisplayName("getSpecialistDiagnosisData should return the recorded diagnosis") + void getSpecialistDiagnosis_shouldReturnRecordedDiagnosis() throws Exception { + org.mockito.Mockito.when(generalOPDDoctorServiceImpl.getGeneralOPDDiagnosisDetails(11L, 22L)) + .thenReturn("{}"); + + org.junit.jupiter.api.Assertions.assertEquals("{}", service.getSpecialistDiagnosisData(BEN_REQUEST)); + } + + @Test + @DisplayName("getSpecialistDiagnosisData should fail when no diagnosis was recorded") + void getSpecialistDiagnosis_shouldFailWithoutRecordedDiagnosis() { + org.mockito.Mockito.when(generalOPDDoctorServiceImpl.getGeneralOPDDiagnosisDetails(11L, 22L)) + .thenReturn(null); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.getSpecialistDiagnosisData(BEN_REQUEST)); + } + + @Test + @DisplayName("getPatientsLast_3_Episode should render the three most recent episodes") + void getLastThreeEpisodes_shouldRenderRecentEpisodes() throws Exception { + org.junit.jupiter.api.Assertions.assertNotNull(service.getPatientsLast_3_Episode(BEN_REQUEST)); + } + + @Test + @DisplayName("getPatientBookedSlots should render the slots booked for the beneficiary") + void getBookedSlots_shouldRenderBookedSlots() throws Exception { + org.junit.jupiter.api.Assertions.assertNotNull(service.getPatientBookedSlots(BEN_REQUEST)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("teleconsultation slot booking and booked slots") + class SlotBookingTests { + + private static final String BOOK_REQUEST = "{\"beneficiaryRegID\":11,\"beneficiaryID\":7,\"visitCode\":22," + + "\"createdBy\":\"9999999999\",\"firstName\":\"Asha\",\"lastName\":\"Devi\",\"age\":31," + + "\"ageUnits\":\"years\",\"genderID\":2,\"genderName\":\"Female\",\"districtID\":31," + + "\"districtName\":\"Nagpur\",\"villageId\":41,\"villageName\":\"Kamptee\"," + + "\"providerServiceMapID\":9,\"vanID\":7,\"parkingPlaceID\":8}"; + + private com.iemr.tm.data.nurse.BeneficiaryVisitDetail visit() { + com.iemr.tm.data.nurse.BeneficiaryVisitDetail visit = new com.iemr.tm.data.nurse.BeneficiaryVisitDetail(); + visit.setBeneficiaryRegID(11L); + visit.setBenVisitID(3L); + visit.setVisitCode(22L); + visit.setVisitReason("New Chief Complaint"); + visit.setVisitCategory("COVID-19 Screening"); + visit.setVisitNo((short) 1); + return visit; + } + + private com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ tcRequest(Long tmRequestID) { + com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ tcRequest = + new com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ(); + tcRequest.setUserID(41); + tcRequest.setSpecializationID(3); + tcRequest.setTmRequestID(tmRequestID); + tcRequest.setAllocationDate(new java.sql.Timestamp(System.currentTimeMillis())); + return tcRequest; + } + + @Test + @DisplayName("bookTCSlotData should create the beneficiary flow record for the booked slot") + void bookTCSlot_shouldCreateFlowRecord() throws Exception { + org.mockito.Mockito.when(benVisitDetailRepo.getVisitDetails(11L, 22L)).thenReturn(visit()); + org.mockito.Mockito.when(commonServiceImpl.createTcRequest(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(tcRequest(77L)); + com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus saved = + new com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus(); + saved.setBenFlowID(5L); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(saved); + + org.junit.jupiter.api.Assertions.assertEquals(1, + service.bookTCSlotData(BOOK_REQUEST, "Bearer session-token")); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus.class); + org.mockito.Mockito.verify(beneficiaryFlowStatusRepo).save(captor.capture()); + org.junit.jupiter.api.Assertions.assertEquals("Asha Devi", captor.getValue().getBenName()); + org.junit.jupiter.api.Assertions.assertEquals((short) 1, captor.getValue().getSpecialist_flag()); + } + + @Test + @DisplayName("bookTCSlotData should fail when the flow record could not be created") + void bookTCSlot_shouldFailWhenFlowRecordNotCreated() throws Exception { + org.mockito.Mockito.when(benVisitDetailRepo.getVisitDetails(11L, 22L)).thenReturn(visit()); + org.mockito.Mockito.when(commonServiceImpl.createTcRequest(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(tcRequest(77L)); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(null); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.bookTCSlotData(BOOK_REQUEST, "Bearer session-token")); + } + + @Test + @DisplayName("bookTCSlotData should fail when the slot could not be booked") + void bookTCSlot_shouldFailWhenSlotNotBooked() throws Exception { + org.mockito.Mockito.when(benVisitDetailRepo.getVisitDetails(11L, 22L)).thenReturn(visit()); + org.mockito.Mockito.when(commonServiceImpl.createTcRequest(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(tcRequest(null)); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.bookTCSlotData(BOOK_REQUEST, "Bearer session-token")); + } + + @Test + @DisplayName("bookTCSlotData should fail when the beneficiary has no visit") + void bookTCSlot_shouldFailWithoutVisit() { + org.mockito.Mockito.when(benVisitDetailRepo.getVisitDetails(11L, 22L)).thenReturn(null); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.bookTCSlotData(BOOK_REQUEST, "Bearer session-token")); + } + + private com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus bookedSlot(Long beneficiaryRegID) { + com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus slot = + new com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus(); + slot.setBeneficiaryRegID(beneficiaryRegID); + slot.setBeneficiaryID(7L); + slot.setBenName("Asha Devi"); + slot.setAge("31 years"); + slot.setGenderName("Female"); + slot.settCSpecialistUserID(41); + slot.settCRequestDate(new java.sql.Timestamp(System.currentTimeMillis())); + return slot; + } + + private void stubQuickblox() { + com.iemr.tm.data.quickBlox.Quickblox quickblox = + new com.iemr.tm.data.quickBlox.Quickblox(); + quickblox.setSpecialistBenQuickbloxID(1L); + quickblox.setSpecialistBenQuickBloxPass("qb-pass"); + org.mockito.Mockito.when(quickBloxRepo.getQuickbloxIds(org.mockito.ArgumentMatchers.anyInt())) + .thenReturn(quickblox); + } + + @Test + @DisplayName("getPatientBookedSlots should return the slot booked for this beneficiary") + void getBookedSlots_shouldReturnSlotForThisBeneficiary() throws Exception { + stubQuickblox(); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.getBenSlotDetails("9999999999")) + .thenReturn(new java.util.ArrayList<>(java.util.Arrays.asList(bookedSlot(11L)))); + + String result = service.getPatientBookedSlots(BOOK_REQUEST); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("\"isAnyActiveSlotForSameBen\":true")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("qb-pass")); + } + + @Test + @DisplayName("getPatientBookedSlots should return the last slot when none belongs to this beneficiary") + void getBookedSlots_shouldReturnLastSlotForAnotherBeneficiary() throws Exception { + stubQuickblox(); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.getBenSlotDetails("9999999999")) + .thenReturn(new java.util.ArrayList<>( + java.util.Arrays.asList(bookedSlot(99L), bookedSlot(98L)))); + + String result = service.getPatientBookedSlots(BOOK_REQUEST); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("\"isAnyActiveSlotForSameBen\":false")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("patientDetails")); + } + + @Test + @DisplayName("getPatientBookedSlots should report no active slot") + void getBookedSlots_shouldReportNoActiveSlot() throws Exception { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.getBenSlotDetails("9999999999")) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertTrue( + service.getPatientBookedSlots(BOOK_REQUEST).contains("\"isAnyActiveSlot\":false")); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/pnc/PNCDoctorServiceImplTest.java b/src/test/java/com/iemr/tm/service/pnc/PNCDoctorServiceImplTest.java new file mode 100644 index 00000000..b510e6f3 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/pnc/PNCDoctorServiceImplTest.java @@ -0,0 +1,235 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.pnc; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.nurse.pnc.PNCDiagnosisRepo; +import com.iemr.tm.repo.quickConsultation.PrescriptionDetailRepo; +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("PNCDoctorServiceImpl Test Suite") +class PNCDoctorServiceImplTest { + + @Mock + private PNCDiagnosisRepo pncDiagnosisRepo; + @Mock + private PrescriptionDetailRepo prescriptionDetailRepo; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + + @InjectMocks + private PNCDoctorServiceImpl service; + + @Test + @DisplayName("saveBenPNCDiagnosis should answer for a well formed request") + void saveBenPNCDiagnosis_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenPNCDiagnosis(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("getPNCDiagnosisDetails should answer for a well formed request") + void getPNCDiagnosisDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getPNCDiagnosisDetails(11L, 11L)); + } + + @Test + @DisplayName("updateBenPNCDiagnosis should answer for a well formed request") + void updateBenPNCDiagnosis_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenPNCDiagnosis(new com.iemr.tm.data.pnc.PNCDiagnosis())); + } + + @org.junit.jupiter.api.Nested + @DisplayName("PNC diagnosis") + class DiagnosisTests { + + @Test + @DisplayName("saveBenPNCDiagnosis should store the diagnosis against the prescription") + void saveDiagnosis_shouldStoreAgainstPrescription() throws Exception { + com.iemr.tm.data.pnc.PNCDiagnosis stored = new com.iemr.tm.data.pnc.PNCDiagnosis(); + stored.setID(4L); + org.mockito.Mockito.when(pncDiagnosisRepo.save(org.mockito.ArgumentMatchers.any())).thenReturn(stored); + + com.google.gson.JsonObject request = com.google.gson.JsonParser.parseString( + "{\"beneficiaryRegID\":11,\"visitCode\":22,\"benVisitID\":3,\"providerServiceMapID\":9," + + "\"createdBy\":\"doctor1\",\"pncDiagnosis\":\"Anaemia\"}").getAsJsonObject(); + + org.junit.jupiter.api.Assertions.assertEquals(4L, service.saveBenPNCDiagnosis(request, 5L)); + } + + @Test + @DisplayName("saveBenPNCDiagnosis should report a failure when nothing was stored") + void saveDiagnosis_shouldReportFailureWhenNothingStored() throws Exception { + org.mockito.Mockito.when(pncDiagnosisRepo.save(org.mockito.ArgumentMatchers.any())).thenReturn(null); + + org.junit.jupiter.api.Assertions.assertNull(service.saveBenPNCDiagnosis( + com.google.gson.JsonParser.parseString("{\"beneficiaryRegID\":11}").getAsJsonObject(), 5L)); + } + + @Test + @DisplayName("getPNCDiagnosisDetails should render the stored diagnosis for the visit") + void getDiagnosisDetails_shouldRenderStoredDiagnosis() { + com.iemr.tm.data.pnc.PNCDiagnosis stored = new com.iemr.tm.data.pnc.PNCDiagnosis(); + stored.setID(4L); + org.mockito.Mockito.when(pncDiagnosisRepo.findByBeneficiaryRegIDAndVisitCode(11L, 22L)) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(stored))); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getPNCDiagnosisDetails(11L, 22L)); + } + + @Test + @DisplayName("getPNCDiagnosisDetails should render an empty payload when the visit has no diagnosis") + void getDiagnosisDetails_shouldRenderEmptyPayloadWithoutDiagnosis() { + org.mockito.Mockito.when(pncDiagnosisRepo.findByBeneficiaryRegIDAndVisitCode(11L, 22L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getPNCDiagnosisDetails(11L, 22L)); + } + + @Test + @DisplayName("updateBenPNCDiagnosis should mark an already processed diagnosis as updated") + void updateDiagnosis_shouldMarkProcessedAsUpdated() throws Exception { + com.iemr.tm.data.pnc.PNCDiagnosis diagnosis = new com.iemr.tm.data.pnc.PNCDiagnosis(); + diagnosis.setBeneficiaryRegID(11L); + diagnosis.setVisitCode(22L); + org.mockito.Mockito.when(pncDiagnosisRepo.getPNCDiagnosisStatus(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.any())).thenReturn("P"); + + org.junit.jupiter.api.Assertions.assertEquals(0, service.updateBenPNCDiagnosis(diagnosis)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("SNOMED diagnosis lists") + class SnomedDiagnosisTests { + + private static final String TWO_DIAGNOSES = "{\"beneficiaryRegID\":11,\"visitCode\":22,\"benVisitID\":3," + + "\"createdBy\":\"doctor1\"," + + "\"provisionalDiagnosisList\":[{\"conceptID\":\"111\",\"term\":\"Anaemia\"}," + + " {\"term\":\"Hypertension\"}]," + + "\"confirmatoryDiagnosisList\":[{\"conceptID\":\"222\",\"term\":\"Sepsis\"}," + + " {\"term\":\"Fever\"}]}"; + + @Test + @DisplayName("saveBenPNCDiagnosis should flatten both diagnosis lists with their concept ids") + void saveDiagnosis_shouldFlattenBothDiagnosisLists() throws Exception { + com.iemr.tm.data.pnc.PNCDiagnosis stored = new com.iemr.tm.data.pnc.PNCDiagnosis(); + stored.setID(4L); + org.mockito.Mockito.when(pncDiagnosisRepo.save(org.mockito.ArgumentMatchers.any())).thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, service.saveBenPNCDiagnosis( + com.google.gson.JsonParser.parseString(TWO_DIAGNOSES).getAsJsonObject(), 31L)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(com.iemr.tm.data.pnc.PNCDiagnosis.class); + org.mockito.Mockito.verify(pncDiagnosisRepo).save(captor.capture()); + com.iemr.tm.data.pnc.PNCDiagnosis saved = captor.getValue(); + org.junit.jupiter.api.Assertions.assertEquals(31L, saved.getPrescriptionID()); + org.junit.jupiter.api.Assertions.assertEquals("Anaemia || Hypertension", saved.getProvisionalDiagnosis()); + org.junit.jupiter.api.Assertions.assertEquals("111 || N/A", saved.getProvisionalDiagnosisSCTCode()); + org.junit.jupiter.api.Assertions.assertEquals("Sepsis || Fever", saved.getConfirmatoryDiagnosis()); + org.junit.jupiter.api.Assertions.assertEquals("222 || N/A", saved.getConfirmatoryDiagnosisSCTCode()); + } + + @Test + @DisplayName("getPNCDiagnosisDetails should expand the stored diagnosis back into concept lists") + void getDiagnosisDetails_shouldExpandStoredDiagnosisIntoLists() { + com.iemr.tm.data.pnc.PNCDiagnosis stored = new com.iemr.tm.data.pnc.PNCDiagnosis(); + stored.setProvisionalDiagnosis("Anaemia || Hypertension"); + stored.setProvisionalDiagnosisSCTCode("111 || N/A"); + stored.setConfirmatoryDiagnosis("Sepsis || Fever"); + stored.setConfirmatoryDiagnosisSCTCode("222 || N/A"); + org.mockito.Mockito.when(pncDiagnosisRepo.findByBeneficiaryRegIDAndVisitCode(11L, 22L)) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(stored))); + java.util.ArrayList prescription = new java.util.ArrayList<>(); + prescription.add(new Object[] { "Ultrasound", "review in a week" }); + org.mockito.Mockito.when(prescriptionDetailRepo.getExternalinvestigationForVisitCode(11L, 22L)) + .thenReturn(prescription); + + String result = service.getPNCDiagnosisDetails(11L, 22L); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Anaemia")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Hypertension")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Sepsis")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Ultrasound")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("review in a week")); + } + + @Test + @DisplayName("updateBenPNCDiagnosis should flatten both diagnosis lists before updating") + void updateDiagnosis_shouldFlattenBothDiagnosisLists() throws Exception { + com.iemr.tm.data.pnc.PNCDiagnosis diagnosis = com.iemr.tm.utils.mapper.InputMapper.gson().fromJson( + TWO_DIAGNOSES, com.iemr.tm.data.pnc.PNCDiagnosis.class); + org.mockito.Mockito.when(pncDiagnosisRepo.getPNCDiagnosisStatus(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.any())).thenReturn("P"); + org.mockito.Mockito.when(pncDiagnosisRepo.updatePNCDiagnosis(org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.eq("U"), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBenPNCDiagnosis(diagnosis)); + org.junit.jupiter.api.Assertions.assertEquals("Anaemia || Hypertension", + diagnosis.getProvisionalDiagnosis()); + } + + @Test + @DisplayName("updateBenPNCDiagnosis should store a fresh diagnosis when none was recorded before") + void updateDiagnosis_shouldStoreFreshDiagnosis() throws Exception { + com.iemr.tm.data.pnc.PNCDiagnosis diagnosis = com.iemr.tm.utils.mapper.InputMapper.gson().fromJson( + TWO_DIAGNOSES, com.iemr.tm.data.pnc.PNCDiagnosis.class); + com.iemr.tm.data.pnc.PNCDiagnosis stored = new com.iemr.tm.data.pnc.PNCDiagnosis(); + stored.setID(4L); + org.mockito.Mockito.when(pncDiagnosisRepo.getPNCDiagnosisStatus(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.any())).thenReturn(null); + org.mockito.Mockito.when(pncDiagnosisRepo.save(org.mockito.ArgumentMatchers.any())).thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBenPNCDiagnosis(diagnosis)); + } + + @Test + @DisplayName("saveBenPNCDiagnosis should store an empty diagnosis when no concept was picked") + void saveDiagnosis_shouldStoreEmptyDiagnosisWithoutConcepts() throws Exception { + com.iemr.tm.data.pnc.PNCDiagnosis stored = new com.iemr.tm.data.pnc.PNCDiagnosis(); + stored.setID(4L); + org.mockito.Mockito.when(pncDiagnosisRepo.save(org.mockito.ArgumentMatchers.any())).thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, service.saveBenPNCDiagnosis( + com.google.gson.JsonParser.parseString("{\"beneficiaryRegID\":11,\"visitCode\":22}") + .getAsJsonObject(), 31L)); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/pnc/PNCNurseServiceImplTest.java b/src/test/java/com/iemr/tm/service/pnc/PNCNurseServiceImplTest.java new file mode 100644 index 00000000..1fb7d889 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/pnc/PNCNurseServiceImplTest.java @@ -0,0 +1,71 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.pnc; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.nurse.pnc.PNCCareRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("PNCNurseServiceImpl Test Suite") +class PNCNurseServiceImplTest { + + @Mock + private PNCCareRepo pncCareRepo; + + @InjectMocks + private PNCNurseServiceImpl service; + + @Test + @DisplayName("saveBenPncCareDetails should answer for a well formed request") + void saveBenPncCareDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenPncCareDetails(org.mockito.Mockito.mock(com.iemr.tm.data.pnc.PNCCare.class))); + } + + @Test + @DisplayName("getPNCCareDetails should answer for a well formed request") + void getPNCCareDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getPNCCareDetails(11L, 11L)); + } + + @Test + @DisplayName("updateBenPNCCareDetails should answer for a well formed request") + void updateBenPNCCareDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenPNCCareDetails(org.mockito.Mockito.mock(com.iemr.tm.data.pnc.PNCCare.class))); + } + + @Test + @DisplayName("updateBenPNCCare should answer for a well formed request") + void updateBenPNCCare_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBenPNCCare(org.mockito.Mockito.mock(com.iemr.tm.data.pnc.PNCCare.class))); + } +} diff --git a/src/test/java/com/iemr/tm/service/pnc/PNCServiceImplTest.java b/src/test/java/com/iemr/tm/service/pnc/PNCServiceImplTest.java new file mode 100644 index 00000000..060b429a --- /dev/null +++ b/src/test/java/com/iemr/tm/service/pnc/PNCServiceImplTest.java @@ -0,0 +1,572 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.pnc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyShort; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.iemr.tm.data.nurse.CommonUtilityClass; +import com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.nurse.anc.BenAdherenceRepo; +import com.iemr.tm.repo.quickConsultation.BenChiefComplaintRepo; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.service.labtechnician.LabTechnicianServiceImpl; +import com.iemr.tm.service.tele_consultation.SMSGatewayServiceImpl; +import com.iemr.tm.service.tele_consultation.TeleConsultationServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("PNCServiceImpl Test Suite") +class PNCServiceImplTest { + + private static final String AUTHORIZATION = "Bearer session-token"; + private static final Long BEN_REG_ID = 11L; + private static final Long VISIT_ID = 3L; + private static final Long VISIT_CODE = 22L; + + private static final String FULL_HISTORY = "{\"pastHistory\":{},\"comorbidConditions\":{}," + + "\"medicationHistory\":{\"medicationHistoryList\":[{\"currentMedication\":\"Iron\"}]}," + + "\"personalHistory\":{},\"familyHistory\":{},\"menstrualHistory\":{}," + + "\"femaleObstetricHistory\":{},\"immunizationHistory\":{},\"childVaccineDetails\":{}}"; + + private static final String FULL_EXAMINATION = "{\"generalExamination\":{},\"headToToeExamination\":{}," + + "\"gastroIntestinalExamination\":{},\"cardioVascularExamination\":{}," + + "\"respiratorySystemExamination\":{},\"centralNervousSystemExamination\":{}," + + "\"musculoskeletalSystemExamination\":{},\"genitoUrinarySystemExamination\":{}}"; + + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private PNCNurseServiceImpl pncNurseServiceImpl; + @Mock + private PNCDoctorServiceImpl pncDoctorServiceImpl; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private LabTechnicianServiceImpl labTechnicianServiceImpl; + @Mock + private TeleConsultationServiceImpl teleConsultationServiceImpl; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private BenChiefComplaintRepo benChiefComplaintRepo; + @Mock + private BenAdherenceRepo benAdherenceRepo; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + + @InjectMocks + private PNCServiceImpl service; + + private JsonObject json(String raw) { + return JsonParser.parseString(raw).getAsJsonObject(); + } + + private JsonObject nurseRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"providerServiceMapID\":9,\"vanID\":7,\"sessionID\":1," + + "\"benFlowID\":5,\"createdBy\":\"nurse1\"," + + "\"visitDetails\":{" + + " \"visitDetails\":{\"beneficiaryRegID\":11,\"visitReason\":\"New Chief Complaint\"," + + " \"visitCategory\":\"PNC\"}," + + " \"chiefComplaints\":[{\"chiefComplaintID\":3}]" + + "}," + + "\"pNCDeatils\":{},\"historyDetails\":" + FULL_HISTORY + + ",\"vitalDetails\":{\"height_cm\":170},\"examinationDetails\":" + FULL_EXAMINATION + "}"); + } + + private CommonUtilityClass utility() { + CommonUtilityClass utility = new CommonUtilityClass(); + utility.setBeneficiaryRegID(BEN_REG_ID); + utility.setVanID(7); + utility.setSessionID(1); + utility.setProviderServiceMapID(9); + utility.setCreatedBy("nurse1"); + return utility; + } + + private TeleconsultationRequestOBJ teleconsultationRequest(boolean walkIn) { + TeleconsultationRequestOBJ request = new TeleconsultationRequestOBJ(); + request.setWalkIn(walkIn); + request.setUserID(42); + request.setSpecializationID(2); + request.setTmRequestID(8L); + request.setAllocationDate(new Timestamp(1_700_000_000_000L)); + return request; + } + + @BeforeEach + @DisplayName("Wire the collaborators that every save path needs") + void setUp() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(0); + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(VISIT_ID); + when(commonNurseServiceImpl.generateVisitCode(anyLong(), any(), any())).thenReturn(VISIT_CODE); + when(commonNurseServiceImpl.saveBenChiefComplaints(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalAnthropometryDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPastHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenComorbidConditions(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMedicationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePersonalHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveAllergyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenFamilyHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenMenstrualHistory(any())).thenReturn(1); + when(commonNurseServiceImpl.saveFemaleObstetricHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveImmunizationHistory(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveChildOptionalVaccineDetail(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePhyGeneralExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.savePhyHeadToToeExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysGastrointestinalExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysCardiovascularExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysRespiratoryExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysCentralNervousExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysMusculoskeletalSystemExamination(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveSysGenitourinarySystemExamination(any())).thenReturn(1L); + when(pncNurseServiceImpl.saveBenPncCareDetails(any())).thenReturn(1L); + when(commonBenStatusFlowServiceImpl.updateBenFlowNurseAfterNurseActivity(any(), anyLong(), anyLong(), + anyString(), anyString(), anyShort(), anyShort(), anyShort(), anyShort(), anyShort(), anyLong(), any(), + anyShort(), any(), any())).thenReturn(1); + } + + @Nested + @DisplayName("savePNCNurseData") + class SaveNurseDataTests { + + @Test + @DisplayName("savePNCNurseData should save the visit, PNC care, history, vitals and examination") + void saveNurseData_shouldSaveEverySection() throws Exception { + String result = service.savePNCNurseData(nurseRequest(), AUTHORIZATION); + + assertTrue(result.contains("Data saved successfully")); + assertTrue(result.contains("\"visitCode\":\"22\"")); + } + + @Test + @DisplayName("savePNCNurseData should report an already saved visit") + void saveNurseData_shouldReportAlreadySavedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.savePNCNurseData(nurseRequest(), AUTHORIZATION).contains("Data already saved")); + } + + @Test + @DisplayName("savePNCNurseData should reject a request without visit details") + void saveNurseData_shouldRejectRequestWithoutVisitDetails() { + assertThrows(Exception.class, () -> service.savePNCNurseData(json("{}"), AUTHORIZATION)); + } + + @Test + @DisplayName("savePNCNurseData should fail when the visit could not be created") + void saveNurseData_shouldFailWhenVisitNotCreated() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryVisitDetails(any())).thenReturn(0L); + + assertThrows(RuntimeException.class, () -> service.savePNCNurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("savePNCNurseData should fail when the PNC care details could not be stored") + void saveNurseData_shouldFailWhenPncCareNotStored() throws Exception { + when(pncNurseServiceImpl.saveBenPncCareDetails(any())).thenReturn(null); + + assertThrows(RuntimeException.class, () -> service.savePNCNurseData(nurseRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("savePNCNurseData should notify a scheduled teleconsultation by SMS") + void saveNurseData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.savePNCNurseData(nurseRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), + anyString(), anyString(), any(), eq(AUTHORIZATION)); + } + + @Test + @DisplayName("savePNCNurseData should not notify a walk-in teleconsultation") + void saveNurseData_shouldNotNotifyWalkInTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())).thenReturn(teleconsultationRequest(true)); + + service.savePNCNurseData(nurseRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl, never()).smsSenderGateway(anyString(), anyLong(), anyInt(), anyLong(), any(), + anyString(), anyString(), any(), anyString()); + } + } + + @Nested + @DisplayName("deleteVisitDetails") + class DeleteVisitDetailsTests { + + @Test + @DisplayName("deleteVisitDetails should remove the visit rows for a created visit") + void deleteVisitDetails_shouldRemoveVisitRows() throws Exception { + when(benVisitDetailRepo.getVisitCode(BEN_REG_ID, 9)).thenReturn(VISIT_CODE); + + service.deleteVisitDetails(nurseRequest()); + + verify(benVisitDetailRepo).deleteVisitDetails(VISIT_CODE); + } + + @Test + @DisplayName("deleteVisitDetails should do nothing for a request without visit details") + void deleteVisitDetails_shouldDoNothingWithoutVisitDetails() throws Exception { + service.deleteVisitDetails(json("{}")); + + verify(benVisitDetailRepo, never()).deleteVisitDetails(anyLong()); + } + } + + @Nested + @DisplayName("nurse section saves") + class SectionSaveTests { + + @Test + @DisplayName("saveBenVisitDetails should return the new visit id and code") + void saveBenVisitDetails_shouldReturnVisitIdAndCode() throws Exception { + Map result = service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), + utility()); + + assertEquals(VISIT_ID, result.get("visitID")); + assertEquals(VISIT_CODE, result.get("visitCode")); + } + + @Test + @DisplayName("saveBenVisitDetails should return nothing when the visit was already recorded") + void saveBenVisitDetails_shouldReturnNothingForAlreadyRecordedVisit() throws Exception { + when(commonNurseServiceImpl.getMaxCurrentdate(anyLong(), anyString(), anyString())).thenReturn(1); + + assertTrue(service.saveBenVisitDetails(nurseRequest().getAsJsonObject("visitDetails"), utility()) + .isEmpty()); + } + + @Test + @DisplayName("saveBenPNCDetails should store the captured PNC care details") + void savePncDetails_shouldStoreCapturedCareDetails() throws Exception { + assertEquals(1L, service.saveBenPNCDetails(json("{\"pNCDeatils\":{}}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenPNCDetails should succeed when no PNC care details were captured") + void savePncDetails_shouldSucceedWithoutCareDetails() throws Exception { + assertEquals(1L, service.saveBenPNCDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenPNCHistoryDetails should store every captured history section") + void saveHistory_shouldStoreCapturedSections() throws Exception { + assertEquals(1L, service.saveBenPNCHistoryDetails(json(FULL_HISTORY), VISIT_ID, VISIT_CODE)); + verify(commonNurseServiceImpl).saveBenPastHistory(any()); + } + + @Test + @DisplayName("saveBenPNCVitalDetails should store the anthropometry and the physical vitals") + void saveVitals_shouldStoreAnthropometryAndPhysicalVitals() throws Exception { + assertEquals(1L, service.saveBenPNCVitalDetails(json("{\"height_cm\":170}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenPNCVitalDetails should report a failure when the physical vitals were not stored") + void saveVitals_shouldReportFailureWhenPhysicalVitalsNotStored() throws Exception { + when(commonNurseServiceImpl.saveBeneficiaryPhysicalVitalDetails(any())).thenReturn(null); + + assertNull(service.saveBenPNCVitalDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenExaminationDetails should succeed when no examination section was captured") + void saveExamination_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1L, service.saveBenExaminationDetails(json("{}"), VISIT_ID, VISIT_CODE)); + } + + @Test + @DisplayName("saveBenExaminationDetails should store every captured examination section") + void saveExamination_shouldStoreCapturedSections() throws Exception { + assertEquals(1L, service.saveBenExaminationDetails(json(FULL_EXAMINATION), VISIT_ID, VISIT_CODE)); + verify(commonNurseServiceImpl).savePhyHeadToToeExamination(any()); + } + } + + @Nested + @DisplayName("read endpoints") + class ReadTests { + + @Test + @DisplayName("getBenVisitDetailsFrmNursePNC should assemble the visit and the chief complaints") + void getVisitDetails_shouldAssembleVisitSections() { + when(commonNurseServiceImpl.getBenChiefComplaints(BEN_REG_ID, VISIT_CODE)).thenReturn("[]"); + + String result = service.getBenVisitDetailsFrmNursePNC(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("PNCNurseVisitDetail")); + assertTrue(result.contains("BenChiefComplaints")); + } + + @Test + @DisplayName("getBenPNCDetailsFrmNursePNC should assemble the PNC care details") + void getPncDetails_shouldAssemblePncSections() { + when(pncNurseServiceImpl.getPNCCareDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + + assertTrue(service.getBenPNCDetailsFrmNursePNC(BEN_REG_ID, VISIT_CODE).contains("PNCCareDetail")); + } + + @Test + @DisplayName("getBenHistoryDetails should assemble every stored history section") + void getHistoryDetails_shouldAssembleStoredSections() { + when(commonNurseServiceImpl.getPastHistoryData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.BenMedHistory()); + + assertTrue(service.getBenHistoryDetails(BEN_REG_ID, VISIT_CODE).contains("PastHistory")); + } + + @Test + @DisplayName("getBeneficiaryVitalDetails should assemble the anthropometry and the physical vitals") + void getVitalDetails_shouldAssembleVitalSections() { + when(commonNurseServiceImpl.getBeneficiaryPhysicalAnthropometryDetails(BEN_REG_ID, VISIT_CODE)) + .thenReturn("{}"); + when(commonNurseServiceImpl.getBeneficiaryPhysicalVitalDetails(BEN_REG_ID, VISIT_CODE)).thenReturn("{}"); + + assertTrue(service.getBeneficiaryVitalDetails(BEN_REG_ID, VISIT_CODE).contains("benAnthropometryDetail")); + } + + @Test + @DisplayName("getPNCExaminationDetailsData should assemble every stored examination section") + void getExaminationDetails_shouldAssembleStoredSections() { + when(commonNurseServiceImpl.getGeneralExaminationData(BEN_REG_ID, VISIT_CODE)) + .thenReturn(new com.iemr.tm.data.anc.PhyGeneralExamination()); + + assertTrue(service.getPNCExaminationDetailsData(BEN_REG_ID, VISIT_CODE).contains("generalExamination")); + } + + @Test + @DisplayName("getBenPNCNurseData should assemble the nurse captured sections") + void getNurseData_shouldAssembleNurseSections() { + assertTrue(service.getBenPNCNurseData(BEN_REG_ID, VISIT_CODE).contains("history")); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorPNC should assemble the whole doctor case record") + void getCaseRecord_shouldAssembleDoctorCaseRecord() throws Exception { + when(commonNurseServiceImpl.getGraphicalTrendData(anyLong(), anyString())).thenReturn(new HashMap<>()); + + String result = service.getBenCaseRecordFromDoctorPNC(BEN_REG_ID, VISIT_CODE); + + assertTrue(result.contains("findings")); + assertTrue(result.contains("LabReport")); + } + } + + @Nested + @DisplayName("savePNCDoctorData") + class SaveDoctorDataTests { + + private JsonObject doctorRequest() { + return json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22,\"providerServiceMapID\":9," + + "\"createdBy\":\"doctor1\",\"doctorSignatureFlag\":true,\"findings\":{}," + + "\"diagnosis\":{\"specialistDiagnosis\":\"PNC follow up\"}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + } + + @BeforeEach + void stubDoctorCollaborators() throws Exception { + when(commonDoctorServiceImpl.saveDocFindings(any())).thenReturn(1); + when(commonNurseServiceImpl.savePrescriptionDetailsAndGetPrescriptionID(any(), any(), any(), any(), any(), + any(), any(), any(), any(), any())).thenReturn(4L); + when(pncDoctorServiceImpl.saveBenPNCDiagnosis(any(), any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.saveBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + } + + @Test + @DisplayName("savePNCDoctorData should save the findings, diagnosis, tests, drugs and referral") + void saveDoctorData_shouldSaveWholeCaseRecord() throws Exception { + assertEquals(1L, service.savePNCDoctorData(doctorRequest(), AUTHORIZATION)); + + verify(commonDoctorServiceImpl).saveDocFindings(any()); + verify(pncDoctorServiceImpl).saveBenPNCDiagnosis(any(), any()); + } + + @Test + @DisplayName("savePNCDoctorData should succeed for a case record with only findings and an investigation") + void saveDoctorData_shouldSucceedForMinimalCaseRecord() throws Exception { + assertEquals(1L, service.savePNCDoctorData( + json("{\"beneficiaryRegID\":11,\"findings\":{},\"investigation\":{}}"), AUTHORIZATION)); + } + + @Test + @DisplayName("savePNCDoctorData should fail when the findings section is absent") + void saveDoctorData_shouldFailWithoutFindings() throws Exception { + assertThrows(RuntimeException.class, + () -> service.savePNCDoctorData(json("{\"beneficiaryRegID\":11,\"investigation\":{}}"), + AUTHORIZATION)); + } + + @Test + @DisplayName("savePNCDoctorData should fail when the beneficiary flow could not be advanced") + void saveDoctorData_shouldFailWhenFlowNotAdvanced() throws Exception { + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(0); + + assertThrows(RuntimeException.class, () -> service.savePNCDoctorData(doctorRequest(), AUTHORIZATION)); + } + + @Test + @DisplayName("savePNCDoctorData should notify a scheduled teleconsultation by SMS") + void saveDoctorData_shouldNotifyScheduledTeleconsultation() throws Exception { + when(commonServiceImpl.createTcRequest(any(), any(), anyString())) + .thenReturn(teleconsultationRequest(false)); + + service.savePNCDoctorData(doctorRequest(), AUTHORIZATION); + + verify(sMSGatewayServiceImpl).smsSenderGateway(eq("schedule"), anyLong(), anyInt(), anyLong(), any(), any(), + anyString(), any(), eq(AUTHORIZATION)); + } + } + + @Nested + @DisplayName("update endpoints") + class UpdateTests { + + @Test + @DisplayName("updateBenPNCDetails should succeed when no PNC section was captured") + void updatePncDetails_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1, service.updateBenPNCDetails(json("{}"))); + } + + @Test + @DisplayName("updateBenPNCDetails should update the captured PNC care details") + void updatePncDetails_shouldUpdateCapturedCareDetails() throws Exception { + when(pncNurseServiceImpl.updateBenPNCCareDetails(any())).thenReturn(1); + + assertEquals(1, service.updateBenPNCDetails(json("{\"PNCDetails\":{}}"))); + } + + @Test + @DisplayName("updateBenHistoryDetails should succeed when no history section was captured") + void updateHistory_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1, service.updateBenHistoryDetails(json("{}"))); + } + + @Test + @DisplayName("updateBenVitalDetails should confirm the update when both sections changed") + void updateVitals_shouldConfirmUpdate() throws Exception { + when(commonNurseServiceImpl.updateANCAnthropometryDetails(any())).thenReturn(1); + when(commonNurseServiceImpl.updateANCPhysicalVitalDetails(any())).thenReturn(1); + + assertEquals(1, service.updateBenVitalDetails(json("{\"height_cm\":170}"))); + } + + @Test + @DisplayName("updateBenExaminationDetails should succeed when no examination section was captured") + void updateExamination_shouldSucceedWithoutCapturedSections() throws Exception { + assertEquals(1, service.updateBenExaminationDetails(json("{}"))); + } + + @Test + @DisplayName("updatePNCDoctorData should return nothing for a null request") + void updateDoctorData_shouldReturnNothingForNullRequest() throws Exception { + assertNull(service.updatePNCDoctorData(null, AUTHORIZATION)); + } + + @Test + @DisplayName("updatePNCDoctorData should update the whole case record") + void updateDoctorData_shouldUpdateWholeCaseRecord() throws Exception { + when(commonDoctorServiceImpl.updateDocFindings(any())).thenReturn(1); + when(commonNurseServiceImpl.updatePrescription(any())).thenReturn(1); + when(pncDoctorServiceImpl.updateBenPNCDiagnosis(any())).thenReturn(1); + when(commonNurseServiceImpl.saveBenInvestigation(any())).thenReturn(1L); + when(commonNurseServiceImpl.saveBenPrescribedDrugsList(any())).thenReturn(1); + when(commonDoctorServiceImpl.updateBenReferDetails(any())).thenReturn(1L); + when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataUpdate(any(), anyBoolean(), anyBoolean(), any(), + anyBoolean())).thenReturn(1); + + JsonObject request = json("{" + "\"beneficiaryRegID\":11,\"benVisitID\":3,\"visitCode\":22," + + "\"findings\":{},\"diagnosis\":{\"prescriptionID\":4}," + + "\"investigation\":{\"laboratoryList\":[{\"testID\":1}]}," + + "\"prescription\":[{\"drugID\":1,\"formName\":\"Tablet\"}]," + + "\"refer\":{\"referredToInstituteID\":1}" + "}"); + + assertEquals(1L, service.updatePNCDoctorData(request, AUTHORIZATION)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("PNC captured-section updates") + class CapturedSectionUpdateTests { + + @Test + @DisplayName("updateBenHistoryDetails should update every captured history section") + void updateBenHistoryDetails_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertDoesNotThrow(() -> service.updateBenHistoryDetails( + com.google.gson.JsonParser.parseString("{\"pastHistory\":{},\"comorbidConditions\":{},\"medicationHistory\":{},\"personalHistory\":{},\"familyHistory\":{},\"menstrualHistory\":{},\"femaleObstetricHistory\":{},\"immunizationHistory\":{},\"childVaccineDetails\":{},\"developmentHistory\":{},\"feedingHistory\":{},\"perinatalHistroy\":{},\"allergyHistory\":{}}").getAsJsonObject())); + } + + @Test + @DisplayName("updateBenExaminationDetails should update every captured examination section") + void updateBenExaminationDetails_shouldUpdateEveryCapturedSection() throws Exception { + org.junit.jupiter.api.Assertions.assertDoesNotThrow(() -> service.updateBenExaminationDetails( + com.google.gson.JsonParser.parseString("{\"generalExamination\":{},\"headToToeExamination\":{},\"gastroIntestinalExamination\":{},\"cardioVascularExamination\":{},\"respiratorySystemExamination\":{},\"centralNervousSystemExamination\":{},\"musculoskeletalSystemExamination\":{},\"genitoUrinarySystemExamination\":{},\"obstetricExamination\":{}}").getAsJsonObject())); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/quickBlox/QuickbloxServiceImplTest.java b/src/test/java/com/iemr/tm/service/quickBlox/QuickbloxServiceImplTest.java new file mode 100644 index 00000000..4a725e68 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/quickBlox/QuickbloxServiceImplTest.java @@ -0,0 +1,54 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.quickBlox; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.quickBlox.QuickBloxRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("QuickbloxServiceImpl Test Suite") +class QuickbloxServiceImplTest { + + @Mock + private QuickBloxRepo quickBloxRepo; + + @InjectMocks + private QuickbloxServiceImpl service; + + @Test + @DisplayName("getQuickbloxIds should reject a request that carries no beneficiary details") + void getQuickbloxIds_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(NullPointerException.class, () -> service.getQuickbloxIds("{}")); + } +} diff --git a/src/test/java/com/iemr/tm/service/quickConsultation/QuickConsultationServiceImplTest.java b/src/test/java/com/iemr/tm/service/quickConsultation/QuickConsultationServiceImplTest.java new file mode 100644 index 00000000..193791d4 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/quickConsultation/QuickConsultationServiceImplTest.java @@ -0,0 +1,519 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.quickConsultation; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.nurse.BenPhysicalVitalRepo; +import com.iemr.tm.repo.nurse.BenVisitDetailRepo; +import com.iemr.tm.repo.nurse.anc.BenAdherenceRepo; +import com.iemr.tm.repo.quickConsultation.BenChiefComplaintRepo; +import com.iemr.tm.repo.quickConsultation.BenClinicalObservationsRepo; +import com.iemr.tm.repo.quickConsultation.ExternalTestOrderRepo; +import com.iemr.tm.repo.quickConsultation.PrescriptionDetailRepo; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; +import com.iemr.tm.service.common.transaction.CommonDoctorServiceImpl; +import com.iemr.tm.service.common.transaction.CommonNurseServiceImpl; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.service.generalOPD.GeneralOPDDoctorServiceImpl; +import com.iemr.tm.service.labtechnician.LabTechnicianServiceImpl; +import com.iemr.tm.service.tele_consultation.SMSGatewayServiceImpl; +import com.iemr.tm.service.tele_consultation.TeleConsultationServiceImpl; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("QuickConsultationServiceImpl Test Suite") +class QuickConsultationServiceImplTest { + + @Mock + private BenChiefComplaintRepo benChiefComplaintRepo; + @Mock + private BenClinicalObservationsRepo benClinicalObservationsRepo; + @Mock + private PrescriptionDetailRepo prescriptionDetailRepo; + @Mock + private ExternalTestOrderRepo externalTestOrderRepo; + @Mock + private CommonNurseServiceImpl commonNurseServiceImpl; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private LabTechnicianServiceImpl labTechnicianServiceImpl; + @Mock + private CommonDoctorServiceImpl commonDoctorServiceImpl; + @Mock + private GeneralOPDDoctorServiceImpl generalOPDDoctorServiceImpl; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private TeleConsultationServiceImpl teleConsultationServiceImpl; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + @Mock + private BenPhysicalVitalRepo benPhysicalVitalRepo; + @Mock + private BenVisitDetailRepo benVisitDetailRepo; + @Mock + private BenAdherenceRepo benAdherenceRepo; + + @InjectMocks + private QuickConsultationServiceImpl service; + + @Test + @DisplayName("saveBeneficiaryChiefComplaint should answer for a well formed request") + void saveBeneficiaryChiefComplaint_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBeneficiaryChiefComplaint(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("saveBeneficiaryClinicalObservations should answer for a well formed request") + void saveBeneficiaryClinicalObservations_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBeneficiaryClinicalObservations(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("saveBenPrescriptionForANC should answer for a well formed request") + void saveBenPrescriptionForANC_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBenPrescriptionForANC(new com.iemr.tm.data.quickConsultation.PrescriptionDetail())); + } + + @Test + @DisplayName("saveBeneficiaryExternalLabTestOrderDetails should answer for a well formed request") + void saveBeneficiaryExternalLabTestOrderDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.saveBeneficiaryExternalLabTestOrderDetails(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("quickConsultNurseDataInsert should reject a request it cannot act on") + void quickConsultNurseDataInsert_shouldRejectRequestItCannotActOn() { + assertThrows(Exception.class, () -> service.quickConsultNurseDataInsert(new com.google.gson.JsonObject(), "{}")); + } + + @Test + @DisplayName("deleteVisitDetails should answer for a well formed request") + void deleteVisitDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.deleteVisitDetails(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("quickConsultDoctorDataInsert should reject a request it cannot act on") + void quickConsultDoctorDataInsert_shouldRejectRequestItCannotActOn() { + assertThrows(RuntimeException.class, () -> service.quickConsultDoctorDataInsert(new com.google.gson.JsonObject(), "{}")); + } + + @Test + @DisplayName("getBenDataFrmNurseToDocVisitDetailsScreen should answer for a well formed request") + void getBenDataFrmNurseToDocVisitDetailsScreen_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDataFrmNurseToDocVisitDetailsScreen(11L, 11L)); + } + + @Test + @DisplayName("getBeneficiaryVitalDetails should answer for a well formed request") + void getBeneficiaryVitalDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBeneficiaryVitalDetails(11L, 11L)); + } + + @Test + @DisplayName("getBenQuickConsultNurseData should answer for a well formed request") + void getBenQuickConsultNurseData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenQuickConsultNurseData(11L, 11L)); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorQuickConsult should answer for a well formed request") + void getBenCaseRecordFromDoctorQuickConsult_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenCaseRecordFromDoctorQuickConsult(11L, 11L)); + } + + @Test + @DisplayName("updateGeneralOPDQCDoctorData should reject a request it cannot act on") + void updateGeneralOPDQCDoctorData_shouldRejectRequestItCannotActOn() { + assertThrows(RuntimeException.class, () -> service.updateGeneralOPDQCDoctorData(new com.google.gson.JsonObject(), "{}")); + } + + @Test + @DisplayName("updateBeneficiaryClinicalObservations should answer for a well formed request") + void updateBeneficiaryClinicalObservations_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBeneficiaryClinicalObservations(new com.google.gson.JsonObject())); + } + + @org.junit.jupiter.api.Nested + @DisplayName("quick consultation capture") + class QuickConsultationCaptureTests { + + private com.google.gson.JsonObject nurseRequest() { + return com.google.gson.JsonParser.parseString("{" + + "\"beneficiaryRegID\":11,\"providerServiceMapID\":9,\"vanID\":7,\"sessionID\":1," + + "\"benFlowID\":5,\"createdBy\":\"nurse1\"," + + "\"visitDetails\":{\"beneficiaryRegID\":11,\"visitReason\":\"New Chief Complaint\"," + + " \"visitCategory\":\"Quick Consultation\"}," + + "\"vitalsDetails\":{\"height_cm\":170}}").getAsJsonObject(); + } + + @org.junit.jupiter.api.BeforeEach + void stubNurseCollaborators() throws Exception { + org.mockito.Mockito.when(commonNurseServiceImpl.getMaxCurrentdate(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString())).thenReturn(0); + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBeneficiaryVisitDetails(org.mockito.ArgumentMatchers.any())).thenReturn(3L); + org.mockito.Mockito.when(commonNurseServiceImpl.generateVisitCode(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(22L); + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBeneficiaryPhysicalAnthropometryDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBeneficiaryPhysicalVitalDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.updateBenFlowNurseAfterNurseActivity( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.anyShort(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.anyShort(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + } + + @Test + @DisplayName("quickConsultNurseDataInsert should save the visit and the vitals") + void nurseDataInsert_shouldSaveVisitAndVitals() throws Exception { + String result = service.quickConsultNurseDataInsert(nurseRequest(), "Bearer session-token"); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Data saved successfully")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("\"visitCode\":\"22\"")); + } + + @Test + @DisplayName("quickConsultNurseDataInsert should report an already saved visit") + void nurseDataInsert_shouldReportAlreadySavedVisit() throws Exception { + org.mockito.Mockito.when(commonNurseServiceImpl.getMaxCurrentdate(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertTrue(service + .quickConsultNurseDataInsert(nurseRequest(), "Bearer session-token") + .contains("Data already saved")); + } + + @Test + @DisplayName("quickConsultNurseDataInsert should fail when the vitals could not be stored") + void nurseDataInsert_shouldFailWhenVitalsNotStored() throws Exception { + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBeneficiaryPhysicalVitalDetails(org.mockito.ArgumentMatchers.any())).thenReturn(null); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.quickConsultNurseDataInsert(nurseRequest(), "Bearer session-token")); + } + + @Test + @DisplayName("deleteVisitDetails should remove the visit rows for a created visit") + void deleteVisitDetails_shouldRemoveVisitRows() throws Exception { + org.mockito.Mockito.when(benVisitDetailRepo.getVisitCode(11L, 9)).thenReturn(22L); + + service.deleteVisitDetails(nurseRequest()); + + org.mockito.Mockito.verify(benVisitDetailRepo).deleteVisitDetails(22L); + } + + @Test + @DisplayName("saveBeneficiaryClinicalObservations should return the stored observation id") + void saveClinicalObservations_shouldReturnStoredId() throws Exception { + com.iemr.tm.data.quickConsultation.BenClinicalObservations stored = + new com.iemr.tm.data.quickConsultation.BenClinicalObservations(); + stored.setClinicalObservationID(4L); + org.mockito.Mockito.when(benClinicalObservationsRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, service.saveBeneficiaryClinicalObservations( + com.google.gson.JsonParser.parseString("{\"beneficiaryRegID\":11}").getAsJsonObject())); + } + + @Test + @DisplayName("saveBenPrescriptionForANC should return the stored prescription id") + void savePrescription_shouldReturnStoredId() { + com.iemr.tm.data.quickConsultation.PrescriptionDetail stored = + new com.iemr.tm.data.quickConsultation.PrescriptionDetail(); + stored.setPrescriptionID(4L); + org.mockito.Mockito.when(prescriptionDetailRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, service.saveBenPrescriptionForANC( + new com.iemr.tm.data.quickConsultation.PrescriptionDetail())); + } + + @Test + @DisplayName("getBenDataFrmNurseToDocVisitDetailsScreen should assemble the visit and the complaints") + void getVisitDetailsScreen_shouldAssembleVisitSections() { + org.mockito.Mockito.when(commonNurseServiceImpl.getBenChiefComplaints(11L, 22L)).thenReturn("[]"); + + org.junit.jupiter.api.Assertions.assertNotNull( + service.getBenDataFrmNurseToDocVisitDetailsScreen(11L, 22L)); + } + + @Test + @DisplayName("getBeneficiaryVitalDetails should assemble the anthropometry and the physical vitals") + void getVitalDetails_shouldAssembleVitalSections() { + org.mockito.Mockito.when(commonNurseServiceImpl + .getBeneficiaryPhysicalAnthropometryDetails(11L, 22L)).thenReturn("{}"); + org.mockito.Mockito.when(commonNurseServiceImpl + .getBeneficiaryPhysicalVitalDetails(11L, 22L)).thenReturn("{}"); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getBeneficiaryVitalDetails(11L, 22L)); + } + + @Test + @DisplayName("getBenQuickConsultNurseData should assemble the nurse captured sections") + void getNurseData_shouldAssembleNurseSections() { + org.junit.jupiter.api.Assertions.assertNotNull(service.getBenQuickConsultNurseData(11L, 22L)); + } + + @Test + @DisplayName("getBenCaseRecordFromDoctorQuickConsult should assemble the doctor case record") + void getCaseRecord_shouldAssembleDoctorCaseRecord() throws Exception { + org.mockito.Mockito.when(commonNurseServiceImpl.getGraphicalTrendData( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(new java.util.HashMap<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getBenCaseRecordFromDoctorQuickConsult(11L, 22L)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("quick consultation doctor data") + class DoctorDataTests { + + private static final String DOCTOR_REQUEST = "{\"beneficiaryRegID\":11,\"beneficiaryID\":9,\"benVisitID\":3," + + "\"visitCode\":22,\"benFlowID\":5,\"providerServiceMapID\":9,\"createdBy\":\"doctor1\"," + + "\"prescriptionID\":31,\"doctorSignatureFlag\":true,\"isSpecialist\":false," + + "\"clinicalObservations\":{\"otherSymptoms\":\"fever\"}," + + "\"chiefComplaints\":[{\"chiefComplaint\":\"fever\",\"duration\":2," + + " \"unitOfDuration\":\"days\"}]," + + "\"prescription\":[{\"drugID\":4,\"drugName\":\"Paracetamol\",\"dose\":\"1\"," + + " \"frequency\":\"TDS\",\"duration\":\"3\",\"unitOfDuration\":\"days\"}]," + + "\"labTestOrders\":[{\"testID\":7,\"testName\":\"CBC\"}]," + + "\"rbsTestResult\":\"110\",\"rbsTestRemarks\":\"normal\"," + + "\"refer\":{\"beneficiaryRegID\":11,\"visitCode\":22,\"referralReason\":\"follow up\"}}"; + + private com.google.gson.JsonObject doctorRequest() { + return com.google.gson.JsonParser.parseString(DOCTOR_REQUEST).getAsJsonObject(); + } + + private com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ tcRequest(boolean walkIn) { + com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ tcRequest = + new com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ(); + tcRequest.setUserID(41); + tcRequest.setSpecializationID(3); + tcRequest.setTmRequestID(77L); + tcRequest.setWalkIn(walkIn); + tcRequest.setAllocationDate(new java.sql.Timestamp(System.currentTimeMillis())); + return tcRequest; + } + + @org.junit.jupiter.api.BeforeEach + void stubDoctorCollaborators() throws Exception { + com.iemr.tm.data.quickConsultation.BenClinicalObservations observations = + new com.iemr.tm.data.quickConsultation.BenClinicalObservations(); + observations.setClinicalObservationID(4L); + org.mockito.Mockito.when(benClinicalObservationsRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(observations); + org.mockito.Mockito.when(benChiefComplaintRepo.saveAll(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBeneficiaryPrescription(org.mockito.ArgumentMatchers.any())).thenReturn(31L); + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBenPrescribedDrugsList(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(commonNurseServiceImpl.saveBeneficiaryLabTestOrderDetails( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(commonNurseServiceImpl + .updatePrescription(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(benPhysicalVitalRepo.updatePhysicalVitalDetailsQCDoctor( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(commonDoctorServiceImpl + .saveBenReferDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(commonDoctorServiceImpl + .updateBenReferDetails(org.mockito.ArgumentMatchers.any())).thenReturn(1L); + org.mockito.Mockito.when(commonDoctorServiceImpl + .updateBenClinicalObservations(org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataUpdate( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + } + + @Test + @DisplayName("quickConsultDoctorDataInsert should save every doctor section for a walk in visit") + void doctorDataInsert_shouldSaveEverySection() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(1, + service.quickConsultDoctorDataInsert(doctorRequest(), "Bearer session-token")); + + org.mockito.Mockito.verify(commonNurseServiceImpl) + .saveBenPrescribedDrugsList(org.mockito.ArgumentMatchers.any()); + org.mockito.Mockito.verify(commonNurseServiceImpl).saveBeneficiaryLabTestOrderDetails( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.eq(31L)); + org.mockito.Mockito.verify(benPhysicalVitalRepo).updatePhysicalVitalDetailsQCDoctor("110", "normal", 11L, + 22L); + } + + @Test + @DisplayName("quickConsultDoctorDataInsert should text the beneficiary about a scheduled teleconsultation") + void doctorDataInsert_shouldTextBeneficiaryAboutSchedule() throws Exception { + org.mockito.Mockito.when(commonServiceImpl.createTcRequest(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(tcRequest(false)); + + org.junit.jupiter.api.Assertions.assertEquals(1, + service.quickConsultDoctorDataInsert(doctorRequest(), "Bearer session-token")); + + org.mockito.Mockito.verify(sMSGatewayServiceImpl).smsSenderGateway( + org.mockito.ArgumentMatchers.eq("schedule"), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyInt(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.anyString()); + } + + @Test + @DisplayName("quickConsultDoctorDataInsert should save a visit without a prescription or a test") + void doctorDataInsert_shouldSaveVisitWithoutPrescriptionOrTest() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(1, service.quickConsultDoctorDataInsert( + com.google.gson.JsonParser.parseString("{\"beneficiaryRegID\":11,\"visitCode\":22," + + "\"clinicalObservations\":{},\"chiefComplaints\":[{\"chiefComplaint\":\"fever\"}]}") + .getAsJsonObject(), + "Bearer session-token")); + } + + @Test + @DisplayName("quickConsultDoctorDataInsert should fail when the beneficiary flow could not be advanced") + void doctorDataInsert_shouldFailWhenFlowNotAdvanced() throws Exception { + org.mockito.Mockito.when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataSave( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.quickConsultDoctorDataInsert(doctorRequest(), "Bearer session-token")); + } + + @Test + @DisplayName("quickConsultDoctorDataInsert should fail when the prescription could not be created") + void doctorDataInsert_shouldFailWhenPrescriptionNotCreated() throws Exception { + org.mockito.Mockito.when(commonNurseServiceImpl + .saveBeneficiaryPrescription(org.mockito.ArgumentMatchers.any())).thenReturn(0L); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.quickConsultDoctorDataInsert(doctorRequest(), "Bearer session-token")); + } + + @Test + @DisplayName("updateGeneralOPDQCDoctorData should update every doctor section") + void doctorDataUpdate_shouldUpdateEverySection() throws Exception { + org.junit.jupiter.api.Assertions.assertNotNull( + service.updateGeneralOPDQCDoctorData(doctorRequest(), "Bearer session-token")); + + org.mockito.Mockito.verify(commonNurseServiceImpl) + .updatePrescription(org.mockito.ArgumentMatchers.any()); + org.mockito.Mockito.verify(commonDoctorServiceImpl) + .updateBenReferDetails(org.mockito.ArgumentMatchers.any()); + } + + @Test + @DisplayName("updateGeneralOPDQCDoctorData should text the beneficiary about a scheduled teleconsultation") + void doctorDataUpdate_shouldTextBeneficiaryAboutSchedule() throws Exception { + org.mockito.Mockito.when(commonServiceImpl.createTcRequest(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(tcRequest(false)); + + org.junit.jupiter.api.Assertions.assertNotNull( + service.updateGeneralOPDQCDoctorData(doctorRequest(), "Bearer session-token")); + + org.mockito.Mockito.verify(sMSGatewayServiceImpl).smsSenderGateway( + org.mockito.ArgumentMatchers.eq("schedule"), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyInt(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.anyString()); + } + + @Test + @DisplayName("updateGeneralOPDQCDoctorData should update a visit without a prescription or a test") + void doctorDataUpdate_shouldUpdateVisitWithoutPrescriptionOrTest() throws Exception { + org.junit.jupiter.api.Assertions.assertNotNull(service.updateGeneralOPDQCDoctorData( + com.google.gson.JsonParser.parseString("{\"beneficiaryRegID\":11,\"visitCode\":22," + + "\"prescriptionID\":31,\"clinicalObservations\":{}," + + "\"chiefComplaints\":[{\"chiefComplaint\":\"fever\"}]}").getAsJsonObject(), + "Bearer session-token")); + } + + @Test + @DisplayName("updateGeneralOPDQCDoctorData should fail when the beneficiary flow could not be advanced") + void doctorDataUpdate_shouldFailWhenFlowNotAdvanced() throws Exception { + org.mockito.Mockito.when(commonDoctorServiceImpl.updateBenFlowtableAfterDocDataUpdate( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.updateGeneralOPDQCDoctorData(doctorRequest(), "Bearer session-token")); + } + + @Test + @DisplayName("updateGeneralOPDQCDoctorData should fail when the observations could not be updated") + void doctorDataUpdate_shouldFailWhenObservationsNotUpdated() throws Exception { + org.mockito.Mockito.when(commonDoctorServiceImpl + .updateBenClinicalObservations(org.mockito.ArgumentMatchers.any())).thenReturn(0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.updateGeneralOPDQCDoctorData(doctorRequest(), "Bearer session-token")); + } + + @Test + @DisplayName("updateBeneficiaryClinicalObservations should attach the SNOMED code for the entered symptoms") + void updateObservations_shouldAttachSnomedCode() throws Exception { + org.mockito.Mockito.when(commonDoctorServiceImpl.getSnomedCTcode("fever")) + .thenReturn(new String[] { "386661006", "Fever" }); + org.mockito.Mockito.when(commonDoctorServiceImpl + .updateBenClinicalObservations(org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBeneficiaryClinicalObservations( + com.google.gson.JsonParser.parseString("{\"otherSymptoms\":\"fever\"}").getAsJsonObject())); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass( + com.iemr.tm.data.quickConsultation.BenClinicalObservations.class); + org.mockito.Mockito.verify(commonDoctorServiceImpl).updateBenClinicalObservations(captor.capture()); + org.junit.jupiter.api.Assertions.assertEquals("386661006", captor.getValue().getOtherSymptomsSCTCode()); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/registrar/RegistrarServiceImplTest.java b/src/test/java/com/iemr/tm/service/registrar/RegistrarServiceImplTest.java new file mode 100644 index 00000000..18081d5e --- /dev/null +++ b/src/test/java/com/iemr/tm/service/registrar/RegistrarServiceImplTest.java @@ -0,0 +1,837 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.registrar; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.registrar.BeneficiaryDemographicAdditionalRepo; +import com.iemr.tm.repo.registrar.BeneficiaryImageRepo; +import com.iemr.tm.repo.registrar.RegistrarRepoBenData; +import com.iemr.tm.repo.registrar.RegistrarRepoBenDemoData; +import com.iemr.tm.repo.registrar.RegistrarRepoBenGovIdMapping; +import com.iemr.tm.repo.registrar.RegistrarRepoBenPhoneMapData; +import com.iemr.tm.repo.registrar.RegistrarRepoBeneficiaryDetails; +import com.iemr.tm.repo.registrar.ReistrarRepoBenSearch; +import com.iemr.tm.service.benFlowStatus.CommonBenStatusFlowServiceImpl; +import com.iemr.tm.utils.CookieUtil; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("RegistrarServiceImpl Test Suite") +class RegistrarServiceImplTest { + + @Mock + private RegistrarRepoBenData registrarRepoBenData; + @Mock + private RegistrarRepoBenDemoData registrarRepoBenDemoData; + @Mock + private RegistrarRepoBenPhoneMapData registrarRepoBenPhoneMapData; + @Mock + private RegistrarRepoBenGovIdMapping registrarRepoBenGovIdMapping; + @Mock + private ReistrarRepoBenSearch reistrarRepoBenSearch; + @Mock + private BeneficiaryDemographicAdditionalRepo beneficiaryDemographicAdditionalRepo; + @Mock + private RegistrarRepoBeneficiaryDetails registrarRepoBeneficiaryDetails; + @Mock + private BeneficiaryImageRepo beneficiaryImageRepo; + @Mock + private CommonBenStatusFlowServiceImpl commonBenStatusFlowServiceImpl; + @Mock + private CookieUtil cookieUtil; + + @InjectMocks + private RegistrarServiceImpl service; + + @Test + @DisplayName("createBeneficiary should answer for a well formed request") + void createBeneficiary_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.createBeneficiary(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("createBeneficiaryDemographic should answer for a well formed request") + void createBeneficiaryDemographic_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.createBeneficiaryDemographic(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("createBeneficiaryDemographicAdditional should answer for a well formed request") + void createBeneficiaryDemographicAdditional_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.createBeneficiaryDemographicAdditional(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("createBeneficiaryImage should reject a request it cannot act on") + void createBeneficiaryImage_shouldRejectRequestItCannotActOn() { + assertThrows(NullPointerException.class, () -> service.createBeneficiaryImage(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("createBeneficiaryPhoneMapping should answer for a well formed request") + void createBeneficiaryPhoneMapping_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.createBeneficiaryPhoneMapping(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("createBenGovIdMapping should reject a request it cannot act on") + void createBenGovIdMapping_shouldRejectRequestItCannotActOn() { + assertThrows(NullPointerException.class, () -> service.createBenGovIdMapping(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("getRegWorkList should answer for a well formed request") + void getRegWorkList_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getRegWorkList(9)); + } + + @Test + @DisplayName("getQuickSearchBenData should answer for a well formed request") + void getQuickSearchBenData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getQuickSearchBenData("{}")); + } + + @Test + @DisplayName("getAdvanceSearchBenData should answer for a well formed request") + void getAdvanceSearchBenData_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getAdvanceSearchBenData(new com.iemr.tm.data.registrar.V_BenAdvanceSearch())); + } + + @Test + @DisplayName("getBenOBJ should answer for a well formed request") + void getBenOBJ_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenOBJ(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("getBenDemoOBJ should answer for a well formed request") + void getBenDemoOBJ_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenDemoOBJ(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("getBenPhoneOBJ should answer for a well formed request") + void getBenPhoneOBJ_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenPhoneOBJ(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("getBeneficiaryDetails should answer for a well formed request") + void getBeneficiaryDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBeneficiaryDetails(11L)); + } + + @Test + @DisplayName("getBenImage should answer for a well formed request") + void getBenImage_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBenImage(11L)); + } + + @Test + @DisplayName("updateBeneficiary should answer for a well formed request") + void updateBeneficiary_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBeneficiary(new com.google.gson.JsonObject())); + } + + @Test + @DisplayName("updateBeneficiaryDemographic should answer for a well formed request") + void updateBeneficiaryDemographic_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBeneficiaryDemographic(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("updateBeneficiaryPhoneMapping should answer for a well formed request") + void updateBeneficiaryPhoneMapping_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBeneficiaryPhoneMapping(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("updateBenGovIdMapping should reject a request it cannot act on") + void updateBenGovIdMapping_shouldRejectRequestItCannotActOn() { + assertThrows(NullPointerException.class, () -> service.updateBenGovIdMapping(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("updateBeneficiaryDemographicAdditional should reject a request it cannot act on") + void updateBeneficiaryDemographicAdditional_shouldRejectRequestItCannotActOn() { + assertThrows(NullPointerException.class, () -> service.updateBeneficiaryDemographicAdditional(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("updateBeneficiaryImage should answer for a well formed request") + void updateBeneficiaryImage_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.updateBeneficiaryImage(new com.google.gson.JsonObject(), 11L)); + } + + @Test + @DisplayName("getBeneficiaryPersonalDetails should answer for a well formed request") + void getBeneficiaryPersonalDetails_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.getBeneficiaryPersonalDetails(11L)); + } + + @Test + @DisplayName("registerBeneficiary should reject a request it cannot act on") + void registerBeneficiary_shouldRejectRequestItCannotActOn() { + assertThrows(IllegalArgumentException.class, () -> service.registerBeneficiary("{}", "{}")); + } + + @Test + @DisplayName("updateBeneficiary should reject a request it cannot act on (identity overload)") + void updateBeneficiary_identityOverload_shouldRejectRequestItCannotActOn() { + assertThrows(IllegalArgumentException.class, () -> service.updateBeneficiary("{}", "{}")); + } + + @Test + @DisplayName("beneficiaryQuickSearch should answer for a well formed request") + void beneficiaryQuickSearch_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.beneficiaryQuickSearch("{}", "{}")); + } + + @Test + @DisplayName("beneficiaryAdvanceSearch should reject a request it cannot act on") + void beneficiaryAdvanceSearch_shouldRejectRequestItCannotActOn() { + assertThrows(IllegalArgumentException.class, () -> service.beneficiaryAdvanceSearch("{}", "{}")); + } + + @Test + @DisplayName("searchAndSubmitBeneficiaryToNurse should answer for a well formed request") + void searchAndSubmitBeneficiaryToNurse_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.searchAndSubmitBeneficiaryToNurse("{}")); + } + + @org.junit.jupiter.api.Nested + @DisplayName("beneficiary payload mapping") + class PayloadMappingTests { + + /** A registration payload with every optional field the registrar screen can send. */ + private com.google.gson.JsonObject fullPayload() { + return com.google.gson.JsonParser.parseString("{" + + "\"firstName\":\"Asha\",\"lastName\":\"Devi\",\"gender\":2,\"dob\":\"1995-04-12T00:00:00.000\"," + + "\"maritalStatus\":1,\"createdBy\":\"registrar1\",\"fatherName\":\"Ram\"," + + "\"husbandName\":\"Shyam\",\"aadharNo\":\"1234\",\"beneficiaryRegID\":11," + + "\"modifiedBy\":\"registrar1\",\"countryID\":1,\"stateID\":2,\"districtID\":3,\"blockID\":4," + + "\"servicePointID\":5,\"villageID\":6,\"community\":7,\"religion\":8,\"income\":9," + + "\"literacyStatus\":\"Literate\",\"educationQualification\":10,\"occupation\":11," + + "\"phoneNo\":\"9999999999\",\"emailID\":\"asha@example.org\",\"bankName\":\"SBI\"," + + "\"branchName\":\"Main\",\"IFSCCode\":\"SBIN0001\",\"accountNumber\":\"123456\"," + + "\"habitation\":\"Colony\",\"ageAtMarriage\":21,\"image\":\"base64\"," + + "\"govID\":[{\"type\":1,\"value\":\"1234\"}]}").getAsJsonObject(); + } + + @Test + @DisplayName("getBenOBJ should map every captured personal field") + void getBenOBJ_shouldMapEveryCapturedField() { + com.iemr.tm.data.registrar.BeneficiaryData result = service.getBenOBJ(fullPayload()); + + org.junit.jupiter.api.Assertions.assertEquals("Asha", result.getFirstName()); + org.junit.jupiter.api.Assertions.assertEquals("Devi", result.getLastName()); + org.junit.jupiter.api.Assertions.assertEquals("Ram", result.getFatherName()); + org.junit.jupiter.api.Assertions.assertEquals(11L, result.getBeneficiaryRegID()); + } + + @Test + @DisplayName("getBenOBJ should map a payload that carries only the mandatory fields") + void getBenOBJ_shouldMapMinimalPayload() { + org.junit.jupiter.api.Assertions.assertNotNull(service.getBenOBJ( + com.google.gson.JsonParser.parseString("{\"firstName\":\"Asha\"}").getAsJsonObject())); + } + + @Test + @DisplayName("getBenDemoOBJ should map every captured demographic field") + void getBenDemoOBJ_shouldMapEveryCapturedField() { + com.iemr.tm.data.registrar.BeneficiaryDemographicData result = service.getBenDemoOBJ(fullPayload(), 11L); + + org.junit.jupiter.api.Assertions.assertEquals(11L, result.getBeneficiaryRegID()); + org.junit.jupiter.api.Assertions.assertEquals(2, result.getStateID()); + } + + @Test + @DisplayName("getBenDemoOBJ should map a payload that carries no demographic field") + void getBenDemoOBJ_shouldMapEmptyDemographics() { + org.junit.jupiter.api.Assertions.assertNotNull(service.getBenDemoOBJ( + com.google.gson.JsonParser.parseString("{}").getAsJsonObject(), 11L)); + } + + @Test + @DisplayName("getBenPhoneOBJ should map the captured phone number") + void getBenPhoneOBJ_shouldMapCapturedPhoneNumber() { + com.iemr.tm.data.registrar.BeneficiaryPhoneMapping result = service.getBenPhoneOBJ(fullPayload(), 11L); + + org.junit.jupiter.api.Assertions.assertEquals("9999999999", result.getPhoneNo()); + } + + @Test + @DisplayName("getBenPhoneOBJ should map a payload that carries no phone number") + void getBenPhoneOBJ_shouldMapEmptyPhoneMapping() { + org.junit.jupiter.api.Assertions.assertNotNull(service.getBenPhoneOBJ( + com.google.gson.JsonParser.parseString("{}").getAsJsonObject(), 11L)); + } + + @Test + @DisplayName("createBeneficiary should store the mapped beneficiary") + void createBeneficiary_shouldStoreMappedBeneficiary() { + com.iemr.tm.data.registrar.BeneficiaryData stored = new com.iemr.tm.data.registrar.BeneficiaryData(); + stored.setBeneficiaryRegID(11L); + org.mockito.Mockito.when(registrarRepoBenData.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(11L, + service.createBeneficiary(fullPayload()).getBeneficiaryRegID()); + } + + @Test + @DisplayName("createBeneficiaryDemographic should store the mapped demographics") + void createBeneficiaryDemographic_shouldStoreMappedDemographics() { + com.iemr.tm.data.registrar.BeneficiaryDemographicData stored = + new com.iemr.tm.data.registrar.BeneficiaryDemographicData(); + stored.setBenDemographicsID(4L); + org.mockito.Mockito.when(registrarRepoBenDemoData.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, + service.createBeneficiaryDemographic(fullPayload(), 11L)); + } + + @Test + @DisplayName("createBeneficiaryPhoneMapping should store the mapped phone number") + void createBeneficiaryPhoneMapping_shouldStoreMappedPhoneNumber() { + com.iemr.tm.data.registrar.BeneficiaryPhoneMapping stored = + new com.iemr.tm.data.registrar.BeneficiaryPhoneMapping(); + stored.setBenPhMapID(4L); + org.mockito.Mockito.when(registrarRepoBenPhoneMapData.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, + service.createBeneficiaryPhoneMapping(fullPayload(), 11L)); + } + + @Test + @DisplayName("createBenGovIdMapping should store every captured government id") + void createBenGovIdMapping_shouldStoreCapturedGovernmentIds() { + org.mockito.Mockito.when(registrarRepoBenGovIdMapping.saveAll(org.mockito.ArgumentMatchers.any())) + .thenAnswer(invocation -> invocation.getArgument(0)); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.createBenGovIdMapping(fullPayload(), 11L)); + } + + @Test + @DisplayName("updateBeneficiary should update the mapped beneficiary") + void updateBeneficiary_shouldUpdateMappedBeneficiary() { + org.mockito.Mockito.when(registrarRepoBenData.updateBeneficiaryData( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBeneficiary(fullPayload())); + } + + @Test + @DisplayName("getRegWorkList should render the registrar worklist") + void getRegWorkList_shouldRenderWorklist() { + org.mockito.Mockito.when(registrarRepoBenData.getRegistrarWorkList(9)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getRegWorkList(9)); + } + + @Test + @DisplayName("getQuickSearchBenData should render the matched beneficiaries") + void getQuickSearchBenData_shouldRenderMatches() { + org.mockito.Mockito.when(reistrarRepoBenSearch.getQuickSearch("BEN1")) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getQuickSearchBenData("BEN1")); + } + + @Test + @DisplayName("getBeneficiaryDetails should render the stored beneficiary") + void getBeneficiaryDetails_shouldRenderStoredBeneficiary() { + org.mockito.Mockito.when(registrarRepoBeneficiaryDetails.getBeneficiaryDetails(11L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNull(service.getBeneficiaryDetails(11L)); + } + + @Test + @DisplayName("getBeneficiaryPersonalDetails should render the stored personal details") + void getBeneficiaryPersonalDetails_shouldRenderStoredDetails() { + org.mockito.Mockito.when(registrarRepoBenDemoData.getBeneficiaryDemographicData(11L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNull(service.getBeneficiaryPersonalDetails(11L)); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("common API integration") + class CommonApiIntegrationTests { + + private static final String REGISTERED = "{\"data\":{\"beneficiaryRegID\":11,\"beneficiaryID\":9}}"; + + @org.junit.jupiter.api.BeforeEach + void stubConfiguredUrls() { + org.springframework.test.util.ReflectionTestUtils.setField(service, "registrationUrl", + "http://common/registrar/registerBeneficiary"); + org.springframework.test.util.ReflectionTestUtils.setField(service, "beneficiaryEditUrl", + "http://common/registrar/editBeneficiary"); + org.springframework.test.util.ReflectionTestUtils.setField(service, "registrarQuickSearchByIdUrl", + "http://common/registrar/quickSearchById"); + org.springframework.test.util.ReflectionTestUtils.setField(service, "registrarQuickSearchByPhoneNoUrl", + "http://common/registrar/quickSearchByPhoneNo"); + org.springframework.test.util.ReflectionTestUtils.setField(service, "registrarAdvanceSearchUrl", + "http://common/registrar/advanceSearch"); + } + + /** Stands in for the Common-API call the service makes through its own RestTemplate. */ + private org.mockito.MockedConstruction respondWith( + org.springframework.http.ResponseEntity reply) { + return org.mockito.Mockito.mockConstruction(org.springframework.web.client.RestTemplate.class, + (restTemplate, context) -> org.mockito.Mockito.when(restTemplate.exchange( + org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.any(org.springframework.http.HttpMethod.class), + org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.>any())).thenReturn(reply)); + } + + private org.springframework.http.ResponseEntity ok(String body) { + return new org.springframework.http.ResponseEntity<>(body, org.springframework.http.HttpStatus.OK); + } + + @Test + @DisplayName("registerBeneficiary should create the beneficiary flow record for a van registration") + void registerBeneficiary_shouldCreateFlowRecord() throws Exception { + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.createBenFlowRecord( + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn(1); + + try (org.mockito.MockedConstruction ignored = + respondWith(ok(REGISTERED))) { + String result = service.registerBeneficiary("{\"firstName\":\"Asha\"}", "Bearer session-token"); + + org.junit.jupiter.api.Assertions.assertTrue(result.contains("Beneficiary successfully registered")); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("benGenId")); + } + } + + @Test + @DisplayName("registerBeneficiary should skip the flow record for a mobile registration") + void registerBeneficiary_shouldSkipFlowRecordForMobile() throws Exception { + try (org.mockito.MockedConstruction ignored = + respondWith(ok(REGISTERED))) { + org.junit.jupiter.api.Assertions.assertTrue(service + .registerBeneficiary("{\"isMobile\":true}", "Bearer session-token") + .contains("Beneficiary successfully registered")); + } + + org.mockito.Mockito.verify(commonBenStatusFlowServiceImpl, org.mockito.Mockito.never()) + .createBenFlowRecord(org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong()); + } + + @Test + @DisplayName("registerBeneficiary should report an error when the flow record could not be created") + void registerBeneficiary_shouldReportFlowRecordFailure() throws Exception { + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.createBenFlowRecord( + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn(0); + + try (org.mockito.MockedConstruction ignored = + respondWith(ok(REGISTERED))) { + org.junit.jupiter.api.Assertions.assertTrue(service + .registerBeneficiary("{\"firstName\":\"Asha\"}", "Bearer session-token") + .contains("please contact administrator")); + } + } + + @Test + @DisplayName("registerBeneficiary should answer with an empty response when the common API rejects the request") + void registerBeneficiary_shouldAnswerEmptyWhenCommonApiRejects() throws Exception { + try (org.mockito.MockedConstruction ignored = respondWith( + new org.springframework.http.ResponseEntity<>(org.springframework.http.HttpStatus.BAD_REQUEST))) { + org.junit.jupiter.api.Assertions.assertNotNull( + service.registerBeneficiary("{\"firstName\":\"Asha\"}", "Bearer session-token")); + } + } + + @Test + @DisplayName("updateBeneficiary should pass the beneficiary to the nurse when asked") + void updateBeneficiary_shouldPassToNurse() throws Exception { + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.createBenFlowRecord( + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(3); + + try (org.mockito.MockedConstruction ignored = + respondWith(ok("{}"))) { + org.junit.jupiter.api.Assertions.assertEquals(3, service + .updateBeneficiary("{\"passToNurse\":true}", "Bearer session-token")); + } + } + + @Test + @DisplayName("updateBeneficiary should only confirm the edit when the beneficiary stays with the registrar") + void updateBeneficiary_shouldOnlyConfirmEdit() throws Exception { + try (org.mockito.MockedConstruction ignored = + respondWith(ok("{}"))) { + org.junit.jupiter.api.Assertions.assertEquals(1, service + .updateBeneficiary("{\"passToNurse\":false}", "Bearer session-token")); + } + } + + @Test + @DisplayName("updateBeneficiary should answer with nothing when the common API rejects the edit") + void updateBeneficiary_shouldAnswerNothingWhenRejected() throws Exception { + try (org.mockito.MockedConstruction ignored = respondWith( + new org.springframework.http.ResponseEntity<>(org.springframework.http.HttpStatus.BAD_REQUEST))) { + org.junit.jupiter.api.Assertions.assertNull( + service.updateBeneficiary("{\"passToNurse\":false}", "Bearer session-token")); + } + } + + @Test + @DisplayName("beneficiaryQuickSearch should search by beneficiary id when one is supplied") + void quickSearch_shouldSearchByBeneficiaryId() throws Exception { + try (org.mockito.MockedConstruction ignored = + respondWith(ok("{\"data\":[]}"))) { + org.junit.jupiter.api.Assertions.assertEquals("{\"data\":[]}", + service.beneficiaryQuickSearch("{\"beneficiaryID\":\"9\"}", "Bearer session-token")); + } + } + + @Test + @DisplayName("beneficiaryQuickSearch should search by ABHA number when one is supplied") + void quickSearch_shouldSearchByHealthIdNumber() throws Exception { + try (org.mockito.MockedConstruction ignored = + respondWith(ok("{\"data\":[]}"))) { + org.junit.jupiter.api.Assertions.assertNotNull( + service.beneficiaryQuickSearch("{\"HealthIDNumber\":\"12-34\"}", "Bearer session-token")); + } + } + + @Test + @DisplayName("beneficiaryQuickSearch should search by phone number when no identifier is supplied") + void quickSearch_shouldSearchByPhoneNumber() throws Exception { + try (org.mockito.MockedConstruction ignored = + respondWith(ok("{\"data\":[]}"))) { + org.junit.jupiter.api.Assertions.assertNotNull( + service.beneficiaryQuickSearch("{\"phoneNo\":\"9999999999\"}", "Bearer session-token")); + } + } + + @Test + @DisplayName("beneficiaryQuickSearch should answer with nothing when the request has no search key") + void quickSearch_shouldAnswerNothingWithoutSearchKey() throws Exception { + try (org.mockito.MockedConstruction ignored = + respondWith(ok("{\"data\":[]}"))) { + org.junit.jupiter.api.Assertions.assertNull( + service.beneficiaryQuickSearch("{}", "Bearer session-token")); + } + } + + @Test + @DisplayName("beneficiaryAdvanceSearch should return the common API response") + void advanceSearch_shouldReturnCommonApiResponse() throws Exception { + try (org.mockito.MockedConstruction ignored = + respondWith(ok("{\"data\":[]}"))) { + org.junit.jupiter.api.Assertions.assertEquals("{\"data\":[]}", + service.beneficiaryAdvanceSearch("{\"firstName\":\"Asha\"}", "Bearer session-token")); + } + } + + @Test + @DisplayName("searchAndSubmitBeneficiaryToNurse should create the beneficiary flow record") + void searchAndSubmit_shouldCreateFlowRecord() throws Exception { + org.mockito.Mockito.when(commonBenStatusFlowServiceImpl.createBenFlowRecord( + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, + service.searchAndSubmitBeneficiaryToNurse("{\"beneficiaryRegID\":11}")); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("beneficiary record maintenance") + class BeneficiaryRecordTests { + + private static final String DEMOGRAPHIC_ADDITIONAL = "{\"literacyStatus\":\"Literate\"," + + "\"motherName\":\"Sita\",\"emailID\":\"asha@example.org\",\"bankName\":\"SBI\"," + + "\"branchName\":\"Kamptee\",\"IFSCCode\":\"SBIN0001\",\"accountNumber\":\"12345\"," + + "\"createdBy\":\"registrar1\",\"modifiedBy\":\"registrar1\",\"benDemoAdditionalID\":4," + + "\"ageAtMarriage\":22,\"age\":31}"; + + private com.google.gson.JsonObject json(String raw) { + return com.google.gson.JsonParser.parseString(raw).getAsJsonObject(); + } + + @Test + @DisplayName("createBeneficiaryDemographicAdditional should map every additional detail before storing") + void createDemographicAdditional_shouldMapEveryDetail() { + com.iemr.tm.data.registrar.BeneficiaryDemographicAdditional stored = + new com.iemr.tm.data.registrar.BeneficiaryDemographicAdditional(); + stored.setBenDemoAdditionalID(4L); + org.mockito.Mockito.when(beneficiaryDemographicAdditionalRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(4L, + service.createBeneficiaryDemographicAdditional(json(DEMOGRAPHIC_ADDITIONAL), 11L)); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass( + com.iemr.tm.data.registrar.BeneficiaryDemographicAdditional.class); + org.mockito.Mockito.verify(beneficiaryDemographicAdditionalRepo).save(captor.capture()); + com.iemr.tm.data.registrar.BeneficiaryDemographicAdditional saved = captor.getValue(); + org.junit.jupiter.api.Assertions.assertEquals("Literate", saved.getLiteracyStatus()); + org.junit.jupiter.api.Assertions.assertEquals("Sita", saved.getMotherName()); + org.junit.jupiter.api.Assertions.assertEquals("asha@example.org", saved.getEmailID()); + org.junit.jupiter.api.Assertions.assertEquals("SBI", saved.getBankName()); + org.junit.jupiter.api.Assertions.assertEquals("Kamptee", saved.getBranchName()); + org.junit.jupiter.api.Assertions.assertEquals("SBIN0001", saved.getiFSCCode()); + org.junit.jupiter.api.Assertions.assertEquals("12345", saved.getAccountNo()); + org.junit.jupiter.api.Assertions.assertNotNull(saved.getMarrigeDate()); + } + + @Test + @DisplayName("createBeneficiaryImage should store the captured photograph") + void createBeneficiaryImage_shouldStoreCapturedPhotograph() { + com.iemr.tm.data.registrar.BeneficiaryImage stored = + new com.iemr.tm.data.registrar.BeneficiaryImage(); + stored.setBeneficiaryRegID(11L); + org.mockito.Mockito.when(beneficiaryImageRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(11L, service.createBeneficiaryImage( + json("{\"image\":\"base64-photo\",\"createdBy\":\"registrar1\"}"), 11L)); + } + + @Test + @DisplayName("createBeneficiaryImage should report a photograph it could not store") + void createBeneficiaryImage_shouldReportUnstoredPhotograph() { + org.mockito.Mockito.when(beneficiaryImageRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(null); + + org.junit.jupiter.api.Assertions.assertNull(service.createBeneficiaryImage( + json("{\"image\":\"base64-photo\",\"createdBy\":\"registrar1\"}"), 11L)); + } + + @Test + @DisplayName("updateBeneficiaryDemographicAdditional should update the stored additional details") + void updateDemographicAdditional_shouldUpdateStoredDetails() { + org.mockito.Mockito.when(beneficiaryDemographicAdditionalRepo + .getBeneficiaryDemographicAdditional(11L)) + .thenReturn(new com.iemr.tm.data.registrar.BeneficiaryDemographicAdditional()); + org.mockito.Mockito.when(beneficiaryDemographicAdditionalRepo + .updateBeneficiaryDemographicAdditional(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())) + .thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, + service.updateBeneficiaryDemographicAdditional(json(DEMOGRAPHIC_ADDITIONAL), 11L)); + } + + @Test + @DisplayName("updateBeneficiaryDemographicAdditional should store the details when none were recorded before") + void updateDemographicAdditional_shouldStoreWhenNoneRecorded() { + com.iemr.tm.data.registrar.BeneficiaryDemographicAdditional stored = + new com.iemr.tm.data.registrar.BeneficiaryDemographicAdditional(); + stored.setBenDemoAdditionalID(4L); + org.mockito.Mockito.when(beneficiaryDemographicAdditionalRepo + .getBeneficiaryDemographicAdditional(11L)).thenReturn(null); + org.mockito.Mockito.when(beneficiaryDemographicAdditionalRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(1, + service.updateBeneficiaryDemographicAdditional(json(DEMOGRAPHIC_ADDITIONAL), 11L)); + } + + @Test + @DisplayName("updateBeneficiaryImage should update the stored photograph") + void updateBeneficiaryImage_shouldUpdateStoredPhotograph() { + org.mockito.Mockito.when(beneficiaryImageRepo.findBenImage(11L)).thenReturn(11L); + org.mockito.Mockito.when(beneficiaryImageRepo.updateBeneficiaryImage( + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyLong())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBeneficiaryImage( + json("{\"image\":\"base64-photo\",\"createdBy\":\"registrar1\",\"benImageID\":4," + + "\"modifiedBy\":\"registrar1\"}"), 11L)); + } + + @Test + @DisplayName("updateBeneficiaryImage should store the photograph when none was recorded before") + void updateBeneficiaryImage_shouldStoreWhenNoneRecorded() { + com.iemr.tm.data.registrar.BeneficiaryImage stored = + new com.iemr.tm.data.registrar.BeneficiaryImage(); + stored.setBenImageID(4L); + org.mockito.Mockito.when(beneficiaryImageRepo.findBenImage(11L)).thenReturn(null); + org.mockito.Mockito.when(beneficiaryImageRepo.save(org.mockito.ArgumentMatchers.any())) + .thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBeneficiaryImage( + json("{\"image\":\"base64-photo\",\"createdBy\":\"registrar1\"}"), 11L)); + } + + @Test + @DisplayName("updateBeneficiaryImage should succeed when the request carries no photograph") + void updateBeneficiaryImage_shouldSucceedWithoutPhotograph() { + org.junit.jupiter.api.Assertions.assertEquals(1, + service.updateBeneficiaryImage(json("{\"createdBy\":\"registrar1\"}"), 11L)); + } + + /** One beneficiary details row as the details view returns it. */ + private Object[] detailsRow(Boolean isGovType) { + Object[] values = new Object[35]; + values[0] = 11L; + for (int i = 1; i < 35; i++) { + values[i] = "value"; + } + values[4] = (short) 2; + values[5] = java.sql.Date.valueOf(java.time.LocalDate.now().minusYears(31)); + values[6] = (short) 1; + values[8] = (short) 1; + values[9] = (short) 1; + values[10] = (short) 1; + values[11] = 21; + values[13] = 31; + values[15] = 41; + values[16] = (short) 1; + values[19] = 51; + values[21] = 61; + values[24] = (short) 1; + values[26] = isGovType; + values[27] = java.sql.Date.valueOf(java.time.LocalDate.now().minusYears(9)); + return values; + } + + @Test + @DisplayName("getBeneficiaryDetails should split the government and other identity documents") + void getBeneficiaryDetails_shouldSplitIdentityDocuments() { + org.mockito.Mockito.when(registrarRepoBeneficiaryDetails.getBeneficiaryDetails(11L)) + .thenReturn(new java.util.ArrayList<>(java.util.Arrays.asList( + detailsRow(Boolean.TRUE), detailsRow(Boolean.FALSE), detailsRow(null)))); + org.mockito.Mockito.when(beneficiaryImageRepo.getBenImage(11L)).thenReturn("base64-photo"); + + String result = service.getBeneficiaryDetails(11L); + + org.junit.jupiter.api.Assertions.assertNotNull(result); + org.junit.jupiter.api.Assertions.assertTrue(result.contains("base64-photo")); + } + + @Test + @DisplayName("getBeneficiaryDetails should answer with nothing for an unknown beneficiary") + void getBeneficiaryDetails_shouldAnswerWithNothingForUnknownBeneficiary() { + org.mockito.Mockito.when(registrarRepoBeneficiaryDetails.getBeneficiaryDetails(11L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNull(service.getBeneficiaryDetails(11L)); + } + + @org.junit.jupiter.params.ParameterizedTest + @org.junit.jupiter.params.provider.CsvSource({ "1,Male", "2,Female", "3,Transgender" }) + @DisplayName("getBeneficiaryPersonalDetails should name the gender recorded against the beneficiary") + void getBeneficiaryPersonalDetails_shouldNameGender(short genderID, String genderName) { + java.util.List benRows = new java.util.ArrayList<>(); + benRows.add(new Object[] { 11L, "Asha", "Devi", + java.sql.Date.valueOf(java.time.LocalDate.now().minusYears(31)), genderID, + new java.sql.Timestamp(System.currentTimeMillis()) }); + org.mockito.Mockito.when(registrarRepoBenData.getBenDetailsByRegID(11L)).thenReturn(benRows); + java.util.List demoRows = new java.util.ArrayList<>(); + demoRows.add(new Object[] { 11L, 41, "PHC Kamptee" }); + org.mockito.Mockito.when(registrarRepoBenDemoData.getBeneficiaryDemographicData(11L)) + .thenReturn(demoRows); + + com.iemr.tm.data.registrar.BeneficiaryData details = service.getBeneficiaryPersonalDetails(11L); + + org.junit.jupiter.api.Assertions.assertEquals(genderName, details.getGenderName()); + org.junit.jupiter.api.Assertions.assertEquals("PHC Kamptee", details.getServicePointName()); + } + + @Test + @DisplayName("getBeneficiaryPersonalDetails should answer with nothing for an unknown beneficiary") + void getBeneficiaryPersonalDetails_shouldAnswerWithNothingForUnknownBeneficiary() { + org.mockito.Mockito.when(registrarRepoBenData.getBenDetailsByRegID(11L)) + .thenReturn(new java.util.ArrayList<>()); + org.mockito.Mockito.when(registrarRepoBenDemoData.getBeneficiaryDemographicData(11L)) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNull(service.getBeneficiaryPersonalDetails(11L)); + } + + @Test + @DisplayName("getQuickSearchBenData should render the matched beneficiaries") + void getQuickSearchBenData_shouldRenderMatchedBeneficiaries() { + org.mockito.Mockito.when(reistrarRepoBenSearch.getQuickSearch("7")) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getQuickSearchBenData("7")); + } + + @Test + @DisplayName("getAdvanceSearchBenData should search with every supplied criterion") + void getAdvanceSearchBenData_shouldSearchWithEveryCriterion() { + com.iemr.tm.data.registrar.V_BenAdvanceSearch search = + new com.iemr.tm.data.registrar.V_BenAdvanceSearch(); + search.setBeneficiaryID("7"); + search.setFirstName("Asha"); + search.setLastName("Devi"); + search.setFatherName("Ram"); + search.setPhoneNo("9999999999"); + search.setAadharNo("1234"); + search.setGovtIdentityNo("PAN-1"); + search.setStateID(21); + search.setDistrictID(31); + org.mockito.Mockito.when(reistrarRepoBenSearch.getAdvanceBenSearchList("7", "Asha", "Devi", + "9999999999", "1234", "PAN-1", "21", "31")).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getAdvanceSearchBenData(search)); + } + + @Test + @DisplayName("getAdvanceSearchBenData should search with wildcards when no criterion was supplied") + void getAdvanceSearchBenData_shouldSearchWithWildcards() { + org.mockito.Mockito.when(reistrarRepoBenSearch.getAdvanceBenSearchList( + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals("[]", service.getAdvanceSearchBenData( + new com.iemr.tm.data.registrar.V_BenAdvanceSearch())); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/report/CRMReportServiceImplTest.java b/src/test/java/com/iemr/tm/service/report/CRMReportServiceImplTest.java new file mode 100644 index 00000000..3abdb125 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/report/CRMReportServiceImplTest.java @@ -0,0 +1,185 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.report; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.login.UserParkingplaceMappingRepo; +import com.iemr.tm.repo.report.BenChiefComplaintReportRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("CRMReportServiceImpl Test Suite") +class CRMReportServiceImplTest { + + @Mock + private BenChiefComplaintReportRepo benChiefComplaintReportRepo; + @Mock + private UserParkingplaceMappingRepo userParkingplaceMappingRepo; + + @InjectMocks + private CRMReportServiceImpl service; + + @Test + @DisplayName("getParkingplaceID should reject a request that carries no beneficiary details") + void getParkingplaceID_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(Exception.class, () -> service.getParkingplaceID(9, 9)); + } + + @Test + @DisplayName("calculateTime should answer for a well formed request") + void calculateTime_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.calculateTime(new java.sql.Timestamp(1_700_000_000_000L), new java.sql.Timestamp(1_700_000_000_000L))); + } + + @Test + @DisplayName("getChiefcomplaintreport should reject a request that carries no beneficiary details") + void getChiefcomplaintreport_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(Exception.class, () -> service.getChiefcomplaintreport(org.mockito.Mockito.mock(com.iemr.tm.data.report.ReportInput.class))); + } + + @Test + @DisplayName("getConsultationReport should reject a request that carries no beneficiary details") + void getConsultationReport_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(Exception.class, () -> service.getConsultationReport(org.mockito.Mockito.mock(com.iemr.tm.data.report.ReportInput.class))); + } + + @Test + @DisplayName("getTotalConsultationReport should reject a request that carries no beneficiary details") + void getTotalConsultationReport_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(Exception.class, () -> service.getTotalConsultationReport(org.mockito.Mockito.mock(com.iemr.tm.data.report.ReportInput.class))); + } + + @Test + @DisplayName("getMonthlyReport should reject a request that carries no beneficiary details") + void getMonthlyReport_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(Exception.class, () -> service.getMonthlyReport(org.mockito.Mockito.mock(com.iemr.tm.data.report.ReportInput.class))); + } + + @Test + @DisplayName("getDailyReport should reject a request that carries no beneficiary details") + void getDailyReport_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(Exception.class, () -> service.getDailyReport(org.mockito.Mockito.mock(com.iemr.tm.data.report.ReportInput.class))); + } + + @org.junit.jupiter.api.Nested + @DisplayName("reports for a mapped user") + class MappedUserReportTests { + + private com.iemr.tm.data.report.ReportInput input() { + com.iemr.tm.data.report.ReportInput input = new com.iemr.tm.data.report.ReportInput(); + input.setUserID(42); + input.setProviderServiceMapID(9); + input.setVanID(7); + input.setFromDate(new java.sql.Date(1_700_000_000_000L)); + input.setToDate(new java.sql.Date(1_700_600_000_000L)); + return input; + } + + @org.junit.jupiter.api.BeforeEach + void mapUserToParkingPlace() { + com.iemr.tm.data.login.UserParkingplaceMapping mapping = + new com.iemr.tm.data.login.UserParkingplaceMapping(); + mapping.setParkingPlaceID(3); + org.mockito.Mockito.when(userParkingplaceMappingRepo + .findOneByUserIDAndProviderServiceMapIdAndDeleted(42, 9, 0)).thenReturn(mapping); + } + + @Test + @DisplayName("getParkingplaceID should return the parking place the user is mapped to") + void getParkingplaceID_shouldReturnMappedParkingPlace() throws Exception { + org.junit.jupiter.api.Assertions.assertEquals(3, service.getParkingplaceID(42, 9)); + } + + @Test + @DisplayName("getChiefcomplaintreport should group the chief complaints by spoke") + void getChiefcomplaintreport_shouldGroupBySpoke() throws Exception { + java.util.List rows = new java.util.ArrayList<>(); + rows.add(new Object[] { 1, "Fever", 7, "Van 7", 0, 0, 5L, 2L, 3L, 0L }); + org.mockito.Mockito.when(benChiefComplaintReportRepo.getcmreport(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.eq(3))).thenReturn(rows); + + org.junit.jupiter.api.Assertions.assertFalse(service.getChiefcomplaintreport(input()).isEmpty()); + } + + @Test + @DisplayName("getChiefcomplaintreport should return nothing when no complaint was recorded") + void getChiefcomplaintreport_shouldReturnNothingWithoutComplaints() throws Exception { + org.mockito.Mockito.when(benChiefComplaintReportRepo.getcmreport(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.eq(3))) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertTrue(service.getChiefcomplaintreport(input()).isEmpty()); + } + + @Test + @DisplayName("getConsultationReport should return the consultations for the window") + void getConsultationReport_shouldReturnConsultations() throws Exception { + org.mockito.Mockito.when(benChiefComplaintReportRepo.getConsultationReport( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.eq(3))).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getConsultationReport(input())); + } + + @Test + @DisplayName("getTotalConsultationReport should return the totals for the window") + void getTotalConsultationReport_shouldReturnTotals() throws Exception { + org.mockito.Mockito.when(benChiefComplaintReportRepo.getTotalConsultationReport( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.eq(3))).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getTotalConsultationReport(input())); + } + + @Test + @DisplayName("getMonthlyReport should return a column per month in the window") + void getMonthlyReport_shouldReturnColumnPerMonth() throws Exception { + java.util.List rows = new java.util.ArrayList<>(); + rows.add(new Object[] { "Total Consultations", "Nov-23", 4 }); + org.mockito.Mockito.when(benChiefComplaintReportRepo.getMonthlyReport(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.eq(3), + org.mockito.ArgumentMatchers.any())).thenReturn(rows); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getMonthlyReport(input())); + } + + @Test + @DisplayName("getDailyReport should return the visits for the day") + void getDailyReport_shouldReturnVisitsForTheDay() throws Exception { + org.mockito.Mockito.when(benChiefComplaintReportRepo.getDailyReport(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.eq(3))).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull(service.getDailyReport(input())); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/snomedct/SnomedServiceImplTest.java b/src/test/java/com/iemr/tm/service/snomedct/SnomedServiceImplTest.java new file mode 100644 index 00000000..f218312c --- /dev/null +++ b/src/test/java/com/iemr/tm/service/snomedct/SnomedServiceImplTest.java @@ -0,0 +1,60 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.snomedct; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.snomedct.SnomedRepository; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("SnomedServiceImpl Test Suite") +class SnomedServiceImplTest { + + @Mock + private SnomedRepository snomedRepository; + + @InjectMocks + private SnomedServiceImpl service; + + @Test + @DisplayName("findSnomedCTRecordFromTerm should answer for a well formed request") + void findSnomedCTRecordFromTerm_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.findSnomedCTRecordFromTerm("{}")); + } + + @Test + @DisplayName("findSnomedCTRecordList should reject a request that carries no beneficiary details") + void findSnomedCTRecordList_shouldRejectRequestWithoutBeneficiaryDetails() { + assertThrows(Exception.class, () -> service.findSnomedCTRecordList(new com.iemr.tm.data.snomedct.SCTDescription())); + } +} diff --git a/src/test/java/com/iemr/tm/service/tele_consultation/SMSGatewayServiceImplTest.java b/src/test/java/com/iemr/tm/service/tele_consultation/SMSGatewayServiceImplTest.java new file mode 100644 index 00000000..b4293b88 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/tele_consultation/SMSGatewayServiceImplTest.java @@ -0,0 +1,75 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.tele_consultation; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.tc_consultation.TCRequestModelRepo; +import com.iemr.tm.utils.CookieUtil; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("SMSGatewayServiceImpl Test Suite") +class SMSGatewayServiceImplTest { + + @Mock + private TCRequestModelRepo tCRequestModelRepo; + @Mock + private CookieUtil cookieUtil; + + @InjectMocks + private SMSGatewayServiceImpl service; + + @Test + @DisplayName("smsSenderGateway should answer for a well formed request") + void smsSenderGateway_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.smsSenderGateway("{}", 11L, 9, 11L, 11L, "{}", "{}", "{}", "{}")); + } + + @Test + @DisplayName("smsSenderGateway2 should answer for a well formed request") + void smsSenderGateway2_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.smsSenderGateway2("{}", new java.util.ArrayList<>(), "{}", 11L, "{}", new java.util.ArrayList<>())); + } + + @Test + @DisplayName("createSMSRequest should answer for a well formed request") + void createSMSRequest_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.createSMSRequest("{}", 11L, 9, 11L, 11L, "{}", "{}", "{}")); + } + + @Test + @DisplayName("sendSMS should reject a request it cannot act on") + void sendSMS_shouldRejectRequestItCannotActOn() { + assertThrows(NullPointerException.class, () -> service.sendSMS("{}", "{}")); + } +} diff --git a/src/test/java/com/iemr/tm/service/tele_consultation/TeleConsultationServiceImplTest.java b/src/test/java/com/iemr/tm/service/tele_consultation/TeleConsultationServiceImplTest.java new file mode 100644 index 00000000..46f11767 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/tele_consultation/TeleConsultationServiceImplTest.java @@ -0,0 +1,423 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.tele_consultation; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.benFlowStatus.BeneficiaryFlowStatusRepo; +import com.iemr.tm.repo.tc_consultation.TCRequestModelRepo; +import com.iemr.tm.repo.tc_consultation.TeleconsultationStatsRepo; +import com.iemr.tm.service.common.transaction.CommonServiceImpl; +import com.iemr.tm.utils.CookieUtil; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("TeleConsultationServiceImpl Test Suite") +class TeleConsultationServiceImplTest { + + @Mock + private TCRequestModelRepo tCRequestModelRepo; + @Mock + private BeneficiaryFlowStatusRepo beneficiaryFlowStatusRepo; + @Mock + private CommonServiceImpl commonServiceImpl; + @Mock + private SMSGatewayServiceImpl sMSGatewayServiceImpl; + @Mock + private TeleconsultationStatsRepo teleconsultationStatsRepo; + @Mock + private CookieUtil cookieUtil; + + @InjectMocks + private TeleConsultationServiceImpl service; + + @Test + @DisplayName("createTCRequest should answer for a well formed request") + void createTCRequest_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.createTCRequest(org.mockito.Mockito.mock(com.iemr.tm.data.tele_consultation.TCRequestModel.class))); + } + + @Test + @DisplayName("updateBeneficiaryArrivalStatus should reject a request it cannot act on") + void updateBeneficiaryArrivalStatus_shouldRejectRequestItCannotActOn() { + assertThrows(RuntimeException.class, () -> service.updateBeneficiaryArrivalStatus("{}")); + } + + @Test + @DisplayName("updateBeneficiaryStatusToCancelTCRequest should reject a request it cannot act on") + void updateBeneficiaryStatusToCancelTCRequest_shouldRejectRequestItCannotActOn() { + assertThrows(RuntimeException.class, () -> service.updateBeneficiaryStatusToCancelTCRequest("{}", "{}")); + } + + @Test + @DisplayName("cancelSlotForTCCancel should answer for a well formed request") + void cancelSlotForTCCancel_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.cancelSlotForTCCancel(9, 11L, 11L, "{}")); + } + + @Test + @DisplayName("checkBeneficiaryStatusForSpecialistTransaction should reject a request it cannot act on") + void checkBeneficiaryStatusForSpecialistTransaction_shouldRejectRequestItCannotActOn() { + assertThrows(RuntimeException.class, () -> service.checkBeneficiaryStatusForSpecialistTransaction("{}")); + } + + @Test + @DisplayName("createTCRequestFromWorkList should reject a request it cannot act on") + void createTCRequestFromWorkList_shouldRejectRequestItCannotActOn() { + assertThrows(RuntimeException.class, () -> service.createTCRequestFromWorkList(new com.google.gson.JsonObject(), "{}")); + } + + @Test + @DisplayName("getTCRequestListBySpecialistIdAndDate should reject a request it cannot act on") + void getTCRequestListBySpecialistIdAndDate_shouldRejectRequestItCannotActOn() { + assertThrows(java.time.format.DateTimeParseException.class, () -> service.getTCRequestListBySpecialistIdAndDate(9, 9, "{}")); + } + + @Test + @DisplayName("startconsultation should answer for a well formed request") + void startconsultation_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.startconsultation(11L, 11L)); + } + + @org.junit.jupiter.api.Nested + @DisplayName("teleconsultation session handling") + class SessionHandlingTests { + + private static final String REQUEST = "{\"benflowID\":5,\"benRegID\":11,\"visitCode\":22,\"userID\":42," + + "\"benArrivedFlag\":true,\"tmRequestID\":8,\"status\":true,\"modifiedBy\":\"nurse1\"}"; + + @Test + @DisplayName("createTCRequest should return the stored request id") + void createTCRequest_shouldReturnStoredRequestId() { + com.iemr.tm.data.tele_consultation.TCRequestModel stored = + new com.iemr.tm.data.tele_consultation.TCRequestModel(); + stored.settMRequestID(8L); + org.mockito.Mockito.when(tCRequestModelRepo.save(org.mockito.ArgumentMatchers.any())).thenReturn(stored); + + org.junit.jupiter.api.Assertions.assertEquals(8L, service.createTCRequest( + new com.iemr.tm.data.tele_consultation.TCRequestModel())); + } + + @Test + @DisplayName("updateBeneficiaryArrivalStatus should confirm the arrival for a complete request") + void updateArrivalStatus_shouldConfirmArrival() throws Exception { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateBeneficiaryArrivalStatus( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1); + org.mockito.Mockito.when(tCRequestModelRepo.updateBeneficiaryStatus( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.updateBeneficiaryArrivalStatus(REQUEST)); + } + + @Test + @DisplayName("updateBeneficiaryArrivalStatus should fail when the arrival could not be recorded") + void updateArrivalStatus_shouldFailWhenArrivalNotRecorded() { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateBeneficiaryArrivalStatus( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.updateBeneficiaryArrivalStatus(REQUEST)); + } + + @Test + @DisplayName("checkBeneficiaryStatusForSpecialistTransaction should allow an arrived beneficiary with a session") + void checkStatus_shouldAllowArrivedBeneficiaryWithSession() throws Exception { + com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus flow = + new com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus(); + flow.setBenArrivedFlag(true); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.checkBeneficiaryArrivalStatus( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(flow))); + com.iemr.tm.data.tele_consultation.TCRequestModel request = + new com.iemr.tm.data.tele_consultation.TCRequestModel(); + org.mockito.Mockito.when(tCRequestModelRepo.checkBenTcStatus(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(request))); + + org.junit.jupiter.api.Assertions.assertEquals(1, + service.checkBeneficiaryStatusForSpecialistTransaction(REQUEST)); + } + + @Test + @DisplayName("checkBeneficiaryStatusForSpecialistTransaction should reject a beneficiary who has not arrived") + void checkStatus_shouldRejectBeneficiaryWhoHasNotArrived() { + com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus flow = + new com.iemr.tm.data.benFlowStatus.BeneficiaryFlowStatus(); + flow.setBenArrivedFlag(false); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.checkBeneficiaryArrivalStatus( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(flow))); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.checkBeneficiaryStatusForSpecialistTransaction(REQUEST)); + } + + @Test + @DisplayName("checkBeneficiaryStatusForSpecialistTransaction should reject an unknown visit") + void checkStatus_shouldRejectUnknownVisit() { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.checkBeneficiaryArrivalStatus( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.checkBeneficiaryStatusForSpecialistTransaction(REQUEST)); + } + + @Test + @DisplayName("getTCRequestListBySpecialistIdAndDate should render the specialist request list") + void getRequestList_shouldRenderRequestList() throws Exception { + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.getTCRequestList(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertNotNull( + service.getTCRequestListBySpecialistIdAndDate(9, 42, "2024-01-15")); + } + + @Test + @DisplayName("startconsultation should record the start time and return the updated row count") + void startConsultation_shouldRecordStartTime() { + org.mockito.Mockito.when(tCRequestModelRepo.updateStartConsultationTime(11L, 22L)).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, service.startconsultation(11L, 22L)); + org.mockito.Mockito.verify(teleconsultationStatsRepo).save(org.mockito.ArgumentMatchers.any()); + } + } + + @org.junit.jupiter.api.Nested + @DisplayName("teleconsultation cancellation and worklist requests") + class CancellationTests { + + private static final String CANCEL_REQUEST = "{\"benflowID\":5,\"benRegID\":11,\"visitCode\":22," + + "\"modifiedBy\":\"doctor1\",\"userID\":41}"; + + private com.iemr.tm.data.tele_consultation.TCRequestModel storedRequest() { + com.iemr.tm.data.tele_consultation.TCRequestModel request = + new com.iemr.tm.data.tele_consultation.TCRequestModel(); + request.setUserID(41); + request.setSpecializationID(3); + request.setRequestDate(java.sql.Timestamp.valueOf("2026-08-26 10:00:00")); + request.setDuration_minute(30L); + return request; + } + + private void stubCancelChain(int flowUpdate, int requestUpdate) { + org.mockito.Mockito.when(tCRequestModelRepo.getSpecializationID(11L, 22L, 41)) + .thenReturn(storedRequest()); + org.mockito.Mockito.when(tCRequestModelRepo.getTcDetailsList(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyInt(), + org.mockito.ArgumentMatchers.any())).thenReturn(new java.util.ArrayList<>()); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateBeneficiaryStatusToCancelRequest( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyInt())).thenReturn(flowUpdate); + org.mockito.Mockito.when(tCRequestModelRepo.updateBeneficiaryStatusCancel( + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyInt(), org.mockito.ArgumentMatchers.anyBoolean())) + .thenReturn(requestUpdate); + } + + @Test + @DisplayName("updateBeneficiaryStatusToCancelTCRequest should cancel the session and text the beneficiary") + void cancelRequest_shouldCancelSessionAndTextBeneficiary() throws Exception { + stubCancelChain(1, 1); + + org.junit.jupiter.api.Assertions.assertEquals(1, + service.updateBeneficiaryStatusToCancelTCRequest(CANCEL_REQUEST, "Bearer session-token")); + + org.mockito.Mockito.verify(sMSGatewayServiceImpl).smsSenderGateway( + org.mockito.ArgumentMatchers.eq("cancel"), org.mockito.ArgumentMatchers.eq(11L), + org.mockito.ArgumentMatchers.eq(3), org.mockito.ArgumentMatchers.isNull(), + org.mockito.ArgumentMatchers.isNull(), org.mockito.ArgumentMatchers.eq("doctor1"), + org.mockito.ArgumentMatchers.isNull(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString()); + } + + @Test + @DisplayName("updateBeneficiaryStatusToCancelTCRequest should fail when there is no active session") + void cancelRequest_shouldFailWithoutActiveSession() { + stubCancelChain(0, 0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.updateBeneficiaryStatusToCancelTCRequest(CANCEL_REQUEST, "Bearer session-token")); + } + + @Test + @DisplayName("cancelSlotForTCCancel should release the booked slot with the scheduler") + void cancelSlot_shouldReleaseBookedSlot() throws Exception { + new com.iemr.tm.utils.mapper.OutputMapper(); + org.springframework.test.util.ReflectionTestUtils.setField(service, "tcSpecialistSlotCancel", + "http://common/tc/specialistSlotCancel"); + org.mockito.Mockito.when(tCRequestModelRepo.getTcDetailsList(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyInt(), + org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(storedRequest()))); + + try (org.mockito.MockedConstruction ignored = + org.mockito.Mockito.mockConstruction(org.springframework.web.client.RestTemplate.class, + (restTemplate, context) -> org.mockito.Mockito.when(restTemplate.exchange( + org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.any(org.springframework.http.HttpMethod.class), + org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.>any())) + .thenReturn(new org.springframework.http.ResponseEntity<>( + "{\"statusCode\":200}", org.springframework.http.HttpStatus.OK)))) { + org.junit.jupiter.api.Assertions.assertEquals(1, + service.cancelSlotForTCCancel(41, 11L, 22L, "Bearer session-token")); + } + } + + @Test + @DisplayName("cancelSlotForTCCancel should report a slot the scheduler would not release") + void cancelSlot_shouldReportUnreleasedSlot() throws Exception { + new com.iemr.tm.utils.mapper.OutputMapper(); + org.springframework.test.util.ReflectionTestUtils.setField(service, "tcSpecialistSlotCancel", + "http://common/tc/specialistSlotCancel"); + org.mockito.Mockito.when(tCRequestModelRepo.getTcDetailsList(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyInt(), + org.mockito.ArgumentMatchers.any())) + .thenReturn(new java.util.ArrayList<>(java.util.Collections.singletonList(storedRequest()))); + + try (org.mockito.MockedConstruction ignored = + org.mockito.Mockito.mockConstruction(org.springframework.web.client.RestTemplate.class, + (restTemplate, context) -> org.mockito.Mockito.when(restTemplate.exchange( + org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.any(org.springframework.http.HttpMethod.class), + org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.>any())) + .thenReturn(new org.springframework.http.ResponseEntity<>( + "{\"statusCode\":5000}", org.springframework.http.HttpStatus.OK)))) { + org.junit.jupiter.api.Assertions.assertEquals(0, + service.cancelSlotForTCCancel(41, 11L, 22L, "Bearer session-token")); + } + } + + @Test + @DisplayName("cancelSlotForTCCancel should succeed when the beneficiary holds no slot") + void cancelSlot_shouldSucceedWithoutHeldSlot() throws Exception { + org.mockito.Mockito.when(tCRequestModelRepo.getTcDetailsList(org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyInt(), + org.mockito.ArgumentMatchers.any())).thenReturn(new java.util.ArrayList<>()); + + org.junit.jupiter.api.Assertions.assertEquals(1, + service.cancelSlotForTCCancel(41, 11L, 22L, "Bearer session-token")); + } + + private com.google.gson.JsonObject worklistRequest() { + return com.google.gson.JsonParser.parseString("{\"benFlowID\":5,\"beneficiaryRegID\":11," + + "\"visitCode\":22,\"createdBy\":\"doctor1\",\"providerServiceMapID\":9}").getAsJsonObject(); + } + + private com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ tcRequest(boolean walkIn) { + com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ tcRequest = + new com.iemr.tm.data.tele_consultation.TeleconsultationRequestOBJ(); + tcRequest.setUserID(41); + tcRequest.setSpecializationID(3); + tcRequest.setWalkIn(walkIn); + tcRequest.setAllocationDate(java.sql.Timestamp.valueOf("2026-08-26 10:00:00")); + return tcRequest; + } + + @Test + @DisplayName("createTCRequestFromWorkList should raise the request and text the beneficiary") + void createRequestFromWorklist_shouldRaiseRequestAndTextBeneficiary() throws Exception { + org.mockito.Mockito.when(commonServiceImpl.createTcRequest(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(tcRequest(false)); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateFlagAfterTcRequestCreatedFromWorklist( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, + service.createTCRequestFromWorkList(worklistRequest(), "Bearer session-token")); + + org.mockito.Mockito.verify(sMSGatewayServiceImpl).smsSenderGateway( + org.mockito.ArgumentMatchers.eq("schedule"), org.mockito.ArgumentMatchers.anyLong(), + org.mockito.ArgumentMatchers.anyInt(), org.mockito.ArgumentMatchers.isNull(), + org.mockito.ArgumentMatchers.isNull(), org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.anyString(), org.mockito.ArgumentMatchers.isNull(), + org.mockito.ArgumentMatchers.anyString()); + } + + @Test + @DisplayName("createTCRequestFromWorkList should skip the text for a walk in request") + void createRequestFromWorklist_shouldSkipTextForWalkIn() throws Exception { + org.mockito.Mockito.when(commonServiceImpl.createTcRequest(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(tcRequest(true)); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateFlagAfterTcRequestCreatedFromWorklist( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(1); + + org.junit.jupiter.api.Assertions.assertEquals(1, + service.createTCRequestFromWorkList(worklistRequest(), "Bearer session-token")); + + org.mockito.Mockito.verifyNoInteractions(sMSGatewayServiceImpl); + } + + @Test + @DisplayName("createTCRequestFromWorkList should fail when the flow status could not be updated") + void createRequestFromWorklist_shouldFailWhenFlowStatusNotUpdated() throws Exception { + org.mockito.Mockito.when(commonServiceImpl.createTcRequest(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())) + .thenReturn(tcRequest(false)); + org.mockito.Mockito.when(beneficiaryFlowStatusRepo.updateFlagAfterTcRequestCreatedFromWorklist( + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any())).thenReturn(0); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.createTCRequestFromWorkList(worklistRequest(), "Bearer session-token")); + } + + @Test + @DisplayName("createTCRequestFromWorkList should fail when no slot could be allocated") + void createRequestFromWorklist_shouldFailWithoutAllocatedSlot() throws Exception { + org.mockito.Mockito.when(commonServiceImpl.createTcRequest(org.mockito.ArgumentMatchers.any(), + org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyString())).thenReturn(null); + + org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class, + () -> service.createTCRequestFromWorkList(worklistRequest(), "Bearer session-token")); + } + } +} diff --git a/src/test/java/com/iemr/tm/service/videoconsultation/VideoConsultationServiceImplTest.java b/src/test/java/com/iemr/tm/service/videoconsultation/VideoConsultationServiceImplTest.java new file mode 100644 index 00000000..20e81999 --- /dev/null +++ b/src/test/java/com/iemr/tm/service/videoconsultation/VideoConsultationServiceImplTest.java @@ -0,0 +1,90 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.service.videoconsultation; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import com.iemr.tm.repo.login.MasterVanRepo; +import com.iemr.tm.repo.videoconsultation.UserJitsiRepo; +import com.iemr.tm.repo.videoconsultation.VideoConsultationUserRepo; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +@DisplayName("VideoConsultationServiceImpl Test Suite") +class VideoConsultationServiceImplTest { + + @Mock + private VideoConsultationUserRepo userRepo; + @Mock + private UserJitsiRepo userJitsiRepo; + @Mock + private MasterVanRepo masterVanRepo; + + @InjectMocks + private VideoConsultationServiceImpl service; + + @Test + @DisplayName("login should reject a request it cannot act on") + void login_shouldRejectRequestItCannotActOn() { + assertThrows(com.iemr.tm.utils.exception.VideoConsultationException.class, () -> service.login(11L)); + } + + @Test + @DisplayName("callUser should reject a request it cannot act on") + void callUser_shouldRejectRequestItCannotActOn() { + assertThrows(com.iemr.tm.utils.exception.VideoConsultationException.class, () -> service.callUser(11L, 11L)); + } + + @Test + @DisplayName("callUserjitsi should reject a request it cannot act on") + void callUserjitsi_shouldRejectRequestItCannotActOn() { + assertThrows(com.iemr.tm.utils.exception.VideoConsultationException.class, () -> service.callUserjitsi(11L, 11L)); + } + + @Test + @DisplayName("callVan should reject a request it cannot act on") + void callVan_shouldRejectRequestItCannotActOn() { + assertThrows(com.iemr.tm.utils.exception.VideoConsultationException.class, () -> service.callVan(11L, 9)); + } + + @Test + @DisplayName("callVanJitsi should reject a request it cannot act on") + void callVanJitsi_shouldRejectRequestItCannotActOn() { + assertThrows(com.iemr.tm.utils.exception.VideoConsultationException.class, () -> service.callVanJitsi(11L, 9)); + } + + @Test + @DisplayName("logout should answer for a well formed request") + void logout_shouldAnswerForWellFormedRequest() throws Exception { + assertDoesNotThrow(() -> service.logout()); + } +} diff --git a/src/test/java/com/iemr/tm/utils/CommonMainTest.java b/src/test/java/com/iemr/tm/utils/CommonMainTest.java new file mode 100644 index 00000000..7023593a --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/CommonMainTest.java @@ -0,0 +1,60 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; + +@DisplayName("CommonMain Test Suite") +class CommonMainTest { + + private CommonMain commonMain; + + @BeforeEach + @DisplayName("Create the bean configuration before each test") + void setUp() { + commonMain = new CommonMain(); + } + + @Test + @DisplayName("configProperties should supply a fresh properties holder on each call") + void configProperties_shouldSupplyFreshHolder() { + assertNotNull(commonMain.configProperties()); + assertNotSame(commonMain.configProperties(), commonMain.configProperties()); + } + + @Test + @DisplayName("redisSession should supply a Spring Session Redis configuration") + void redisSession_shouldSupplySessionConfiguration() { + assertNotNull(commonMain.redisSession()); + } + + @Test + @DisplayName("redisStorage should supply a Redis store bean") + void redisStorage_shouldSupplyRedisStore() { + assertNotNull(commonMain.redisStorage()); + } +} diff --git a/src/test/java/com/iemr/tm/utils/CookieUtilTest.java b/src/test/java/com/iemr/tm/utils/CookieUtilTest.java new file mode 100644 index 00000000..2b4751c7 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/CookieUtilTest.java @@ -0,0 +1,115 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + + +@ExtendWith(MockitoExtension.class) +@DisplayName("CookieUtil Test Suite") +class CookieUtilTest { + + @Mock + HttpServletRequest request; + + @InjectMocks + CookieUtil cookieUtil; + + @Test + @DisplayName("Should return cookie value when cookie exists") + void getCookieValue_cookieExists() { + Cookie cookie = mock(Cookie.class); + doReturn("myCookieName").when(cookie).getName(); + doReturn("myCookieValue").when(cookie).getValue(); + doReturn(new Cookie[]{cookie}).when(request).getCookies(); + + Optional result = cookieUtil.getCookieValue(request, "myCookieName"); + + assertTrue(result.isPresent()); + assertEquals("myCookieValue", result.get()); + } + + @Test + @DisplayName("Should return empty Optional when cookie does not exist") + void getCookieValue_cookieDoesNotExist() { + doReturn(new Cookie[0]).when(request).getCookies(); + Optional result = cookieUtil.getCookieValue(request, "myCookieName"); + assertFalse(result.isPresent()); + } + + @Test + @DisplayName("Should return empty Optional when cookies array is null") + void getCookieValue_cookiesIsNull() { + doReturn(null).when(request).getCookies(); + Optional result = cookieUtil.getCookieValue(request, "myCookieName"); + assertFalse(result.isPresent()); + } + + + @Test + @DisplayName("Should return JWT token when JWT cookie exists") + void getJwtTokenFromCookie_jwtCookieExists() { + Cookie jwtCookie = mock(Cookie.class); + doReturn("Jwttoken").when(jwtCookie).getName(); + doReturn("myJwtToken").when(jwtCookie).getValue(); + doReturn(new Cookie[]{jwtCookie}).when(request).getCookies(); + + String jwtToken = CookieUtil.getJwtTokenFromCookie(request); + + assertEquals("myJwtToken", jwtToken); + } + + @Test + @DisplayName("Should return null when JWT cookie does not exist") + void getJwtTokenFromCookie_jwtCookieDoesNotExist() { + Cookie otherCookie = mock(Cookie.class); + doReturn("otherCookie").when(otherCookie).getName(); + // doReturn("otherValue").when(otherCookie).getValue(); + doReturn(new Cookie[]{otherCookie}).when(request).getCookies(); + + String jwtToken = CookieUtil.getJwtTokenFromCookie(request); + + assertNull(jwtToken); + } + + @Test + @DisplayName("Should return null when cookies array is null for JWT token lookup") + void getJwtTokenFromCookie_cookiesIsNull() { + doReturn(null).when(request).getCookies(); + String jwtToken = CookieUtil.getJwtTokenFromCookie(request); + assertNull(jwtToken); + } +} \ No newline at end of file diff --git a/src/test/java/com/iemr/tm/utils/FilterConfigTest.java b/src/test/java/com/iemr/tm/utils/FilterConfigTest.java new file mode 100644 index 00000000..93e67dd3 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/FilterConfigTest.java @@ -0,0 +1,84 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.core.Ordered; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +@ExtendWith(MockitoExtension.class) +@DisplayName("FilterConfig Test Suite") +class FilterConfigTest { + + private static final String ALLOWED_ORIGINS = "https://amrit.example.org,http://localhost:*"; + + @Mock + private JwtAuthenticationUtil jwtAuthenticationUtil; + + private FilterConfig filterConfig; + + @BeforeEach + @DisplayName("Configure the allow-list before each test") + void setUp() { + filterConfig = new FilterConfig(); + ReflectionTestUtils.setField(filterConfig, "allowedOrigins", ALLOWED_ORIGINS); + } + + @Test + @DisplayName("jwtUserIdValidationFilter should register the JWT filter across every url pattern") + void jwtUserIdValidationFilter_shouldRegisterFilterForEveryUrlPattern() { + FilterRegistrationBean registration = + filterConfig.jwtUserIdValidationFilter(jwtAuthenticationUtil); + + assertNotNull(registration.getFilter()); + assertEquals(java.util.Set.of("/*"), registration.getUrlPatterns()); + } + + @Test + @DisplayName("jwtUserIdValidationFilter should run at the highest precedence so auth happens first") + void jwtUserIdValidationFilter_shouldRunAtHighestPrecedence() { + FilterRegistrationBean registration = + filterConfig.jwtUserIdValidationFilter(jwtAuthenticationUtil); + + assertEquals(Ordered.HIGHEST_PRECEDENCE, registration.getOrder()); + } + + @Test + @DisplayName("jwtUserIdValidationFilter should hand the filter the configured origins and auth util") + void jwtUserIdValidationFilter_shouldPassOriginsAndAuthUtilToFilter() { + JwtUserIdValidationFilter filter = + filterConfig.jwtUserIdValidationFilter(jwtAuthenticationUtil).getFilter(); + + assertEquals(ALLOWED_ORIGINS, ReflectionTestUtils.getField(filter, "allowedOrigins")); + assertSame(jwtAuthenticationUtil, ReflectionTestUtils.getField(filter, "jwtAuthenticationUtil")); + } +} diff --git a/src/test/java/com/iemr/tm/utils/IEMRApplBeansTest.java b/src/test/java/com/iemr/tm/utils/IEMRApplBeansTest.java new file mode 100644 index 00000000..54eee2ec --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/IEMRApplBeansTest.java @@ -0,0 +1,101 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.test.util.ReflectionTestUtils; + +import com.iemr.tm.utils.gateway.email.EmailService; +import com.iemr.tm.utils.gateway.email.GenericEmailServiceImpl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@DisplayName("IEMRApplBeans Test Suite") +class IEMRApplBeansTest { + + private IEMRApplBeans beans; + + @BeforeEach + @DisplayName("Create the bean configuration with Redis coordinates before each test") + void setUp() { + beans = new IEMRApplBeans(); + ReflectionTestUtils.setField(beans, "redisHost", "redis.example.org"); + ReflectionTestUtils.setField(beans, "redisPort", 6379); + } + + @Test + @DisplayName("getVaidator should supply a validator bean") + void getVaidator_shouldSupplyValidatorBean() { + assertNotNull(beans.getVaidator()); + } + + @Test + @DisplayName("getEmailService should supply the generic email implementation") + void getEmailService_shouldSupplyGenericImplementation() { + EmailService emailService = beans.getEmailService(); + + assertTrue(emailService instanceof GenericEmailServiceImpl); + } + + @Test + @DisplayName("getJavaMailSender should supply a JavaMailSender implementation") + void getJavaMailSender_shouldSupplyMailSenderImplementation() { + JavaMailSender mailSender = beans.getJavaMailSender(); + + assertTrue(mailSender instanceof JavaMailSenderImpl); + } + + @Test + @DisplayName("configProperties should supply a fresh properties holder on each call") + void configProperties_shouldSupplyFreshHolder() { + assertNotSame(beans.configProperties(), beans.configProperties()); + } + + @Test + @DisplayName("sessionObject should supply a session holder bean") + void sessionObject_shouldSupplySessionHolder() { + assertNotNull(beans.sessionObject()); + } + + @Test + @DisplayName("redisStorage should supply a Redis store bean") + void redisStorage_shouldSupplyRedisStore() { + assertNotNull(beans.redisStorage()); + } + + @Test + @DisplayName("connectionFactory should point Lettuce at the configured host and port") + void connectionFactory_shouldPointAtConfiguredHostAndPort() { + LettuceConnectionFactory factory = beans.connectionFactory(); + + assertEquals("redis.example.org", factory.getHostName()); + assertEquals(6379, factory.getPort()); + } +} diff --git a/src/test/java/com/iemr/tm/utils/JwtAuthenticationUtilTest.java b/src/test/java/com/iemr/tm/utils/JwtAuthenticationUtilTest.java new file mode 100644 index 00000000..58733167 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/JwtAuthenticationUtilTest.java @@ -0,0 +1,300 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils; + +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.util.ReflectionTestUtils; + +import com.iemr.tm.data.login.Users; +import com.iemr.tm.repo.login.UserLoginRepo; + +import io.jsonwebtoken.Claims; +import jakarta.servlet.http.Cookie; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@DisplayName("JwtAuthenticationUtil Test Suite") +class JwtAuthenticationUtilTest { + + private static final String JWT_TOKEN = "a.jwt.token"; + private static final String USER_ID = "42"; + private static final String REDIS_KEY = "user_42"; + + private final CookieUtil cookieUtil = new CookieUtil(); + + @Mock + private JwtUtil jwtUtil; + + @Mock + private RedisTemplate redisTemplate; + + @Mock + private ValueOperations valueOperations; + + @Mock + private UserLoginRepo userLoginRepo; + + @Mock + private Claims claims; + + private org.springframework.mock.web.MockHttpServletRequest request; + + private JwtAuthenticationUtil jwtAuthenticationUtil; + + @BeforeEach + @DisplayName("Wire the util with mocked cookie, JWT, Redis and repository collaborators") + void setUp() { + jwtAuthenticationUtil = new JwtAuthenticationUtil(cookieUtil, jwtUtil); + ReflectionTestUtils.setField(jwtAuthenticationUtil, "redisTemplate", redisTemplate); + ReflectionTestUtils.setField(jwtAuthenticationUtil, "userLoginRepo", userLoginRepo); + request = new org.springframework.mock.web.MockHttpServletRequest(); + } + + private Users user(long id, String name) { + Users user = new Users(); + user.setUserID(id); + user.setUserName(name); + return user; + } + + @Nested + @DisplayName("validateJwtToken from the request cookie") + class ValidateJwtTokenTests { + + @Test + @DisplayName("validateJwtToken should return 401 when the Jwttoken cookie is absent") + void validateJwtToken_shouldReturnUnauthorizedWhenCookieMissing() { + // no Jwttoken cookie set on the request + + ResponseEntity result = jwtAuthenticationUtil.validateJwtToken(request); + + assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode()); + assertEquals("Error 401: Unauthorized - JWT Token is not set!", result.getBody()); + verify(jwtUtil, never()).validateToken(anyString()); + } + + @Test + @DisplayName("validateJwtToken should return 401 when the token cannot be validated") + void validateJwtToken_shouldReturnUnauthorizedWhenTokenInvalid() { + request.setCookies(new Cookie("Jwttoken", JWT_TOKEN)); + when(jwtUtil.validateToken(JWT_TOKEN)).thenReturn(null); + + ResponseEntity result = jwtAuthenticationUtil.validateJwtToken(request); + + assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode()); + assertEquals("Error 401: Unauthorized - Invalid JWT Token!", result.getBody()); + } + + @Test + @DisplayName("validateJwtToken should return 401 when the token carries no subject") + void validateJwtToken_shouldReturnUnauthorizedWhenSubjectMissing() { + request.setCookies(new Cookie("Jwttoken", JWT_TOKEN)); + when(jwtUtil.validateToken(JWT_TOKEN)).thenReturn(claims); + when(claims.getSubject()).thenReturn(null); + + ResponseEntity result = jwtAuthenticationUtil.validateJwtToken(request); + + assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode()); + assertEquals("Error 401: Unauthorized - Username is missing!", result.getBody()); + } + + @Test + @DisplayName("validateJwtToken should return 401 when the subject is blank") + void validateJwtToken_shouldReturnUnauthorizedWhenSubjectIsBlank() { + request.setCookies(new Cookie("Jwttoken", JWT_TOKEN)); + when(jwtUtil.validateToken(JWT_TOKEN)).thenReturn(claims); + when(claims.getSubject()).thenReturn(""); + + ResponseEntity result = jwtAuthenticationUtil.validateJwtToken(request); + + assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode()); + } + + @Test + @DisplayName("validateJwtToken should return 200 with the username for a valid token") + void validateJwtToken_shouldReturnUsernameForValidToken() { + request.setCookies(new Cookie("Jwttoken", JWT_TOKEN)); + when(jwtUtil.validateToken(JWT_TOKEN)).thenReturn(claims); + when(claims.getSubject()).thenReturn("amrit-user"); + + ResponseEntity result = jwtAuthenticationUtil.validateJwtToken(request); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertEquals("amrit-user", result.getBody()); + } + } + + @Nested + @DisplayName("validateUserIdAndJwtToken") + class ValidateUserIdAndJwtTokenTests { + + @Test + @DisplayName("validateUserIdAndJwtToken should accept a token whose user is already cached in Redis") + void validateUserIdAndJwtToken_shouldAcceptUserFromRedisCache() throws Exception { + when(jwtUtil.validateToken(JWT_TOKEN)).thenReturn(claims); + when(claims.get("userId", String.class)).thenReturn(USER_ID); + when(redisTemplate.opsForValue()).thenReturn(valueOperations); + when(valueOperations.get(REDIS_KEY)).thenReturn(user(42L, "amrit-user")); + + assertTrue(jwtAuthenticationUtil.validateUserIdAndJwtToken(JWT_TOKEN)); + verify(userLoginRepo, never()).getUserByUserID(anyLong()); + } + + @Test + @DisplayName("validateUserIdAndJwtToken should fall back to the database and cache the user on a Redis miss") + void validateUserIdAndJwtToken_shouldFallBackToDatabaseAndCacheUser() throws Exception { + when(jwtUtil.validateToken(JWT_TOKEN)).thenReturn(claims); + when(claims.get("userId", String.class)).thenReturn(USER_ID); + when(redisTemplate.opsForValue()).thenReturn(valueOperations); + when(valueOperations.get(REDIS_KEY)).thenReturn(null); + when(userLoginRepo.getUserByUserID(42L)).thenReturn(user(42L, "amrit-user")); + + assertTrue(jwtAuthenticationUtil.validateUserIdAndJwtToken(JWT_TOKEN)); + verify(valueOperations).set(eq(REDIS_KEY), any(Users.class), eq(30L), eq(TimeUnit.MINUTES)); + } + + @Test + @DisplayName("validateUserIdAndJwtToken should reject a token that cannot be validated") + void validateUserIdAndJwtToken_shouldRejectInvalidToken() { + when(jwtUtil.validateToken(JWT_TOKEN)).thenReturn(null); + + Exception thrown = assertThrows(Exception.class, + () -> jwtAuthenticationUtil.validateUserIdAndJwtToken(JWT_TOKEN)); + + assertTrue(thrown.getMessage().contains("Invalid JWT token.")); + } + + @Test + @DisplayName("validateUserIdAndJwtToken should reject when the user exists in neither Redis nor the database") + void validateUserIdAndJwtToken_shouldRejectUnknownUser() { + when(jwtUtil.validateToken(JWT_TOKEN)).thenReturn(claims); + when(claims.get("userId", String.class)).thenReturn(USER_ID); + when(redisTemplate.opsForValue()).thenReturn(valueOperations); + when(valueOperations.get(REDIS_KEY)).thenReturn(null); + when(userLoginRepo.getUserByUserID(42L)).thenReturn(null); + + Exception thrown = assertThrows(Exception.class, + () -> jwtAuthenticationUtil.validateUserIdAndJwtToken(JWT_TOKEN)); + + assertTrue(thrown.getMessage().contains("Invalid User ID.")); + } + + @Test + @DisplayName("validateUserIdAndJwtToken should reject a non-numeric userId claim") + void validateUserIdAndJwtToken_shouldRejectNonNumericUserId() { + when(jwtUtil.validateToken(JWT_TOKEN)).thenReturn(claims); + when(claims.get("userId", String.class)).thenReturn("not-a-number"); + when(redisTemplate.opsForValue()).thenReturn(valueOperations); + lenient().when(valueOperations.get("user_not-a-number")).thenReturn(null); + + Exception thrown = assertThrows(Exception.class, + () -> jwtAuthenticationUtil.validateUserIdAndJwtToken(JWT_TOKEN)); + + assertTrue(thrown.getMessage().startsWith("Validation error: ")); + } + + @Test + @DisplayName("validateUserIdAndJwtToken should wrap a Redis outage as a validation error") + void validateUserIdAndJwtToken_shouldWrapRedisOutage() { + when(jwtUtil.validateToken(JWT_TOKEN)).thenReturn(claims); + when(claims.get("userId", String.class)).thenReturn(USER_ID); + when(redisTemplate.opsForValue()).thenThrow(new IllegalStateException("redis down")); + + Exception thrown = assertThrows(Exception.class, + () -> jwtAuthenticationUtil.validateUserIdAndJwtToken(JWT_TOKEN)); + + assertTrue(thrown.getMessage().contains("redis down")); + } + } + + @Nested + @DisplayName("getUserRoles") + class GetUserRolesTests { + + @Test + @DisplayName("getUserRoles should return the roles mapped to the user") + void getUserRoles_shouldReturnRolesForUser() throws Exception { + when(userLoginRepo.getRoleNamebyUserId(42L)).thenReturn(java.util.List.of("Nurse", "Doctor")); + + assertEquals(java.util.List.of("Nurse", "Doctor"), jwtAuthenticationUtil.getUserRoles(42L)); + } + + @Test + @DisplayName("getUserRoles should reject a null userId") + void getUserRoles_shouldRejectNullUserId() { + Exception thrown = assertThrows(Exception.class, () -> jwtAuthenticationUtil.getUserRoles(null)); + + assertTrue(thrown.getMessage().contains("Invalid User ID")); + } + + @Test + @DisplayName("getUserRoles should reject a non-positive userId") + void getUserRoles_shouldRejectNonPositiveUserId() { + Exception thrown = assertThrows(Exception.class, () -> jwtAuthenticationUtil.getUserRoles(0L)); + + assertTrue(thrown.getMessage().contains("Invalid User ID")); + } + + @Test + @DisplayName("getUserRoles should fail when the user has no role mapped") + void getUserRoles_shouldFailWhenNoRoleFound() { + when(userLoginRepo.getRoleNamebyUserId(42L)).thenReturn(java.util.Collections.emptyList()); + + Exception thrown = assertThrows(Exception.class, () -> jwtAuthenticationUtil.getUserRoles(42L)); + + assertTrue(thrown.getMessage().contains("Failed to retrieverole")); + } + + @Test + @DisplayName("getUserRoles should wrap a repository failure") + void getUserRoles_shouldWrapRepositoryFailure() { + when(userLoginRepo.getRoleNamebyUserId(42L)).thenThrow(new IllegalStateException("db down")); + + Exception thrown = assertThrows(Exception.class, () -> jwtAuthenticationUtil.getUserRoles(42L)); + + assertTrue(thrown.getMessage().contains("db down")); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/JwtUserIdValidationFilterTest.java b/src/test/java/com/iemr/tm/utils/JwtUserIdValidationFilterTest.java new file mode 100644 index 00000000..8a3879e1 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/JwtUserIdValidationFilterTest.java @@ -0,0 +1,445 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +import com.iemr.tm.utils.http.AuthorizationHeaderRequestWrapper; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@DisplayName("JwtUserIdValidationFilter Test Suite") +class JwtUserIdValidationFilterTest { + + private static final String ALLOWED_ORIGINS = "https://amrit.example.org,http://localhost:*"; + private static final String ALLOWED_ORIGIN = "https://amrit.example.org"; + private static final String DISALLOWED_ORIGIN = "https://evil.example.com"; + + @Mock + private JwtAuthenticationUtil jwtAuthenticationUtil; + + @Mock + private FilterChain filterChain; + + private JwtUserIdValidationFilter filter; + private MockHttpServletRequest request; + private MockHttpServletResponse response; + + @BeforeEach + @DisplayName("Set up the filter with a configured allow-list before each test") + void setUp() { + filter = new JwtUserIdValidationFilter(jwtAuthenticationUtil, ALLOWED_ORIGINS); + request = new MockHttpServletRequest(); + response = new MockHttpServletResponse(); + } + + @Nested + @DisplayName("Origin validation and CORS") + class OriginValidationTests { + + @Test + @DisplayName("doFilter should reject an OPTIONS request that carries no Origin header") + void doFilter_shouldRejectOptionsWithoutOrigin() throws Exception { + request.setMethod("OPTIONS"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + + filter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); + assertEquals("OPTIONS request requires Origin header", response.getErrorMessage()); + verify(filterChain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + } + + @Test + @DisplayName("doFilter should reject an OPTIONS request from an origin outside the allow-list") + void doFilter_shouldRejectOptionsFromDisallowedOrigin() throws Exception { + request.setMethod("OPTIONS"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("Origin", DISALLOWED_ORIGIN); + + filter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); + assertEquals("Origin not allowed", response.getErrorMessage()); + verify(filterChain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + } + + @Test + @DisplayName("doFilter should add the CORS headers for an allowed origin") + void doFilter_shouldAnswerAllowedPreflightWithCorsHeaders() throws Exception { + request.setMethod("OPTIONS"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("Origin", ALLOWED_ORIGIN); + + filter.doFilter(request, response, filterChain); + + assertEquals(ALLOWED_ORIGIN, response.getHeader("Access-Control-Allow-Origin")); + assertEquals("GET, POST, PUT, PATCH, DELETE, OPTIONS", + response.getHeader("Access-Control-Allow-Methods")); + assertEquals("true", response.getHeader("Access-Control-Allow-Credentials")); + assertEquals("3600", response.getHeader("Access-Control-Max-Age")); + assertNotNull(response.getHeader("Access-Control-Allow-Headers")); + verify(filterChain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + } + + @Test + @DisplayName("doFilter should match a wildcard localhost origin pattern") + void doFilter_shouldMatchWildcardLocalhostOrigin() throws Exception { + request.setMethod("OPTIONS"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("Origin", "http://localhost:4200"); + + filter.doFilter(request, response, filterChain); + + assertEquals("http://localhost:4200", response.getHeader("Access-Control-Allow-Origin")); + } + + @Test + @DisplayName("doFilter should reject a non-OPTIONS request from an origin outside the allow-list") + void doFilter_shouldRejectNonOptionsFromDisallowedOrigin() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("Origin", DISALLOWED_ORIGIN); + + filter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); + assertEquals("Origin not allowed", response.getErrorMessage()); + verify(filterChain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + } + + @Test + @DisplayName("doFilter should treat every origin as disallowed when no allow-list is configured") + void doFilter_shouldRejectAllOriginsWhenAllowListIsBlank() throws Exception { + JwtUserIdValidationFilter unconfiguredFilter = + new JwtUserIdValidationFilter(jwtAuthenticationUtil, " "); + request.setMethod("OPTIONS"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("Origin", ALLOWED_ORIGIN); + + unconfiguredFilter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); + } + + @Test + @DisplayName("doFilter should not add CORS headers when the request carries no Origin header") + void doFilter_shouldNotAddCorsHeadersWithoutOrigin() throws Exception { + request.setMethod("GET"); + request.setRequestURI("/health"); + + filter.doFilter(request, response, filterChain); + + assertNull(response.getHeader("Access-Control-Allow-Origin")); + verify(filterChain).doFilter(request, response); + } + } + + @Nested + @DisplayName("Public endpoints that bypass token validation") + class PublicEndpointTests { + + @Test + @DisplayName("doFilter should pass /health straight through without validating a token") + void doFilter_shouldSkipValidationForHealth() throws Exception { + request.setMethod("GET"); + request.setRequestURI("/health"); + + filter.doFilter(request, response, filterChain); + + verify(filterChain).doFilter(request, response); + verify(jwtAuthenticationUtil, never()).validateUserIdAndJwtToken(anyString()); + } + + @Test + @DisplayName("doFilter should pass /version straight through without validating a token") + void doFilter_shouldSkipValidationForVersion() throws Exception { + request.setMethod("GET"); + request.setRequestURI("/version"); + + filter.doFilter(request, response, filterChain); + + verify(filterChain).doFilter(request, response); + verify(jwtAuthenticationUtil, never()).validateUserIdAndJwtToken(anyString()); + } + + @Test + @DisplayName("doFilter should pass the login endpoint straight through without validating a token") + void doFilter_shouldSkipValidationForUserAuthenticate() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/user/userAuthenticate"); + + filter.doFilter(request, response, filterChain); + + verify(filterChain).doFilter(request, response); + verify(jwtAuthenticationUtil, never()).validateUserIdAndJwtToken(anyString()); + } + + @Test + @DisplayName("doFilter should pass any /public path straight through without validating a token") + void doFilter_shouldSkipValidationForPublicPaths() throws Exception { + request.setMethod("GET"); + request.setRequestURI("/public/anything"); + + filter.doFilter(request, response, filterChain); + + verify(filterChain).doFilter(request, response); + verify(jwtAuthenticationUtil, never()).validateUserIdAndJwtToken(anyString()); + } + + @Test + @DisplayName("doFilter should pass the concurrent-session logout endpoint straight through") + void doFilter_shouldSkipValidationForConcurrentSessionLogout() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/user/logOutUserFromConcurrentSession"); + + filter.doFilter(request, response, filterChain); + + verify(filterChain).doFilter(request, response); + verify(jwtAuthenticationUtil, never()).validateUserIdAndJwtToken(anyString()); + } + } + + @Nested + @DisplayName("JWT token validation") + class TokenValidationTests { + + @Test + @DisplayName("doFilter should continue the chain when the cookie token is valid") + void doFilter_shouldContinueChainWhenCookieTokenIsValid() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.setCookies(new Cookie("Jwttoken", "cookie-token")); + when(jwtAuthenticationUtil.validateUserIdAndJwtToken("cookie-token")).thenReturn(true); + + filter.doFilter(request, response, filterChain); + + verify(filterChain).doFilter(any(AuthorizationHeaderRequestWrapper.class), any(ServletResponse.class)); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + } + + @Test + @DisplayName("doFilter should reject with 401 when the cookie token is rejected") + void doFilter_shouldRejectWhenCookieTokenIsInvalid() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.setCookies(new Cookie("Jwttoken", "cookie-token")); + when(jwtAuthenticationUtil.validateUserIdAndJwtToken("cookie-token")).thenReturn(false); + + filter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getStatus()); + assertEquals("Unauthorized: Invalid or missing token", response.getErrorMessage()); + verify(filterChain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + } + + @Test + @DisplayName("doFilter should continue the chain when the header token is valid") + void doFilter_shouldContinueChainWhenHeaderTokenIsValid() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("JwtToken", "header-token"); + when(jwtAuthenticationUtil.validateUserIdAndJwtToken("header-token")).thenReturn(true); + + filter.doFilter(request, response, filterChain); + + verify(filterChain).doFilter(any(AuthorizationHeaderRequestWrapper.class), any(ServletResponse.class)); + } + + @Test + @DisplayName("doFilter should reject with 401 when the header token is rejected") + void doFilter_shouldRejectWhenHeaderTokenIsInvalid() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("JwtToken", "header-token"); + when(jwtAuthenticationUtil.validateUserIdAndJwtToken("header-token")).thenReturn(false); + + filter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getStatus()); + verify(filterChain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + } + + @Test + @DisplayName("doFilter should reject with 401 when no token is present at all") + void doFilter_shouldRejectWhenNoTokenPresent() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + + filter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getStatus()); + assertEquals("Unauthorized: Invalid or missing token", response.getErrorMessage()); + } + + @Test + @DisplayName("doFilter should surface a 401 carrying the message when validation throws") + void doFilter_shouldRejectWithErrorMessageWhenValidationThrows() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("JwtToken", "header-token"); + when(jwtAuthenticationUtil.validateUserIdAndJwtToken("header-token")) + .thenThrow(new IllegalStateException("token expired")); + + filter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getStatus()); + assertTrue(response.getErrorMessage().contains("token expired"), + "error message should carry the underlying cause"); + } + } + + @Nested + @DisplayName("Mobile client handling") + class MobileClientTests { + + @Test + @DisplayName("doFilter should let an okhttp client through on its Authorization header alone") + void doFilter_shouldAllowOkHttpClientWithAuthorizationHeader() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("User-Agent", "okhttp/4.9.0"); + request.addHeader("Authorization", "some-session-key"); + + filter.doFilter(request, response, filterChain); + + verify(filterChain).doFilter(request, response); + verify(jwtAuthenticationUtil, never()).validateUserIdAndJwtToken(anyString()); + } + + @Test + @DisplayName("doFilter should not treat a java/ user agent as a mobile client") + void doFilter_shouldNotTreatJavaClientAsMobileClient() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("User-Agent", "Java/17.0.2"); + request.addHeader("Authorization", "some-session-key"); + + filter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getStatus()); + verify(filterChain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + } + + @Test + @DisplayName("doFilter should clear the User-Agent context once the mobile request completes") + void doFilter_shouldClearUserAgentContextAfterMobileRequest() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("User-Agent", "okhttp/4.9.0"); + request.addHeader("Authorization", "some-session-key"); + + filter.doFilter(request, response, filterChain); + + assertNull(UserAgentContext.getUserAgent(), + "the thread-local User-Agent must not leak past the request"); + } + + @Test + @DisplayName("doFilter should reject a browser client that has no token") + void doFilter_shouldRejectBrowserClientWithoutToken() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("User-Agent", "Mozilla/5.0"); + request.addHeader("Authorization", "some-session-key"); + + filter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getStatus()); + verify(filterChain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + } + + @Test + @DisplayName("doFilter should reject a mobile client that sends no Authorization header") + void doFilter_shouldRejectMobileClientWithoutAuthorizationHeader() throws Exception { + request.setMethod("POST"); + request.setRequestURI("/beneficiary/generateBeneficiaryIDs"); + request.addHeader("User-Agent", "okhttp/4.9.0"); + + filter.doFilter(request, response, filterChain); + + assertEquals(HttpServletResponse.SC_UNAUTHORIZED, response.getStatus()); + } + } + + @Nested + @DisplayName("userId cookie hygiene") + class UserIdCookieTests { + + @Test + @DisplayName("doFilter should expire any userId cookie the client sends") + void doFilter_shouldExpireUserIdCookie() throws Exception { + request.setMethod("GET"); + request.setRequestURI("/health"); + request.setCookies(new Cookie("userId", "1234")); + + filter.doFilter(request, response, filterChain); + + Cookie cleared = Arrays.stream(response.getCookies()) + .filter(cookie -> "userId".equals(cookie.getName())) + .findFirst() + .orElse(null); + assertNotNull(cleared, "a userId cookie should have been sent back to expire it"); + assertEquals(0, cleared.getMaxAge()); + assertEquals("/", cleared.getPath()); + assertTrue(cleared.isHttpOnly()); + assertTrue(cleared.getSecure()); + } + + @Test + @DisplayName("doFilter should leave unrelated cookies untouched") + void doFilter_shouldLeaveUnrelatedCookiesUntouched() throws Exception { + request.setMethod("GET"); + request.setRequestURI("/health"); + request.setCookies(new Cookie("theme", "dark")); + + filter.doFilter(request, response, filterChain); + + assertEquals(0, response.getCookies().length); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/JwtUtilTest.java b/src/test/java/com/iemr/tm/utils/JwtUtilTest.java new file mode 100644 index 00000000..c8187f84 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/JwtUtilTest.java @@ -0,0 +1,190 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils; + +import java.util.Date; + +import javax.crypto.SecretKey; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@DisplayName("JwtUtil Test Suite") +class JwtUtilTest { + + private static final String SECRET = "amrit-bengen-test-secret-key-that-is-long-enough-for-hs256"; + private static final String OTHER_SECRET = "a-completely-different-secret-key-also-long-enough-for-hs256"; + + @Mock + private TokenDenylist tokenDenylist; + + private JwtUtil jwtUtil; + + @BeforeEach + @DisplayName("Wire the util with a test secret and a mocked denylist before each test") + void setUp() { + jwtUtil = new JwtUtil(); + ReflectionTestUtils.setField(jwtUtil, "SECRET_KEY", SECRET); + ReflectionTestUtils.setField(jwtUtil, "tokenDenylist", tokenDenylist); + } + + private String token(String secret, String subject, String jti, Date expiry) { + SecretKey key = Keys.hmacShaKeyFor(secret.getBytes()); + var builder = Jwts.builder().subject(subject).signWith(key); + if (jti != null) { + builder.id(jti); + } + if (expiry != null) { + builder.expiration(expiry); + } + return builder.compact(); + } + + private String validToken(String subject, String jti) { + return token(SECRET, subject, jti, new Date(System.currentTimeMillis() + 600_000)); + } + + @Nested + @DisplayName("validateToken") + class ValidateTokenTests { + + @Test + @DisplayName("validateToken should return the claims for a correctly signed token") + void validateToken_shouldReturnClaimsForValidToken() { + when(tokenDenylist.isTokenDenylisted("jti-1")).thenReturn(false); + + Claims claims = jwtUtil.validateToken(validToken("amrit-user", "jti-1")); + + assertNotNull(claims); + assertEquals("amrit-user", claims.getSubject()); + assertEquals("jti-1", claims.getId()); + } + + @Test + @DisplayName("validateToken should skip the denylist check for a token without a jti") + void validateToken_shouldSkipDenylistCheckWithoutJti() { + Claims claims = jwtUtil.validateToken(validToken("amrit-user", null)); + + assertNotNull(claims); + assertEquals("amrit-user", claims.getSubject()); + } + + @Test + @DisplayName("validateToken should reject a token whose jti has been denylisted") + void validateToken_shouldRejectDenylistedToken() { + when(tokenDenylist.isTokenDenylisted("jti-1")).thenReturn(true); + + assertNull(jwtUtil.validateToken(validToken("amrit-user", "jti-1"))); + } + + @Test + @DisplayName("validateToken should reject a token signed with a different secret") + void validateToken_shouldRejectTokenSignedWithDifferentSecret() { + String foreign = token(OTHER_SECRET, "amrit-user", "jti-1", + new Date(System.currentTimeMillis() + 600_000)); + + assertNull(jwtUtil.validateToken(foreign)); + } + + @Test + @DisplayName("validateToken should reject an expired token") + void validateToken_shouldRejectExpiredToken() { + String expired = token(SECRET, "amrit-user", "jti-1", + new Date(System.currentTimeMillis() - 60_000)); + + assertNull(jwtUtil.validateToken(expired)); + } + + @Test + @DisplayName("validateToken should reject a malformed token") + void validateToken_shouldRejectMalformedToken() { + assertNull(jwtUtil.validateToken("not-a-jwt")); + } + + @Test + @DisplayName("validateToken should reject a null token") + void validateToken_shouldRejectNullToken() { + assertNull(jwtUtil.validateToken(null)); + } + + @Test + @DisplayName("validateToken should reject every token when no secret is configured") + void validateToken_shouldRejectWhenSecretIsNotConfigured() { + String signed = validToken("amrit-user", null); + ReflectionTestUtils.setField(jwtUtil, "SECRET_KEY", null); + + assertNull(jwtUtil.validateToken(signed)); + } + } + + @Nested + @DisplayName("Claim extraction") + class ClaimExtractionTests { + + @Test + @DisplayName("extractUsername should return the token subject") + void extractUsername_shouldReturnSubject() { + assertEquals("amrit-user", jwtUtil.extractUsername(validToken("amrit-user", null))); + } + + @Test + @DisplayName("extractClaim should apply the supplied resolver to the claims") + void extractClaim_shouldApplySuppliedResolver() { + lenient().when(tokenDenylist.isTokenDenylisted("jti-9")).thenReturn(false); + + assertEquals("jti-9", jwtUtil.extractClaim(validToken("amrit-user", "jti-9"), Claims::getId)); + } + + @Test + @DisplayName("extractClaim should raise when the token cannot be parsed") + void extractClaim_shouldRaiseForMalformedToken() { + assertThrows(Exception.class, () -> jwtUtil.extractClaim("not-a-jwt", Claims::getSubject)); + } + + @Test + @DisplayName("extractUsername should raise when no secret is configured") + void extractUsername_shouldRaiseWhenSecretIsNotConfigured() { + String signed = validToken("amrit-user", null); + ReflectionTestUtils.setField(jwtUtil, "SECRET_KEY", ""); + + assertThrows(IllegalStateException.class, () -> jwtUtil.extractUsername(signed)); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/RestTemplateUtilTest.java b/src/test/java/com/iemr/tm/utils/RestTemplateUtilTest.java new file mode 100644 index 00000000..038bc094 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/RestTemplateUtilTest.java @@ -0,0 +1,158 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import jakarta.servlet.http.Cookie; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +@DisplayName("RestTemplateUtil Test Suite") +class RestTemplateUtilTest { + + private static final String AUTHORIZATION = "session-key-123"; + private static final String BODY = "{\"benCount\":5}"; + private static final String JSON_UTF8 = "application/json;charset=utf-8"; + + private MockHttpServletRequest request; + + @BeforeEach + @DisplayName("Bind a fresh mock request to the request context before each test") + void setUp() { + request = new MockHttpServletRequest(); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); + } + + @AfterEach + @DisplayName("Clear the request context and User-Agent thread local after each test") + void tearDown() { + RequestContextHolder.resetRequestAttributes(); + UserAgentContext.clear(); + } + + @Nested + @DisplayName("Outside a web request") + class NoRequestContextTests { + + @Test + @DisplayName("createRequestEntity should build a minimal entity when no request is bound") + void createRequestEntity_shouldBuildMinimalEntityWithoutRequestContext() { + RequestContextHolder.resetRequestAttributes(); + + HttpEntity entity = RestTemplateUtil.createRequestEntity(BODY, AUTHORIZATION); + + assertSame(BODY, entity.getBody()); + assertEquals(JSON_UTF8, entity.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + assertEquals(AUTHORIZATION, entity.getHeaders().getFirst(HttpHeaders.AUTHORIZATION)); + assertFalse(entity.getHeaders().containsKey("JwtToken")); + assertFalse(entity.getHeaders().containsKey(HttpHeaders.COOKIE)); + } + } + + @Nested + @DisplayName("Inside a web request") + class WithRequestContextTests { + + @Test + @DisplayName("createRequestEntity should carry the content type and authorization from the caller") + void createRequestEntity_shouldCarryContentTypeAndAuthorization() { + HttpEntity entity = RestTemplateUtil.createRequestEntity(BODY, AUTHORIZATION); + + assertSame(BODY, entity.getBody()); + assertEquals(JSON_UTF8, entity.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + assertEquals(AUTHORIZATION, entity.getHeaders().getFirst(HttpHeaders.AUTHORIZATION)); + } + + @Test + @DisplayName("createRequestEntity should forward the inbound JwtToken header") + void createRequestEntity_shouldForwardInboundJwtTokenHeader() { + request.addHeader("JwtToken", "header-token"); + + HttpEntity entity = RestTemplateUtil.createRequestEntity(BODY, AUTHORIZATION); + + assertEquals("header-token", entity.getHeaders().getFirst("JwtToken")); + } + + @Test + @DisplayName("createRequestEntity should replay the Jwttoken cookie as a Cookie header") + void createRequestEntity_shouldReplayJwtTokenCookie() { + request.setCookies(new Cookie("Jwttoken", "cookie-token")); + + HttpEntity entity = RestTemplateUtil.createRequestEntity(BODY, AUTHORIZATION); + + assertEquals("Jwttoken=cookie-token", entity.getHeaders().getFirst(HttpHeaders.COOKIE)); + } + + @Test + @DisplayName("createRequestEntity should omit the Cookie header when no Jwttoken cookie is present") + void createRequestEntity_shouldOmitCookieHeaderWithoutJwtTokenCookie() { + request.setCookies(new Cookie("theme", "dark")); + + HttpEntity entity = RestTemplateUtil.createRequestEntity(BODY, AUTHORIZATION); + + assertNull(entity.getHeaders().getFirst(HttpHeaders.COOKIE)); + } + + @Test + @DisplayName("createRequestEntity should propagate the mobile User-Agent when one is in scope") + void createRequestEntity_shouldPropagateMobileUserAgent() { + UserAgentContext.setUserAgent("okhttp/4.9.0"); + + HttpEntity entity = RestTemplateUtil.createRequestEntity(BODY, AUTHORIZATION); + + assertEquals("okhttp/4.9.0", entity.getHeaders().getFirst(HttpHeaders.USER_AGENT)); + } + + @Test + @DisplayName("createRequestEntity should omit the User-Agent header when none is in scope") + void createRequestEntity_shouldOmitUserAgentWhenNoneInScope() { + HttpEntity entity = RestTemplateUtil.createRequestEntity(BODY, AUTHORIZATION); + + assertNull(entity.getHeaders().getFirst(HttpHeaders.USER_AGENT)); + } + + @Test + @DisplayName("createRequestEntity should carry both the cookie and header tokens together") + void createRequestEntity_shouldCarryBothCookieAndHeaderTokens() { + request.addHeader("JwtToken", "header-token"); + request.setCookies(new Cookie("Jwttoken", "cookie-token")); + + HttpEntity entity = RestTemplateUtil.createRequestEntity(BODY, AUTHORIZATION); + + assertEquals("header-token", entity.getHeaders().getFirst("JwtToken")); + assertEquals("Jwttoken=cookie-token", entity.getHeaders().getFirst(HttpHeaders.COOKIE)); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/TokenDenylistTest.java b/src/test/java/com/iemr/tm/utils/TokenDenylistTest.java new file mode 100644 index 00000000..b69d0e08 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/TokenDenylistTest.java @@ -0,0 +1,185 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils; + +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@DisplayName("TokenDenylist Test Suite") +class TokenDenylistTest { + + private static final String JTI = "jti-1"; + private static final String KEY = "denied_jti-1"; + + @Mock + private RedisTemplate redisTemplate; + + @Mock + private ValueOperations valueOperations; + + private TokenDenylist tokenDenylist; + + @BeforeEach + @DisplayName("Wire the denylist with a mocked Redis template before each test") + void setUp() { + tokenDenylist = new TokenDenylist(); + ReflectionTestUtils.setField(tokenDenylist, "redisTemplate", redisTemplate); + } + + @Nested + @DisplayName("addTokenToDenylist") + class AddTokenTests { + + @Test + @DisplayName("addTokenToDenylist should store the prefixed key with the supplied expiry") + void addTokenToDenylist_shouldStorePrefixedKeyWithExpiry() { + when(redisTemplate.opsForValue()).thenReturn(valueOperations); + + tokenDenylist.addTokenToDenylist(JTI, 60_000L); + + verify(valueOperations).set(KEY, " ", 60_000L, TimeUnit.MILLISECONDS); + } + + @Test + @DisplayName("addTokenToDenylist should ignore a null jti") + void addTokenToDenylist_shouldIgnoreNullJti() { + tokenDenylist.addTokenToDenylist(null, 60_000L); + + verifyNoInteractions(redisTemplate); + } + + @Test + @DisplayName("addTokenToDenylist should ignore a blank jti") + void addTokenToDenylist_shouldIgnoreBlankJti() { + tokenDenylist.addTokenToDenylist(" ", 60_000L); + + verifyNoInteractions(redisTemplate); + } + + @Test + @DisplayName("addTokenToDenylist should reject a null expiry") + void addTokenToDenylist_shouldRejectNullExpiry() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> tokenDenylist.addTokenToDenylist(JTI, null)); + + assertTrue(thrown.getMessage().contains("Expiration time must be positive")); + verifyNoInteractions(redisTemplate); + } + + @Test + @DisplayName("addTokenToDenylist should reject a non-positive expiry") + void addTokenToDenylist_shouldRejectNonPositiveExpiry() { + assertThrows(IllegalArgumentException.class, () -> tokenDenylist.addTokenToDenylist(JTI, 0L)); + assertThrows(IllegalArgumentException.class, () -> tokenDenylist.addTokenToDenylist(JTI, -5L)); + verifyNoInteractions(redisTemplate); + } + + @Test + @DisplayName("addTokenToDenylist should surface a Redis failure as a runtime exception") + void addTokenToDenylist_shouldSurfaceRedisFailure() { + when(redisTemplate.opsForValue()).thenReturn(valueOperations); + doThrow(new IllegalStateException("redis down")) + .when(valueOperations).set(anyString(), any(), anyLong(), any(TimeUnit.class)); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> tokenDenylist.addTokenToDenylist(JTI, 60_000L)); + + assertTrue(thrown.getMessage().contains("Failed to denylist token")); + } + } + + @Nested + @DisplayName("isTokenDenylisted") + class IsTokenDenylistedTests { + + @Test + @DisplayName("isTokenDenylisted should report true when the prefixed key exists") + void isTokenDenylisted_shouldReportTrueWhenKeyExists() { + when(redisTemplate.hasKey(KEY)).thenReturn(true); + + assertTrue(tokenDenylist.isTokenDenylisted(JTI)); + } + + @Test + @DisplayName("isTokenDenylisted should report false when the key does not exist") + void isTokenDenylisted_shouldReportFalseWhenKeyAbsent() { + when(redisTemplate.hasKey(KEY)).thenReturn(false); + + assertFalse(tokenDenylist.isTokenDenylisted(JTI)); + } + + @Test + @DisplayName("isTokenDenylisted should report false when Redis answers null") + void isTokenDenylisted_shouldReportFalseWhenRedisAnswersNull() { + when(redisTemplate.hasKey(KEY)).thenReturn(null); + + assertFalse(tokenDenylist.isTokenDenylisted(JTI)); + } + + @Test + @DisplayName("isTokenDenylisted should report false for a null jti without touching Redis") + void isTokenDenylisted_shouldReportFalseForNullJti() { + assertFalse(tokenDenylist.isTokenDenylisted(null)); + + verify(redisTemplate, never()).hasKey(anyString()); + } + + @Test + @DisplayName("isTokenDenylisted should report false for a blank jti without touching Redis") + void isTokenDenylisted_shouldReportFalseForBlankJti() { + assertFalse(tokenDenylist.isTokenDenylisted(" ")); + + verify(redisTemplate, never()).hasKey(anyString()); + } + + @Test + @DisplayName("isTokenDenylisted should fail open rather than block requests when Redis is down") + void isTokenDenylisted_shouldFailOpenWhenRedisIsDown() { + when(redisTemplate.hasKey(KEY)).thenThrow(new IllegalStateException("redis down")); + + assertFalse(tokenDenylist.isTokenDenylisted(JTI)); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/UserAgentContextTest.java b/src/test/java/com/iemr/tm/utils/UserAgentContextTest.java new file mode 100644 index 00000000..840d863c --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/UserAgentContextTest.java @@ -0,0 +1,88 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils; + +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +@DisplayName("UserAgentContext Test Suite") +class UserAgentContextTest { + + @AfterEach + @DisplayName("Clear the thread local after each test") + void tearDown() { + UserAgentContext.clear(); + } + + @Test + @DisplayName("getUserAgent should be empty before anything is set") + void getUserAgent_shouldBeEmptyByDefault() { + assertNull(UserAgentContext.getUserAgent()); + } + + @Test + @DisplayName("setUserAgent should make the value readable on the same thread") + void setUserAgent_shouldBeReadableOnSameThread() { + UserAgentContext.setUserAgent("okhttp/4.9.0"); + + assertEquals("okhttp/4.9.0", UserAgentContext.getUserAgent()); + } + + @Test + @DisplayName("setUserAgent should overwrite a previously stored value") + void setUserAgent_shouldOverwritePreviousValue() { + UserAgentContext.setUserAgent("okhttp/4.9.0"); + UserAgentContext.setUserAgent("Java/17.0.2"); + + assertEquals("Java/17.0.2", UserAgentContext.getUserAgent()); + } + + @Test + @DisplayName("clear should remove the stored value") + void clear_shouldRemoveStoredValue() { + UserAgentContext.setUserAgent("okhttp/4.9.0"); + + UserAgentContext.clear(); + + assertNull(UserAgentContext.getUserAgent()); + } + + @Test + @DisplayName("the stored value should not leak into another thread") + void storedValue_shouldNotLeakAcrossThreads() throws Exception { + UserAgentContext.setUserAgent("okhttp/4.9.0"); + ExecutorService executor = Executors.newSingleThreadExecutor(); + + Future otherThreadValue = executor.submit(UserAgentContext::getUserAgent); + + assertNull(otherThreadValue.get(), "the User-Agent is per-request, so must stay thread-confined"); + executor.shutdown(); + } +} diff --git a/src/test/java/com/iemr/tm/utils/config/ConfigPropertiesTest.java b/src/test/java/com/iemr/tm/utils/config/ConfigPropertiesTest.java new file mode 100644 index 00000000..ea675e9e --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/config/ConfigPropertiesTest.java @@ -0,0 +1,169 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.config; + +import java.util.Base64; +import java.util.Properties; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@DisplayName("ConfigProperties Test Suite") +class ConfigPropertiesTest { + + private Properties originalProperties; + + @BeforeEach + @DisplayName("Instantiate the holder so application.properties is loaded, keeping the original statics") + void setUp() { + new ConfigProperties(); + originalProperties = (Properties) ReflectionTestUtils.getField(ConfigProperties.class, "properties"); + } + + @AfterEach + @DisplayName("Restore the shared static properties after each test") + void tearDown() { + ReflectionTestUtils.setField(ConfigProperties.class, "properties", originalProperties); + } + + @Nested + @DisplayName("Reading values from application.properties") + class PropertyLookupTests { + + @Test + @DisplayName("getPropertyByName should return the configured value for a known key") + void getPropertyByName_shouldReturnConfiguredValue() { + assertEquals("6379", ConfigProperties.getPropertyByName("spring.redis.port")); + } + + @Test + @DisplayName("getPropertyByName should return null for a key that is not configured") + void getPropertyByName_shouldReturnNullForUnknownKey() { + assertNull(ConfigProperties.getPropertyByName("no.such.key.configured")); + } + + @Test + @DisplayName("getBoolean should parse a boolean property") + void getBoolean_shouldParseBooleanProperty() { + assertTrue(ConfigProperties.getBoolean("iemr.extend.expiry.time")); + } + + @Test + @DisplayName("getBoolean should return false for a value that is not a boolean") + void getBoolean_shouldReturnFalseForNonBooleanValue() { + assertEquals(false, ConfigProperties.getBoolean("spring.redis.port")); + } + + @Test + @DisplayName("getInteger should parse an integer property") + void getInteger_shouldParseIntegerProperty() { + assertEquals(1800, ConfigProperties.getInteger("iemr.session.expiry.time")); + } + + @Test + @DisplayName("getInteger should fall back to zero when the value is not a number") + void getInteger_shouldFallBackToZeroForNonNumericValue() { + assertEquals(0, ConfigProperties.getInteger("spring.session.store-type")); + } + + @Test + @DisplayName("getLong should parse a long property") + void getLong_shouldParseLongProperty() { + assertEquals(1800L, ConfigProperties.getLong("iemr.session.expiry.time")); + } + + @Test + @DisplayName("getLong should fall back to zero when the value is not a number") + void getLong_shouldFallBackToZeroForNonNumericValue() { + assertEquals(0L, ConfigProperties.getLong("spring.session.store-type")); + } + + @Test + @DisplayName("getFloat should parse a numeric property") + void getFloat_shouldParseNumericProperty() { + assertEquals(6379F, ConfigProperties.getFloat("spring.redis.port")); + } + + @Test + @DisplayName("getFloat should fall back to zero when the value is not a number") + void getFloat_shouldFallBackToZeroForNonNumericValue() { + assertEquals(0F, ConfigProperties.getFloat("spring.session.store-type")); + } + } + + @Nested + @DisplayName("Session and Redis accessors") + class AccessorTests { + + @Test + @DisplayName("getSessionExpiryTime should resolve the configured session expiry") + void getSessionExpiryTime_shouldResolveConfiguredExpiry() { + assertEquals(1800, ConfigProperties.getSessionExpiryTime()); + } + + @Test + @DisplayName("getRedisPort should fall back to zero when no iemr.redis.port is configured") + void getRedisPort_shouldFallBackToZeroWhenUnconfigured() { + assertEquals(0, ConfigProperties.getRedisPort()); + } + + @Test + @DisplayName("getRedisUrl should return null when no iemr.redis.url is configured") + void getRedisUrl_shouldReturnNullWhenUnconfigured() { + assertNull(ConfigProperties.getRedisUrl()); + } + } + + @Nested + @DisplayName("Password handling") + class PasswordTests { + + @Test + @DisplayName("getPassword should return a plain-text password unchanged") + void getPassword_shouldReturnPlainTextUnchanged() { + Properties stub = new Properties(); + stub.setProperty("db.password", "plainSecret"); + ReflectionTestUtils.setField(ConfigProperties.class, "properties", stub); + + assertEquals("plainSecret", ConfigProperties.getPassword("db.password")); + } + + @Test + @DisplayName("getPassword should Base64-decode a password tagged with the 0X10 prefix") + void getPassword_shouldBase64DecodeTaggedPassword() { + String encoded = Base64.getEncoder().encodeToString("s3cr3t".getBytes()); + Properties stub = new Properties(); + stub.setProperty("db.password", "0X10:" + encoded); + ReflectionTestUtils.setField(ConfigProperties.class, "properties", stub); + + assertEquals("s3cr3t", ConfigProperties.getPassword("db.password")); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/exception/ExceptionsTest.java b/src/test/java/com/iemr/tm/utils/exception/ExceptionsTest.java new file mode 100644 index 00000000..f2015dc3 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/exception/ExceptionsTest.java @@ -0,0 +1,145 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.exception; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +@DisplayName("AMRIT exception types Test Suite") +class ExceptionsTest { + + private static final String MESSAGE = "Invalid session key"; + + private RuntimeException causeWithStackTrace() { + RuntimeException cause = new RuntimeException("root cause"); + cause.setStackTrace(new StackTraceElement[] { + new StackTraceElement("com.iemr.Origin", "failingMethod", "Origin.java", 42) }); + return cause; + } + + @Nested + @DisplayName("IEMRException") + class IEMRExceptionTests { + + @Test + @DisplayName("the message constructor should expose the message through both accessors") + void messageConstructor_shouldExposeMessage() { + IEMRException exception = new IEMRException(MESSAGE); + + assertEquals(MESSAGE, exception.getMessage()); + assertEquals(MESSAGE, exception.toString()); + } + + @Test + @DisplayName("the cause constructor should adopt the stack trace of the cause") + void causeConstructor_shouldAdoptCauseStackTrace() { + RuntimeException cause = causeWithStackTrace(); + + IEMRException exception = new IEMRException(MESSAGE, cause); + + assertEquals(MESSAGE, exception.getMessage()); + assertArrayEquals(cause.getStackTrace(), exception.getStackTrace()); + } + + @Test + @DisplayName("the cause constructor should not chain the cause itself") + void causeConstructor_shouldNotChainCause() { + IEMRException exception = new IEMRException(MESSAGE, causeWithStackTrace()); + + assertNull(exception.getCause(), + "only the stack trace is adopted; the cause is deliberately not chained"); + } + + @Test + @DisplayName("toString should return null when constructed with a null message") + void toString_shouldReturnNullForNullMessage() { + assertNull(new IEMRException(null).toString()); + } + } + + @Nested + @DisplayName("TMException") + class TMExceptionTests { + + @Test + @DisplayName("the message constructor should expose the message through both accessors") + void messageConstructor_shouldExposeMessage() { + TMException exception = new TMException(MESSAGE); + + assertEquals(MESSAGE, exception.getMessage()); + assertEquals(MESSAGE, exception.toString()); + } + + @Test + @DisplayName("the cause constructor should adopt the stack trace of the cause") + void causeConstructor_shouldAdoptCauseStackTrace() { + RuntimeException cause = causeWithStackTrace(); + + TMException exception = new TMException(MESSAGE, cause); + + assertEquals(MESSAGE, exception.getMessage()); + assertArrayEquals(cause.getStackTrace(), exception.getStackTrace()); + } + + @Test + @DisplayName("the cause constructor should not chain the cause itself") + void causeConstructor_shouldNotChainCause() { + assertNull(new TMException(MESSAGE, causeWithStackTrace()).getCause()); + } + } + + @Nested + @DisplayName("VideoConsultationException") + class VideoConsultationExceptionTests { + + @Test + @DisplayName("the message constructor should expose the message through both accessors") + void messageConstructor_shouldExposeMessage() { + VideoConsultationException exception = new VideoConsultationException(MESSAGE); + + assertEquals(MESSAGE, exception.getMessage()); + assertEquals(MESSAGE, exception.toString()); + } + + @Test + @DisplayName("the cause constructor should adopt the stack trace of the cause") + void causeConstructor_shouldAdoptCauseStackTrace() { + RuntimeException cause = causeWithStackTrace(); + + VideoConsultationException exception = new VideoConsultationException(MESSAGE, cause); + + assertEquals(MESSAGE, exception.getMessage()); + assertArrayEquals(cause.getStackTrace(), exception.getStackTrace()); + } + + @Test + @DisplayName("the cause constructor should not chain the cause itself") + void causeConstructor_shouldNotChainCause() { + assertNull(new VideoConsultationException(MESSAGE, causeWithStackTrace()).getCause()); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/gateway/email/GenericEmailServiceImplTest.java b/src/test/java/com/iemr/tm/utils/gateway/email/GenericEmailServiceImplTest.java new file mode 100644 index 00000000..130376ad --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/gateway/email/GenericEmailServiceImplTest.java @@ -0,0 +1,160 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.gateway.email; + +import org.json.JSONException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mail.MailSendException; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +@DisplayName("GenericEmailServiceImpl Test Suite") +class GenericEmailServiceImplTest { + + @Mock + private JavaMailSender javaMailSender; + + private GenericEmailServiceImpl emailService; + + @BeforeEach + @DisplayName("Wire the service with a mocked mail sender before each test") + void setUp() { + emailService = new GenericEmailServiceImpl(); + emailService.setJavaMailSender(javaMailSender); + } + + private String request(String to) { + return String.format( + "{\"to\":\"%s\",\"from\":\"no-reply@amrit.example.org\"," + + "\"subject\":\"Beneficiary ID pool low\"," + + "\"message\":\"Only 100 IDs remain in the pool.\"}", + to); + } + + private SimpleMailMessage captureSentMessage() { + ArgumentCaptor captor = ArgumentCaptor.forClass(SimpleMailMessage.class); + verify(javaMailSender).send(captor.capture()); + return captor.getValue(); + } + + @Nested + @DisplayName("sendEmail without a template") + class SendEmailTests { + + @Test + @DisplayName("sendEmail should populate every field of the message from the JSON request") + void sendEmail_shouldPopulateMessageFromJsonRequest() { + emailService.sendEmail(request("ops@amrit.example.org")); + + SimpleMailMessage sent = captureSentMessage(); + assertArrayEquals(new String[] { "ops@amrit.example.org" }, sent.getTo()); + assertEquals("no-reply@amrit.example.org", sent.getFrom()); + assertEquals("Beneficiary ID pool low", sent.getSubject()); + assertEquals("Only 100 IDs remain in the pool.", sent.getText()); + } + + @Test + @DisplayName("sendEmail should split a semicolon-separated recipient list into multiple addresses") + void sendEmail_shouldSplitSemicolonSeparatedRecipients() { + emailService.sendEmail(request("ops@amrit.example.org;admin@amrit.example.org")); + + assertArrayEquals(new String[] { "ops@amrit.example.org", "admin@amrit.example.org" }, + captureSentMessage().getTo()); + } + + @Test + @DisplayName("sendEmail should still dispatch when a mandatory field is missing, leaving it unset") + void sendEmail_shouldStillDispatchWhenMandatoryFieldMissing() { + String incomplete = "{\"to\":\"ops@amrit.example.org\"}"; + + emailService.sendEmail(incomplete); + + SimpleMailMessage sent = captureSentMessage(); + assertNull(sent.getTo()); + assertNull(sent.getSubject()); + } + + @Test + @DisplayName("sendEmail should propagate a mail transport failure") + void sendEmail_shouldPropagateTransportFailure() { + doThrow(new MailSendException("smtp unreachable")) + .when(javaMailSender).send(org.mockito.ArgumentMatchers.any(SimpleMailMessage.class)); + + assertThrows(MailSendException.class, () -> emailService.sendEmail(request("ops@amrit.example.org"))); + } + } + + @Nested + @DisplayName("sendEmail with a template") + class SendEmailWithTemplateTests { + + @Test + @DisplayName("sendEmail with a template should populate the message from the JSON request") + void sendEmail_withTemplate_shouldPopulateMessageFromJsonRequest() { + emailService.sendEmail(request("ops@amrit.example.org"), "pool-warning-template"); + + SimpleMailMessage sent = captureSentMessage(); + assertArrayEquals(new String[] { "ops@amrit.example.org" }, sent.getTo()); + assertEquals("Beneficiary ID pool low", sent.getSubject()); + assertEquals("Only 100 IDs remain in the pool.", sent.getText()); + } + + @Test + @DisplayName("sendEmail with a template should keep a semicolon list as a single recipient") + void sendEmail_withTemplate_shouldKeepRecipientListUnsplit() { + emailService.sendEmail(request("ops@amrit.example.org;admin@amrit.example.org"), + "pool-warning-template"); + + assertArrayEquals(new String[] { "ops@amrit.example.org;admin@amrit.example.org" }, + captureSentMessage().getTo()); + } + } + + @Nested + @DisplayName("sendEmailWithAttachment") + class SendEmailWithAttachmentTests { + + @Test + @DisplayName("sendEmailWithAttachment is not implemented and should send nothing") + void sendEmailWithAttachment_shouldSendNothing() { + emailService.sendEmailWithAttachment(request("ops@amrit.example.org"), "template"); + + verify(javaMailSender, never()).send(org.mockito.ArgumentMatchers.any(SimpleMailMessage.class)); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/http/AuthorizationHeaderRequestWrapperTest.java b/src/test/java/com/iemr/tm/utils/http/AuthorizationHeaderRequestWrapperTest.java new file mode 100644 index 00000000..3cfed401 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/http/AuthorizationHeaderRequestWrapperTest.java @@ -0,0 +1,128 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.http; + +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockHttpServletRequest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@DisplayName("AuthorizationHeaderRequestWrapper Test Suite") +class AuthorizationHeaderRequestWrapperTest { + + private MockHttpServletRequest request; + + @BeforeEach + @DisplayName("Create a request carrying an inbound Authorization header before each test") + void setUp() { + request = new MockHttpServletRequest(); + request.addHeader("Authorization", "inbound-key"); + request.addHeader("JwtToken", "header-token"); + } + + @Test + @DisplayName("getHeader should return the overridden value for Authorization") + void getHeader_shouldReturnOverriddenAuthorization() { + AuthorizationHeaderRequestWrapper wrapper = + new AuthorizationHeaderRequestWrapper(request, "overridden-key"); + + assertEquals("overridden-key", wrapper.getHeader("Authorization")); + } + + @Test + @DisplayName("getHeader should match the Authorization name case-insensitively") + void getHeader_shouldMatchAuthorizationCaseInsensitively() { + AuthorizationHeaderRequestWrapper wrapper = + new AuthorizationHeaderRequestWrapper(request, "overridden-key"); + + assertEquals("overridden-key", wrapper.getHeader("authorization")); + assertEquals("overridden-key", wrapper.getHeader("AUTHORIZATION")); + } + + @Test + @DisplayName("getHeader should pass every other header through to the wrapped request") + void getHeader_shouldPassOtherHeadersThrough() { + AuthorizationHeaderRequestWrapper wrapper = + new AuthorizationHeaderRequestWrapper(request, "overridden-key"); + + assertEquals("header-token", wrapper.getHeader("JwtToken")); + assertNull(wrapper.getHeader("X-Not-Present")); + } + + @Test + @DisplayName("getHeader should return the blank override the JWT filter installs") + void getHeader_shouldReturnBlankOverride() { + AuthorizationHeaderRequestWrapper wrapper = new AuthorizationHeaderRequestWrapper(request, ""); + + assertEquals("", wrapper.getHeader("Authorization"), + "the filter blanks Authorization once the JWT has been validated"); + } + + @Test + @DisplayName("getHeaders should return the overridden Authorization as a single-valued enumeration") + void getHeaders_shouldReturnOverriddenAuthorizationAsSingleValue() { + AuthorizationHeaderRequestWrapper wrapper = + new AuthorizationHeaderRequestWrapper(request, "overridden-key"); + + assertEquals(List.of("overridden-key"), Collections.list(wrapper.getHeaders("Authorization"))); + } + + @Test + @DisplayName("getHeaders should pass every other header through to the wrapped request") + void getHeaders_shouldPassOtherHeadersThrough() { + AuthorizationHeaderRequestWrapper wrapper = + new AuthorizationHeaderRequestWrapper(request, "overridden-key"); + + assertEquals(List.of("header-token"), Collections.list(wrapper.getHeaders("JwtToken"))); + } + + @Test + @DisplayName("getHeaderNames should still list Authorization alongside the wrapped names") + void getHeaderNames_shouldListAuthorizationAlongsideWrappedNames() { + AuthorizationHeaderRequestWrapper wrapper = + new AuthorizationHeaderRequestWrapper(request, "overridden-key"); + + List names = Collections.list(wrapper.getHeaderNames()); + assertTrue(names.contains("Authorization")); + assertTrue(names.contains("JwtToken")); + assertEquals(1, names.stream().filter("Authorization"::equals).count(), + "Authorization must not be duplicated when the wrapped request already carries it"); + } + + @Test + @DisplayName("getHeaderNames should add Authorization when the wrapped request lacks it") + void getHeaderNames_shouldAddAuthorizationWhenWrappedRequestLacksIt() { + MockHttpServletRequest bare = new MockHttpServletRequest(); + bare.addHeader("JwtToken", "header-token"); + AuthorizationHeaderRequestWrapper wrapper = + new AuthorizationHeaderRequestWrapper(bare, "overridden-key"); + + assertTrue(Collections.list(wrapper.getHeaderNames()).contains("Authorization")); + } +} diff --git a/src/test/java/com/iemr/tm/utils/http/HTTPRequestInterceptorTest.java b/src/test/java/com/iemr/tm/utils/http/HTTPRequestInterceptorTest.java new file mode 100644 index 00000000..9c346cba --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/http/HTTPRequestInterceptorTest.java @@ -0,0 +1,184 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.http; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +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.ValueSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.servlet.ModelAndView; + +import com.iemr.tm.utils.sessionobject.SessionObject; + +@ExtendWith(MockitoExtension.class) +@DisplayName("HTTPRequestInterceptor Test Suite") +class HTTPRequestInterceptorTest { + + private static final String TOKEN = "session-token"; + + @Mock + private SessionObject sessionObject; + + private HTTPRequestInterceptor interceptor; + private MockHttpServletRequest request; + private MockHttpServletResponse response; + + @BeforeEach + @DisplayName("Wire the interceptor with a mocked session store before each test") + void setUp() { + interceptor = new HTTPRequestInterceptor(); + interceptor.setSessionObject(sessionObject); + ReflectionTestUtils.setField(interceptor, "allowedOrigins", "https://amrit.example.org,http://localhost:*"); + request = new MockHttpServletRequest(); + response = new MockHttpServletResponse(); + request.setMethod("POST"); + request.setRequestURI("/anc/save/nurseData"); + } + + @Nested + @DisplayName("preHandle") + class PreHandleTests { + + @Test + @DisplayName("preHandle should let a request without an Authorization header through untouched") + void preHandle_shouldAllowRequestWithoutAuthorizationHeader() throws Exception { + assertTrue(interceptor.preHandle(request, response, new Object())); + verifyNoInteractions(sessionObject); + } + + @Test + @DisplayName("preHandle should let a request with a blank Authorization header through untouched") + void preHandle_shouldAllowRequestWithBlankAuthorizationHeader() throws Exception { + request.addHeader("Authorization", ""); + + assertTrue(interceptor.preHandle(request, response, new Object())); + verifyNoInteractions(sessionObject); + } + + @Test + @DisplayName("preHandle should strip the Bearer prefix and allow a normal API call") + void preHandle_shouldStripBearerPrefixAndAllowApiCall() throws Exception { + request.addHeader("Authorization", "Bearer " + TOKEN); + + assertTrue(interceptor.preHandle(request, response, new Object())); + } + + @Test + @DisplayName("preHandle should allow an OPTIONS preflight request without inspecting the URI") + void preHandle_shouldAllowOptionsPreflight() throws Exception { + request.setMethod("OPTIONS"); + request.addHeader("Authorization", TOKEN); + + assertTrue(interceptor.preHandle(request, response, new Object())); + } + + @ParameterizedTest + @ValueSource(strings = { "swagger-ui.html", "index.html", "swagger-initializer.js", "swagger-config", "ui", + "swagger-resources", "api-docs" }) + @DisplayName("preHandle should allow the documentation endpoints") + void preHandle_shouldAllowDocumentationEndpoints(String endpoint) throws Exception { + request.setRequestURI("/" + endpoint); + request.addHeader("Authorization", TOKEN); + + assertTrue(interceptor.preHandle(request, response, new Object())); + } + + @Test + @DisplayName("preHandle should reject the error dispatch endpoint") + void preHandle_shouldRejectErrorEndpoint() throws Exception { + request.setRequestURI("/error"); + request.addHeader("Authorization", TOKEN); + + assertFalse(interceptor.preHandle(request, response, new Object())); + } + } + + @Nested + @DisplayName("postHandle") + class PostHandleTests { + + @Test + @DisplayName("postHandle should refresh the session for a bare token") + void postHandle_shouldRefreshSessionForBareToken() throws Exception { + request.addHeader("Authorization", TOKEN); + when(sessionObject.getSessionObject(TOKEN)).thenReturn("session-payload"); + + interceptor.postHandle(request, response, new Object(), new ModelAndView()); + + verify(sessionObject).updateSessionObject(TOKEN, "session-payload"); + } + + @Test + @DisplayName("postHandle should strip the Bearer prefix before refreshing the session") + void postHandle_shouldStripBearerPrefixBeforeRefreshingSession() throws Exception { + request.addHeader("Authorization", "Bearer " + TOKEN); + when(sessionObject.getSessionObject(TOKEN)).thenReturn("session-payload"); + + interceptor.postHandle(request, response, new Object(), new ModelAndView()); + + verify(sessionObject).updateSessionObject(TOKEN, "session-payload"); + } + + @Test + @DisplayName("postHandle should do nothing when no Authorization header is present") + void postHandle_shouldDoNothingWithoutAuthorizationHeader() throws Exception { + interceptor.postHandle(request, response, new Object(), new ModelAndView()); + + verify(sessionObject, never()).updateSessionObject(anyString(), anyString()); + } + + @Test + @DisplayName("postHandle should swallow a session store failure") + void postHandle_shouldSwallowSessionStoreFailure() throws Exception { + request.addHeader("Authorization", TOKEN); + when(sessionObject.getSessionObject(TOKEN)).thenThrow(new IllegalStateException("redis down")); + + interceptor.postHandle(request, response, new Object(), new ModelAndView()); + + verify(sessionObject, never()).updateSessionObject(anyString(), anyString()); + } + } + + @Test + @DisplayName("afterCompletion should complete without touching the response") + void afterCompletion_shouldCompleteQuietly() throws Exception { + interceptor.afterCompletion(request, response, new Object(), null); + + assertTrue(response.getHeaderNames().isEmpty()); + } +} diff --git a/src/test/java/com/iemr/tm/utils/http/HttpUtilsTest.java b/src/test/java/com/iemr/tm/utils/http/HttpUtilsTest.java new file mode 100644 index 00000000..8125e799 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/http/HttpUtilsTest.java @@ -0,0 +1,278 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.http; + +import java.util.HashMap; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@DisplayName("HttpUtils Test Suite") +class HttpUtilsTest { + + private static final String URI = "http://localhost:8080/api/resource"; + + @Mock + private RestTemplate restTemplate; + + private HttpUtils httpUtils; + + @BeforeEach + @DisplayName("Replace the internal RestTemplate with a mock before each test") + void setUp() { + httpUtils = new HttpUtils(); + ReflectionTestUtils.setField(httpUtils, "rest", restTemplate); + } + + @SuppressWarnings("unchecked") + private ArgumentCaptor> captureRequest(HttpMethod method, ResponseEntity reply) { + ArgumentCaptor> captor = ArgumentCaptor.forClass(HttpEntity.class); + when(restTemplate.exchange(eq(URI), eq(method), captor.capture(), eq(String.class))).thenReturn(reply); + return captor; + } + + @Nested + @DisplayName("GET requests") + class GetTests + + { + @Test + @DisplayName("get should return the response body and record the status") + void get_shouldReturnBodyAndRecordStatus() { + when(restTemplate.exchange(eq(URI), eq(HttpMethod.GET), any(HttpEntity.class), eq(String.class))) + .thenReturn(new ResponseEntity<>("{\"ok\":true}", HttpStatus.OK)); + + assertEquals("{\"ok\":true}", httpUtils.get(URI)); + assertEquals(HttpStatus.OK, httpUtils.getStatus()); + } + + @Test + @DisplayName("get should send an empty header set when no headers are supplied") + void get_shouldSendEmptyHeaderSetWhenNoneSupplied() { + ArgumentCaptor> captor = + captureRequest(HttpMethod.GET, new ResponseEntity<>("body", HttpStatus.OK)); + + httpUtils.get(URI); + + assertNull(captor.getValue().getHeaders().getFirst("Content-Type")); + assertNull(captor.getValue().getBody()); + } + + @Test + @DisplayName("get should record a non-OK status returned by the server") + void get_shouldRecordNonOkStatus() { + when(restTemplate.exchange(eq(URI), eq(HttpMethod.GET), any(HttpEntity.class), eq(String.class))) + .thenReturn(new ResponseEntity<>(null, HttpStatus.NOT_FOUND)); + + assertNull(httpUtils.get(URI)); + assertEquals(HttpStatus.NOT_FOUND, httpUtils.getStatus()); + } + + @Test + @DisplayName("get with headers should forward the supplied Authorization header") + void get_shouldForwardSuppliedAuthorizationHeader() { + HashMap header = new HashMap<>(); + header.put(HttpHeaders.AUTHORIZATION, "session-key-123"); + ArgumentCaptor> captor = + captureRequest(HttpMethod.GET, new ResponseEntity<>("body", HttpStatus.OK)); + + assertEquals("body", httpUtils.get(URI, header)); + assertEquals("session-key-123", + captor.getValue().getHeaders().getFirst(HttpHeaders.AUTHORIZATION)); + } + + @Test + @DisplayName("get with headers should forward an explicit Content-Type") + void get_shouldForwardExplicitContentType() { + HashMap header = new HashMap<>(); + header.put(HttpHeaders.CONTENT_TYPE, "application/xml"); + ArgumentCaptor> captor = + captureRequest(HttpMethod.GET, new ResponseEntity<>("body", HttpStatus.OK)); + + httpUtils.get(URI, header); + + assertEquals("application/xml", + captor.getValue().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + } + + @Test + @DisplayName("get with headers should default the Content-Type to JSON when none is supplied") + void get_shouldDefaultContentTypeToJson() { + ArgumentCaptor> captor = + captureRequest(HttpMethod.GET, new ResponseEntity<>("body", HttpStatus.OK)); + + httpUtils.get(URI, new HashMap<>()); + + assertEquals("application/json", + captor.getValue().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE)); + } + + @Test + @DisplayName("get should propagate a transport failure to the caller") + void get_shouldPropagateTransportFailure() { + when(restTemplate.exchange(eq(URI), eq(HttpMethod.GET), any(HttpEntity.class), eq(String.class))) + .thenThrow(new RestClientException("connection refused")); + + assertThrows(RestClientException.class, () -> httpUtils.get(URI)); + } + } + + @Nested + @DisplayName("POST requests") + class PostTests { + + @Test + @DisplayName("post should send the JSON payload and return the response body") + void post_shouldSendPayloadAndReturnBody() { + ArgumentCaptor> captor = + captureRequest(HttpMethod.POST, new ResponseEntity<>("created", HttpStatus.CREATED)); + + assertEquals("created", httpUtils.post(URI, "{\"count\":5}")); + assertEquals("{\"count\":5}", captor.getValue().getBody()); + assertEquals(HttpStatus.CREATED, httpUtils.getStatus()); + } + + @Test + @DisplayName("post with headers should forward the supplied Authorization header") + void post_shouldForwardSuppliedAuthorizationHeader() { + HashMap header = new HashMap<>(); + header.put(HttpHeaders.AUTHORIZATION, "session-key-123"); + ArgumentCaptor> captor = + captureRequest(HttpMethod.POST, new ResponseEntity<>("created", HttpStatus.CREATED)); + + assertEquals("created", httpUtils.post(URI, "{\"count\":5}", header)); + assertEquals("session-key-123", + captor.getValue().getHeaders().getFirst(HttpHeaders.AUTHORIZATION)); + assertEquals("{\"count\":5}", captor.getValue().getBody()); + } + + @Test + @DisplayName("post with headers should omit the Authorization header when none is supplied") + void post_shouldOmitAuthorizationHeaderWhenNoneSupplied() { + ArgumentCaptor> captor = + captureRequest(HttpMethod.POST, new ResponseEntity<>("created", HttpStatus.CREATED)); + + httpUtils.post(URI, "{}", new HashMap<>()); + + assertNull(captor.getValue().getHeaders().getFirst(HttpHeaders.AUTHORIZATION)); + } + + @Test + @DisplayName("post should record a server error status") + void post_shouldRecordServerErrorStatus() { + when(restTemplate.exchange(eq(URI), eq(HttpMethod.POST), any(HttpEntity.class), eq(String.class))) + .thenReturn(new ResponseEntity<>("boom", HttpStatus.INTERNAL_SERVER_ERROR)); + + httpUtils.post(URI, "{}"); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, httpUtils.getStatus()); + } + + @Test + @DisplayName("post should issue the request against the supplied URI with the POST method") + void post_shouldIssueRequestWithPostMethod() { + when(restTemplate.exchange(eq(URI), eq(HttpMethod.POST), any(HttpEntity.class), eq(String.class))) + .thenReturn(new ResponseEntity<>("created", HttpStatus.CREATED)); + + httpUtils.post(URI, "{}"); + + verify(restTemplate).exchange(eq(URI), eq(HttpMethod.POST), any(HttpEntity.class), eq(String.class)); + } + } + + @Nested + @DisplayName("Status tracking") + class StatusTests { + + @Test + @DisplayName("getStatus should be null until a request has been made") + void getStatus_shouldBeNullBeforeAnyRequest() { + assertNull(httpUtils.getStatus()); + } + + @Test + @DisplayName("setStatus should record the supplied status code") + void setStatus_shouldRecordSuppliedStatusCode() { + httpUtils.setStatus(HttpStatus.ACCEPTED); + + assertEquals(HttpStatus.ACCEPTED, httpUtils.getStatus()); + } + } + + @Nested + @DisplayName("POST with the whole response") + class PostWithResponseEntityTests { + + @Test + @DisplayName("postWithResponseEntity should return the whole response the server sent") + void postWithResponseEntity_shouldReturnWholeResponse() { + when(restTemplate.exchange(eq(URI), eq(HttpMethod.POST), org.mockito.ArgumentMatchers.any(), + eq(String.class))).thenReturn(new ResponseEntity<>("{\"ok\":true}", HttpStatus.OK)); + + ResponseEntity response = httpUtils.postWithResponseEntity(URI, "{}", + new java.util.HashMap<>()); + + org.junit.jupiter.api.Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); + org.junit.jupiter.api.Assertions.assertEquals("{\"ok\":true}", response.getBody()); + } + + @Test + @DisplayName("postWithResponseEntity should forward the supplied authorization and api key headers") + void postWithResponseEntity_shouldForwardSuppliedHeaders() { + ArgumentCaptor> captor = captureRequest(HttpMethod.POST, + new ResponseEntity<>("{}", HttpStatus.OK)); + java.util.HashMap header = new java.util.HashMap<>(); + header.put("Authorization", "Bearer session-token"); + header.put("apiKey", "api-key-1"); + + httpUtils.postWithResponseEntity(URI, "{}", header); + + org.junit.jupiter.api.Assertions.assertEquals("Bearer session-token", + captor.getValue().getHeaders().getFirst("Authorization")); + org.junit.jupiter.api.Assertions.assertEquals("api-key-1", + captor.getValue().getHeaders().getFirst("apiKey")); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/mapper/InputMapperTest.java b/src/test/java/com/iemr/tm/utils/mapper/InputMapperTest.java new file mode 100644 index 00000000..121ab800 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/mapper/InputMapperTest.java @@ -0,0 +1,133 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.mapper; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.google.gson.JsonSyntaxException; + +@DisplayName("InputMapper Test Suite") +class InputMapperTest { + + static class TestPojo { + String name; + int value; + Date date; + + public String getName() { return name; } + public int getValue() { return value; } + public Date getDate() { return date; } + } + + @Test + @DisplayName("Should return valid InputMapper instance from gson factory method") + void testGsonStaticFactoryMethod() throws Exception { + InputMapper mapper = InputMapper.gson(); + assertNotNull(mapper); + assertTrue(mapper instanceof InputMapper); + } + + @Test + @DisplayName("Should successfully parse valid JSON to object") + void testFromJson_validJson() throws Exception { + InputMapper mapper = InputMapper.gson(); + String json = "{\"name\":\"testName\", \"value\":100}"; + + TestPojo result = mapper.fromJson(json, TestPojo.class); + + assertNotNull(result); + assertEquals("testName", result.getName()); + assertEquals(100, result.getValue()); + assertNull(result.getDate()); + } + + @Test + @DisplayName("Should successfully parse JSON with date field") + void testFromJson_validJsonWithDate() throws Exception { + InputMapper mapper = InputMapper.gson(); + String json = "{\"name\":\"itemWithDate\", \"value\":200, \"date\":\"2023-10-26T10:30:45.123\"}"; + + TestPojo result = mapper.fromJson(json, TestPojo.class); + + assertNotNull(result); + assertEquals("itemWithDate", result.getName()); + assertEquals(200, result.getValue()); + assertNotNull(result.getDate()); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); + Date expectedDate = sdf.parse("2023-10-26T10:30:45.123"); + + assertEquals(expectedDate.getTime(), result.getDate().getTime()); + } + + @Test + @DisplayName("Should return null when JSON input is null") + void testFromJson_nullJson() throws Exception { + InputMapper mapper = InputMapper.gson(); + String json = null; + + TestPojo result = mapper.fromJson(json, TestPojo.class); + assertNull(result); + } + + @Test + @DisplayName("Should return null when JSON input is empty string") + void testFromJson_emptyJsonString() throws Exception { + InputMapper mapper = InputMapper.gson(); + String json = ""; + + // InputMapper's fromJson method (likely catching JsonSyntaxException internally) + // returns null when given an empty string. + TestPojo result = mapper.fromJson(json, TestPojo.class); + assertNull(result); + } + + @Test + @DisplayName("Should create object with default values when JSON is empty object") + void testFromJson_emptyJsonObject() throws Exception { + InputMapper mapper = InputMapper.gson(); + String json = "{}"; + + TestPojo result = mapper.fromJson(json, TestPojo.class); + + assertNotNull(result); + assertNull(result.getName()); + assertEquals(0, result.getValue()); + assertNull(result.getDate()); + } + + @Test + @DisplayName("Should throw JsonSyntaxException when JSON is malformed") + void testFromJson_malformedJson() throws Exception { + InputMapper mapper = InputMapper.gson(); + String json = "{\"name\":\"testName\", \"value\":,}"; + + JsonSyntaxException thrown = assertThrows(JsonSyntaxException.class, () -> mapper.fromJson(json, TestPojo.class)); + assertNotNull(thrown); + } +} diff --git a/src/test/java/com/iemr/tm/utils/redis/RedisStorageTest.java b/src/test/java/com/iemr/tm/utils/redis/RedisStorageTest.java new file mode 100644 index 00000000..efe59eae --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/redis/RedisStorageTest.java @@ -0,0 +1,189 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.redis; + +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisStringCommands.SetOption; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@DisplayName("RedisStorage Test Suite") +class RedisStorageTest { + + private static final String KEY = "session-key"; + private static final int EXPIRY_SECONDS = 7200; + + @Mock + private LettuceConnectionFactory connectionFactory; + + @Mock + private RedisConnection redisConnection; + + private RedisStorage redisStorage; + + @BeforeEach + @DisplayName("Wire the store with a mocked Lettuce connection factory before each test") + void setUp() { + redisStorage = new RedisStorage(); + ReflectionTestUtils.setField(redisStorage, "connection", connectionFactory); + when(connectionFactory.getConnection()).thenReturn(redisConnection); + } + + private byte[] bytes(String value) { + return value.getBytes(StandardCharsets.UTF_8); + } + + @Nested + @DisplayName("setObject") + class SetObjectTests { + + @Test + @DisplayName("setObject should write the value when no session is stored yet") + void setObject_shouldWriteValueWhenKeyIsAbsent() throws RedisSessionException { + when(redisConnection.get(bytes(KEY))).thenReturn(null); + + assertEquals(KEY, redisStorage.setObject(KEY, "payload", EXPIRY_SECONDS)); + verify(redisConnection).set(eq(bytes(KEY)), eq(bytes("payload")), + eq(Expiration.seconds(EXPIRY_SECONDS)), eq(SetOption.UPSERT)); + } + + @Test + @DisplayName("setObject should write the value when the stored session is empty") + void setObject_shouldWriteValueWhenStoredSessionIsEmpty() throws RedisSessionException { + when(redisConnection.get(bytes(KEY))).thenReturn(bytes("")); + + assertEquals(KEY, redisStorage.setObject(KEY, "payload", EXPIRY_SECONDS)); + verify(redisConnection).set(any(byte[].class), any(byte[].class), any(Expiration.class), any(SetOption.class)); + } + + @Test + @DisplayName("setObject should leave an existing session untouched") + void setObject_shouldLeaveExistingSessionUntouched() throws RedisSessionException { + when(redisConnection.get(bytes(KEY))).thenReturn(bytes("existing")); + + assertEquals(KEY, redisStorage.setObject(KEY, "payload", EXPIRY_SECONDS)); + verify(redisConnection, never()).set(any(byte[].class), any(byte[].class), + any(Expiration.class), any(SetOption.class)); + } + } + + @Nested + @DisplayName("getObject") + class GetObjectTests { + + @Test + @DisplayName("getObject should return the stored session and extend its expiry") + void getObject_shouldReturnStoredSessionAndExtendExpiry() throws RedisSessionException { + when(redisConnection.get(bytes(KEY))).thenReturn(bytes("payload")); + + assertEquals("payload", redisStorage.getObject(KEY, true, EXPIRY_SECONDS)); + verify(redisConnection).expire(bytes(KEY), EXPIRY_SECONDS); + } + + @Test + @DisplayName("getObject should raise a session exception when the key is absent") + void getObject_shouldRaiseWhenKeyIsAbsent() { + when(redisConnection.get(bytes(KEY))).thenReturn(null); + + RedisSessionException thrown = assertThrows(RedisSessionException.class, + () -> redisStorage.getObject(KEY, true, EXPIRY_SECONDS)); + + assertEquals("Unable to fetch session object from Redis server", thrown.getMessage()); + } + + @Test + @DisplayName("getObject should raise a session exception when the stored value is blank") + void getObject_shouldRaiseWhenStoredValueIsBlank() { + when(redisConnection.get(bytes(KEY))).thenReturn(bytes(" ")); + + assertThrows(RedisSessionException.class, + () -> redisStorage.getObject(KEY, true, EXPIRY_SECONDS)); + verify(redisConnection, never()).expire(any(byte[].class), any(Long.class)); + } + } + + @Nested + @DisplayName("updateObject") + class UpdateObjectTests { + + @Test + @DisplayName("updateObject should overwrite an existing session") + void updateObject_shouldOverwriteExistingSession() throws RedisSessionException { + when(redisConnection.get(bytes(KEY))).thenReturn(bytes("old")); + + assertEquals(KEY, redisStorage.updateObject(KEY, "new", true, EXPIRY_SECONDS)); + verify(redisConnection).set(eq(bytes(KEY)), eq(bytes("new")), + eq(Expiration.seconds(EXPIRY_SECONDS)), eq(SetOption.UPSERT)); + } + + @Test + @DisplayName("updateObject should raise a session exception when there is nothing to update") + void updateObject_shouldRaiseWhenKeyIsAbsent() { + when(redisConnection.get(bytes(KEY))).thenReturn(null); + + RedisSessionException thrown = assertThrows(RedisSessionException.class, + () -> redisStorage.updateObject(KEY, "new", true, EXPIRY_SECONDS)); + + assertEquals("Unable to fetch session object from Redis server", thrown.getMessage()); + } + } + + @Nested + @DisplayName("deleteObject") + class DeleteObjectTests { + + @Test + @DisplayName("deleteObject should return the number of keys Redis removed") + void deleteObject_shouldReturnNumberOfKeysRemoved() throws RedisSessionException { + when(redisConnection.del(bytes(KEY))).thenReturn(1L); + + assertEquals(1L, redisStorage.deleteObject(KEY)); + } + + @Test + @DisplayName("deleteObject should return zero when the key was not present") + void deleteObject_shouldReturnZeroWhenKeyAbsent() throws RedisSessionException { + when(redisConnection.del(bytes(KEY))).thenReturn(0L); + + assertEquals(0L, redisStorage.deleteObject(KEY)); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/response/OutputResponseTest.java b/src/test/java/com/iemr/tm/utils/response/OutputResponseTest.java new file mode 100644 index 00000000..e1623625 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/response/OutputResponseTest.java @@ -0,0 +1,303 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.response; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.net.ConnectException; +import java.sql.SQLException; +import java.text.ParseException; + +import org.json.JSONException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import com.iemr.tm.utils.exception.IEMRException; +import com.iemr.tm.utils.exception.TMException; +import com.iemr.tm.utils.exception.VideoConsultationException; + +@DisplayName("OutputResponse Test Suite") +class OutputResponseTest { + + private OutputResponse response; + + @BeforeEach + @DisplayName("Start from a freshly constructed response") + void setUp() { + response = new OutputResponse(); + } + + @Nested + @DisplayName("default state") + class DefaultStateTests { + + @Test + @DisplayName("a new response should default to the generic failure state") + void newResponse_shouldDefaultToGenericFailure() { + assertEquals(OutputResponse.GENERIC_FAILURE, response.getStatusCode()); + assertEquals("Failed with generic error", response.getErrorMessage()); + assertEquals("FAILURE", response.getStatus()); + assertFalse(response.isSuccess()); + } + + @Test + @DisplayName("a new response should carry no data") + void newResponse_shouldCarryNoData() throws JSONException { + assertNull(response.getData()); + } + } + + @Nested + @DisplayName("setResponse") + class SetResponseTests { + + @Test + @DisplayName("setResponse should keep a JSON object payload as-is and mark the call successful") + void setResponse_shouldKeepJsonObjectPayload() throws JSONException { + response.setResponse("{\"beneficiaryRegID\":123}"); + + assertTrue(response.isSuccess()); + assertEquals(OutputResponse.SUCCESS, response.getStatusCode()); + assertEquals("Success", response.getErrorMessage()); + assertEquals("Success", response.getStatus()); + assertTrue(response.getData().contains("beneficiaryRegID")); + } + + @Test + @DisplayName("setResponse should keep a JSON array payload as-is") + void setResponse_shouldKeepJsonArrayPayload() throws JSONException { + response.setResponse("[{\"id\":1},{\"id\":2}]"); + + assertTrue(response.isSuccess()); + assertTrue(response.getData().startsWith("[")); + } + + @Test + @DisplayName("setResponse should wrap a plain string payload in a response envelope") + void setResponse_shouldWrapPlainStringPayload() throws JSONException { + response.setResponse("data saved successfully"); + + assertTrue(response.isSuccess()); + assertTrue(response.getData().contains("response")); + assertTrue(response.getData().contains("data saved successfully")); + } + + @Test + @DisplayName("setResponse should wrap an empty payload without failing") + void setResponse_shouldWrapEmptyPayload() throws JSONException { + response.setResponse(""); + + assertTrue(response.isSuccess()); + assertTrue(response.getData().contains("response")); + } + } + + @Nested + @DisplayName("setError(Throwable) mapping") + class SetErrorThrowableTests { + + @Test + @DisplayName("an IEMRException should map to the user login failure code") + void setError_shouldMapIemrException() { + response.setError(new IEMRException("bad credentials")); + + assertEquals(OutputResponse.USERID_FAILURE, response.getStatusCode()); + assertEquals("User login failed", response.getStatus()); + assertEquals("bad credentials", response.getErrorMessage()); + } + + @Test + @DisplayName("a VideoConsultationException should map to the video consultation code") + void setError_shouldMapVideoConsultationException() { + response.setError(new VideoConsultationException("meeting unavailable")); + + assertEquals(OutputResponse.VIDEOCONSULTATION_EXCEPTION, response.getStatusCode()); + assertEquals("Video Consultation integration error", response.getStatus()); + assertEquals("meeting unavailable", response.getErrorMessage()); + } + + @Test + @DisplayName("a TMException should map to the invalid input code") + void setError_shouldMapTmException() { + response.setError(new TMException("invalid visit code")); + + assertEquals(OutputResponse.TM_EXCEPTION, response.getStatusCode()); + assertEquals("Invalid input", response.getStatus()); + assertEquals("invalid visit code", response.getErrorMessage()); + } + + @Test + @DisplayName("a JSONException should map to the object conversion failure code") + void setError_shouldMapJsonException() { + response.setError(new JSONException("not json")); + + assertEquals(OutputResponse.OBJECT_FAILURE, response.getStatusCode()); + assertEquals("Invalid object conversion", response.getStatus()); + assertEquals("Invalid object conversion", response.getErrorMessage()); + } + + @Test + @DisplayName("a SQLException should map to the internal code exception") + void setError_shouldMapSqlException() { + response.setError(new SQLException("deadlock")); + + assertEquals(OutputResponse.CODE_EXCEPTION, response.getStatusCode()); + assertTrue(response.getStatus().startsWith("Failed with internal errors")); + assertEquals("deadlock", response.getErrorMessage()); + } + + @Test + @DisplayName("a ParseException should map to the internal code exception") + void setError_shouldMapParseException() { + response.setError(new ParseException("bad date", 0)); + + assertEquals(OutputResponse.CODE_EXCEPTION, response.getStatusCode()); + } + + @Test + @DisplayName("a NullPointerException should map to the internal code exception") + void setError_shouldMapNullPointerException() { + response.setError(new NullPointerException("npe")); + + assertEquals(OutputResponse.CODE_EXCEPTION, response.getStatusCode()); + } + + @Test + @DisplayName("an IOException should map to the environment exception") + void setError_shouldMapIoException() { + response.setError(new IOException("disk gone")); + + assertEquals(OutputResponse.ENVIRONMENT_EXCEPTION, response.getStatusCode()); + assertTrue(response.getStatus().startsWith("Failed with connection issues")); + } + + @Test + @DisplayName("a ConnectException should map to the environment exception") + void setError_shouldMapConnectException() { + response.setError(new ConnectException("refused")); + + assertEquals(OutputResponse.ENVIRONMENT_EXCEPTION, response.getStatusCode()); + } + + @Test + @DisplayName("an unrecognised exception should fall back to the generic failure code") + void setError_shouldFallBackToGenericFailure() { + response.setError(new IllegalStateException("boom")); + + assertEquals(OutputResponse.GENERIC_FAILURE, response.getStatusCode()); + assertTrue(response.getStatus().startsWith("Failed with boom")); + assertEquals("boom", response.getErrorMessage()); + } + } + + @Nested + @DisplayName("setError with explicit codes") + class SetErrorExplicitTests { + + @Test + @DisplayName("setError(code, message, status) should apply all three values") + void setError_shouldApplyCodeMessageAndStatus() { + response.setError(OutputResponse.BAD_REQUEST, "missing field", "Bad Request"); + + assertEquals(OutputResponse.BAD_REQUEST, response.getStatusCode()); + assertEquals("missing field", response.getErrorMessage()); + assertEquals("Bad Request", response.getStatus()); + } + + @Test + @DisplayName("setError(code, message) should reuse the message as the status") + void setError_shouldReuseMessageAsStatus() { + response.setError(OutputResponse.PASSWORD_FAILURE, "wrong password"); + + assertEquals(OutputResponse.PASSWORD_FAILURE, response.getStatusCode()); + assertEquals("wrong password", response.getErrorMessage()); + assertEquals("wrong password", response.getStatus()); + } + } + + @Nested + @DisplayName("serialisation") + class SerialisationTests { + + @Test + @DisplayName("toString should emit only the exposed fields") + void toString_shouldEmitOnlyExposedFields() { + response.setResponse("{\"id\":7}"); + + String json = response.toString(); + + assertTrue(json.contains("statusCode")); + assertTrue(json.contains("errorMessage")); + assertTrue(json.contains("data")); + assertFalse(json.contains("logger")); + } + + @Test + @DisplayName("toStringWithSerialization should include null fields") + void toStringWithSerialization_shouldIncludeNullFields() { + String json = response.toStringWithSerialization(); + + assertTrue(json.contains("\"data\":null")); + } + + @Test + @DisplayName("toStringWithHttpStatus should return 200 for a successful response") + void toStringWithHttpStatus_shouldReturnOkForSuccess() { + response.setResponse("{\"id\":7}"); + + ResponseEntity entity = response.toStringWithHttpStatus(); + + assertEquals(HttpStatus.OK, entity.getStatusCode()); + assertTrue(entity.getBody().contains("\"statusCode\":200")); + } + + @Test + @DisplayName("toStringWithHttpStatus should return 500 for a generic failure") + void toStringWithHttpStatus_shouldReturnServerErrorForGenericFailure() { + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.toStringWithHttpStatus().getStatusCode()); + } + + @Test + @DisplayName("toStringWithHttpStatus should return 400 for a bad request") + void toStringWithHttpStatus_shouldReturnBadRequest() { + response.setError(OutputResponse.BAD_REQUEST, "missing field"); + + assertEquals(HttpStatus.BAD_REQUEST, response.toStringWithHttpStatus().getStatusCode()); + } + + @Test + @DisplayName("toStringWithHttpStatus should return 503 for any other status code") + void toStringWithHttpStatus_shouldReturnServiceUnavailableForOtherCodes() { + response.setError(new IEMRException("bad credentials")); + + assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.toStringWithHttpStatus().getStatusCode()); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/sessionobject/SessionObjectTest.java b/src/test/java/com/iemr/tm/utils/sessionobject/SessionObjectTest.java new file mode 100644 index 00000000..5bf1be8e --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/sessionobject/SessionObjectTest.java @@ -0,0 +1,143 @@ +/* +* AMRIT - Accessible Medical Records via Integrated Technologies +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.sessionobject; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.iemr.tm.utils.config.ConfigProperties; +import com.iemr.tm.utils.redis.RedisSessionException; +import com.iemr.tm.utils.redis.RedisStorage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@DisplayName("SessionObject Test Suite") +class SessionObjectTest { + + private static final String KEY = "session-key"; + private static final String VALUE = "{\"userName\":\"amrit-user\"}"; + + @Mock + private RedisStorage objectStore; + + private SessionObject sessionObject; + private int expectedExpiry; + private boolean expectedExtend; + + @BeforeEach + @DisplayName("Wire the session holder with a mocked Redis store before each test") + void setUp() { + sessionObject = new SessionObject(); + sessionObject.setObjectStore(objectStore); + expectedExpiry = ConfigProperties.getSessionExpiryTime(); + expectedExtend = ConfigProperties.getExtendExpiryTime(); + } + + @Nested + @DisplayName("Reading and writing the session") + class ReadWriteTests { + + @Test + @DisplayName("getSessionObject should delegate to the store with the configured expiry settings") + void getSessionObject_shouldDelegateWithConfiguredExpiry() throws RedisSessionException { + when(objectStore.getObject(KEY, expectedExtend, expectedExpiry)).thenReturn(VALUE); + + assertEquals(VALUE, sessionObject.getSessionObject(KEY)); + verify(objectStore).getObject(KEY, expectedExtend, expectedExpiry); + } + + @Test + @DisplayName("setSessionObject should delegate to the store with the configured expiry") + void setSessionObject_shouldDelegateWithConfiguredExpiry() throws RedisSessionException { + when(objectStore.setObject(KEY, VALUE, expectedExpiry)).thenReturn(KEY); + + assertEquals(KEY, sessionObject.setSessionObject(KEY, VALUE)); + verify(objectStore).setObject(KEY, VALUE, expectedExpiry); + } + + @Test + @DisplayName("updateSessionObject should delegate to the store with the configured expiry settings") + void updateSessionObject_shouldDelegateWithConfiguredExpiry() throws RedisSessionException { + when(objectStore.updateObject(KEY, VALUE, expectedExtend, expectedExpiry)).thenReturn(KEY); + + assertEquals(KEY, sessionObject.updateSessionObject(KEY, VALUE)); + verify(objectStore).updateObject(KEY, VALUE, expectedExtend, expectedExpiry); + } + + @Test + @DisplayName("deleteSessionObject should delegate the removal to the store") + void deleteSessionObject_shouldDelegateRemoval() throws RedisSessionException { + when(objectStore.deleteObject(KEY)).thenReturn(1L); + + sessionObject.deleteSessionObject(KEY); + + verify(objectStore).deleteObject(KEY); + } + } + + @Nested + @DisplayName("Propagating store failures") + class FailureTests { + + @Test + @DisplayName("getSessionObject should propagate a missing-session failure") + void getSessionObject_shouldPropagateMissingSessionFailure() throws RedisSessionException { + when(objectStore.getObject(anyString(), anyBoolean(), anyInt())) + .thenThrow(new RedisSessionException("Unable to fetch session object from Redis server")); + + RedisSessionException thrown = assertThrows(RedisSessionException.class, + () -> sessionObject.getSessionObject(KEY)); + + assertEquals("Unable to fetch session object from Redis server", thrown.getMessage()); + } + + @Test + @DisplayName("updateSessionObject should propagate a missing-session failure") + void updateSessionObject_shouldPropagateMissingSessionFailure() throws RedisSessionException { + when(objectStore.updateObject(eq(KEY), eq(VALUE), anyBoolean(), anyInt())) + .thenThrow(new RedisSessionException("Unable to fetch session object from Redis server")); + + assertThrows(RedisSessionException.class, () -> sessionObject.updateSessionObject(KEY, VALUE)); + } + + @Test + @DisplayName("deleteSessionObject should propagate a store failure") + void deleteSessionObject_shouldPropagateStoreFailure() throws RedisSessionException { + when(objectStore.deleteObject(KEY)).thenThrow(new RedisSessionException("redis down")); + + assertThrows(RedisSessionException.class, () -> sessionObject.deleteSessionObject(KEY)); + } + } +} diff --git a/src/test/java/com/iemr/tm/utils/validator/ValidatorTest.java b/src/test/java/com/iemr/tm/utils/validator/ValidatorTest.java new file mode 100644 index 00000000..a1da4fe0 --- /dev/null +++ b/src/test/java/com/iemr/tm/utils/validator/ValidatorTest.java @@ -0,0 +1,209 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.tm.utils.validator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.json.JSONObject; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +import com.iemr.tm.utils.exception.IEMRException; +import com.iemr.tm.utils.redis.RedisSessionException; +import com.iemr.tm.utils.sessionobject.SessionObject; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Validator Test Suite") +class ValidatorTest { + + private static final String KEY = "session-key"; + private static final String IP = "192.168.1.100"; + private static final String OTHER_IP = "192.168.1.101"; + + @Mock + private SessionObject sessionObject; + + private Validator validator; + + @BeforeEach + @DisplayName("Wire the validator with a mocked session store before each test") + void setUp() { + validator = new Validator(); + validator.setSessionObject(sessionObject); + } + + @AfterEach + @DisplayName("Reset the shared IP validation flag after each test") + void tearDown() { + ReflectionTestUtils.setField(Validator.class, "enableIPValidation", Boolean.FALSE); + } + + private JSONObject response(String ip) throws Exception { + JSONObject obj = new JSONObject(); + obj.put("loginIPAddress", ip); + return obj; + } + + @Nested + @DisplayName("updateCacheObj") + class UpdateCacheObjTests { + + @Test + @DisplayName("updateCacheObj should create a session when no session exists yet") + void updateCacheObj_shouldCreateSessionWhenNoneExists() throws Exception { + when(sessionObject.getSessionObject(KEY)).thenReturn(null); + + JSONObject result = validator.updateCacheObj(response(IP), KEY, "ipKey"); + + assertEquals("login success", result.getString("sessionStatus")); + assertEquals(KEY, result.getString("key")); + verify(sessionObject).setSessionObject(anyString(), anyString()); + } + + @Test + @DisplayName("updateCacheObj should create a session when the stored session is blank") + void updateCacheObj_shouldCreateSessionWhenStoredSessionIsBlank() throws Exception { + when(sessionObject.getSessionObject(KEY)).thenReturn(" "); + + JSONObject result = validator.updateCacheObj(response(IP), KEY, "ipKey"); + + assertEquals("login success", result.getString("sessionStatus")); + } + + @Test + @DisplayName("updateCacheObj should refresh the session when the stored IP matches") + void updateCacheObj_shouldRefreshSessionWhenIpMatches() throws Exception { + when(sessionObject.getSessionObject(KEY)).thenReturn(response(IP).toString()); + + JSONObject result = validator.updateCacheObj(response(IP), KEY, "ipKey"); + + assertEquals("login success", result.getString("sessionStatus")); + verify(sessionObject).setSessionObject(anyString(), anyString()); + } + + @Test + @DisplayName("updateCacheObj should report the other login IP and not overwrite the session") + void updateCacheObj_shouldReportOtherLoginIp() throws Exception { + when(sessionObject.getSessionObject(KEY)).thenReturn(response(OTHER_IP).toString()); + + JSONObject result = validator.updateCacheObj(response(IP), KEY, "ipKey"); + + assertEquals("login success, but user logged in from " + OTHER_IP, result.getString("sessionStatus")); + assertFalse(result.has("loginIPAddress")); + verify(sessionObject, never()).setSessionObject(anyString(), anyString()); + } + + @Test + @DisplayName("updateCacheObj should recover from a Redis session failure and still create the session") + void updateCacheObj_shouldRecoverFromRedisFailure() throws Exception { + when(sessionObject.getSessionObject(KEY)).thenThrow(new RedisSessionException("redis down")); + + JSONObject result = validator.updateCacheObj(response(IP), KEY, "ipKey"); + + assertEquals("login success", result.getString("sessionStatus")); + } + } + + @Nested + @DisplayName("getSessionObject") + class GetSessionObjectTests { + + @Test + @DisplayName("getSessionObject should delegate to the session store") + void getSessionObject_shouldDelegateToSessionStore() throws Exception { + when(sessionObject.getSessionObject(KEY)).thenReturn("payload"); + + assertEquals("payload", validator.getSessionObject(KEY)); + } + + @Test + @DisplayName("getSessionObject should propagate a Redis session failure") + void getSessionObject_shouldPropagateRedisFailure() throws Exception { + when(sessionObject.getSessionObject(KEY)).thenThrow(new RedisSessionException("redis down")); + + assertThrows(RedisSessionException.class, () -> validator.getSessionObject(KEY)); + } + } + + @Nested + @DisplayName("checkKeyExists") + class CheckKeyExistsTests { + + @Test + @DisplayName("checkKeyExists should accept a live session when IP validation is disabled") + void checkKeyExists_shouldAcceptLiveSessionWhenIpValidationDisabled() throws Exception { + when(sessionObject.getSessionObject(KEY)).thenReturn(response(IP).toString()); + + validator.checkKeyExists(KEY, OTHER_IP); + } + + @Test + @DisplayName("checkKeyExists should accept a matching IP when IP validation is enabled") + void checkKeyExists_shouldAcceptMatchingIpWhenIpValidationEnabled() throws Exception { + ReflectionTestUtils.setField(Validator.class, "enableIPValidation", Boolean.TRUE); + when(sessionObject.getSessionObject(KEY)).thenReturn(response(IP).toString()); + + validator.checkKeyExists(KEY, IP); + } + + @Test + @DisplayName("checkKeyExists should reject a mismatched IP when IP validation is enabled") + void checkKeyExists_shouldRejectMismatchedIpWhenIpValidationEnabled() throws Exception { + ReflectionTestUtils.setField(Validator.class, "enableIPValidation", Boolean.TRUE); + when(sessionObject.getSessionObject(KEY)).thenReturn(response(IP).toString()); + + IEMRException thrown = assertThrows(IEMRException.class, () -> validator.checkKeyExists(KEY, OTHER_IP)); + + assertTrue(thrown.getMessage().contains("Invalid login key or session is expired")); + } + + @Test + @DisplayName("checkKeyExists should reject an expired session") + void checkKeyExists_shouldRejectExpiredSession() throws Exception { + when(sessionObject.getSessionObject(KEY)).thenReturn(null); + + assertThrows(IEMRException.class, () -> validator.checkKeyExists(KEY, IP)); + } + + @Test + @DisplayName("checkKeyExists should reject when the session store fails") + void checkKeyExists_shouldRejectWhenSessionStoreFails() throws Exception { + when(sessionObject.getSessionObject(KEY)).thenThrow(new RedisSessionException("redis down")); + + assertThrows(IEMRException.class, () -> validator.checkKeyExists(KEY, IP)); + } + } +}