Skip to content

Commit c00df30

Browse files
committed
test: add unit coverage for Django session cookie/auth flows
1 parent 237fd9f commit c00df30

5 files changed

Lines changed: 526 additions & 0 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.openimis.imisclaims;
2+
3+
import static org.junit.Assert.assertFalse;
4+
import static org.junit.Assert.assertSame;
5+
import static org.junit.Assert.assertTrue;
6+
import static org.mockito.Mockito.doReturn;
7+
import static org.mockito.Mockito.never;
8+
import static org.mockito.Mockito.spy;
9+
import static org.mockito.Mockito.verify;
10+
import static org.mockito.Mockito.when;
11+
12+
import android.content.SharedPreferences;
13+
14+
import org.junit.Before;
15+
import org.junit.Test;
16+
import org.junit.runner.RunWith;
17+
import org.mockito.Mock;
18+
import org.mockito.junit.MockitoJUnitRunner;
19+
import org.openimis.imisclaims.network.util.PersistentCookieJar;
20+
21+
@RunWith(MockitoJUnitRunner.class)
22+
public class GlobalSessionTest {
23+
24+
@Mock
25+
private Token token;
26+
@Mock
27+
private SharedPreferences preferences;
28+
@Mock
29+
private PersistentCookieJar cookieJar;
30+
31+
private Global global;
32+
33+
@Before
34+
public void setUp() {
35+
global = spy(new Global());
36+
doReturn(token).when(global).getJWTToken();
37+
doReturn(preferences).when(global).getDefaultSharedPreferences();
38+
global.setCookieJar(cookieJar);
39+
}
40+
41+
@Test
42+
public void isLoggedIn_trueOnlyWhenJwtValidAndSessionNotExpired_otherwiseClearsState() {
43+
when(token.isTokenValidJWT()).thenReturn(true);
44+
when(preferences.getLong("session_expiry", 0)).thenReturn(System.currentTimeMillis() + 5_000);
45+
assertTrue(global.isLoggedIn());
46+
verify(token, never()).clearToken();
47+
48+
when(token.isTokenValidJWT()).thenReturn(false);
49+
when(preferences.getLong("session_expiry", 0)).thenReturn(System.currentTimeMillis() + 5_000);
50+
assertFalse(global.isLoggedIn());
51+
verify(token).clearToken();
52+
verify(cookieJar).clear();
53+
54+
when(token.isTokenValidJWT()).thenReturn(true);
55+
when(preferences.getLong("session_expiry", 0)).thenReturn(System.currentTimeMillis() - 5_000);
56+
assertFalse(global.isLoggedIn());
57+
}
58+
59+
@Test
60+
public void cookieJar_reference_setAndGet_roundTrip() {
61+
PersistentCookieJar anotherJar = org.mockito.Mockito.mock(PersistentCookieJar.class);
62+
global.setCookieJar(anotherJar);
63+
assertSame(anotherJar, global.getCookieJar());
64+
}
65+
}
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
package org.openimis.imisclaims.network.okhttp;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNull;
5+
import static org.junit.Assert.assertTrue;
6+
import static org.mockito.Mockito.mock;
7+
import static org.mockito.Mockito.times;
8+
import static org.mockito.Mockito.verify;
9+
import static org.mockito.Mockito.when;
10+
11+
import org.junit.Before;
12+
import org.junit.Test;
13+
import org.openimis.imisclaims.Global;
14+
import org.openimis.imisclaims.Token;
15+
import org.openimis.imisclaims.network.util.PersistentCookieJar;
16+
17+
import java.io.IOException;
18+
import java.util.ArrayList;
19+
import java.util.Arrays;
20+
import java.util.List;
21+
import java.util.concurrent.TimeUnit;
22+
23+
import okhttp3.Call;
24+
import okhttp3.Connection;
25+
import okhttp3.Interceptor;
26+
import okhttp3.Protocol;
27+
import okhttp3.Request;
28+
import okhttp3.Response;
29+
import okhttp3.ResponseBody;
30+
import okio.Timeout;
31+
32+
public class AuthorizationInterceptorTest {
33+
34+
private Global global;
35+
private Token token;
36+
private PersistentCookieJar cookieJar;
37+
private AuthorizationInterceptor interceptor;
38+
39+
@Before
40+
public void setUp() {
41+
global = mock(Global.class);
42+
token = mock(Token.class);
43+
cookieJar = mock(PersistentCookieJar.class);
44+
when(global.getJWTToken()).thenReturn(token);
45+
when(global.getCookieJar()).thenReturn(cookieJar);
46+
interceptor = new AuthorizationInterceptor(global);
47+
}
48+
49+
@Test
50+
public void intercept_addsExpectedHeaders_basedOnAuthAndCsrfState() throws Exception {
51+
when(token.isTokenValidJWT()).thenReturn(true);
52+
when(token.getTokenText()).thenReturn(" jwt_token ");
53+
54+
when(global.getCsrfToken()).thenReturn("csrf-1");
55+
Request request1 = new Request.Builder().url("https://demo.openimis.org/api/graphql").build();
56+
FakeChain chain1 = new FakeChain(request1, responseFor(request1, 200, "ok"));
57+
interceptor.intercept(chain1);
58+
Request sent1 = chain1.proceededRequests.get(0);
59+
assertEquals("bearer jwt_token", sent1.header("Authorization"));
60+
assertEquals("mobile_app", sent1.header("User-Agent"));
61+
assertEquals("csrf-1", sent1.header("X-CSRFToken"));
62+
63+
when(global.getCsrfToken()).thenReturn(" ");
64+
Request request2 = new Request.Builder().url("https://demo.openimis.org/api/graphql").build();
65+
FakeChain chain2 = new FakeChain(request2, responseFor(request2, 200, "ok"));
66+
interceptor.intercept(chain2);
67+
Request sent2 = chain2.proceededRequests.get(0);
68+
assertNull(sent2.header("X-CSRFToken"));
69+
70+
when(token.isTokenValidJWT()).thenReturn(false);
71+
Request request3 = new Request.Builder().url("https://demo.openimis.org/api/graphql").build();
72+
FakeChain chain3 = new FakeChain(request3, responseFor(request3, 200, "ok"));
73+
interceptor.intercept(chain3);
74+
Request sent3 = chain3.proceededRequests.get(0);
75+
assertNull(sent3.header("Authorization"));
76+
assertNull(sent3.header("X-CSRFToken"));
77+
}
78+
79+
@Test
80+
public void intercept_clearsSessionAndRetries_onUnauthorizedSignals() throws Exception {
81+
when(token.isTokenValidJWT()).thenReturn(true);
82+
when(token.getTokenText()).thenReturn("jwt");
83+
when(global.getCsrfToken()).thenReturn("csrf");
84+
85+
Request request1 = new Request.Builder().url("https://demo.openimis.org/api/graphql").build();
86+
FakeChain chain401 = new FakeChain(
87+
request1,
88+
responseFor(request1, 401, "unauthorized"),
89+
responseFor(request1, 200, "retried")
90+
);
91+
Response retried401 = interceptor.intercept(chain401);
92+
assertEquals(2, chain401.proceededRequests.size());
93+
assertEquals(200, retried401.code());
94+
95+
Request request2 = new Request.Builder().url("https://demo.openimis.org/api/graphql").build();
96+
FakeChain chainCsrf = new FakeChain(
97+
request2,
98+
responseFor(request2, 200, "Missing 'csrftoken'"),
99+
responseFor(request2, 200, "retried")
100+
);
101+
Response retriedCsrf = interceptor.intercept(chainCsrf);
102+
assertEquals(2, chainCsrf.proceededRequests.size());
103+
assertEquals(200, retriedCsrf.code());
104+
105+
verify(token, times(2)).clearToken();
106+
verify(cookieJar, times(2)).clear();
107+
}
108+
109+
@Test
110+
public void intercept_noRetry_whenResponseIsAuthorizedAndNoCsrfMarker() throws Exception {
111+
when(token.isTokenValidJWT()).thenReturn(true);
112+
when(token.getTokenText()).thenReturn("jwt");
113+
when(global.getCsrfToken()).thenReturn("csrf");
114+
115+
Request request = new Request.Builder().url("https://demo.openimis.org/api/graphql").build();
116+
FakeChain chain = new FakeChain(request, responseFor(request, 200, "all good"));
117+
118+
Response response = interceptor.intercept(chain);
119+
120+
assertEquals(1, chain.proceededRequests.size());
121+
assertEquals(200, response.code());
122+
assertTrue(response.body().string().contains("all good"));
123+
}
124+
125+
private static Response responseFor(Request request, int code, String body) {
126+
return new Response.Builder()
127+
.request(request)
128+
.protocol(Protocol.HTTP_1_1)
129+
.code(code)
130+
.message("msg")
131+
.body(ResponseBody.create(body, okhttp3.MediaType.parse("application/json")))
132+
.build();
133+
}
134+
135+
private static class FakeChain implements Interceptor.Chain {
136+
private final Request originalRequest;
137+
private final List<Response> queuedResponses;
138+
private int responseIndex = 0;
139+
private final List<Request> proceededRequests = new ArrayList<>();
140+
141+
FakeChain(Request originalRequest, Response... responses) {
142+
this.originalRequest = originalRequest;
143+
this.queuedResponses = Arrays.asList(responses);
144+
}
145+
146+
@Override
147+
public Request request() {
148+
return originalRequest;
149+
}
150+
151+
@Override
152+
public Response proceed(Request request) throws IOException {
153+
proceededRequests.add(request);
154+
if (responseIndex >= queuedResponses.size()) {
155+
throw new IOException("No queued response");
156+
}
157+
return queuedResponses.get(responseIndex++);
158+
}
159+
160+
@Override
161+
public Connection connection() {
162+
return null;
163+
}
164+
165+
@Override
166+
public Call call() {
167+
return new Call() {
168+
@Override
169+
public Request request() {
170+
return originalRequest;
171+
}
172+
173+
@Override
174+
public Response execute() {
175+
throw new UnsupportedOperationException();
176+
}
177+
178+
@Override
179+
public void enqueue(okhttp3.Callback responseCallback) {
180+
throw new UnsupportedOperationException();
181+
}
182+
183+
@Override
184+
public void cancel() {
185+
}
186+
187+
@Override
188+
public boolean isExecuted() {
189+
return false;
190+
}
191+
192+
@Override
193+
public boolean isCanceled() {
194+
return false;
195+
}
196+
197+
@Override
198+
public Timeout timeout() {
199+
return Timeout.NONE;
200+
}
201+
202+
@Override
203+
public Call clone() {
204+
return this;
205+
}
206+
};
207+
}
208+
209+
@Override
210+
public int connectTimeoutMillis() {
211+
return 0;
212+
}
213+
214+
@Override
215+
public Interceptor.Chain withConnectTimeout(int timeout, TimeUnit unit) {
216+
return this;
217+
}
218+
219+
@Override
220+
public int readTimeoutMillis() {
221+
return 0;
222+
}
223+
224+
@Override
225+
public Interceptor.Chain withReadTimeout(int timeout, TimeUnit unit) {
226+
return this;
227+
}
228+
229+
@Override
230+
public int writeTimeoutMillis() {
231+
return 0;
232+
}
233+
234+
@Override
235+
public Interceptor.Chain withWriteTimeout(int timeout, TimeUnit unit) {
236+
return this;
237+
}
238+
}
239+
}

0 commit comments

Comments
 (0)