diff --git a/claimManagement/build.gradle b/claimManagement/build.gradle index 5bc67aa0..4af03081 100644 --- a/claimManagement/build.gradle +++ b/claimManagement/build.gradle @@ -202,6 +202,9 @@ apollo { } tasks.withType(Test).configureEach { + maxHeapSize = "1536m" + maxParallelForks = 1 + forkEvery = 50 testLogging { events "passed", "skipped", "failed" exceptionFormat "full" diff --git a/claimManagement/src/main/java/org/openimis/imisclaims/Global.java b/claimManagement/src/main/java/org/openimis/imisclaims/Global.java index aa01e284..35965dce 100644 --- a/claimManagement/src/main/java/org/openimis/imisclaims/Global.java +++ b/claimManagement/src/main/java/org/openimis/imisclaims/Global.java @@ -60,6 +60,7 @@ import static org.openimis.imisclaims.BuildConfig.RAR_PASSWORD; import org.openimis.imisclaims.tools.Log; +import org.openimis.imisclaims.network.util.PersistentCookieJar; public class Global extends Application { private static final String SHPREF_NAME = "SHPref"; @@ -76,6 +77,7 @@ public class Global extends Application { private static final String _DefaultRarPassword = RAR_PASSWORD; private Token JWTToken; private String[] permissions; + private PersistentCookieJar cookieJar; public static boolean isRunningTest() { try { @@ -129,6 +131,14 @@ public void setUserId(int userId) { UserId = userId; } + public void setCookieJar(PersistentCookieJar jar) { + this.cookieJar = jar; + } + + public PersistentCookieJar getCookieJar() { + return cookieJar; + } + public void setOfficerName(String officerName) { OfficerName = officerName; } @@ -153,10 +163,20 @@ public String getCsrfToken(){ } public boolean isLoggedIn() { - boolean isLoggedIn = getJWTToken().isTokenValidJWT(); + long expiry = getDefaultSharedPreferences() + .getLong("session_expiry", 0); + + boolean isLoggedIn = getJWTToken().isTokenValidJWT() + && expiry > System.currentTimeMillis(); + if (!isLoggedIn) { getJWTToken().clearToken(); + + if (cookieJar != null) { + cookieJar.clear(); + } } + return isLoggedIn; } diff --git a/claimManagement/src/main/java/org/openimis/imisclaims/network/okhttp/AuthorizationInterceptor.java b/claimManagement/src/main/java/org/openimis/imisclaims/network/okhttp/AuthorizationInterceptor.java index bb9ba761..314e94b9 100644 --- a/claimManagement/src/main/java/org/openimis/imisclaims/network/okhttp/AuthorizationInterceptor.java +++ b/claimManagement/src/main/java/org/openimis/imisclaims/network/okhttp/AuthorizationInterceptor.java @@ -14,6 +14,7 @@ import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; +import okhttp3.ResponseBody; public class AuthorizationInterceptor implements Interceptor { @@ -34,10 +35,21 @@ public Response intercept(@NonNull Chain chain) throws IOException { Request.Builder builder = chain.request().newBuilder(); builder.addHeader("Authorization", "bearer " + token.getTokenText().trim()); builder.addHeader("User-Agent", USER_AGENT); + if (csrfToken != null && !csrfToken.trim().isEmpty()) { + builder.addHeader("X-CSRFToken", csrfToken); + } Response response = chain.proceed(builder.build()); - if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) { + ResponseBody body = response.peekBody(Long.MAX_VALUE); + String bodyString = body.string(); + + if (bodyString.contains("'csrftoken'") || response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) { + global.getJWTToken().clearToken(); - } + + if (global.getCookieJar() != null) { + global.getCookieJar().clear(); + } + response = chain.proceed(chain.request());} return response; } return chain.proceed(chain.request()); diff --git a/claimManagement/src/main/java/org/openimis/imisclaims/network/request/GetHealthfacilityGraphQLRequest.java b/claimManagement/src/main/java/org/openimis/imisclaims/network/request/GetHealthfacilityGraphQLRequest.java index 63cd3825..29d70006 100644 --- a/claimManagement/src/main/java/org/openimis/imisclaims/network/request/GetHealthfacilityGraphQLRequest.java +++ b/claimManagement/src/main/java/org/openimis/imisclaims/network/request/GetHealthfacilityGraphQLRequest.java @@ -12,7 +12,16 @@ public class GetHealthfacilityGraphQLRequest extends BaseGraphQLRequest{ @NonNull @WorkerThread - public List get (@NonNull String HfCode) throws Exception { - return makeSynchronous(new GetHealthFacilityQuery(Input.fromNullable(HfCode))).getData().healthFacilities().edges(); + public List get(@NonNull String hfCode) throws Exception { + + GetHealthFacilityQuery.Data data = + makeSynchronous(new GetHealthFacilityQuery(Input.fromNullable(hfCode))) + .getData(); + + if (data == null || data.healthFacilities() == null || data.healthFacilities().edges() == null) { + return java.util.Collections.emptyList(); + } + + return data.healthFacilities().edges(); } } diff --git a/claimManagement/src/main/java/org/openimis/imisclaims/network/util/OkHttpUtils.java b/claimManagement/src/main/java/org/openimis/imisclaims/network/util/OkHttpUtils.java index 3b59abed..2f095137 100644 --- a/claimManagement/src/main/java/org/openimis/imisclaims/network/util/OkHttpUtils.java +++ b/claimManagement/src/main/java/org/openimis/imisclaims/network/util/OkHttpUtils.java @@ -7,6 +7,7 @@ import org.openimis.imisclaims.BuildConfig; import org.openimis.imisclaims.Global; import org.openimis.imisclaims.network.okhttp.AuthorizationInterceptor; +import org.openimis.imisclaims.network.util.PersistentCookieJar; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; @@ -23,12 +24,17 @@ private OkHttpUtils() { throw new IllegalAccessError("This constructor is private"); } + @NonNull public static OkHttpClient getDefaultOkHttpClient() { if (client == null) { synchronized (OkHttpUtils.class) { if (client == null) { OkHttpClient.Builder builder = new OkHttpClient.Builder(); + PersistentCookieJar cookieJar = + new PersistentCookieJar(Global.getGlobal().getDefaultSharedPreferences()); + Global.getGlobal().setCookieJar(cookieJar); + builder.cookieJar(cookieJar); HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.BASIC); builder.addInterceptor(interceptor); diff --git a/claimManagement/src/main/java/org/openimis/imisclaims/network/util/PersistentCookieJar.java b/claimManagement/src/main/java/org/openimis/imisclaims/network/util/PersistentCookieJar.java new file mode 100644 index 00000000..42eaa268 --- /dev/null +++ b/claimManagement/src/main/java/org/openimis/imisclaims/network/util/PersistentCookieJar.java @@ -0,0 +1,63 @@ +package org.openimis.imisclaims.network.util; + +import android.content.SharedPreferences; + +import androidx.annotation.NonNull; + +import okhttp3.Cookie; +import okhttp3.CookieJar; +import okhttp3.HttpUrl; + +import java.util.ArrayList; +import java.util.List; + +public class PersistentCookieJar implements CookieJar { + + private final SharedPreferences prefs; + + public PersistentCookieJar(SharedPreferences prefs) { + this.prefs = prefs; + } + + @Override + public void saveFromResponse(@NonNull HttpUrl url, List cookies) { + + for (Cookie cookie : cookies) { + if ("openimis_session".equals(cookie.name())) { + + prefs.edit() + .putString("session_value", cookie.value()) + .putLong("session_expiry", cookie.expiresAt()) + .apply(); + } + } + } + + @NonNull + @Override + public List loadForRequest(HttpUrl url) { + + String value = prefs.getString("session_value", null); + long expiry = prefs.getLong("session_expiry", -1); + + if (value == null || expiry == -1) { + return new ArrayList<>(); + } + + Cookie cookie = new Cookie.Builder() + .name("openimis_session") + .value(value) + .domain(url.host()) + .path("/") + .expiresAt(expiry) + .build(); + + List cookies = new ArrayList<>(); + cookies.add(cookie); + return cookies; + } + + public void clear() { + prefs.edit().clear().apply(); + } +} \ No newline at end of file diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/GlobalSessionTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/GlobalSessionTest.java new file mode 100644 index 00000000..c353d23d --- /dev/null +++ b/claimManagement/src/test/java/org/openimis/imisclaims/GlobalSessionTest.java @@ -0,0 +1,65 @@ +package org.openimis.imisclaims; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.SharedPreferences; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.openimis.imisclaims.network.util.PersistentCookieJar; + +@RunWith(MockitoJUnitRunner.class) +public class GlobalSessionTest { + + @Mock + private Token token; + @Mock + private SharedPreferences preferences; + @Mock + private PersistentCookieJar cookieJar; + + private Global global; + + @Before + public void setUp() { + global = spy(new Global()); + doReturn(token).when(global).getJWTToken(); + doReturn(preferences).when(global).getDefaultSharedPreferences(); + global.setCookieJar(cookieJar); + } + + @Test + public void isLoggedIn_trueOnlyWhenJwtValidAndSessionNotExpired_otherwiseClearsState() { + when(token.isTokenValidJWT()).thenReturn(true); + when(preferences.getLong("session_expiry", 0)).thenReturn(System.currentTimeMillis() + 5_000); + assertTrue(global.isLoggedIn()); + verify(token, never()).clearToken(); + + when(token.isTokenValidJWT()).thenReturn(false); + when(preferences.getLong("session_expiry", 0)).thenReturn(System.currentTimeMillis() + 5_000); + assertFalse(global.isLoggedIn()); + verify(token).clearToken(); + verify(cookieJar).clear(); + + when(token.isTokenValidJWT()).thenReturn(true); + when(preferences.getLong("session_expiry", 0)).thenReturn(System.currentTimeMillis() - 5_000); + assertFalse(global.isLoggedIn()); + } + + @Test + public void cookieJar_reference_setAndGet_roundTrip() { + PersistentCookieJar anotherJar = org.mockito.Mockito.mock(PersistentCookieJar.class); + global.setCookieJar(anotherJar); + assertSame(anotherJar, global.getCookieJar()); + } +} diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/network/okhttp/AuthorizationInterceptorTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/network/okhttp/AuthorizationInterceptorTest.java new file mode 100644 index 00000000..9d2a5059 --- /dev/null +++ b/claimManagement/src/test/java/org/openimis/imisclaims/network/okhttp/AuthorizationInterceptorTest.java @@ -0,0 +1,239 @@ +package org.openimis.imisclaims.network.okhttp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.openimis.imisclaims.Global; +import org.openimis.imisclaims.Token; +import org.openimis.imisclaims.network.util.PersistentCookieJar; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import okhttp3.Call; +import okhttp3.Connection; +import okhttp3.Interceptor; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.Timeout; + +public class AuthorizationInterceptorTest { + + private Global global; + private Token token; + private PersistentCookieJar cookieJar; + private AuthorizationInterceptor interceptor; + + @Before + public void setUp() { + global = mock(Global.class); + token = mock(Token.class); + cookieJar = mock(PersistentCookieJar.class); + when(global.getJWTToken()).thenReturn(token); + when(global.getCookieJar()).thenReturn(cookieJar); + interceptor = new AuthorizationInterceptor(global); + } + + @Test + public void intercept_addsExpectedHeaders_basedOnAuthAndCsrfState() throws Exception { + when(token.isTokenValidJWT()).thenReturn(true); + when(token.getTokenText()).thenReturn(" jwt_token "); + + when(global.getCsrfToken()).thenReturn("csrf-1"); + Request request1 = new Request.Builder().url("https://demo.openimis.org/api/graphql").build(); + FakeChain chain1 = new FakeChain(request1, responseFor(request1, 200, "ok")); + interceptor.intercept(chain1); + Request sent1 = chain1.proceededRequests.get(0); + assertEquals("bearer jwt_token", sent1.header("Authorization")); + assertEquals("mobile_app", sent1.header("User-Agent")); + assertEquals("csrf-1", sent1.header("X-CSRFToken")); + + when(global.getCsrfToken()).thenReturn(" "); + Request request2 = new Request.Builder().url("https://demo.openimis.org/api/graphql").build(); + FakeChain chain2 = new FakeChain(request2, responseFor(request2, 200, "ok")); + interceptor.intercept(chain2); + Request sent2 = chain2.proceededRequests.get(0); + assertNull(sent2.header("X-CSRFToken")); + + when(token.isTokenValidJWT()).thenReturn(false); + Request request3 = new Request.Builder().url("https://demo.openimis.org/api/graphql").build(); + FakeChain chain3 = new FakeChain(request3, responseFor(request3, 200, "ok")); + interceptor.intercept(chain3); + Request sent3 = chain3.proceededRequests.get(0); + assertNull(sent3.header("Authorization")); + assertNull(sent3.header("X-CSRFToken")); + } + + @Test + public void intercept_clearsSessionAndRetries_onUnauthorizedSignals() throws Exception { + when(token.isTokenValidJWT()).thenReturn(true); + when(token.getTokenText()).thenReturn("jwt"); + when(global.getCsrfToken()).thenReturn("csrf"); + + Request request1 = new Request.Builder().url("https://demo.openimis.org/api/graphql").build(); + FakeChain chain401 = new FakeChain( + request1, + responseFor(request1, 401, "unauthorized"), + responseFor(request1, 200, "retried") + ); + Response retried401 = interceptor.intercept(chain401); + assertEquals(2, chain401.proceededRequests.size()); + assertEquals(200, retried401.code()); + + Request request2 = new Request.Builder().url("https://demo.openimis.org/api/graphql").build(); + FakeChain chainCsrf = new FakeChain( + request2, + responseFor(request2, 200, "Missing 'csrftoken'"), + responseFor(request2, 200, "retried") + ); + Response retriedCsrf = interceptor.intercept(chainCsrf); + assertEquals(2, chainCsrf.proceededRequests.size()); + assertEquals(200, retriedCsrf.code()); + + verify(token, times(2)).clearToken(); + verify(cookieJar, times(2)).clear(); + } + + @Test + public void intercept_noRetry_whenResponseIsAuthorizedAndNoCsrfMarker() throws Exception { + when(token.isTokenValidJWT()).thenReturn(true); + when(token.getTokenText()).thenReturn("jwt"); + when(global.getCsrfToken()).thenReturn("csrf"); + + Request request = new Request.Builder().url("https://demo.openimis.org/api/graphql").build(); + FakeChain chain = new FakeChain(request, responseFor(request, 200, "all good")); + + Response response = interceptor.intercept(chain); + + assertEquals(1, chain.proceededRequests.size()); + assertEquals(200, response.code()); + assertTrue(response.body().string().contains("all good")); + } + + private static Response responseFor(Request request, int code, String body) { + return new Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(code) + .message("msg") + .body(ResponseBody.create(body, okhttp3.MediaType.parse("application/json"))) + .build(); + } + + private static class FakeChain implements Interceptor.Chain { + private final Request originalRequest; + private final List queuedResponses; + private int responseIndex = 0; + private final List proceededRequests = new ArrayList<>(); + + FakeChain(Request originalRequest, Response... responses) { + this.originalRequest = originalRequest; + this.queuedResponses = Arrays.asList(responses); + } + + @Override + public Request request() { + return originalRequest; + } + + @Override + public Response proceed(Request request) throws IOException { + proceededRequests.add(request); + if (responseIndex >= queuedResponses.size()) { + throw new IOException("No queued response"); + } + return queuedResponses.get(responseIndex++); + } + + @Override + public Connection connection() { + return null; + } + + @Override + public Call call() { + return new Call() { + @Override + public Request request() { + return originalRequest; + } + + @Override + public Response execute() { + throw new UnsupportedOperationException(); + } + + @Override + public void enqueue(okhttp3.Callback responseCallback) { + throw new UnsupportedOperationException(); + } + + @Override + public void cancel() { + } + + @Override + public boolean isExecuted() { + return false; + } + + @Override + public boolean isCanceled() { + return false; + } + + @Override + public Timeout timeout() { + return Timeout.NONE; + } + + @Override + public Call clone() { + return this; + } + }; + } + + @Override + public int connectTimeoutMillis() { + return 0; + } + + @Override + public Interceptor.Chain withConnectTimeout(int timeout, TimeUnit unit) { + return this; + } + + @Override + public int readTimeoutMillis() { + return 0; + } + + @Override + public Interceptor.Chain withReadTimeout(int timeout, TimeUnit unit) { + return this; + } + + @Override + public int writeTimeoutMillis() { + return 0; + } + + @Override + public Interceptor.Chain withWriteTimeout(int timeout, TimeUnit unit) { + return this; + } + } +} diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/network/request/GetHealthfacilityGraphQLRequestTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/network/request/GetHealthfacilityGraphQLRequestTest.java new file mode 100644 index 00000000..2840d500 --- /dev/null +++ b/claimManagement/src/test/java/org/openimis/imisclaims/network/request/GetHealthfacilityGraphQLRequestTest.java @@ -0,0 +1,107 @@ +package org.openimis.imisclaims.network.request; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.apollographql.apollo.api.Input; +import com.apollographql.apollo.api.Operation; +import com.apollographql.apollo.api.Query; +import com.apollographql.apollo.api.Response; + +import org.junit.BeforeClass; +import org.junit.AfterClass; +import org.junit.Test; +import org.openimis.imisclaims.Global; +import org.openimis.imisclaims.GetHealthFacilityQuery; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import android.content.SharedPreferences; + +public class GetHealthfacilityGraphQLRequestTest { + private static Object previousGlobalInstance; + + @BeforeClass + public static void setUpGlobalForStaticClientInit() throws Exception { + Field instanceField = Global.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + previousGlobalInstance = instanceField.get(null); + + Global global = mock(Global.class); + SharedPreferences prefs = mock(SharedPreferences.class); + when(global.getDefaultSharedPreferences()).thenReturn(prefs); + instanceField.set(null, global); + } + + @AfterClass + public static void restoreGlobalInstance() throws Exception { + Field instanceField = Global.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + instanceField.set(null, previousGlobalInstance); + } + + @Test + public void get_returnsEmpty_forNullGraphqlShapes_andEdgesWhenPresent() throws Exception { + TestableGetHealthfacilityGraphQLRequest request = new TestableGetHealthfacilityGraphQLRequest(); + + request.response = responseFor(new GetHealthFacilityQuery(Input.fromNullable("HF1")), null); + assertTrue(request.get("HF1").isEmpty()); + + request.response = responseFor( + new GetHealthFacilityQuery(Input.fromNullable("HF1")), + new GetHealthFacilityQuery.Data(null) + ); + assertTrue(request.get("HF1").isEmpty()); + + request.response = responseFor( + new GetHealthFacilityQuery(Input.fromNullable("HF1")), + new GetHealthFacilityQuery.Data(new NullEdgesHealthFacilities()) + ); + assertTrue(request.get("HF1").isEmpty()); + + GetHealthFacilityQuery.Node node = new GetHealthFacilityQuery.Node("HealthFacilityGQLType", "1", "HF1", "HF Name"); + GetHealthFacilityQuery.Edge edge = new GetHealthFacilityQuery.Edge("HealthFacilityGQLEdge", node); + GetHealthFacilityQuery.HealthFacilities facilities = + new GetHealthFacilityQuery.HealthFacilities("HealthFacilityGQLConnection", Collections.singletonList(edge)); + request.response = responseFor( + new GetHealthFacilityQuery(Input.fromNullable("HF1")), + new GetHealthFacilityQuery.Data(facilities) + ); + + List result = request.get("HF1"); + assertEquals(1, result.size()); + assertEquals("HF1", result.get(0).node().code()); + } + + private static Response responseFor( + Query operation, + GetHealthFacilityQuery.Data data + ) { + return Response.builder(operation) + .data(data) + .build(); + } + + private static class TestableGetHealthfacilityGraphQLRequest extends GetHealthfacilityGraphQLRequest { + Response response; + + @Override + protected Response makeSynchronous(Query query) { + return (Response) response; + } + } + + private static class NullEdgesHealthFacilities extends GetHealthFacilityQuery.HealthFacilities { + NullEdgesHealthFacilities() { + super("HealthFacilityGQLConnection", Collections.emptyList()); + } + + @Override + public List edges() { + return null; + } + } +} diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/network/util/PersistentCookieJarTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/network/util/PersistentCookieJarTest.java new file mode 100644 index 00000000..9e95eb3a --- /dev/null +++ b/claimManagement/src/test/java/org/openimis/imisclaims/network/util/PersistentCookieJarTest.java @@ -0,0 +1,78 @@ +package org.openimis.imisclaims.network.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import okhttp3.Cookie; +import okhttp3.HttpUrl; + +@RunWith(RobolectricTestRunner.class) +public class PersistentCookieJarTest { + + private SharedPreferences prefs; + private PersistentCookieJar cookieJar; + private HttpUrl url; + + @Before + public void setUp() { + Context context = ApplicationProvider.getApplicationContext(); + prefs = context.getSharedPreferences("cookie_jar_test", Context.MODE_PRIVATE); + prefs.edit().clear().commit(); + cookieJar = new PersistentCookieJar(prefs); + url = HttpUrl.get("https://demo.openimis.org/api/graphql"); + } + + @Test + public void sessionCookie_lifecycle_saveLoadClear_behavesCorrectly() { + long expiry = System.currentTimeMillis() + 60_000; + Cookie sessionCookie = new Cookie.Builder() + .name("openimis_session") + .value("abc123") + .domain("demo.openimis.org") + .path("/") + .expiresAt(expiry) + .build(); + + cookieJar.saveFromResponse(url, Collections.singletonList(sessionCookie)); + List loaded = cookieJar.loadForRequest(url); + assertEquals(1, loaded.size()); + assertEquals("openimis_session", loaded.get(0).name()); + assertEquals("abc123", loaded.get(0).value()); + assertEquals(expiry, prefs.getLong("session_expiry", -1)); + + cookieJar.clear(); + assertTrue(cookieJar.loadForRequest(url).isEmpty()); + assertEquals(-1, prefs.getLong("session_expiry", -1)); + } + + @Test + public void saveFromResponse_ignoresNonSessionCookies_andMissingStateReturnsEmpty() { + Cookie otherCookie = new Cookie.Builder() + .name("another_cookie") + .value("zzz") + .domain("demo.openimis.org") + .path("/") + .expiresAt(System.currentTimeMillis() + 60_000) + .build(); + + cookieJar.saveFromResponse(url, Arrays.asList(otherCookie)); + assertTrue(cookieJar.loadForRequest(url).isEmpty()); + + prefs.edit().putString("session_value", "only_value_no_expiry").remove("session_expiry").commit(); + assertTrue(cookieJar.loadForRequest(url).isEmpty()); + } +} diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/usecase/CheckHealthFacilityTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/usecase/CheckHealthFacilityTest.java new file mode 100644 index 00000000..f54a2156 --- /dev/null +++ b/claimManagement/src/test/java/org/openimis/imisclaims/usecase/CheckHealthFacilityTest.java @@ -0,0 +1,37 @@ +package org.openimis.imisclaims.usecase; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.openimis.imisclaims.GetHealthFacilityQuery; +import org.openimis.imisclaims.network.request.GetHealthfacilityGraphQLRequest; + +import java.util.Collections; +import java.util.List; + +public class CheckHealthFacilityTest { + + @Test + public void execute_mapsEmptyVsNonEmptyToBoolean() throws Exception { + FakeGetHealthfacilityGraphQLRequest request = new FakeGetHealthfacilityGraphQLRequest(); + CheckHealthFacility checkHealthFacility = new CheckHealthFacility(request); + + request.nextResult = Collections.emptyList(); + assertFalse(checkHealthFacility.execute("HF1")); + + GetHealthFacilityQuery.Node node = new GetHealthFacilityQuery.Node("HealthFacilityGQLType", "1", "HF1", "HF Name"); + GetHealthFacilityQuery.Edge edge = new GetHealthFacilityQuery.Edge("HealthFacilityGQLEdge", node); + request.nextResult = Collections.singletonList(edge); + assertTrue(checkHealthFacility.execute("HF1")); + } + + private static class FakeGetHealthfacilityGraphQLRequest extends GetHealthfacilityGraphQLRequest { + private List nextResult = Collections.emptyList(); + + @Override + public List get(String hfCode) { + return nextResult; + } + } +} diff --git a/gradle.properties b/gradle.properties index dbb7bf70..58db0bf8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,7 @@ android.enableJetifier=true android.useAndroidX=true +org.gradle.jvmargs=-Xms512m -Xmx2048m -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.workers.max=2 +org.gradle.parallel=false +org.gradle.caching=true +org.gradle.daemon=true